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. 4 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_ 7 8 #include <set> 9 #include <vector> 10 #include <X11/Xlib.h> 11 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/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" 24 25 namespace aura { 26 namespace client { 27 class DragDropDelegate; 28 } 29 } 30 31 namespace gfx { 32 class ImageSkia; 33 class Point; 34 } 35 36 namespace ui { 37 class DragSource; 38 class DropTargetEvent; 39 class OSExchangeData; 40 class OSExchangeDataProviderAuraX11; 41 class SelectionFormatMap; 42 } 43 44 namespace views { 45 class DesktopNativeCursorManager; 46 class Widget; 47 class X11MoveLoop; 48 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 { 56 public: 57 DesktopDragDropClientAuraX11( 58 aura::Window* root_window, 59 views::DesktopNativeCursorManager* cursor_manager, 60 Display* xdisplay, 61 ::Window xwindow); 62 virtual ~DesktopDragDropClientAuraX11(); 63 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); 68 69 void Init(); 70 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); 78 79 // Called when XSelection data has been copied to our process. 80 void OnSelectionNotify(const XSelectionEvent& xselection); 81 82 // Overridden from aura::client::DragDropClient: 83 virtual int StartDragAndDrop( 84 const ui::OSExchangeData& data, 85 aura::Window* root_window, 86 aura::Window* source_window, 87 const gfx::Point& root_location, 88 int operation, 89 ui::DragDropTypes::DragEventSource source) OVERRIDE; 90 virtual void DragUpdate(aura::Window* target, 91 const ui::LocatedEvent& event) OVERRIDE; 92 virtual void Drop(aura::Window* target, 93 const ui::LocatedEvent& event) OVERRIDE; 94 virtual void DragCancel() OVERRIDE; 95 virtual bool IsDragDropInProgress() OVERRIDE; 96 97 // Overridden from aura::WindowObserver: 98 virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE; 99 100 // Overridden from X11WholeScreenMoveLoopDelegate: 101 virtual void OnMouseMovement(XMotionEvent* event) OVERRIDE; 102 virtual void OnMouseReleased() OVERRIDE; 103 virtual void OnMoveLoopEnded() OVERRIDE; 104 105 protected: 106 // The following methods are virtual for the sake of testing. 107 108 // Creates a move loop. 109 virtual scoped_ptr<X11MoveLoop> CreateMoveLoop( 110 X11MoveLoopDelegate* delegate); 111 112 // Finds the topmost X11 window at |screen_point| and returns it if it is 113 // Xdnd aware. Returns NULL otherwise. 114 virtual ::Window FindWindowFor(const gfx::Point& screen_point); 115 116 // Sends |xev| to |xid|, optionally short circuiting the round trip to the X 117 // server. 118 virtual void SendXClientEvent(::Window xid, XEvent* xev); 119 120 private: 121 enum SourceState { 122 // |source_current_window_| will receive a drop once we receive an 123 // XdndStatus from it. 124 SOURCE_STATE_PENDING_DROP, 125 126 // The move looped will be ended once we receive XdndFinished from 127 // |source_current_window_|. We should not send XdndPosition to 128 // |source_current_window_| while in this state. 129 SOURCE_STATE_DROPPED, 130 131 // There is no drag in progress or there is a drag in progress and the 132 // user has not yet released the mouse. 133 SOURCE_STATE_OTHER, 134 }; 135 136 // Processes a mouse move at |screen_point|. 137 void ProcessMouseMove(const gfx::Point& screen_point, 138 unsigned long event_time); 139 140 // Start timer to end the move loop if the target is too slow to respond after 141 // the mouse is released. 142 void StartEndMoveLoopTimer(); 143 144 // Ends the move loop. 145 void EndMoveLoop(); 146 147 // When we receive an position x11 message, we need to translate that into 148 // the underlying aura::Window representation, as moves internal to the X11 149 // window can cause internal drag leave and enter messages. 150 void DragTranslate(const gfx::Point& root_window_location, 151 scoped_ptr<ui::OSExchangeData>* data, 152 scoped_ptr<ui::DropTargetEvent>* event, 153 aura::client::DragDropDelegate** delegate); 154 155 // Called when we need to notify the current aura::Window that we're no 156 // longer dragging over it. 157 void NotifyDragLeave(); 158 159 // Converts our bitfield of actions into an Atom that represents what action 160 // we're most likely to take on drop. 161 ::Atom DragOperationToAtom(int drag_operation); 162 163 // Converts a single action atom to a drag operation. 164 ui::DragDropTypes::DragOperation AtomToDragOperation(::Atom atom); 165 166 // During the blocking StartDragAndDrop() call, this converts the views-style 167 // |drag_operation_| bitfield into a vector of Atoms to offer to other 168 // processes. 169 std::vector< ::Atom> GetOfferedDragOperations(); 170 171 // This returns a representation of the data we're offering in this 172 // drag. This is done to bypass an asynchronous roundtrip with the X11 173 // server. 174 ui::SelectionFormatMap GetFormatMap() const; 175 176 // Handling XdndPosition can be paused while waiting for more data; this is 177 // called either synchronously from OnXdndPosition, or asynchronously after 178 // we've received data requested from the other window. 179 void CompleteXdndPosition(::Window source_window, 180 const gfx::Point& screen_point); 181 182 void SendXdndEnter(::Window dest_window); 183 void SendXdndLeave(::Window dest_window); 184 void SendXdndPosition(::Window dest_window, 185 const gfx::Point& screen_point, 186 unsigned long event_time); 187 void SendXdndDrop(::Window dest_window); 188 189 // Creates a widget for the user to drag around. 190 void CreateDragWidget(const gfx::ImageSkia& image); 191 192 // Returns true if |image| has any visible regions (defined as having a pixel 193 // with alpha > 32). 194 bool IsValidDragImage(const gfx::ImageSkia& image); 195 196 // A nested message loop that notifies this object of events through the 197 // X11MoveLoopDelegate interface. 198 scoped_ptr<X11MoveLoop> move_loop_; 199 200 aura::Window* root_window_; 201 202 Display* xdisplay_; 203 ::Window xwindow_; 204 205 ui::X11AtomCache atom_cache_; 206 207 // Target side information. 208 class X11DragContext; 209 scoped_ptr<X11DragContext> target_current_context_; 210 211 // The Aura window that is currently under the cursor. We need to manually 212 // keep track of this because Windows will only call our drag enter method 213 // once when the user enters the associated X Window. But inside that X 214 // Window there could be multiple aura windows, so we need to generate drag 215 // enter events for them. 216 aura::Window* target_window_; 217 218 // Because Xdnd messages don't contain the position in messages other than 219 // the XdndPosition message, we must manually keep track of the last position 220 // change. 221 gfx::Point target_window_location_; 222 gfx::Point target_window_root_location_; 223 224 // In the Xdnd protocol, we aren't supposed to send another XdndPosition 225 // message until we have received a confirming XdndStatus message. 226 bool waiting_on_status_; 227 228 // If we would send an XdndPosition message while we're waiting for an 229 // XdndStatus response, we need to cache the latest details we'd send. 230 scoped_ptr<std::pair<gfx::Point, unsigned long> > next_position_message_; 231 232 // Reprocesses the most recent mouse move event if the mouse has not moved 233 // in a while in case the window stacking order has changed and 234 // |source_current_window_| needs to be updated. 235 base::OneShotTimer<DesktopDragDropClientAuraX11> repeat_mouse_move_timer_; 236 237 // When the mouse is released, we need to wait for the last XdndStatus message 238 // only if we have previously received a status message from 239 // |source_current_window_|. 240 bool status_received_since_enter_; 241 242 // Source side information. 243 ui::OSExchangeDataProviderAuraX11 const* source_provider_; 244 ::Window source_current_window_; 245 SourceState source_state_; 246 247 // The current drag-drop client that has an active operation. Since we have 248 // multiple root windows and multiple DesktopDragDropClientAuraX11 instances 249 // it is important to maintain only one drag and drop operation at any time. 250 static DesktopDragDropClientAuraX11* g_current_drag_drop_client; 251 252 // The operation bitfield as requested by StartDragAndDrop. 253 int drag_operation_; 254 255 // We offer the other window a list of possible operations, 256 // XdndActionsList. This is the requested action from the other window. This 257 // is DRAG_NONE if we haven't sent out an XdndPosition message yet, haven't 258 // yet received an XdndStatus or if the other window has told us that there's 259 // no action that we can agree on. 260 ui::DragDropTypes::DragOperation negotiated_operation_; 261 262 // Ends the move loop if the target is too slow to respond after the mouse is 263 // released. 264 base::OneShotTimer<DesktopDragDropClientAuraX11> end_move_loop_timer_; 265 266 // Widget that the user drags around. May be NULL. 267 scoped_ptr<Widget> drag_widget_; 268 269 // The offset of |drag_widget_| relative to the mouse position. 270 gfx::Vector2d drag_widget_offset_; 271 272 // We use these cursors while dragging. 273 gfx::NativeCursor grab_cursor_; 274 gfx::NativeCursor copy_grab_cursor_; 275 gfx::NativeCursor move_grab_cursor_; 276 277 base::WeakPtrFactory<DesktopDragDropClientAuraX11> weak_ptr_factory_; 278 279 DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11); 280 }; 281 282 } // namespace views 283 284 #endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_DRAG_DROP_CLIENT_AURAX11_H_ 285