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.
5 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h"
9 #include "base/event_types.h"
10 #include "base/lazy_instance.h"
11 #include "base/message_loop/message_loop.h"
12 #include "ui/aura/window.h"
13 #include "ui/aura/window_tree_host.h"
14 #include "ui/base/clipboard/clipboard.h"
15 #include "ui/base/dragdrop/drop_target_event.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/x/selection_utils.h"
19 #include "ui/base/x/x11_util.h"
20 #include "ui/events/event.h"
21 #include "ui/events/platform/platform_event_source.h"
22 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
23 #include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h"
24 #include "ui/wm/public/drag_drop_client.h"
25 #include "ui/wm/public/drag_drop_delegate.h"
27 using aura::client::DragDropDelegate;
28 using ui::OSExchangeData;
32 const int kMinXdndVersion = 5;
34 const int kWillAcceptDrop = 1;
35 const int kWantFurtherPosEvents = 2;
37 const char kXdndActionCopy[] = "XdndActionCopy";
38 const char kXdndActionMove[] = "XdndActionMove";
39 const char kXdndActionLink[] = "XdndActionLink";
40 const char kXdndActionDirectSave[] = "XdndActionDirectSave";
42 const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER";
43 const char kXdndSelection[] = "XdndSelection";
44 const char kXdndDirectSave0[] = "XdndDirectSave0";
46 const char* kAtomsToCache[] = {
47 kChromiumDragReciever,
50 kXdndActionDirectSave,
62 "XdndProxy", // Proxy windows?
66 ui::Clipboard::kMimeTypeText,
70 // The time to wait for the target to respond after the user has released the
71 // mouse button before ending the move loop.
72 const int kEndMoveLoopTimeoutMs = 1000;
74 // The time to wait since sending the last XdndPosition message before
75 // reprocessing the most recent mouse move event in case that the window
76 // stacking order has changed and |source_current_window_| needs to be updated.
77 const int kRepeatMouseMoveTimeoutMs = 350;
79 static base::LazyInstance<
80 std::map< ::Window, views::DesktopDragDropClientAuraX11*> >::Leaky
81 g_live_client_map = LAZY_INSTANCE_INITIALIZER;
83 // Returns the topmost X11 window at |screen_point| if it is advertising that
84 // is supports the Xdnd protocol. Will return the window under the pointer as
85 // |mouse_window|. If there's a Xdnd aware window, it will be returned in
87 void FindWindowFor(const gfx::Point& screen_point,
88 ::Window* mouse_window,
89 ::Window* dest_window) {
90 views::X11TopmostWindowFinder finder;
91 *mouse_window = finder.FindWindowAt(screen_point);
94 if (*mouse_window == None)
97 // Figure out which window we should test as XdndAware. If mouse_window has
98 // XdndProxy, it will set that proxy on target, and if not, |target|'s
99 // original value will remain.
100 XID target = *mouse_window;
101 ui::GetXIDProperty(*mouse_window, "XdndProxy", &target);
104 if (ui::GetIntProperty(target, "XdndAware", &version) &&
105 version >= kMinXdndVersion) {
106 *dest_window = target;
114 DesktopDragDropClientAuraX11*
115 DesktopDragDropClientAuraX11::g_current_drag_drop_client = NULL;
117 class DesktopDragDropClientAuraX11::X11DragContext
118 : public ui::PlatformEventDispatcher {
120 X11DragContext(ui::X11AtomCache* atom_cache,
121 ::Window local_window,
122 const XClientMessageEvent& event);
123 virtual ~X11DragContext();
125 // When we receive an XdndPosition message, we need to have all the data
126 // copied from the other window before we process the XdndPosition
127 // message. If we have that data already, dispatch immediately. Otherwise,
128 // delay dispatching until we do.
129 void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client,
130 ::Atom suggested_action,
131 ::Window source_window,
132 const gfx::Point& screen_point);
134 // Called to request the next target from the source window. This is only
135 // done on the first XdndPosition; after that, we cache the data offered by
136 // the source window.
137 void RequestNextTarget();
139 // Called when XSelection data has been copied to our process.
140 void OnSelectionNotify(const XSelectionEvent& xselection);
142 // Clones the fetched targets.
143 const ui::SelectionFormatMap& fetched_targets() { return fetched_targets_; }
145 // Reads the "XdndActionList" property from |source_window| and copies it
149 // Creates a ui::DragDropTypes::DragOperation representation of the current
151 int GetDragOperation() const;
154 // Masks the X11 atom |xdnd_operation|'s views representation onto
156 void MaskOpeartion(::Atom xdnd_operation, int* drag_operation) const;
158 // ui::PlatformEventDispatcher:
159 virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE;
160 virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE;
162 // The atom cache owned by our parent.
163 ui::X11AtomCache* atom_cache_;
165 // The XID of our chrome local aura window handling our events.
166 ::Window local_window_;
168 // The XID of the window that's initiated the drag.
169 unsigned long source_window_;
171 // The client we inform once we're done with requesting data.
172 DesktopDragDropClientAuraX11* drag_drop_client_;
174 // Whether we're blocking the handling of an XdndPosition message by waiting
175 // for |unfetched_targets_| to be fetched.
176 bool waiting_to_handle_position_;
178 // Where the cursor is on screen.
179 gfx::Point screen_point_;
181 // A SelectionFormatMap of data that we have in our process.
182 ui::SelectionFormatMap fetched_targets_;
184 // The names of various data types offered by the other window that we
185 // haven't fetched and put in |fetched_targets_| yet.
186 std::vector<Atom> unfetched_targets_;
188 // XdndPosition messages have a suggested action. Qt applications exclusively
189 // use this, instead of the XdndActionList which is backed by |actions_|.
190 Atom suggested_action_;
193 std::vector<Atom> actions_;
195 DISALLOW_COPY_AND_ASSIGN(X11DragContext);
198 DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
199 ui::X11AtomCache* atom_cache,
200 ::Window local_window,
201 const XClientMessageEvent& event)
202 : atom_cache_(atom_cache),
203 local_window_(local_window),
204 source_window_(event.data.l[0]),
205 drag_drop_client_(NULL),
206 waiting_to_handle_position_(false),
207 suggested_action_(None) {
208 bool get_types = ((event.data.l[1] & 1) != 0);
211 if (!ui::GetAtomArrayProperty(source_window_,
213 &unfetched_targets_)) {
217 // data.l[2,3,4] contain the first three types. Unused slots can be None.
218 for (int i = 0; i < 3; ++i) {
219 if (event.data.l[2+i] != None) {
220 unfetched_targets_.push_back(event.data.l[2+i]);
225 DesktopDragDropClientAuraX11* client =
226 DesktopDragDropClientAuraX11::GetForWindow(source_window_);
228 // The window doesn't have a DesktopDragDropClientAuraX11, that means it's
229 // created by some other process. Listen for messages on it.
230 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
231 XSelectInput(gfx::GetXDisplay(), source_window_, PropertyChangeMask);
233 // We must perform a full sync here because we could be racing
235 XSync(gfx::GetXDisplay(), False);
237 // This drag originates from an aura window within our process. This means
238 // that we can shortcut the X11 server and ask the owning SelectionOwner
239 // for the data it's offering.
240 fetched_targets_ = client->GetFormatMap();
241 unfetched_targets_.clear();
247 DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() {
248 // Unsubscribe from message events.
249 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
252 void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage(
253 DesktopDragDropClientAuraX11* client,
254 ::Atom suggested_action,
255 ::Window source_window,
256 const gfx::Point& screen_point) {
257 DCHECK_EQ(source_window_, source_window);
258 suggested_action_ = suggested_action;
260 if (!unfetched_targets_.empty()) {
261 // We have unfetched targets. That means we need to pause the handling of
262 // the position message and ask the other window for its data.
263 screen_point_ = screen_point;
264 drag_drop_client_ = client;
265 waiting_to_handle_position_ = true;
267 fetched_targets_ = ui::SelectionFormatMap();
270 client->CompleteXdndPosition(source_window, screen_point);
274 void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() {
275 ::Atom target = unfetched_targets_.back();
276 unfetched_targets_.pop_back();
278 XConvertSelection(gfx::GetXDisplay(),
279 atom_cache_->GetAtom(kXdndSelection),
281 atom_cache_->GetAtom(kChromiumDragReciever),
286 void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify(
287 const XSelectionEvent& event) {
288 if (!waiting_to_handle_position_) {
289 // A misbehaved window may send SelectionNotify without us requesting data
290 // via XConvertSelection().
293 DCHECK(drag_drop_client_);
294 DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever));
296 scoped_refptr<base::RefCountedMemory> data;
298 if (ui::GetRawBytesOfProperty(local_window_, event.property,
299 &data, NULL, NULL, &type)) {
300 fetched_targets_.Insert(event.target, data);
303 if (!unfetched_targets_.empty()) {
306 waiting_to_handle_position_ = false;
307 drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_);
308 drag_drop_client_ = NULL;
312 void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
313 DesktopDragDropClientAuraX11* client =
314 DesktopDragDropClientAuraX11::GetForWindow(source_window_);
316 std::vector<Atom> atom_array;
317 if (!ui::GetAtomArrayProperty(source_window_,
322 actions_.swap(atom_array);
325 // We have a property notify set up for other windows in case they change
326 // their action list. Thankfully, the views interface is static and you
327 // can't change the action list after you enter StartDragAndDrop().
328 actions_ = client->GetOfferedDragOperations();
332 int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const {
333 int drag_operation = ui::DragDropTypes::DRAG_NONE;
334 for (std::vector<Atom>::const_iterator it = actions_.begin();
335 it != actions_.end(); ++it) {
336 MaskOpeartion(*it, &drag_operation);
339 MaskOpeartion(suggested_action_, &drag_operation);
341 return drag_operation;
344 void DesktopDragDropClientAuraX11::X11DragContext::MaskOpeartion(
345 ::Atom xdnd_operation,
346 int* drag_operation) const {
347 if (xdnd_operation == atom_cache_->GetAtom(kXdndActionCopy))
348 *drag_operation |= ui::DragDropTypes::DRAG_COPY;
349 else if (xdnd_operation == atom_cache_->GetAtom(kXdndActionMove))
350 *drag_operation |= ui::DragDropTypes::DRAG_MOVE;
351 else if (xdnd_operation == atom_cache_->GetAtom(kXdndActionLink))
352 *drag_operation |= ui::DragDropTypes::DRAG_LINK;
355 bool DesktopDragDropClientAuraX11::X11DragContext::CanDispatchEvent(
356 const ui::PlatformEvent& event) {
357 return event->xany.window == source_window_;
360 uint32_t DesktopDragDropClientAuraX11::X11DragContext::DispatchEvent(
361 const ui::PlatformEvent& event) {
362 if (event->type == PropertyNotify &&
363 event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) {
365 return ui::POST_DISPATCH_STOP_PROPAGATION;
367 return ui::POST_DISPATCH_NONE;
370 ///////////////////////////////////////////////////////////////////////////////
372 DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
373 aura::Window* root_window,
374 views::DesktopNativeCursorManager* cursor_manager,
378 root_window_(root_window),
381 atom_cache_(xdisplay_, kAtomsToCache),
382 target_window_(NULL),
383 waiting_on_status_(false),
384 status_received_since_enter_(false),
385 source_provider_(NULL),
386 source_current_window_(None),
387 source_state_(SOURCE_STATE_OTHER),
389 negotiated_operation_(ui::DragDropTypes::DRAG_NONE),
390 grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorGrabbing)),
391 copy_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorCopy)),
392 move_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorMove)),
393 weak_ptr_factory_(this) {
394 DCHECK(g_live_client_map.Get().find(xwindow) ==
395 g_live_client_map.Get().end());
396 g_live_client_map.Get().insert(std::make_pair(xwindow, this));
398 // Mark that we are aware of drag and drop concepts.
399 unsigned long xdnd_version = kMinXdndVersion;
400 XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"),
401 XA_ATOM, 32, PropModeReplace,
402 reinterpret_cast<unsigned char*>(&xdnd_version), 1);
405 DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() {
406 g_live_client_map.Get().erase(xwindow_);
407 // Make sure that all observers are unregistered from source and target
408 // windows. This may be necessary when the parent native widget gets destroyed
409 // while a drag operation is in progress.
414 DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow(
416 std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it =
417 g_live_client_map.Get().find(window);
418 if (it == g_live_client_map.Get().end())
423 void DesktopDragDropClientAuraX11::OnXdndEnter(
424 const XClientMessageEvent& event) {
425 DVLOG(1) << "XdndEnter";
427 int version = (event.data.l[1] & 0xff000000) >> 24;
429 LOG(ERROR) << "Received old XdndEnter message.";
433 // Make sure that we've run ~X11DragContext() before creating another one.
434 target_current_context_.reset();
435 target_current_context_.reset(
436 new X11DragContext(&atom_cache_, xwindow_, event));
438 // In the Windows implementation, we immediately call DesktopDropTargetWin::
439 // Translate(). Here, we wait until we receive an XdndPosition message
440 // because the enter message doesn't convey any positioning
444 void DesktopDragDropClientAuraX11::OnXdndLeave(
445 const XClientMessageEvent& event) {
446 DVLOG(1) << "XdndLeave";
448 target_current_context_.reset();
451 void DesktopDragDropClientAuraX11::OnXdndPosition(
452 const XClientMessageEvent& event) {
453 DVLOG(1) << "XdndPosition";
455 unsigned long source_window = event.data.l[0];
456 int x_root_window = event.data.l[2] >> 16;
457 int y_root_window = event.data.l[2] & 0xffff;
458 ::Atom suggested_action = event.data.l[4];
460 if (!target_current_context_.get()) {
465 // If we already have all the data from this drag, we complete it
467 target_current_context_->OnStartXdndPositionMessage(
468 this, suggested_action, source_window,
469 gfx::Point(x_root_window, y_root_window));
472 void DesktopDragDropClientAuraX11::OnXdndStatus(
473 const XClientMessageEvent& event) {
474 DVLOG(1) << "XdndStatus";
476 unsigned long source_window = event.data.l[0];
478 if (source_window != source_current_window_)
481 if (source_state_ != SOURCE_STATE_PENDING_DROP &&
482 source_state_ != SOURCE_STATE_OTHER) {
486 waiting_on_status_ = false;
487 status_received_since_enter_ = true;
489 if (event.data.l[1] & 1) {
490 ::Atom atom_operation = event.data.l[4];
491 negotiated_operation_ = AtomToDragOperation(atom_operation);
493 negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
496 if (source_state_ == SOURCE_STATE_PENDING_DROP) {
497 // We were waiting on the status message so we could send the XdndDrop.
498 if (negotiated_operation_ == ui::DragDropTypes::DRAG_NONE) {
499 move_loop_.EndMoveLoop();
502 source_state_ = SOURCE_STATE_DROPPED;
503 SendXdndDrop(source_window);
507 switch (negotiated_operation_) {
508 case ui::DragDropTypes::DRAG_COPY:
509 move_loop_.UpdateCursor(copy_grab_cursor_);
511 case ui::DragDropTypes::DRAG_MOVE:
512 move_loop_.UpdateCursor(move_grab_cursor_);
515 move_loop_.UpdateCursor(grab_cursor_);
519 // Note: event.data.[2,3] specify a rectangle. It is a request by the other
520 // window to not send further XdndPosition messages while the cursor is
521 // within it. However, it is considered advisory and (at least according to
522 // the spec) the other side must handle further position messages within
523 // it. GTK+ doesn't bother with this, so neither should we.
525 if (next_position_message_.get()) {
526 // We were waiting on the status message so we could send off the next
527 // position message we queued up.
528 gfx::Point p = next_position_message_->first;
529 unsigned long event_time = next_position_message_->second;
530 next_position_message_.reset();
532 SendXdndPosition(source_window, p, event_time);
536 void DesktopDragDropClientAuraX11::OnXdndFinished(
537 const XClientMessageEvent& event) {
538 DVLOG(1) << "XdndFinished";
540 // Clear |source_current_window_| to avoid sending XdndLeave upon ending the
542 source_current_window_ = None;
543 move_loop_.EndMoveLoop();
546 void DesktopDragDropClientAuraX11::OnXdndDrop(
547 const XClientMessageEvent& event) {
548 DVLOG(1) << "XdndDrop";
550 unsigned long source_window = event.data.l[0];
552 int drag_operation = ui::DragDropTypes::DRAG_NONE;
553 if (target_window_) {
554 aura::client::DragDropDelegate* delegate =
555 aura::client::GetDragDropDelegate(target_window_);
557 ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11(
558 xwindow_, target_current_context_->fetched_targets()));
560 ui::DropTargetEvent event(data,
561 target_window_location_,
562 target_window_root_location_,
563 target_current_context_->GetDragOperation());
564 drag_operation = delegate->OnPerformDrop(event);
567 target_window_->RemoveObserver(this);
568 target_window_ = NULL;
572 xev.xclient.type = ClientMessage;
573 xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished");
574 xev.xclient.format = 32;
575 xev.xclient.window = source_window;
576 xev.xclient.data.l[0] = xwindow_;
577 xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0;
578 xev.xclient.data.l[2] = DragOperationToAtom(drag_operation);
580 SendXClientEvent(source_window, &xev);
583 void DesktopDragDropClientAuraX11::OnSelectionNotify(
584 const XSelectionEvent& xselection) {
585 if (target_current_context_)
586 target_current_context_->OnSelectionNotify(xselection);
588 // ICCCM requires us to delete the property passed into SelectionNotify.
589 if (xselection.property != None)
590 XDeleteProperty(xdisplay_, xwindow_, xselection.property);
593 int DesktopDragDropClientAuraX11::StartDragAndDrop(
594 const ui::OSExchangeData& data,
595 aura::Window* root_window,
596 aura::Window* source_window,
597 const gfx::Point& root_location,
599 ui::DragDropTypes::DragEventSource source) {
600 source_current_window_ = None;
601 DCHECK(!g_current_drag_drop_client);
602 g_current_drag_drop_client = this;
603 waiting_on_status_ = false;
604 next_position_message_.reset();
605 status_received_since_enter_ = false;
606 source_state_ = SOURCE_STATE_OTHER;
607 drag_operation_ = operation;
608 negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
610 const ui::OSExchangeData::Provider* provider = &data.provider();
611 source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>(
614 source_provider_->TakeOwnershipOfSelection();
616 std::vector< ::Atom> actions = GetOfferedDragOperations();
617 if (!source_provider_->file_contents_name().empty()) {
618 actions.push_back(atom_cache_.GetAtom(kXdndActionDirectSave));
619 ui::SetStringProperty(
621 atom_cache_.GetAtom(kXdndDirectSave0),
622 atom_cache_.GetAtom(ui::Clipboard::kMimeTypeText),
623 source_provider_->file_contents_name().AsUTF8Unsafe());
625 ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions);
627 // It is possible for the DesktopWindowTreeHostX11 to be destroyed during the
628 // move loop, which would also destroy this drag-client. So keep track of
629 // whether it is alive after the drag ends.
630 base::WeakPtr<DesktopDragDropClientAuraX11> alive(
631 weak_ptr_factory_.GetWeakPtr());
633 // Windows has a specific method, DoDragDrop(), which performs the entire
634 // drag. We have to emulate this, so we spin off a nested runloop which will
635 // track all cursor movement and reroute events to a specific handler.
636 move_loop_.SetDragImage(source_provider_->GetDragImage(),
637 source_provider_->GetDragImageOffset());
638 move_loop_.RunMoveLoop(source_window, grab_cursor_);
641 move_loop_.SetDragImage(gfx::ImageSkia(), gfx::Vector2dF());
643 source_provider_ = NULL;
644 g_current_drag_drop_client = NULL;
646 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList"));
647 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom(kXdndDirectSave0));
649 return negotiated_operation_;
651 return ui::DragDropTypes::DRAG_NONE;
654 void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target,
655 const ui::LocatedEvent& event) {
659 void DesktopDragDropClientAuraX11::Drop(aura::Window* target,
660 const ui::LocatedEvent& event) {
664 void DesktopDragDropClientAuraX11::DragCancel() {
665 move_loop_.EndMoveLoop();
668 bool DesktopDragDropClientAuraX11::IsDragDropInProgress() {
669 return !!g_current_drag_drop_client;
672 void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
673 DCHECK_EQ(target_window_, window);
674 target_window_ = NULL;
677 void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) {
678 repeat_mouse_move_timer_.Stop();
679 ProcessMouseMove(gfx::Point(event->x_root, event->y_root), event->time);
682 void DesktopDragDropClientAuraX11::OnMouseReleased() {
683 repeat_mouse_move_timer_.Stop();
685 if (source_state_ != SOURCE_STATE_OTHER) {
686 // The user has previously released the mouse and is clicking in
688 move_loop_.EndMoveLoop();
692 if (source_current_window_ != None) {
693 if (waiting_on_status_) {
694 if (status_received_since_enter_) {
695 // If we are waiting for an XdndStatus message, we need to wait for it
697 source_state_ = SOURCE_STATE_PENDING_DROP;
699 // Start timer to end the move loop if the target takes too long to send
700 // the XdndStatus and XdndFinished messages.
701 StartEndMoveLoopTimer();
705 move_loop_.EndMoveLoop();
709 if (negotiated_operation_ != ui::DragDropTypes::DRAG_NONE) {
710 // We have negotiated an action with the other end.
711 source_state_ = SOURCE_STATE_DROPPED;
712 SendXdndDrop(source_current_window_);
714 // Start timer to end the move loop if the target takes too long to send
715 // an XdndFinished message.
716 StartEndMoveLoopTimer();
721 move_loop_.EndMoveLoop();
724 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() {
725 if (source_current_window_ != None) {
726 SendXdndLeave(source_current_window_);
727 source_current_window_ = None;
729 target_current_context_.reset();
730 repeat_mouse_move_timer_.Stop();
731 end_move_loop_timer_.Stop();
734 void DesktopDragDropClientAuraX11::ProcessMouseMove(
735 const gfx::Point& screen_point,
736 unsigned long event_time) {
737 if (source_state_ != SOURCE_STATE_OTHER)
740 // Find the current window the cursor is over.
741 ::Window mouse_window = None;
742 ::Window dest_window = None;
743 FindWindowFor(screen_point, &mouse_window, &dest_window);
745 if (source_current_window_ != dest_window) {
746 if (source_current_window_ != None)
747 SendXdndLeave(source_current_window_);
749 source_current_window_ = dest_window;
750 waiting_on_status_ = false;
751 next_position_message_.reset();
752 status_received_since_enter_ = false;
753 negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
755 if (source_current_window_ != None)
756 SendXdndEnter(source_current_window_);
759 if (source_current_window_ != None) {
760 if (waiting_on_status_) {
761 next_position_message_.reset(
762 new std::pair<gfx::Point, unsigned long>(screen_point, event_time));
764 SendXdndPosition(dest_window, screen_point, event_time);
769 void DesktopDragDropClientAuraX11::StartEndMoveLoopTimer() {
770 end_move_loop_timer_.Start(FROM_HERE,
771 base::TimeDelta::FromMilliseconds(
772 kEndMoveLoopTimeoutMs),
774 &DesktopDragDropClientAuraX11::EndMoveLoop);
777 void DesktopDragDropClientAuraX11::EndMoveLoop() {
778 move_loop_.EndMoveLoop();
781 void DesktopDragDropClientAuraX11::DragTranslate(
782 const gfx::Point& root_window_location,
783 scoped_ptr<ui::OSExchangeData>* data,
784 scoped_ptr<ui::DropTargetEvent>* event,
785 aura::client::DragDropDelegate** delegate) {
786 gfx::Point root_location = root_window_location;
787 root_window_->GetHost()->ConvertPointFromNativeScreen(&root_location);
788 aura::Window* target_window =
789 root_window_->GetEventHandlerForPoint(root_location);
790 bool target_window_changed = false;
791 if (target_window != target_window_) {
794 target_window_ = target_window;
796 target_window_->AddObserver(this);
797 target_window_changed = true;
802 *delegate = aura::client::GetDragDropDelegate(target_window_);
806 data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11(
807 xwindow_, target_current_context_->fetched_targets())));
808 gfx::Point location = root_location;
809 aura::Window::ConvertPointToTarget(root_window_, target_window_, &location);
811 target_window_location_ = location;
812 target_window_root_location_ = root_location;
814 event->reset(new ui::DropTargetEvent(
818 target_current_context_->GetDragOperation()));
819 if (target_window_changed)
820 (*delegate)->OnDragEntered(*event->get());
823 void DesktopDragDropClientAuraX11::NotifyDragLeave() {
826 DragDropDelegate* delegate =
827 aura::client::GetDragDropDelegate(target_window_);
829 delegate->OnDragExited();
830 target_window_->RemoveObserver(this);
831 target_window_ = NULL;
834 ::Atom DesktopDragDropClientAuraX11::DragOperationToAtom(
835 int drag_operation) {
836 if (drag_operation & ui::DragDropTypes::DRAG_COPY)
837 return atom_cache_.GetAtom(kXdndActionCopy);
838 if (drag_operation & ui::DragDropTypes::DRAG_MOVE)
839 return atom_cache_.GetAtom(kXdndActionMove);
840 if (drag_operation & ui::DragDropTypes::DRAG_LINK)
841 return atom_cache_.GetAtom(kXdndActionLink);
846 ui::DragDropTypes::DragOperation
847 DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
848 if (atom == atom_cache_.GetAtom(kXdndActionCopy))
849 return ui::DragDropTypes::DRAG_COPY;
850 if (atom == atom_cache_.GetAtom(kXdndActionMove))
851 return ui::DragDropTypes::DRAG_MOVE;
852 if (atom == atom_cache_.GetAtom(kXdndActionLink))
853 return ui::DragDropTypes::DRAG_LINK;
855 return ui::DragDropTypes::DRAG_NONE;
858 std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() {
859 std::vector< ::Atom> operations;
860 if (drag_operation_ & ui::DragDropTypes::DRAG_COPY)
861 operations.push_back(atom_cache_.GetAtom(kXdndActionCopy));
862 if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE)
863 operations.push_back(atom_cache_.GetAtom(kXdndActionMove));
864 if (drag_operation_ & ui::DragDropTypes::DRAG_LINK)
865 operations.push_back(atom_cache_.GetAtom(kXdndActionLink));
869 ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const {
870 return source_provider_ ? source_provider_->GetFormatMap() :
871 ui::SelectionFormatMap();
874 void DesktopDragDropClientAuraX11::CompleteXdndPosition(
875 ::Window source_window,
876 const gfx::Point& screen_point) {
877 int drag_operation = ui::DragDropTypes::DRAG_NONE;
878 scoped_ptr<ui::OSExchangeData> data;
879 scoped_ptr<ui::DropTargetEvent> drop_target_event;
880 DragDropDelegate* delegate = NULL;
881 DragTranslate(screen_point, &data, &drop_target_event, &delegate);
883 drag_operation = delegate->OnDragUpdated(*drop_target_event);
885 // Sends an XdndStatus message back to the source_window. l[2,3]
886 // theoretically represent an area in the window where the current action is
887 // the same as what we're returning, but I can't find any implementation that
888 // actually making use of this. A client can return (0, 0) and/or set the
889 // first bit of l[1] to disable the feature, and it appears that gtk neither
890 // sets this nor respects it if set.
892 xev.xclient.type = ClientMessage;
893 xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus");
894 xev.xclient.format = 32;
895 xev.xclient.window = source_window;
896 xev.xclient.data.l[0] = xwindow_;
897 xev.xclient.data.l[1] = (drag_operation != 0) ?
898 (kWantFurtherPosEvents | kWillAcceptDrop) : 0;
899 xev.xclient.data.l[2] = 0;
900 xev.xclient.data.l[3] = 0;
901 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation);
903 SendXClientEvent(source_window, &xev);
906 void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) {
908 xev.xclient.type = ClientMessage;
909 xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter");
910 xev.xclient.format = 32;
911 xev.xclient.window = dest_window;
912 xev.xclient.data.l[0] = xwindow_;
913 xev.xclient.data.l[1] = (kMinXdndVersion << 24); // The version number.
914 xev.xclient.data.l[2] = 0;
915 xev.xclient.data.l[3] = 0;
916 xev.xclient.data.l[4] = 0;
918 std::vector<Atom> targets;
919 source_provider_->RetrieveTargets(&targets);
921 if (targets.size() > 3) {
922 xev.xclient.data.l[1] |= 1;
923 ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets);
925 // Pack the targets into the enter message.
926 for (size_t i = 0; i < targets.size(); ++i)
927 xev.xclient.data.l[2 + i] = targets[i];
930 SendXClientEvent(dest_window, &xev);
933 void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
935 xev.xclient.type = ClientMessage;
936 xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave");
937 xev.xclient.format = 32;
938 xev.xclient.window = dest_window;
939 xev.xclient.data.l[0] = xwindow_;
940 xev.xclient.data.l[1] = 0;
941 xev.xclient.data.l[2] = 0;
942 xev.xclient.data.l[3] = 0;
943 xev.xclient.data.l[4] = 0;
944 SendXClientEvent(dest_window, &xev);
947 void DesktopDragDropClientAuraX11::SendXdndPosition(
948 ::Window dest_window,
949 const gfx::Point& screen_point,
950 unsigned long event_time) {
951 waiting_on_status_ = true;
954 xev.xclient.type = ClientMessage;
955 xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition");
956 xev.xclient.format = 32;
957 xev.xclient.window = dest_window;
958 xev.xclient.data.l[0] = xwindow_;
959 xev.xclient.data.l[1] = 0;
960 xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y();
961 xev.xclient.data.l[3] = event_time;
962 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
963 SendXClientEvent(dest_window, &xev);
965 // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html and
966 // the Xdnd protocol both recommend that drag events should be sent
968 repeat_mouse_move_timer_.Start(
970 base::TimeDelta::FromMilliseconds(kRepeatMouseMoveTimeoutMs),
971 base::Bind(&DesktopDragDropClientAuraX11::ProcessMouseMove,
972 base::Unretained(this),
977 void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {
979 xev.xclient.type = ClientMessage;
980 xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop");
981 xev.xclient.format = 32;
982 xev.xclient.window = dest_window;
983 xev.xclient.data.l[0] = xwindow_;
984 xev.xclient.data.l[1] = 0;
985 xev.xclient.data.l[2] = CurrentTime;
986 xev.xclient.data.l[3] = None;
987 xev.xclient.data.l[4] = None;
988 SendXClientEvent(dest_window, &xev);
991 void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid,
993 DCHECK_EQ(ClientMessage, xev->type);
995 // Don't send messages to the X11 message queue if we can help it.
996 DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid);
998 Atom message_type = xev->xclient.message_type;
999 if (message_type == atom_cache_.GetAtom("XdndEnter")) {
1000 short_circuit->OnXdndEnter(xev->xclient);
1002 } else if (message_type == atom_cache_.GetAtom("XdndLeave")) {
1003 short_circuit->OnXdndLeave(xev->xclient);
1005 } else if (message_type == atom_cache_.GetAtom("XdndPosition")) {
1006 short_circuit->OnXdndPosition(xev->xclient);
1008 } else if (message_type == atom_cache_.GetAtom("XdndStatus")) {
1009 short_circuit->OnXdndStatus(xev->xclient);
1011 } else if (message_type == atom_cache_.GetAtom("XdndFinished")) {
1012 short_circuit->OnXdndFinished(xev->xclient);
1014 } else if (message_type == atom_cache_.GetAtom("XdndDrop")) {
1015 short_circuit->OnXdndDrop(xev->xclient);
1020 // I don't understand why the GTK+ code is doing what it's doing here. It
1021 // goes out of its way to send the XEvent so that it receives a callback on
1022 // success or failure, and when it fails, it then sends an internal
1023 // GdkEvent about the failed drag. (And sending this message doesn't appear
1024 // to go through normal xlib machinery, but instead passes through the low
1025 // level xProto (the x11 wire format) that I don't understand.
1027 // I'm unsure if I have to jump through those hoops, or if XSendEvent is
1029 XSendEvent(xdisplay_, xid, False, 0, xev);
1032 } // namespace views