- add sources.
[platform/framework/web/crosswalk.git] / src / ui / views / widget / desktop_aura / desktop_drag_drop_client_aurax11.cc
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/drop_target_event.h"
17 #include "ui/base/dragdrop/os_exchange_data.h"
18 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
19 #include "ui/base/x/selection_utils.h"
20 #include "ui/base/x/x11_util.h"
21 #include "ui/events/event.h"
22 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
23
24 using aura::client::DragDropDelegate;
25 using ui::OSExchangeData;
26
27 namespace {
28
29 const int kMinXdndVersion = 5;
30
31 const int kWillAcceptDrop = 1;
32 const int kWantFurtherPosEvents = 2;
33
34 const char kXdndActionCopy[] = "XdndActionCopy";
35 const char kXdndActionMove[] = "XdndActionMove";
36 const char kXdndActionLink[] = "XdndActionLink";
37
38 const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER";
39 const char kXdndSelection[] = "XdndSelection";
40
41 const char* kAtomsToCache[] = {
42   kChromiumDragReciever,
43   "XdndActionAsk",
44   kXdndActionCopy,
45   kXdndActionLink,
46   "XdndActionList",
47   kXdndActionMove,
48   "XdndActionPrivate",
49   "XdndAware",
50   "XdndDrop",
51   "XdndEnter",
52   "XdndFinished",
53   "XdndLeave",
54   "XdndPosition",
55   "XdndProxy",  // Proxy windows?
56   kXdndSelection,
57   "XdndStatus",
58   "XdndTypeList",
59   NULL
60 };
61
62 // Helper class to FindWindowFor which looks for a drag target under the
63 // cursor.
64 class DragTargetWindowFinder : public ui::EnumerateWindowsDelegate {
65  public:
66   DragTargetWindowFinder(XID ignored_icon_window,
67                          gfx::Point screen_loc)
68       : ignored_icon_window_(ignored_icon_window),
69         output_window_(None),
70         screen_loc_(screen_loc) {
71     ui::EnumerateTopLevelWindows(this);
72   }
73
74   virtual ~DragTargetWindowFinder() {}
75
76   XID window() const { return output_window_; }
77
78  protected:
79   virtual bool ShouldStopIterating(XID window) OVERRIDE {
80     if (window == ignored_icon_window_)
81       return false;
82
83     if (!ui::IsWindowVisible(window))
84       return false;
85
86     if (!ui::WindowContainsPoint(window, screen_loc_))
87       return false;
88
89     if (ui::PropertyExists(window, "WM_STATE")) {
90       output_window_ = window;
91       return true;
92     }
93
94     return false;
95   }
96
97  private:
98   XID ignored_icon_window_;
99   XID output_window_;
100   gfx::Point screen_loc_;
101
102   DISALLOW_COPY_AND_ASSIGN(DragTargetWindowFinder);
103 };
104
105 // Returns the topmost X11 window at |screen_point| if it is advertising that
106 // is supports the Xdnd protocol. Will return the window under the pointer as
107 // |mouse_window|. If there's a Xdnd aware window, it will be returned in
108 // |dest_window|.
109 void FindWindowFor(const gfx::Point& screen_point,
110                    ::Window* mouse_window, ::Window* dest_window) {
111   DragTargetWindowFinder finder(None, screen_point);
112   *mouse_window = finder.window();
113   *dest_window = None;
114
115   if (finder.window() == None)
116     return;
117
118   // Figure out which window we should test as XdndAware. If mouse_window has
119   // XdndProxy, it will set that proxy on target, and if not, |target|'s
120   // original value will remain.
121   XID target = *mouse_window;
122   ui::GetXIDProperty(*mouse_window, "XdndProxy", &target);
123
124   int version;
125   if (ui::GetIntProperty(target, "XdndAware", &version) &&
126       version >= kMinXdndVersion) {
127     *dest_window = target;
128   }
129 }
130
131 }  // namespace
132
133 namespace views {
134
135 std::map< ::Window, DesktopDragDropClientAuraX11*>
136     DesktopDragDropClientAuraX11::g_live_client_map;
137
138 class DesktopDragDropClientAuraX11::X11DragContext :
139     public base::MessageLoop::Dispatcher {
140  public:
141   X11DragContext(ui::X11AtomCache* atom_cache,
142                  ::Window local_window,
143                  const XClientMessageEvent& event);
144   virtual ~X11DragContext();
145
146   // When we receive an XdndPosition message, we need to have all the data
147   // copied from the other window before we process the XdndPosition
148   // message. If we have that data already, dispatch immediately. Otherwise,
149   // delay dispatching until we do.
150   void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client,
151                                   ::Window source_window,
152                                   const gfx::Point& screen_point);
153
154   // Called to request the next target from the source window. This is only
155   // done on the first XdndPosition; after that, we cache the data offered by
156   // the source window.
157   void RequestNextTarget();
158
159   // Called when XSelection data has been copied to our process.
160   void OnSelectionNotify(const XSelectionEvent& xselection);
161
162   // Clones the fetched targets.
163   const ui::SelectionFormatMap& fetched_targets() { return fetched_targets_; }
164
165   // Reads the "XdndActionList" property from |source_window| and copies it
166   // into |actions|.
167   void ReadActions();
168
169   // Creates a ui::DragDropTypes::DragOperation representation of the current
170   // action list.
171   int GetDragOperation() const;
172
173  private:
174   // Overridden from MessageLoop::Dispatcher:
175   virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
176
177   // The atom cache owned by our parent.
178   ui::X11AtomCache* atom_cache_;
179
180   // The XID of our chrome local aura window handling our events.
181   ::Window local_window_;
182
183   // The XID of the window that's initiated the drag.
184   unsigned long source_window_;
185
186   // The client we inform once we're done with requesting data.
187   DesktopDragDropClientAuraX11* drag_drop_client_;
188
189   // Whether we're blocking the handling of an XdndPosition message by waiting
190   // for |unfetched_targets_| to be fetched.
191   bool waiting_to_handle_position_;
192
193   // Where the cursor is on screen.
194   gfx::Point screen_point_;
195
196   // A SelectionFormatMap of data that we have in our process.
197   ui::SelectionFormatMap fetched_targets_;
198
199   // The names of various data types offered by the other window that we
200   // haven't fetched and put in |fetched_targets_| yet.
201   std::vector<Atom> unfetched_targets_;
202
203   // Possible actions.
204   std::vector<Atom> actions_;
205
206   DISALLOW_COPY_AND_ASSIGN(X11DragContext);
207 };
208
209 DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
210     ui::X11AtomCache* atom_cache,
211     ::Window local_window,
212     const XClientMessageEvent& event)
213     : atom_cache_(atom_cache),
214       local_window_(local_window),
215       source_window_(event.data.l[0]),
216       drag_drop_client_(NULL),
217       waiting_to_handle_position_(false) {
218   bool get_types = ((event.data.l[1] & 1) != 0);
219
220   if (get_types) {
221     if (!ui::GetAtomArrayProperty(source_window_,
222                                   "XdndTypeList",
223                                   &unfetched_targets_)) {
224       return;
225     }
226   } else {
227     // data.l[2,3,4] contain the first three types. Unused slots can be None.
228     for (int i = 0; i < 3; ++i) {
229       if (event.data.l[2+i] != None) {
230         unfetched_targets_.push_back(event.data.l[2+i]);
231       }
232     }
233   }
234
235   DesktopDragDropClientAuraX11* client =
236       DesktopDragDropClientAuraX11::GetForWindow(source_window_);
237   if (!client) {
238     // The window doesn't have a DesktopDragDropClientAuraX11, that means it's
239     // created by some other process. Listen for messages on it.
240     base::MessagePumpX11::Current()->AddDispatcherForWindow(
241         this, source_window_);
242     XSelectInput(gfx::GetXDisplay(), source_window_, PropertyChangeMask);
243
244     // We must perform a full sync here because we could be racing
245     // |source_window_|.
246     XSync(gfx::GetXDisplay(), 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::MessagePumpX11::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(gfx::GetXDisplay(),
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::Window* 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::Window* 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_->GetDispatcher()->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