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
25namespace aura {
26namespace client {
27class DragDropDelegate;
28}
29}
30
31namespace gfx {
32class ImageSkia;
33class Point;
34}
35
36namespace ui {
37class DragSource;
38class DropTargetEvent;
39class OSExchangeData;
40class OSExchangeDataProviderAuraX11;
41class SelectionFormatMap;
42}
43
44namespace views {
45class DesktopNativeCursorManager;
46class Widget;
47class 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.
52class 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