1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
|
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_BASE_X_X11_DRAG_DROP_CLIENT_H_
#define UI_BASE_X_X11_DRAG_DROP_CLIENT_H_
#include <vector>
#include "base/component_export.h"
#include "base/memory/raw_ptr.h"
#include "base/timer/timer.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
#include "ui/base/x/selection_utils.h"
#include "ui/base/x/x11_drag_context.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/x/xproto.h"
namespace ui {
class OSExchangeData;
class XOSExchangeDataProvider;
extern const char kXdndDirectSave0[];
// Converts the current set of X masks into the set of ui::EventFlags.
COMPONENT_EXPORT(UI_BASE_X) int XGetMaskAsEventFlags();
// Works for both incoming and outgoing drags. For the incoming drags, receives
// XDND events via HandleXdndEvent() and routes them to OnXdnd...() handlers;
// outgoing drags are handled via Handle...() methods. Both ways end up in
// SendXdnd...() calls that target the window that is currently at the drag
// location. If the target is another Chrome window, the event is delivered
// directly to its XdndHandler, bypassing the X server.
//
// The owner is notified about the ongoing drag through the Delegate interface.
class COMPONENT_EXPORT(UI_BASE_X) XDragDropClient {
public:
// Handlers and callbacks that should be implemented at the consumer side.
class Delegate {
public:
// Get the window being dragged. This window should be ignored when finding
// the topmost window.
virtual std::optional<gfx::AcceleratedWidget> GetDragWidget() = 0;
// Updates the drag status by the new position. Returns the drag operations
// possible at that position.
//
// Handling XdndPosition can be paused while waiting for more data; this is
// called either synchronously from OnXdndPosition, or asynchronously after
// we've received data requested from the other window.
virtual int UpdateDrag(const gfx::Point& screen_point) = 0;
// Updates the mouse cursor shape.
virtual void UpdateCursor(mojom::DragOperation negotiated_operation) = 0;
// Called when data from another application (not Chrome) enters the window.
virtual void OnBeginForeignDrag(x11::Window window) = 0;
// Called when data from another application (not Chrome) is about to leave
// the window.
virtual void OnEndForeignDrag() = 0;
// Called just before the drag leaves the window.
virtual void OnBeforeDragLeave() = 0;
// Drops data at the current location and returns the resulting operation.
virtual mojom::DragOperation PerformDrop() = 0;
// Called to end the drag loop that is maintained by the subclass.
virtual void EndDragLoop() = 0;
protected:
virtual ~Delegate() = default;
};
XDragDropClient(Delegate* delegate, x11::Window xwindow);
virtual ~XDragDropClient();
XDragDropClient(const XDragDropClient&) = delete;
XDragDropClient& operator=(const XDragDropClient&) = delete;
// We maintain a mapping of live XDragDropClient objects to their X11 windows,
// so that we'd able to short circuit sending X11 messages to windows in our
// process.
static XDragDropClient* GetForWindow(x11::Window window);
x11::Window xwindow() const { return xwindow_; }
XDragContext* target_current_context() {
return target_current_context_.get();
}
const XOSExchangeDataProvider* source_provider() const {
return source_provider_;
}
int current_modifier_state() const { return current_modifier_state_; }
// Handling XdndPosition can be paused while waiting for more data; this is
// called by XDragContext either synchronously or asynchronously, depending on
// whether the context has data requested from the other window.
void CompleteXdndPosition(x11::Window source_window,
const gfx::Point& screen_point);
void ProcessMouseMove(const gfx::Point& screen_point,
unsigned long event_time);
// During the blocking StartDragAndDrop() call, this converts the views-style
// |drag_operation_| bitfield into a vector of Atoms to offer to other
// processes.
std::vector<x11::Atom> GetOfferedDragOperations() const;
// Tries to handle the XDND event. Returns true for all known event types:
// XdndEnter, XdndLeave, XdndPosition, XdndStatus, XdndDrop, and XdndFinished;
// returns false if an event of an unexpected type has been passed.
bool HandleXdndEvent(const x11::ClientMessageEvent& event);
// These |Handle...| methods essentially implement the
// views::X11MoveLoopDelegate interface.
void HandleMouseMovement(const gfx::Point& screen_point,
int flags,
base::TimeTicks event_time);
void HandleMouseReleased();
void HandleMoveLoopEnded();
// Called when XSelection data has been copied to our process.
void OnSelectionNotify(const x11::SelectionNotifyEvent& xselection);
// Resets the drag state so the object is ready to handle the drag. Sets
// X window properties so that the desktop environment is aware of available
// actions. Sets |source_provider_| so properties of dragged data can be
// queried afterwards. Should be called before entering the main drag loop.
void InitDrag(int operation, const OSExchangeData* data);
// Cleans up the drag state after the drag is done. Removes the X window
// properties related to the drag operation. Should be called after exiting
// the main drag loop.
void CleanupDrag();
protected:
enum class SourceState {
// |source_current_window_| will receive a drop once we receive an
// XdndStatus from it.
kPendingDrop,
// The move looped will be ended once we receive XdndFinished from
// |source_current_window_|. We should not send XdndPosition to
// |source_current_window_| while in this state.
kDropped,
// There is no drag in progress or there is a drag in progress and the
// user has not yet released the mouse.
kOther,
};
mojom::DragOperation negotiated_operation() const {
return negotiated_operation_;
}
// Updates |current_modifier_state_| with the given set of flags.
void UpdateModifierState(int flags);
// Resets the drag context. Calls |OnEndForeignDrag()| if necessary.
void ResetDragContext();
void StopRepeatMouseMoveTimer();
// Start timer to end the move loop if the target is too slow to respond after
// the mouse is released.
void StartEndMoveLoopTimer();
void StopEndMoveLoopTimer();
private:
// These methods handle the various X11 client messages from the platform.
void OnXdndEnter(const x11::ClientMessageEvent& event);
void OnXdndPosition(const x11::ClientMessageEvent& event);
void OnXdndStatus(const x11::ClientMessageEvent& event);
void OnXdndLeave(const x11::ClientMessageEvent& event);
void OnXdndDrop(const x11::ClientMessageEvent& event);
void OnXdndFinished(const x11::ClientMessageEvent& event);
// Creates an XEvent and fills it in with values typical for XDND messages:
// the type of event is set to ClientMessage, the format is set to 32 (longs),
// and the zero member of data payload is set to |xwindow_|. All other data
// members are zeroed, as per XDND specification.
x11::ClientMessageEvent PrepareXdndClientMessage(const char* message,
x11::Window recipient) const;
// Finds the topmost X11 window at |screen_point| and returns it if it is
// Xdnd aware. Returns x11::None otherwise.
// Virtual for testing.
virtual x11::Window FindWindowFor(const gfx::Point& screen_point);
// Sends |xev| to |window|, optionally short circuiting the round trip to the
// X server. Virtual for testing.
virtual void SendXClientEvent(x11::Window window,
const x11::ClientMessageEvent& xev);
void SendXdndEnter(x11::Window dest_window,
const std::vector<x11::Atom>& targets);
void SendXdndPosition(x11::Window dest_window,
const gfx::Point& screen_point,
unsigned long event_time);
void SendXdndLeave(x11::Window dest_window);
void SendXdndDrop(x11::Window dest_window);
void EndMoveLoop();
const raw_ptr<Delegate> delegate_;
const x11::Window xwindow_;
// Target side information.
x11::Window target_current_window_ = x11::Window::None;
std::unique_ptr<XDragContext> target_current_context_;
// Source side information.
SourceState source_state_ = SourceState::kOther;
raw_ptr<const XOSExchangeDataProvider> source_provider_ = nullptr;
// The operation bitfield as requested by StartDragAndDrop.
int allowed_operations_ = 0;
// The modifier state for the most recent mouse move. Used to bypass an
// asynchronous roundtrip through the X11 server.
int current_modifier_state_ = 0;
// We offer the other window a list of possible operations, XdndActionsList.
// This is the requested action from the other window. This is
// DragOperation::kNone if we haven't sent out an XdndPosition message yet,
// haven't yet received an XdndStatus or if the other window has told us that
// there's no action that we can agree on.
mojom::DragOperation negotiated_operation_ = mojom::DragOperation::kNone;
// In the Xdnd protocol, we aren't supposed to send another XdndPosition
// message until we have received a confirming XdndStatus message.
bool waiting_on_status_ = false;
// If we would send an XdndPosition message while we're waiting for an
// XdndStatus response, we need to cache the latest details we'd send.
std::unique_ptr<std::pair<gfx::Point, unsigned long>> next_position_message_;
// Reprocesses the most recent mouse move event if the mouse has not moved
// in a while in case the window stacking order has changed and
// |source_current_window_| needs to be updated.
base::OneShotTimer repeat_mouse_move_timer_;
// Ends the move loop if the target is too slow to respond after the mouse is
// released.
base::OneShotTimer end_move_loop_timer_;
// When the mouse is released, we need to wait for the last XdndStatus message
// only if we have previously received a status message from
// |source_current_window_|.
bool status_received_since_enter_ = false;
};
} // namespace ui
#endif // UI_BASE_X_X11_DRAG_DROP_CLIENT_H_
|