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 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h"
      6 
      7 #include <X11/Xatom.h>
      8 
      9 #include "base/event_types.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "ui/aura/client/drag_drop_client.h"
     12 #include "ui/aura/client/drag_drop_delegate.h"
     13 #include "ui/aura/root_window.h"
     14 #include "ui/aura/window.h"
     15 #include "ui/base/dragdrop/drag_drop_types.h"
     16 #include "ui/base/dragdrop/os_exchange_data.h"
     17 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
     18 #include "ui/base/events/event.h"
     19 #include "ui/base/x/selection_utils.h"
     20 #include "ui/base/x/x11_util.h"
     21 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
     22 
     23 using aura::client::DragDropDelegate;
     24 using ui::OSExchangeData;
     25 
     26 namespace {
     27 
     28 const int kMinXdndVersion = 5;
     29 
     30 const int kWillAcceptDrop = 1;
     31 const int kWantFurtherPosEvents = 2;
     32 
     33 const char kXdndActionCopy[] = "XdndActionCopy";
     34 const char kXdndActionMove[] = "XdndActionMove";
     35 const char kXdndActionLink[] = "XdndActionLink";
     36 
     37 const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER";
     38 const char kXdndSelection[] = "XdndSelection";
     39 
     40 const char* kAtomsToCache[] = {
     41   kChromiumDragReciever,
     42   "XdndActionAsk",
     43   kXdndActionCopy,
     44   kXdndActionLink,
     45   "XdndActionList",
     46   kXdndActionMove,
     47   "XdndActionPrivate",
     48   "XdndAware",
     49   "XdndDrop",
     50   "XdndEnter",
     51   "XdndFinished",
     52   "XdndLeave",
     53   "XdndPosition",
     54   "XdndProxy",  // Proxy windows?
     55   kXdndSelection,
     56   "XdndStatus",
     57   "XdndTypeList",
     58   NULL
     59 };
     60 
     61 // Helper class to FindWindowFor which looks for a drag target under the
     62 // cursor.
     63 class DragTargetWindowFinder : public ui::EnumerateWindowsDelegate {
     64  public:
     65   DragTargetWindowFinder(XID ignored_icon_window,
     66                          gfx::Point screen_loc)
     67       : ignored_icon_window_(ignored_icon_window),
     68         output_window_(None),
     69         screen_loc_(screen_loc) {
     70     ui::EnumerateTopLevelWindows(this);
     71   }
     72 
     73   virtual ~DragTargetWindowFinder() {}
     74 
     75   XID window() const { return output_window_; }
     76 
     77  protected:
     78   virtual bool ShouldStopIterating(XID window) OVERRIDE {
     79     if (window == ignored_icon_window_)
     80       return false;
     81 
     82     if (!ui::IsWindowVisible(window))
     83       return false;
     84 
     85     if (!ui::WindowContainsPoint(window, screen_loc_))
     86       return false;
     87 
     88     if (ui::PropertyExists(window, "WM_STATE")) {
     89       output_window_ = window;
     90       return true;
     91     }
     92 
     93     return false;
     94   }
     95 
     96  private:
     97   XID ignored_icon_window_;
     98   XID output_window_;
     99   gfx::Point screen_loc_;
    100 
    101   DISALLOW_COPY_AND_ASSIGN(DragTargetWindowFinder);
    102 };
    103 
    104 // Returns the topmost X11 window at |screen_point| if it is advertising that
    105 // is supports the Xdnd protocol. Will return the window under the pointer as
    106 // |mouse_window|. If there's a Xdnd aware window, it will be returned in
    107 // |dest_window|.
    108 void FindWindowFor(const gfx::Point& screen_point,
    109                    ::Window* mouse_window, ::Window* dest_window) {
    110   DragTargetWindowFinder finder(None, screen_point);
    111   *mouse_window = finder.window();
    112   *dest_window = None;
    113 
    114   if (finder.window() == None)
    115     return;
    116 
    117   // Figure out which window we should test as XdndAware. If mouse_window has
    118   // XdndProxy, it will set that proxy on target, and if not, |target|'s
    119   // original value will remain.
    120   XID target = *mouse_window;
    121   ui::GetXIDProperty(*mouse_window, "XdndProxy", &target);
    122 
    123   int version;
    124   if (ui::GetIntProperty(target, "XdndAware", &version) &&
    125       version >= kMinXdndVersion) {
    126     *dest_window = target;
    127   }
    128 }
    129 
    130 }  // namespace
    131 
    132 namespace views {
    133 
    134 std::map< ::Window, DesktopDragDropClientAuraX11*>
    135     DesktopDragDropClientAuraX11::g_live_client_map;
    136 
    137 class DesktopDragDropClientAuraX11::X11DragContext :
    138     public base::MessageLoop::Dispatcher {
    139  public:
    140   X11DragContext(ui::X11AtomCache* atom_cache,
    141                  ::Window local_window,
    142                  const XClientMessageEvent& event);
    143   virtual ~X11DragContext();
    144 
    145   // When we receive an XdndPosition message, we need to have all the data
    146   // copied from the other window before we process the XdndPosition
    147   // message. If we have that data already, dispatch immediately. Otherwise,
    148   // delay dispatching until we do.
    149   void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client,
    150                                   ::Window source_window,
    151                                   const gfx::Point& screen_point);
    152 
    153   // Called to request the next target from the source window. This is only
    154   // done on the first XdndPosition; after that, we cache the data offered by
    155   // the source window.
    156   void RequestNextTarget();
    157 
    158   // Called when XSelection data has been copied to our process.
    159   void OnSelectionNotify(const XSelectionEvent& xselection);
    160 
    161   // Clones the fetched targets.
    162   const ui::SelectionFormatMap& fetched_targets() { return fetched_targets_; }
    163 
    164   // Reads the "XdndActionList" property from |source_window| and copies it
    165   // into |actions|.
    166   void ReadActions();
    167 
    168   // Creates a ui::DragDropTypes::DragOperation representation of the current
    169   // action list.
    170   int GetDragOperation() const;
    171 
    172  private:
    173   // Overridden from MessageLoop::Dispatcher:
    174   virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
    175 
    176   // The atom cache owned by our parent.
    177   ui::X11AtomCache* atom_cache_;
    178 
    179   // The XID of our chrome local aura window handling our events.
    180   ::Window local_window_;
    181 
    182   // The XID of the window that's initiated the drag.
    183   unsigned long source_window_;
    184 
    185   // The client we inform once we're done with requesting data.
    186   DesktopDragDropClientAuraX11* drag_drop_client_;
    187 
    188   // Whether we're blocking the handling of an XdndPosition message by waiting
    189   // for |unfetched_targets_| to be fetched.
    190   bool waiting_to_handle_position_;
    191 
    192   // Where the cursor is on screen.
    193   gfx::Point screen_point_;
    194 
    195   // A SelectionFormatMap of data that we have in our process.
    196   ui::SelectionFormatMap fetched_targets_;
    197 
    198   // The names of various data types offered by the other window that we
    199   // haven't fetched and put in |fetched_targets_| yet.
    200   std::vector<Atom> unfetched_targets_;
    201 
    202   // Possible actions.
    203   std::vector<Atom> actions_;
    204 
    205   DISALLOW_COPY_AND_ASSIGN(X11DragContext);
    206 };
    207 
    208 DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
    209     ui::X11AtomCache* atom_cache,
    210     ::Window local_window,
    211     const XClientMessageEvent& event)
    212     : atom_cache_(atom_cache),
    213       local_window_(local_window),
    214       source_window_(event.data.l[0]),
    215       drag_drop_client_(NULL),
    216       waiting_to_handle_position_(false) {
    217   bool get_types = ((event.data.l[1] & 1) != 0);
    218 
    219   if (get_types) {
    220     if (!ui::GetAtomArrayProperty(source_window_,
    221                                   "XdndTypeList",
    222                                   &unfetched_targets_)) {
    223       return;
    224     }
    225   } else {
    226     // data.l[2,3,4] contain the first three types. Unused slots can be None.
    227     for (int i = 0; i < 3; ++i) {
    228       if (event.data.l[2+i] != None) {
    229         unfetched_targets_.push_back(event.data.l[2+i]);
    230       }
    231     }
    232   }
    233 
    234   DesktopDragDropClientAuraX11* client =
    235       DesktopDragDropClientAuraX11::GetForWindow(source_window_);
    236   if (!client) {
    237     // The window doesn't have a DesktopDragDropClientAuraX11, that means it's
    238     // created by some other process. Listen for messages on it.
    239     base::MessagePumpAuraX11::Current()->AddDispatcherForWindow(
    240         this, source_window_);
    241     XSelectInput(base::MessagePumpAuraX11::GetDefaultXDisplay(),
    242                  source_window_, PropertyChangeMask);
    243 
    244     // We must perform a full sync here because we could be racing
    245     // |source_window_|.
    246     XSync(base::MessagePumpAuraX11::GetDefaultXDisplay(), False);
    247   } else {
    248     // This drag originates from an aura window within our process. This means
    249     // that we can shortcut the X11 server and ask the owning SelectionOwner
    250     // for the data it's offering.
    251     fetched_targets_ = client->GetFormatMap();
    252     unfetched_targets_.clear();
    253   }
    254 
    255   ReadActions();
    256 }
    257 
    258 DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() {
    259   DesktopDragDropClientAuraX11* client =
    260       DesktopDragDropClientAuraX11::GetForWindow(source_window_);
    261   if (!client) {
    262     // Unsubscribe from message events.
    263     base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(
    264         source_window_);
    265   }
    266 }
    267 
    268 void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage(
    269     DesktopDragDropClientAuraX11* client,
    270     ::Window source_window,
    271     const gfx::Point& screen_point) {
    272   DCHECK_EQ(source_window_, source_window);
    273 
    274   if (!unfetched_targets_.empty()) {
    275     // We have unfetched targets. That means we need to pause the handling of
    276     // the position message and ask the other window for its data.
    277     screen_point_ = screen_point;
    278     drag_drop_client_ = client;
    279     waiting_to_handle_position_ = true;
    280 
    281     fetched_targets_ = ui::SelectionFormatMap();
    282     RequestNextTarget();
    283   } else {
    284     client->CompleteXdndPosition(source_window, screen_point);
    285   }
    286 }
    287 
    288 void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() {
    289   ::Atom target = unfetched_targets_.back();
    290   unfetched_targets_.pop_back();
    291 
    292   XConvertSelection(base::MessagePumpAuraX11::GetDefaultXDisplay(),
    293                     atom_cache_->GetAtom(kXdndSelection),
    294                     target,
    295                     atom_cache_->GetAtom(kChromiumDragReciever),
    296                     local_window_,
    297                     CurrentTime);
    298 }
    299 
    300 void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify(
    301     const XSelectionEvent& event) {
    302   DCHECK(waiting_to_handle_position_);
    303   DCHECK(drag_drop_client_);
    304   DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever));
    305 
    306   scoped_refptr<base::RefCountedMemory> data;
    307   ::Atom type = None;
    308   if (ui::GetRawBytesOfProperty(local_window_, event.property,
    309                                 &data, NULL, NULL, &type)) {
    310     fetched_targets_.Insert(event.target, data);
    311   }
    312 
    313   if (!unfetched_targets_.empty()) {
    314     RequestNextTarget();
    315   } else {
    316     waiting_to_handle_position_ = false;
    317     drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_);
    318     drag_drop_client_ = NULL;
    319   }
    320 }
    321 
    322 void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
    323   DesktopDragDropClientAuraX11* client =
    324       DesktopDragDropClientAuraX11::GetForWindow(source_window_);
    325   if (!client) {
    326     std::vector<Atom> atom_array;
    327     if (!ui::GetAtomArrayProperty(source_window_,
    328                                   "XdndActionList",
    329                                   &atom_array)) {
    330       actions_.clear();
    331     } else {
    332       actions_.swap(atom_array);
    333     }
    334   } else {
    335     // We have a property notify set up for other windows in case they change
    336     // their action list. Thankfully, the views interface is static and you
    337     // can't change the action list after you enter StartDragAndDrop().
    338     actions_ = client->GetOfferedDragOperations();
    339   }
    340 }
    341 
    342 int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const {
    343   int drag_operation = ui::DragDropTypes::DRAG_NONE;
    344   for (std::vector<Atom>::const_iterator it = actions_.begin();
    345        it != actions_.end(); ++it) {
    346     if (*it == atom_cache_->GetAtom(kXdndActionCopy))
    347       drag_operation |= ui::DragDropTypes::DRAG_COPY;
    348     else if (*it == atom_cache_->GetAtom(kXdndActionMove))
    349       drag_operation |= ui::DragDropTypes::DRAG_MOVE;
    350     else if (*it == atom_cache_->GetAtom(kXdndActionLink))
    351       drag_operation |= ui::DragDropTypes::DRAG_LINK;
    352   }
    353 
    354   return drag_operation;
    355 }
    356 
    357 bool DesktopDragDropClientAuraX11::X11DragContext::Dispatch(
    358     const base::NativeEvent& event) {
    359   if (event->type == PropertyNotify &&
    360       event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) {
    361     ReadActions();
    362   }
    363   return true;
    364 }
    365 
    366 ///////////////////////////////////////////////////////////////////////////////
    367 
    368 DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
    369     aura::RootWindow* root_window,
    370     views::DesktopNativeCursorManager* cursor_manager,
    371     Display* xdisplay,
    372     ::Window xwindow)
    373     : move_loop_(this),
    374       root_window_(root_window),
    375       xdisplay_(xdisplay),
    376       xwindow_(xwindow),
    377       atom_cache_(xdisplay_, kAtomsToCache),
    378       target_window_(NULL),
    379       source_provider_(NULL),
    380       source_current_window_(None),
    381       drag_drop_in_progress_(false),
    382       drag_operation_(0),
    383       resulting_operation_(0),
    384       grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorGrabbing)),
    385       copy_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorCopy)),
    386       move_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorMove)) {
    387   DCHECK(g_live_client_map.find(xwindow) == g_live_client_map.end());
    388   g_live_client_map.insert(std::make_pair(xwindow, this));
    389 
    390   // Mark that we are aware of drag and drop concepts.
    391   unsigned long xdnd_version = kMinXdndVersion;
    392   XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"),
    393                   XA_ATOM, 32, PropModeReplace,
    394                   reinterpret_cast<unsigned char*>(&xdnd_version), 1);
    395 }
    396 
    397 DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() {
    398   g_live_client_map.erase(xwindow_);
    399 }
    400 
    401 // static
    402 DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow(
    403     ::Window window) {
    404   std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it =
    405       g_live_client_map.find(window);
    406   if (it == g_live_client_map.end())
    407     return NULL;
    408   return it->second;
    409 }
    410 
    411 void DesktopDragDropClientAuraX11::OnXdndEnter(
    412     const XClientMessageEvent& event) {
    413   DVLOG(1) << "XdndEnter";
    414 
    415   int version = (event.data.l[1] & 0xff000000) >> 24;
    416   if (version < 3) {
    417     LOG(ERROR) << "Received old XdndEnter message.";
    418     return;
    419   }
    420 
    421   // Make sure that we've run ~X11DragContext() before creating another one.
    422   target_current_context_.reset();
    423   target_current_context_.reset(
    424       new X11DragContext(&atom_cache_, xwindow_, event));
    425 
    426   // In the Windows implementation, we immediately call DesktopDropTargetWin::
    427   // Translate(). Here, we wait until we receive an XdndPosition message
    428   // because the enter message doesn't convey any positioning
    429   // information.
    430 }
    431 
    432 void DesktopDragDropClientAuraX11::OnXdndLeave(
    433     const XClientMessageEvent& event) {
    434   DVLOG(1) << "XdndLeave";
    435   NotifyDragLeave();
    436   target_current_context_.reset();
    437 }
    438 
    439 void DesktopDragDropClientAuraX11::OnXdndPosition(
    440     const XClientMessageEvent& event) {
    441   DVLOG(1) << "XdndPosition";
    442 
    443   unsigned long source_window = event.data.l[0];
    444   int x_root_window = event.data.l[2] >> 16;
    445   int y_root_window = event.data.l[2] & 0xffff;
    446 
    447   if (!target_current_context_.get()) {
    448     NOTREACHED();
    449     return;
    450   }
    451 
    452   // If we already have all the data from this drag, we complete it
    453   // immediately.
    454   target_current_context_->OnStartXdndPositionMessage(
    455       this, source_window, gfx::Point(x_root_window, y_root_window));
    456 }
    457 
    458 void DesktopDragDropClientAuraX11::OnXdndStatus(
    459     const XClientMessageEvent& event) {
    460   DVLOG(1) << "XdndStatus";
    461 
    462   unsigned long source_window = event.data.l[0];
    463   int drag_operation = ui::DragDropTypes::DRAG_NONE;
    464   if (event.data.l[1] & 1) {
    465     ::Atom atom_operation = event.data.l[4];
    466     negotiated_operation_[source_window] = atom_operation;
    467     drag_operation = AtomToDragOperation(atom_operation);
    468   }
    469 
    470   switch (drag_operation) {
    471     case ui::DragDropTypes::DRAG_COPY:
    472       move_loop_.UpdateCursor(copy_grab_cursor_);
    473       break;
    474     case ui::DragDropTypes::DRAG_MOVE:
    475       move_loop_.UpdateCursor(move_grab_cursor_);
    476       break;
    477     default:
    478       move_loop_.UpdateCursor(grab_cursor_);
    479       break;
    480   }
    481 
    482   // Note: event.data.[2,3] specify a rectangle. It is a request by the other
    483   // window to not send further XdndPosition messages while the cursor is
    484   // within it. However, it is considered advisory and (at least according to
    485   // the spec) the other side must handle further position messages within
    486   // it. GTK+ doesn't bother with this, so neither should we.
    487 
    488   waiting_on_status_.erase(source_window);
    489 
    490   if (ContainsKey(pending_drop_, source_window)) {
    491     // We were waiting on the status message so we could send the XdndDrop.
    492     SendXdndDrop(source_window);
    493     return;
    494   }
    495 
    496   NextPositionMap::iterator it = next_position_message_.find(source_window);
    497   if (it != next_position_message_.end()) {
    498     // We were waiting on the status message so we could send off the next
    499     // position message we queued up.
    500     gfx::Point p = it->second.first;
    501     unsigned long time = it->second.second;
    502     next_position_message_.erase(it);
    503 
    504     SendXdndPosition(source_window, p, time);
    505   }
    506 }
    507 
    508 void DesktopDragDropClientAuraX11::OnXdndFinished(
    509     const XClientMessageEvent& event) {
    510   DVLOG(1) << "XdndFinished";
    511   resulting_operation_ = AtomToDragOperation(
    512       negotiated_operation_[event.data.l[0]]);
    513   move_loop_.EndMoveLoop();
    514 }
    515 
    516 void DesktopDragDropClientAuraX11::OnXdndDrop(
    517     const XClientMessageEvent& event) {
    518   DVLOG(1) << "XdndDrop";
    519 
    520   unsigned long source_window = event.data.l[0];
    521 
    522   int drag_operation = ui::DragDropTypes::DRAG_NONE;
    523   if (target_window_) {
    524     aura::client::DragDropDelegate* delegate =
    525         aura::client::GetDragDropDelegate(target_window_);
    526     if (delegate) {
    527       ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11(
    528           xwindow_, target_current_context_->fetched_targets()));
    529 
    530       ui::DropTargetEvent event(data,
    531                                 target_window_location_,
    532                                 target_window_root_location_,
    533                                 target_current_context_->GetDragOperation());
    534       drag_operation = delegate->OnPerformDrop(event);
    535     }
    536 
    537     target_window_->RemoveObserver(this);
    538     target_window_ = NULL;
    539   }
    540 
    541   XEvent xev;
    542   xev.xclient.type = ClientMessage;
    543   xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished");
    544   xev.xclient.format = 32;
    545   xev.xclient.window = source_window;
    546   xev.xclient.data.l[0] = xwindow_;
    547   xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0;
    548   xev.xclient.data.l[2] = DragOperationToAtom(drag_operation);
    549 
    550   SendXClientEvent(source_window, &xev);
    551 }
    552 
    553 void DesktopDragDropClientAuraX11::OnSelectionNotify(
    554     const XSelectionEvent& xselection) {
    555   if (!target_current_context_) {
    556     NOTIMPLEMENTED();
    557     return;
    558   }
    559 
    560   target_current_context_->OnSelectionNotify(xselection);
    561 }
    562 
    563 int DesktopDragDropClientAuraX11::StartDragAndDrop(
    564     const ui::OSExchangeData& data,
    565     aura::RootWindow* root_window,
    566     aura::Window* source_window,
    567     const gfx::Point& root_location,
    568     int operation,
    569     ui::DragDropTypes::DragEventSource source) {
    570   source_current_window_ = None;
    571   drag_drop_in_progress_ = true;
    572   drag_operation_ = operation;
    573   resulting_operation_ = ui::DragDropTypes::DRAG_NONE;
    574 
    575   const ui::OSExchangeData::Provider* provider = &data.provider();
    576   source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>(
    577       provider);
    578 
    579   source_provider_->TakeOwnershipOfSelection();
    580 
    581   std::vector< ::Atom> actions = GetOfferedDragOperations();
    582   ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions);
    583 
    584   // Windows has a specific method, DoDragDrop(), which performs the entire
    585   // drag. We have to emulate this, so we spin off a nested runloop which will
    586   // track all cursor movement and reroute events to a specific handler.
    587   move_loop_.RunMoveLoop(source_window, grab_cursor_);
    588 
    589   source_provider_ = NULL;
    590   drag_drop_in_progress_ = false;
    591   drag_operation_ = 0;
    592   XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList"));
    593 
    594   return resulting_operation_;
    595 }
    596 
    597 void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target,
    598                                               const ui::LocatedEvent& event) {
    599   NOTIMPLEMENTED();
    600 }
    601 
    602 void DesktopDragDropClientAuraX11::Drop(aura::Window* target,
    603                                         const ui::LocatedEvent& event) {
    604   NOTIMPLEMENTED();
    605 }
    606 
    607 void DesktopDragDropClientAuraX11::DragCancel() {
    608   move_loop_.EndMoveLoop();
    609 }
    610 
    611 bool DesktopDragDropClientAuraX11::IsDragDropInProgress() {
    612   return drag_drop_in_progress_;
    613 }
    614 
    615 void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
    616   DCHECK_EQ(target_window_, window);
    617   target_window_ = NULL;
    618 }
    619 
    620 void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) {
    621   gfx::Point screen_point(event->x_root, event->y_root);
    622 
    623   // Find the current window the cursor is over.
    624   ::Window mouse_window = None;
    625   ::Window dest_window = None;
    626   FindWindowFor(screen_point, &mouse_window, &dest_window);
    627 
    628   if (source_current_window_ != dest_window) {
    629     if (source_current_window_ != None)
    630       SendXdndLeave(source_current_window_);
    631 
    632     source_current_window_ = dest_window;
    633 
    634     if (source_current_window_ != None) {
    635       negotiated_operation_.erase(source_current_window_);
    636       SendXdndEnter(source_current_window_);
    637     }
    638   }
    639 
    640   if (source_current_window_ != None) {
    641     if (ContainsKey(waiting_on_status_, dest_window)) {
    642       next_position_message_[dest_window] =
    643           std::make_pair(screen_point, event->time);
    644     } else {
    645       SendXdndPosition(dest_window, screen_point, event->time);
    646     }
    647   }
    648 }
    649 
    650 void DesktopDragDropClientAuraX11::OnMouseReleased() {
    651   if (source_current_window_ != None) {
    652     if (ContainsKey(waiting_on_status_, source_current_window_)) {
    653       // If we are waiting for an XdndStatus message, we need to wait for it to
    654       // complete.
    655       pending_drop_.insert(source_current_window_);
    656       return;
    657     }
    658 
    659     std::map< ::Window, ::Atom>::iterator it =
    660         negotiated_operation_.find(source_current_window_);
    661     if (it != negotiated_operation_.end() && it->second != None) {
    662       // We have negotiated an action with the other end.
    663       SendXdndDrop(source_current_window_);
    664       return;
    665     }
    666 
    667     SendXdndLeave(source_current_window_);
    668     source_current_window_ = None;
    669   }
    670 
    671   move_loop_.EndMoveLoop();
    672 }
    673 
    674 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() {
    675   target_current_context_.reset();
    676 }
    677 
    678 void DesktopDragDropClientAuraX11::DragTranslate(
    679     const gfx::Point& root_window_location,
    680     scoped_ptr<ui::OSExchangeData>* data,
    681     scoped_ptr<ui::DropTargetEvent>* event,
    682     aura::client::DragDropDelegate** delegate) {
    683   gfx::Point root_location = root_window_location;
    684   root_window_->ConvertPointFromNativeScreen(&root_location);
    685   aura::Window* target_window =
    686       root_window_->GetEventHandlerForPoint(root_location);
    687   bool target_window_changed = false;
    688   if (target_window != target_window_) {
    689     if (target_window_)
    690       NotifyDragLeave();
    691     target_window_ = target_window;
    692     if (target_window_)
    693       target_window_->AddObserver(this);
    694     target_window_changed = true;
    695   }
    696   *delegate = NULL;
    697   if (!target_window_)
    698     return;
    699   *delegate = aura::client::GetDragDropDelegate(target_window_);
    700   if (!*delegate)
    701     return;
    702 
    703   data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11(
    704       xwindow_, target_current_context_->fetched_targets())));
    705   gfx::Point location = root_location;
    706   aura::Window::ConvertPointToTarget(root_window_, target_window_, &location);
    707 
    708   target_window_location_ = location;
    709   target_window_root_location_ = root_location;
    710 
    711   event->reset(new ui::DropTargetEvent(
    712       *(data->get()),
    713       location,
    714       root_location,
    715       target_current_context_->GetDragOperation()));
    716   if (target_window_changed)
    717     (*delegate)->OnDragEntered(*event->get());
    718 }
    719 
    720 void DesktopDragDropClientAuraX11::NotifyDragLeave() {
    721   if (!target_window_)
    722     return;
    723   DragDropDelegate* delegate =
    724       aura::client::GetDragDropDelegate(target_window_);
    725   if (delegate)
    726     delegate->OnDragExited();
    727   target_window_->RemoveObserver(this);
    728   target_window_ = NULL;
    729 }
    730 
    731 ::Atom DesktopDragDropClientAuraX11::DragOperationToAtom(
    732     int drag_operation) {
    733   if (drag_operation & ui::DragDropTypes::DRAG_COPY)
    734     return atom_cache_.GetAtom(kXdndActionCopy);
    735   if (drag_operation & ui::DragDropTypes::DRAG_MOVE)
    736     return atom_cache_.GetAtom(kXdndActionMove);
    737   if (drag_operation & ui::DragDropTypes::DRAG_LINK)
    738     return atom_cache_.GetAtom(kXdndActionLink);
    739 
    740   return None;
    741 }
    742 
    743 int DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
    744   if (atom == atom_cache_.GetAtom(kXdndActionCopy))
    745     return ui::DragDropTypes::DRAG_COPY;
    746   if (atom == atom_cache_.GetAtom(kXdndActionMove))
    747     return ui::DragDropTypes::DRAG_MOVE;
    748   if (atom == atom_cache_.GetAtom(kXdndActionLink))
    749     return ui::DragDropTypes::DRAG_LINK;
    750 
    751   return ui::DragDropTypes::DRAG_NONE;
    752 }
    753 
    754 std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() {
    755   std::vector< ::Atom> operations;
    756   if (drag_operation_ & ui::DragDropTypes::DRAG_COPY)
    757     operations.push_back(atom_cache_.GetAtom(kXdndActionCopy));
    758   if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE)
    759     operations.push_back(atom_cache_.GetAtom(kXdndActionMove));
    760   if (drag_operation_ & ui::DragDropTypes::DRAG_LINK)
    761     operations.push_back(atom_cache_.GetAtom(kXdndActionLink));
    762   return operations;
    763 }
    764 
    765 ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const {
    766   return source_provider_ ? source_provider_->GetFormatMap() :
    767       ui::SelectionFormatMap();
    768 }
    769 
    770 void DesktopDragDropClientAuraX11::CompleteXdndPosition(
    771     ::Window source_window,
    772     const gfx::Point& screen_point) {
    773   int drag_operation = ui::DragDropTypes::DRAG_NONE;
    774   scoped_ptr<ui::OSExchangeData> data;
    775   scoped_ptr<ui::DropTargetEvent> drop_target_event;
    776   DragDropDelegate* delegate = NULL;
    777   DragTranslate(screen_point, &data, &drop_target_event, &delegate);
    778   if (delegate)
    779     drag_operation = delegate->OnDragUpdated(*drop_target_event);
    780 
    781   // Sends an XdndStatus message back to the source_window. l[2,3]
    782   // theoretically represent an area in the window where the current action is
    783   // the same as what we're returning, but I can't find any implementation that
    784   // actually making use of this. A client can return (0, 0) and/or set the
    785   // first bit of l[1] to disable the feature, and it appears that gtk neither
    786   // sets this nor respects it if set.
    787   XEvent xev;
    788   xev.xclient.type = ClientMessage;
    789   xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus");
    790   xev.xclient.format = 32;
    791   xev.xclient.window = source_window;
    792   xev.xclient.data.l[0] = xwindow_;
    793   xev.xclient.data.l[1] = (drag_operation != 0) ?
    794       (kWantFurtherPosEvents | kWillAcceptDrop) : 0;
    795   xev.xclient.data.l[2] = 0;
    796   xev.xclient.data.l[3] = 0;
    797   xev.xclient.data.l[4] = DragOperationToAtom(drag_operation);
    798 
    799   SendXClientEvent(source_window, &xev);
    800 }
    801 
    802 void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) {
    803   XEvent xev;
    804   xev.xclient.type = ClientMessage;
    805   xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter");
    806   xev.xclient.format = 32;
    807   xev.xclient.window = dest_window;
    808   xev.xclient.data.l[0] = xwindow_;
    809   xev.xclient.data.l[1] = (kMinXdndVersion << 24);  // The version number.
    810   xev.xclient.data.l[2] = 0;
    811   xev.xclient.data.l[3] = 0;
    812   xev.xclient.data.l[4] = 0;
    813 
    814   std::vector<Atom> targets;
    815   source_provider_->RetrieveTargets(&targets);
    816 
    817   if (targets.size() > 3) {
    818     xev.xclient.data.l[1] |= 1;
    819     ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets);
    820   } else {
    821     // Pack the targets into the enter message.
    822     for (size_t i = 0; i < targets.size(); ++i)
    823       xev.xclient.data.l[2 + i] = targets[i];
    824   }
    825 
    826   SendXClientEvent(dest_window, &xev);
    827 }
    828 
    829 void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
    830   // If we're sending a leave message, don't wait for status messages anymore.
    831   waiting_on_status_.erase(dest_window);
    832   NextPositionMap::iterator it = next_position_message_.find(dest_window);
    833   if (it != next_position_message_.end())
    834     next_position_message_.erase(it);
    835 
    836   XEvent xev;
    837   xev.xclient.type = ClientMessage;
    838   xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave");
    839   xev.xclient.format = 32;
    840   xev.xclient.window = dest_window;
    841   xev.xclient.data.l[0] = xwindow_;
    842   xev.xclient.data.l[1] = 0;
    843   xev.xclient.data.l[2] = 0;
    844   xev.xclient.data.l[3] = 0;
    845   xev.xclient.data.l[4] = 0;
    846   SendXClientEvent(dest_window, &xev);
    847 }
    848 
    849 void DesktopDragDropClientAuraX11::SendXdndPosition(
    850     ::Window dest_window,
    851     const gfx::Point& screen_point,
    852     unsigned long time) {
    853   waiting_on_status_.insert(dest_window);
    854 
    855   XEvent xev;
    856   xev.xclient.type = ClientMessage;
    857   xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition");
    858   xev.xclient.format = 32;
    859   xev.xclient.window = dest_window;
    860   xev.xclient.data.l[0] = xwindow_;
    861   xev.xclient.data.l[1] = 0;
    862   xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y();
    863   xev.xclient.data.l[3] = time;
    864   xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
    865   SendXClientEvent(dest_window, &xev);
    866 }
    867 
    868 void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {
    869   XEvent xev;
    870   xev.xclient.type = ClientMessage;
    871   xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop");
    872   xev.xclient.format = 32;
    873   xev.xclient.window = dest_window;
    874   xev.xclient.data.l[0] = xwindow_;
    875   xev.xclient.data.l[1] = 0;
    876   xev.xclient.data.l[2] = CurrentTime;
    877   xev.xclient.data.l[3] = None;
    878   xev.xclient.data.l[4] = None;
    879   SendXClientEvent(dest_window, &xev);
    880 }
    881 
    882 void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid,
    883                                                     XEvent* xev) {
    884   DCHECK_EQ(ClientMessage, xev->type);
    885 
    886   // Don't send messages to the X11 message queue if we can help it.
    887   DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid);
    888   if (short_circuit) {
    889     Atom message_type = xev->xclient.message_type;
    890     if (message_type == atom_cache_.GetAtom("XdndEnter")) {
    891       short_circuit->OnXdndEnter(xev->xclient);
    892       return;
    893     } else if (message_type == atom_cache_.GetAtom("XdndLeave")) {
    894       short_circuit->OnXdndLeave(xev->xclient);
    895       return;
    896     } else if (message_type == atom_cache_.GetAtom("XdndPosition")) {
    897       short_circuit->OnXdndPosition(xev->xclient);
    898       return;
    899     } else if (message_type == atom_cache_.GetAtom("XdndStatus")) {
    900       short_circuit->OnXdndStatus(xev->xclient);
    901       return;
    902     } else if (message_type == atom_cache_.GetAtom("XdndFinished")) {
    903       short_circuit->OnXdndFinished(xev->xclient);
    904       return;
    905     } else if (message_type == atom_cache_.GetAtom("XdndDrop")) {
    906       short_circuit->OnXdndDrop(xev->xclient);
    907       return;
    908     }
    909   }
    910 
    911   // I don't understand why the GTK+ code is doing what it's doing here. It
    912   // goes out of its way to send the XEvent so that it receives a callback on
    913   // success or failure, and when it fails, it then sends an internal
    914   // GdkEvent about the failed drag. (And sending this message doesn't appear
    915   // to go through normal xlib machinery, but instead passes through the low
    916   // level xProto (the x11 wire format) that I don't understand.
    917   //
    918   // I'm unsure if I have to jump through those hoops, or if XSendEvent is
    919   // sufficient.
    920   XSendEvent(xdisplay_, xid, False, 0, xev);
    921 }
    922 
    923 }  // namespace views
    924