Home | History | Annotate | Download | only in desktop_aura
      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