1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_AURAX11_H_
6 #define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_AURAX11_H_
12 #include "base/compiler_specific.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/timer/timer.h"
16 #include "ui/aura/window_observer.h"
17 #include "ui/base/cursor/cursor.h"
18 #include "ui/base/dragdrop/drag_drop_types.h"
19 #include "ui/gfx/geometry/point.h"
20 #include "ui/gfx/x/x11_atom_cache.h"
21 #include "ui/views/views_export.h"
22 #include "ui/views/widget/desktop_aura/x11_move_loop_delegate.h"
23 #include "ui/wm/public/drag_drop_client.h"
27 class DragDropDelegate
;
38 class DropTargetEvent
;
40 class OSExchangeDataProviderAuraX11
;
41 class SelectionFormatMap
;
45 class DesktopNativeCursorManager
;
49 // Implements drag and drop on X11 for aura. On one side, this class takes raw
50 // X11 events forwarded from DesktopWindowTreeHostLinux, while on the other, it
51 // handles the views drag events.
52 class VIEWS_EXPORT DesktopDragDropClientAuraX11
53 : public aura::client::DragDropClient
,
54 public aura::WindowObserver
,
55 public X11MoveLoopDelegate
{
57 DesktopDragDropClientAuraX11(
58 aura::Window
* root_window
,
59 views::DesktopNativeCursorManager
* cursor_manager
,
62 ~DesktopDragDropClientAuraX11() override
;
64 // We maintain a mapping of live DesktopDragDropClientAuraX11 objects to
65 // their ::Windows. We do this so that we're able to short circuit sending
66 // X11 messages to windows in our process.
67 static DesktopDragDropClientAuraX11
* GetForWindow(::Window window
);
71 // These methods handle the various X11 client messages from the platform.
72 void OnXdndEnter(const XClientMessageEvent
& event
);
73 void OnXdndLeave(const XClientMessageEvent
& event
);
74 void OnXdndPosition(const XClientMessageEvent
& event
);
75 void OnXdndStatus(const XClientMessageEvent
& event
);
76 void OnXdndFinished(const XClientMessageEvent
& event
);
77 void OnXdndDrop(const XClientMessageEvent
& event
);
79 // Called when XSelection data has been copied to our process.
80 void OnSelectionNotify(const XSelectionEvent
& xselection
);
82 // Overridden from aura::client::DragDropClient:
83 int StartDragAndDrop(const ui::OSExchangeData
& data
,
84 aura::Window
* root_window
,
85 aura::Window
* source_window
,
86 const gfx::Point
& root_location
,
88 ui::DragDropTypes::DragEventSource source
) override
;
89 void DragUpdate(aura::Window
* target
, const ui::LocatedEvent
& event
) override
;
90 void Drop(aura::Window
* target
, const ui::LocatedEvent
& event
) override
;
91 void DragCancel() override
;
92 bool IsDragDropInProgress() override
;
94 // Overridden from aura::WindowObserver:
95 void OnWindowDestroyed(aura::Window
* window
) override
;
97 // Overridden from X11WholeScreenMoveLoopDelegate:
98 void OnMouseMovement(const gfx::Point
& screen_point
,
100 base::TimeDelta event_time
) override
;
101 void OnMouseReleased() override
;
102 void OnMoveLoopEnded() override
;
105 // The following methods are virtual for the sake of testing.
107 // Creates a move loop.
108 virtual scoped_ptr
<X11MoveLoop
> CreateMoveLoop(
109 X11MoveLoopDelegate
* delegate
);
111 // Finds the topmost X11 window at |screen_point| and returns it if it is
112 // Xdnd aware. Returns NULL otherwise.
113 virtual ::Window
FindWindowFor(const gfx::Point
& screen_point
);
115 // Sends |xev| to |xid|, optionally short circuiting the round trip to the X
117 virtual void SendXClientEvent(::Window xid
, XEvent
* xev
);
121 // |source_current_window_| will receive a drop once we receive an
122 // XdndStatus from it.
123 SOURCE_STATE_PENDING_DROP
,
125 // The move looped will be ended once we receive XdndFinished from
126 // |source_current_window_|. We should not send XdndPosition to
127 // |source_current_window_| while in this state.
128 SOURCE_STATE_DROPPED
,
130 // There is no drag in progress or there is a drag in progress and the
131 // user has not yet released the mouse.
135 // Processes a mouse move at |screen_point|.
136 void ProcessMouseMove(const gfx::Point
& screen_point
,
137 unsigned long event_time
);
139 // Start timer to end the move loop if the target is too slow to respond after
140 // the mouse is released.
141 void StartEndMoveLoopTimer();
143 // Ends the move loop.
146 // When we receive an position x11 message, we need to translate that into
147 // the underlying aura::Window representation, as moves internal to the X11
148 // window can cause internal drag leave and enter messages.
149 void DragTranslate(const gfx::Point
& root_window_location
,
150 scoped_ptr
<ui::OSExchangeData
>* data
,
151 scoped_ptr
<ui::DropTargetEvent
>* event
,
152 aura::client::DragDropDelegate
** delegate
);
154 // Called when we need to notify the current aura::Window that we're no
155 // longer dragging over it.
156 void NotifyDragLeave();
158 // Converts our bitfield of actions into an Atom that represents what action
159 // we're most likely to take on drop.
160 ::Atom
DragOperationToAtom(int drag_operation
);
162 // Converts a single action atom to a drag operation.
163 ui::DragDropTypes::DragOperation
AtomToDragOperation(::Atom atom
);
165 // During the blocking StartDragAndDrop() call, this converts the views-style
166 // |drag_operation_| bitfield into a vector of Atoms to offer to other
168 std::vector
< ::Atom
> GetOfferedDragOperations();
170 // This returns a representation of the data we're offering in this
171 // drag. This is done to bypass an asynchronous roundtrip with the X11
173 ui::SelectionFormatMap
GetFormatMap() const;
175 // Returns the modifier state for the most recent mouse move. This is done to
176 // bypass an asynchronous roundtrip with the X11 server.
177 int current_modifier_state() const {
178 return current_modifier_state_
;
181 // Handling XdndPosition can be paused while waiting for more data; this is
182 // called either synchronously from OnXdndPosition, or asynchronously after
183 // we've received data requested from the other window.
184 void CompleteXdndPosition(::Window source_window
,
185 const gfx::Point
& screen_point
);
187 void SendXdndEnter(::Window dest_window
);
188 void SendXdndLeave(::Window dest_window
);
189 void SendXdndPosition(::Window dest_window
,
190 const gfx::Point
& screen_point
,
191 unsigned long event_time
);
192 void SendXdndDrop(::Window dest_window
);
194 // Creates a widget for the user to drag around.
195 void CreateDragWidget(const gfx::ImageSkia
& image
);
197 // Returns true if |image| has any visible regions (defined as having a pixel
199 bool IsValidDragImage(const gfx::ImageSkia
& image
);
201 // A nested message loop that notifies this object of events through the
202 // X11MoveLoopDelegate interface.
203 scoped_ptr
<X11MoveLoop
> move_loop_
;
205 aura::Window
* root_window_
;
207 DesktopNativeCursorManager
* cursor_manager_
;
212 ui::X11AtomCache atom_cache_
;
214 // Target side information.
215 class X11DragContext
;
216 scoped_ptr
<X11DragContext
> target_current_context_
;
218 // The modifier state for the most recent mouse move.
219 int current_modifier_state_
;
221 // The Aura window that is currently under the cursor. We need to manually
222 // keep track of this because Windows will only call our drag enter method
223 // once when the user enters the associated X Window. But inside that X
224 // Window there could be multiple aura windows, so we need to generate drag
225 // enter events for them.
226 aura::Window
* target_window_
;
228 // Because Xdnd messages don't contain the position in messages other than
229 // the XdndPosition message, we must manually keep track of the last position
231 gfx::Point target_window_location_
;
232 gfx::Point target_window_root_location_
;
234 // In the Xdnd protocol, we aren't supposed to send another XdndPosition
235 // message until we have received a confirming XdndStatus message.
236 bool waiting_on_status_
;
238 // If we would send an XdndPosition message while we're waiting for an
239 // XdndStatus response, we need to cache the latest details we'd send.
240 scoped_ptr
<std::pair
<gfx::Point
, unsigned long> > next_position_message_
;
242 // Reprocesses the most recent mouse move event if the mouse has not moved
243 // in a while in case the window stacking order has changed and
244 // |source_current_window_| needs to be updated.
245 base::OneShotTimer
<DesktopDragDropClientAuraX11
> repeat_mouse_move_timer_
;
247 // When the mouse is released, we need to wait for the last XdndStatus message
248 // only if we have previously received a status message from
249 // |source_current_window_|.
250 bool status_received_since_enter_
;
252 // Source side information.
253 ui::OSExchangeDataProviderAuraX11
const* source_provider_
;
254 ::Window source_current_window_
;
255 SourceState source_state_
;
257 // The current drag-drop client that has an active operation. Since we have
258 // multiple root windows and multiple DesktopDragDropClientAuraX11 instances
259 // it is important to maintain only one drag and drop operation at any time.
260 static DesktopDragDropClientAuraX11
* g_current_drag_drop_client
;
262 // The operation bitfield as requested by StartDragAndDrop.
265 // We offer the other window a list of possible operations,
266 // XdndActionsList. This is the requested action from the other window. This
267 // is DRAG_NONE if we haven't sent out an XdndPosition message yet, haven't
268 // yet received an XdndStatus or if the other window has told us that there's
269 // no action that we can agree on.
270 ui::DragDropTypes::DragOperation negotiated_operation_
;
272 // Ends the move loop if the target is too slow to respond after the mouse is
274 base::OneShotTimer
<DesktopDragDropClientAuraX11
> end_move_loop_timer_
;
276 // Widget that the user drags around. May be NULL.
277 scoped_ptr
<Widget
> drag_widget_
;
279 // The offset of |drag_widget_| relative to the mouse position.
280 gfx::Vector2d drag_widget_offset_
;
282 base::WeakPtrFactory
<DesktopDragDropClientAuraX11
> weak_ptr_factory_
;
284 DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11
);
289 #endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_AURAX11_H_