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 "base/message_loop/message_pump_dispatcher.h"
13 #include "ui/aura/window.h"
14 #include "ui/aura/window_tree_host.h"
15 #include "ui/base/clipboard/clipboard.h"
16 #include "ui/base/dragdrop/drag_drop_types.h"
17 #include "ui/base/dragdrop/drop_target_event.h"
18 #include "ui/base/dragdrop/os_exchange_data.h"
19 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
20 #include "ui/base/x/selection_utils.h"
21 #include "ui/base/x/x11_util.h"
22 #include "ui/events/event.h"
23 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.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,
67 "_NET_WM_WINDOW_TYPE_MENU",
71 // The time to wait for the target to respond after the user has released the
72 // mouse button before ending the move loop.
73 const int kEndMoveLoopTimeoutMs = 30000;
75 static base::LazyInstance<
76 std::map< ::Window, views::DesktopDragDropClientAuraX11*> >::Leaky
77 g_live_client_map = LAZY_INSTANCE_INITIALIZER;
79 // Helper class to FindWindowFor which looks for a drag target under the
81 class DragTargetWindowFinder : public ui::EnumerateWindowsDelegate {
83 DragTargetWindowFinder(XID ignored_icon_window,
85 gfx::Point screen_loc)
86 : ignored_icon_window_(ignored_icon_window),
88 menu_type_atom_(menu_type_atom),
89 screen_loc_(screen_loc) {
90 ui::EnumerateTopLevelWindows(this);
93 virtual ~DragTargetWindowFinder() {}
95 XID window() const { return output_window_; }
98 virtual bool ShouldStopIterating(XID window) OVERRIDE {
99 if (window == ignored_icon_window_)
102 if (!ui::IsWindowVisible(window))
105 if (!ui::WindowContainsPoint(window, screen_loc_))
109 if (ui::PropertyExists(window, "WM_STATE") ||
110 (ui::GetIntProperty(window, "_NET_WM_WINDOW_TYPE", &value) &&
111 static_cast<Atom>(value) == menu_type_atom_)) {
112 output_window_ = window;
120 XID ignored_icon_window_;
122 const Atom menu_type_atom_;
123 gfx::Point screen_loc_;
125 DISALLOW_COPY_AND_ASSIGN(DragTargetWindowFinder);
128 // Returns the topmost X11 window at |screen_point| if it is advertising that
129 // is supports the Xdnd protocol. Will return the window under the pointer as
130 // |mouse_window|. If there's a Xdnd aware window, it will be returned in
132 void FindWindowFor(const gfx::Point& screen_point,
133 ::Window* mouse_window, ::Window* dest_window,
134 Atom menu_type_atom) {
135 DragTargetWindowFinder finder(None, menu_type_atom, screen_point);
136 *mouse_window = finder.window();
139 if (finder.window() == None)
142 // Figure out which window we should test as XdndAware. If mouse_window has
143 // XdndProxy, it will set that proxy on target, and if not, |target|'s
144 // original value will remain.
145 XID target = *mouse_window;
146 ui::GetXIDProperty(*mouse_window, "XdndProxy", &target);
149 if (ui::GetIntProperty(target, "XdndAware", &version) &&
150 version >= kMinXdndVersion) {
151 *dest_window = target;
159 DesktopDragDropClientAuraX11*
160 DesktopDragDropClientAuraX11::g_current_drag_drop_client = NULL;
162 class DesktopDragDropClientAuraX11::X11DragContext :
163 public base::MessagePumpDispatcher {
165 X11DragContext(ui::X11AtomCache* atom_cache,
166 ::Window local_window,
167 const XClientMessageEvent& event);
168 virtual ~X11DragContext();
170 // When we receive an XdndPosition message, we need to have all the data
171 // copied from the other window before we process the XdndPosition
172 // message. If we have that data already, dispatch immediately. Otherwise,
173 // delay dispatching until we do.
174 void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client,
175 ::Atom suggested_action,
176 ::Window source_window,
177 const gfx::Point& screen_point);
179 // Called to request the next target from the source window. This is only
180 // done on the first XdndPosition; after that, we cache the data offered by
181 // the source window.
182 void RequestNextTarget();
184 // Called when XSelection data has been copied to our process.
185 void OnSelectionNotify(const XSelectionEvent& xselection);
187 // Clones the fetched targets.
188 const ui::SelectionFormatMap& fetched_targets() { return fetched_targets_; }
190 // Reads the "XdndActionList" property from |source_window| and copies it
194 // Creates a ui::DragDropTypes::DragOperation representation of the current
196 int GetDragOperation() const;
199 // Masks the X11 atom |xdnd_operation|'s views representation onto
201 void MaskOpeartion(::Atom xdnd_operation, int* drag_operation) const;
203 // Overridden from MessagePumpDispatcher:
204 virtual uint32_t Dispatch(const base::NativeEvent& event) OVERRIDE;
206 // The atom cache owned by our parent.
207 ui::X11AtomCache* atom_cache_;
209 // The XID of our chrome local aura window handling our events.
210 ::Window local_window_;
212 // The XID of the window that's initiated the drag.
213 unsigned long source_window_;
215 // The client we inform once we're done with requesting data.
216 DesktopDragDropClientAuraX11* drag_drop_client_;
218 // Whether we're blocking the handling of an XdndPosition message by waiting
219 // for |unfetched_targets_| to be fetched.
220 bool waiting_to_handle_position_;
222 // Where the cursor is on screen.
223 gfx::Point screen_point_;
225 // A SelectionFormatMap of data that we have in our process.
226 ui::SelectionFormatMap fetched_targets_;
228 // The names of various data types offered by the other window that we
229 // haven't fetched and put in |fetched_targets_| yet.
230 std::vector<Atom> unfetched_targets_;
232 // XdndPosition messages have a suggested action. Qt applications exclusively
233 // use this, instead of the XdndActionList which is backed by |actions_|.
234 Atom suggested_action_;
237 std::vector<Atom> actions_;
239 DISALLOW_COPY_AND_ASSIGN(X11DragContext);
242 DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
243 ui::X11AtomCache* atom_cache,
244 ::Window local_window,
245 const XClientMessageEvent& event)
246 : atom_cache_(atom_cache),
247 local_window_(local_window),
248 source_window_(event.data.l[0]),
249 drag_drop_client_(NULL),
250 waiting_to_handle_position_(false),
251 suggested_action_(None) {
252 bool get_types = ((event.data.l[1] & 1) != 0);
255 if (!ui::GetAtomArrayProperty(source_window_,
257 &unfetched_targets_)) {
261 // data.l[2,3,4] contain the first three types. Unused slots can be None.
262 for (int i = 0; i < 3; ++i) {
263 if (event.data.l[2+i] != None) {
264 unfetched_targets_.push_back(event.data.l[2+i]);
269 DesktopDragDropClientAuraX11* client =
270 DesktopDragDropClientAuraX11::GetForWindow(source_window_);
272 // The window doesn't have a DesktopDragDropClientAuraX11, that means it's
273 // created by some other process. Listen for messages on it.
274 base::MessagePumpX11::Current()->AddDispatcherForWindow(
275 this, source_window_);
276 XSelectInput(gfx::GetXDisplay(), source_window_, PropertyChangeMask);
278 // We must perform a full sync here because we could be racing
280 XSync(gfx::GetXDisplay(), False);
282 // This drag originates from an aura window within our process. This means
283 // that we can shortcut the X11 server and ask the owning SelectionOwner
284 // for the data it's offering.
285 fetched_targets_ = client->GetFormatMap();
286 unfetched_targets_.clear();
292 DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() {
293 DesktopDragDropClientAuraX11* client =
294 DesktopDragDropClientAuraX11::GetForWindow(source_window_);
296 // Unsubscribe from message events.
297 base::MessagePumpX11::Current()->RemoveDispatcherForWindow(
302 void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage(
303 DesktopDragDropClientAuraX11* client,
304 ::Atom suggested_action,
305 ::Window source_window,
306 const gfx::Point& screen_point) {
307 DCHECK_EQ(source_window_, source_window);
308 suggested_action_ = suggested_action;
310 if (!unfetched_targets_.empty()) {
311 // We have unfetched targets. That means we need to pause the handling of
312 // the position message and ask the other window for its data.
313 screen_point_ = screen_point;
314 drag_drop_client_ = client;
315 waiting_to_handle_position_ = true;
317 fetched_targets_ = ui::SelectionFormatMap();
320 client->CompleteXdndPosition(source_window, screen_point);
324 void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() {
325 ::Atom target = unfetched_targets_.back();
326 unfetched_targets_.pop_back();
328 XConvertSelection(gfx::GetXDisplay(),
329 atom_cache_->GetAtom(kXdndSelection),
331 atom_cache_->GetAtom(kChromiumDragReciever),
336 void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify(
337 const XSelectionEvent& event) {
338 DCHECK(waiting_to_handle_position_);
339 DCHECK(drag_drop_client_);
340 DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever));
342 scoped_refptr<base::RefCountedMemory> data;
344 if (ui::GetRawBytesOfProperty(local_window_, event.property,
345 &data, NULL, NULL, &type)) {
346 fetched_targets_.Insert(event.target, data);
349 if (!unfetched_targets_.empty()) {
352 waiting_to_handle_position_ = false;
353 drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_);
354 drag_drop_client_ = NULL;
358 void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
359 DesktopDragDropClientAuraX11* client =
360 DesktopDragDropClientAuraX11::GetForWindow(source_window_);
362 std::vector<Atom> atom_array;
363 if (!ui::GetAtomArrayProperty(source_window_,
368 actions_.swap(atom_array);
371 // We have a property notify set up for other windows in case they change
372 // their action list. Thankfully, the views interface is static and you
373 // can't change the action list after you enter StartDragAndDrop().
374 actions_ = client->GetOfferedDragOperations();
378 int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const {
379 int drag_operation = ui::DragDropTypes::DRAG_NONE;
380 for (std::vector<Atom>::const_iterator it = actions_.begin();
381 it != actions_.end(); ++it) {
382 MaskOpeartion(*it, &drag_operation);
385 MaskOpeartion(suggested_action_, &drag_operation);
387 return drag_operation;
390 void DesktopDragDropClientAuraX11::X11DragContext::MaskOpeartion(
391 ::Atom xdnd_operation,
392 int* drag_operation) const {
393 if (xdnd_operation == atom_cache_->GetAtom(kXdndActionCopy))
394 *drag_operation |= ui::DragDropTypes::DRAG_COPY;
395 else if (xdnd_operation == atom_cache_->GetAtom(kXdndActionMove))
396 *drag_operation |= ui::DragDropTypes::DRAG_MOVE;
397 else if (xdnd_operation == atom_cache_->GetAtom(kXdndActionLink))
398 *drag_operation |= ui::DragDropTypes::DRAG_LINK;
401 uint32_t DesktopDragDropClientAuraX11::X11DragContext::Dispatch(
402 const base::NativeEvent& event) {
403 if (event->type == PropertyNotify &&
404 event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) {
407 return POST_DISPATCH_NONE;
410 ///////////////////////////////////////////////////////////////////////////////
412 DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
413 aura::Window* root_window,
414 views::DesktopNativeCursorManager* cursor_manager,
418 root_window_(root_window),
421 atom_cache_(xdisplay_, kAtomsToCache),
422 target_window_(NULL),
423 source_provider_(NULL),
424 source_current_window_(None),
425 source_state_(SOURCE_STATE_OTHER),
427 resulting_operation_(0),
428 grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorGrabbing)),
429 copy_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorCopy)),
430 move_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorMove)),
431 weak_ptr_factory_(this) {
432 DCHECK(g_live_client_map.Get().find(xwindow) ==
433 g_live_client_map.Get().end());
434 g_live_client_map.Get().insert(std::make_pair(xwindow, this));
436 // Mark that we are aware of drag and drop concepts.
437 unsigned long xdnd_version = kMinXdndVersion;
438 XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"),
439 XA_ATOM, 32, PropModeReplace,
440 reinterpret_cast<unsigned char*>(&xdnd_version), 1);
443 DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() {
444 g_live_client_map.Get().erase(xwindow_);
445 // Make sure that all observers are unregistered from source and target
446 // windows. This may be necessary when the parent native widget gets destroyed
447 // while a drag operation is in progress.
452 DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow(
454 std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it =
455 g_live_client_map.Get().find(window);
456 if (it == g_live_client_map.Get().end())
461 void DesktopDragDropClientAuraX11::OnXdndEnter(
462 const XClientMessageEvent& event) {
463 DVLOG(1) << "XdndEnter";
465 int version = (event.data.l[1] & 0xff000000) >> 24;
467 LOG(ERROR) << "Received old XdndEnter message.";
471 // Make sure that we've run ~X11DragContext() before creating another one.
472 target_current_context_.reset();
473 target_current_context_.reset(
474 new X11DragContext(&atom_cache_, xwindow_, event));
476 // In the Windows implementation, we immediately call DesktopDropTargetWin::
477 // Translate(). Here, we wait until we receive an XdndPosition message
478 // because the enter message doesn't convey any positioning
482 void DesktopDragDropClientAuraX11::OnXdndLeave(
483 const XClientMessageEvent& event) {
484 DVLOG(1) << "XdndLeave";
486 target_current_context_.reset();
489 void DesktopDragDropClientAuraX11::OnXdndPosition(
490 const XClientMessageEvent& event) {
491 DVLOG(1) << "XdndPosition";
493 unsigned long source_window = event.data.l[0];
494 int x_root_window = event.data.l[2] >> 16;
495 int y_root_window = event.data.l[2] & 0xffff;
496 ::Atom suggested_action = event.data.l[4];
498 if (!target_current_context_.get()) {
503 // If we already have all the data from this drag, we complete it
505 target_current_context_->OnStartXdndPositionMessage(
506 this, suggested_action, source_window,
507 gfx::Point(x_root_window, y_root_window));
510 void DesktopDragDropClientAuraX11::OnXdndStatus(
511 const XClientMessageEvent& event) {
512 DVLOG(1) << "XdndStatus";
514 unsigned long source_window = event.data.l[0];
516 waiting_on_status_.erase(source_window);
517 if (source_window != source_current_window_)
520 int drag_operation = ui::DragDropTypes::DRAG_NONE;
521 if (event.data.l[1] & 1) {
522 ::Atom atom_operation = event.data.l[4];
523 negotiated_operation_[source_window] = atom_operation;
524 drag_operation = AtomToDragOperation(atom_operation);
527 switch (drag_operation) {
528 case ui::DragDropTypes::DRAG_COPY:
529 move_loop_.UpdateCursor(copy_grab_cursor_);
531 case ui::DragDropTypes::DRAG_MOVE:
532 move_loop_.UpdateCursor(move_grab_cursor_);
535 move_loop_.UpdateCursor(grab_cursor_);
539 // Note: event.data.[2,3] specify a rectangle. It is a request by the other
540 // window to not send further XdndPosition messages while the cursor is
541 // within it. However, it is considered advisory and (at least according to
542 // the spec) the other side must handle further position messages within
543 // it. GTK+ doesn't bother with this, so neither should we.
545 if (source_state_ == SOURCE_STATE_PENDING_DROP) {
546 // We were waiting on the status message so we could send the XdndDrop.
547 source_state_ = SOURCE_STATE_DROPPED;
548 SendXdndDrop(source_window);
552 NextPositionMap::iterator it = next_position_message_.find(source_window);
553 if (source_state_ == SOURCE_STATE_OTHER &&
554 it != next_position_message_.end()) {
555 // We were waiting on the status message so we could send off the next
556 // position message we queued up.
557 gfx::Point p = it->second.first;
558 unsigned long time = it->second.second;
559 next_position_message_.erase(it);
561 SendXdndPosition(source_window, p, time);
565 void DesktopDragDropClientAuraX11::OnXdndFinished(
566 const XClientMessageEvent& event) {
567 DVLOG(1) << "XdndFinished";
568 resulting_operation_ = AtomToDragOperation(
569 negotiated_operation_[event.data.l[0]]);
570 move_loop_.EndMoveLoop();
573 void DesktopDragDropClientAuraX11::OnXdndDrop(
574 const XClientMessageEvent& event) {
575 DVLOG(1) << "XdndDrop";
577 unsigned long source_window = event.data.l[0];
579 int drag_operation = ui::DragDropTypes::DRAG_NONE;
580 if (target_window_) {
581 aura::client::DragDropDelegate* delegate =
582 aura::client::GetDragDropDelegate(target_window_);
584 ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11(
585 xwindow_, target_current_context_->fetched_targets()));
587 ui::DropTargetEvent event(data,
588 target_window_location_,
589 target_window_root_location_,
590 target_current_context_->GetDragOperation());
591 drag_operation = delegate->OnPerformDrop(event);
594 target_window_->RemoveObserver(this);
595 target_window_ = NULL;
599 xev.xclient.type = ClientMessage;
600 xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished");
601 xev.xclient.format = 32;
602 xev.xclient.window = source_window;
603 xev.xclient.data.l[0] = xwindow_;
604 xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0;
605 xev.xclient.data.l[2] = DragOperationToAtom(drag_operation);
607 SendXClientEvent(source_window, &xev);
610 void DesktopDragDropClientAuraX11::OnSelectionNotify(
611 const XSelectionEvent& xselection) {
612 if (!target_current_context_) {
617 target_current_context_->OnSelectionNotify(xselection);
620 int DesktopDragDropClientAuraX11::StartDragAndDrop(
621 const ui::OSExchangeData& data,
622 aura::Window* root_window,
623 aura::Window* source_window,
624 const gfx::Point& root_location,
626 ui::DragDropTypes::DragEventSource source) {
627 source_current_window_ = None;
628 DCHECK(!g_current_drag_drop_client);
629 g_current_drag_drop_client = this;
630 waiting_on_status_.clear();
631 source_state_ = SOURCE_STATE_OTHER;
632 drag_operation_ = operation;
633 resulting_operation_ = ui::DragDropTypes::DRAG_NONE;
635 const ui::OSExchangeData::Provider* provider = &data.provider();
636 source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>(
639 source_provider_->TakeOwnershipOfSelection();
641 std::vector< ::Atom> actions = GetOfferedDragOperations();
642 if (!source_provider_->file_contents_name().empty()) {
643 actions.push_back(atom_cache_.GetAtom(kXdndActionDirectSave));
644 ui::SetStringProperty(
646 atom_cache_.GetAtom(kXdndDirectSave0),
647 atom_cache_.GetAtom(ui::Clipboard::kMimeTypeText),
648 source_provider_->file_contents_name().AsUTF8Unsafe());
650 ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions);
652 // It is possible for the DesktopWindowTreeHostX11 to be destroyed during the
653 // move loop, which would also destroy this drag-client. So keep track of
654 // whether it is alive after the drag ends.
655 base::WeakPtr<DesktopDragDropClientAuraX11> alive(
656 weak_ptr_factory_.GetWeakPtr());
658 // Windows has a specific method, DoDragDrop(), which performs the entire
659 // drag. We have to emulate this, so we spin off a nested runloop which will
660 // track all cursor movement and reroute events to a specific handler.
661 move_loop_.SetDragImage(source_provider_->GetDragImage(),
662 source_provider_->GetDragImageOffset());
663 move_loop_.RunMoveLoop(source_window, grab_cursor_);
666 move_loop_.SetDragImage(gfx::ImageSkia(), gfx::Vector2dF());
668 source_provider_ = NULL;
669 g_current_drag_drop_client = NULL;
671 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList"));
672 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom(kXdndDirectSave0));
674 return resulting_operation_;
676 return ui::DragDropTypes::DRAG_NONE;
679 void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target,
680 const ui::LocatedEvent& event) {
684 void DesktopDragDropClientAuraX11::Drop(aura::Window* target,
685 const ui::LocatedEvent& event) {
689 void DesktopDragDropClientAuraX11::DragCancel() {
690 move_loop_.EndMoveLoop();
693 bool DesktopDragDropClientAuraX11::IsDragDropInProgress() {
694 return !!g_current_drag_drop_client;
697 void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
698 DCHECK_EQ(target_window_, window);
699 target_window_ = NULL;
702 void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) {
703 gfx::Point screen_point(event->x_root, event->y_root);
705 if (source_state_ != SOURCE_STATE_OTHER)
708 // Find the current window the cursor is over.
709 ::Window mouse_window = None;
710 ::Window dest_window = None;
711 FindWindowFor(screen_point, &mouse_window, &dest_window,
712 atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_MENU"));
714 if (source_current_window_ != dest_window) {
715 if (source_current_window_ != None)
716 SendXdndLeave(source_current_window_);
718 source_current_window_ = dest_window;
720 if (source_current_window_ != None) {
721 negotiated_operation_.erase(source_current_window_);
722 SendXdndEnter(source_current_window_);
726 if (source_current_window_ != None) {
727 if (ContainsKey(waiting_on_status_, dest_window)) {
728 next_position_message_[dest_window] =
729 std::make_pair(screen_point, event->time);
731 SendXdndPosition(dest_window, screen_point, event->time);
736 void DesktopDragDropClientAuraX11::OnMouseReleased() {
737 if (source_current_window_ != None) {
738 if (ContainsKey(waiting_on_status_, source_current_window_)) {
739 // If we are waiting for an XdndStatus message, we need to wait for it to
741 source_state_ = SOURCE_STATE_PENDING_DROP;
743 // Start timer to end the move loop if the target takes too long to send
744 // the XdndStatus and XdndFinished messages.
745 StartEndMoveLoopTimer();
749 std::map< ::Window, ::Atom>::iterator it =
750 negotiated_operation_.find(source_current_window_);
751 if (it != negotiated_operation_.end() && it->second != None) {
752 // We have negotiated an action with the other end.
753 source_state_ = SOURCE_STATE_DROPPED;
754 SendXdndDrop(source_current_window_);
756 // Start timer to end the move loop if the target takes too long to send
757 // an XdndFinished message.
758 StartEndMoveLoopTimer();
762 SendXdndLeave(source_current_window_);
763 source_current_window_ = None;
766 move_loop_.EndMoveLoop();
769 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() {
770 if (source_current_window_ != None)
771 SendXdndLeave(source_current_window_);
772 target_current_context_.reset();
773 source_state_ = SOURCE_STATE_OTHER;
774 end_move_loop_timer_.Stop();
777 void DesktopDragDropClientAuraX11::StartEndMoveLoopTimer() {
778 end_move_loop_timer_.Start(FROM_HERE,
779 base::TimeDelta::FromMilliseconds(
780 kEndMoveLoopTimeoutMs),
782 &DesktopDragDropClientAuraX11::EndMoveLoop);
785 void DesktopDragDropClientAuraX11::EndMoveLoop() {
786 move_loop_.EndMoveLoop();
789 void DesktopDragDropClientAuraX11::DragTranslate(
790 const gfx::Point& root_window_location,
791 scoped_ptr<ui::OSExchangeData>* data,
792 scoped_ptr<ui::DropTargetEvent>* event,
793 aura::client::DragDropDelegate** delegate) {
794 gfx::Point root_location = root_window_location;
795 root_window_->GetHost()->ConvertPointFromNativeScreen(&root_location);
796 aura::Window* target_window =
797 root_window_->GetEventHandlerForPoint(root_location);
798 bool target_window_changed = false;
799 if (target_window != target_window_) {
802 target_window_ = target_window;
804 target_window_->AddObserver(this);
805 target_window_changed = true;
810 *delegate = aura::client::GetDragDropDelegate(target_window_);
814 data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11(
815 xwindow_, target_current_context_->fetched_targets())));
816 gfx::Point location = root_location;
817 aura::Window::ConvertPointToTarget(root_window_, target_window_, &location);
819 target_window_location_ = location;
820 target_window_root_location_ = root_location;
822 event->reset(new ui::DropTargetEvent(
826 target_current_context_->GetDragOperation()));
827 if (target_window_changed)
828 (*delegate)->OnDragEntered(*event->get());
831 void DesktopDragDropClientAuraX11::NotifyDragLeave() {
834 DragDropDelegate* delegate =
835 aura::client::GetDragDropDelegate(target_window_);
837 delegate->OnDragExited();
838 target_window_->RemoveObserver(this);
839 target_window_ = NULL;
842 ::Atom DesktopDragDropClientAuraX11::DragOperationToAtom(
843 int drag_operation) {
844 if (drag_operation & ui::DragDropTypes::DRAG_COPY)
845 return atom_cache_.GetAtom(kXdndActionCopy);
846 if (drag_operation & ui::DragDropTypes::DRAG_MOVE)
847 return atom_cache_.GetAtom(kXdndActionMove);
848 if (drag_operation & ui::DragDropTypes::DRAG_LINK)
849 return atom_cache_.GetAtom(kXdndActionLink);
854 int DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
855 if (atom == atom_cache_.GetAtom(kXdndActionCopy))
856 return ui::DragDropTypes::DRAG_COPY;
857 if (atom == atom_cache_.GetAtom(kXdndActionMove))
858 return ui::DragDropTypes::DRAG_MOVE;
859 if (atom == atom_cache_.GetAtom(kXdndActionLink))
860 return ui::DragDropTypes::DRAG_LINK;
862 return ui::DragDropTypes::DRAG_NONE;
865 std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() {
866 std::vector< ::Atom> operations;
867 if (drag_operation_ & ui::DragDropTypes::DRAG_COPY)
868 operations.push_back(atom_cache_.GetAtom(kXdndActionCopy));
869 if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE)
870 operations.push_back(atom_cache_.GetAtom(kXdndActionMove));
871 if (drag_operation_ & ui::DragDropTypes::DRAG_LINK)
872 operations.push_back(atom_cache_.GetAtom(kXdndActionLink));
876 ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const {
877 return source_provider_ ? source_provider_->GetFormatMap() :
878 ui::SelectionFormatMap();
881 void DesktopDragDropClientAuraX11::CompleteXdndPosition(
882 ::Window source_window,
883 const gfx::Point& screen_point) {
884 int drag_operation = ui::DragDropTypes::DRAG_NONE;
885 scoped_ptr<ui::OSExchangeData> data;
886 scoped_ptr<ui::DropTargetEvent> drop_target_event;
887 DragDropDelegate* delegate = NULL;
888 DragTranslate(screen_point, &data, &drop_target_event, &delegate);
890 drag_operation = delegate->OnDragUpdated(*drop_target_event);
892 // Sends an XdndStatus message back to the source_window. l[2,3]
893 // theoretically represent an area in the window where the current action is
894 // the same as what we're returning, but I can't find any implementation that
895 // actually making use of this. A client can return (0, 0) and/or set the
896 // first bit of l[1] to disable the feature, and it appears that gtk neither
897 // sets this nor respects it if set.
899 xev.xclient.type = ClientMessage;
900 xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus");
901 xev.xclient.format = 32;
902 xev.xclient.window = source_window;
903 xev.xclient.data.l[0] = xwindow_;
904 xev.xclient.data.l[1] = (drag_operation != 0) ?
905 (kWantFurtherPosEvents | kWillAcceptDrop) : 0;
906 xev.xclient.data.l[2] = 0;
907 xev.xclient.data.l[3] = 0;
908 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation);
910 SendXClientEvent(source_window, &xev);
913 void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) {
915 xev.xclient.type = ClientMessage;
916 xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter");
917 xev.xclient.format = 32;
918 xev.xclient.window = dest_window;
919 xev.xclient.data.l[0] = xwindow_;
920 xev.xclient.data.l[1] = (kMinXdndVersion << 24); // The version number.
921 xev.xclient.data.l[2] = 0;
922 xev.xclient.data.l[3] = 0;
923 xev.xclient.data.l[4] = 0;
925 std::vector<Atom> targets;
926 source_provider_->RetrieveTargets(&targets);
928 if (targets.size() > 3) {
929 xev.xclient.data.l[1] |= 1;
930 ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets);
932 // Pack the targets into the enter message.
933 for (size_t i = 0; i < targets.size(); ++i)
934 xev.xclient.data.l[2 + i] = targets[i];
937 SendXClientEvent(dest_window, &xev);
940 void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
941 // If we're sending a leave message, don't wait for status messages anymore.
942 waiting_on_status_.erase(dest_window);
943 NextPositionMap::iterator it = next_position_message_.find(dest_window);
944 if (it != next_position_message_.end())
945 next_position_message_.erase(it);
948 xev.xclient.type = ClientMessage;
949 xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave");
950 xev.xclient.format = 32;
951 xev.xclient.window = dest_window;
952 xev.xclient.data.l[0] = xwindow_;
953 xev.xclient.data.l[1] = 0;
954 xev.xclient.data.l[2] = 0;
955 xev.xclient.data.l[3] = 0;
956 xev.xclient.data.l[4] = 0;
957 SendXClientEvent(dest_window, &xev);
960 void DesktopDragDropClientAuraX11::SendXdndPosition(
961 ::Window dest_window,
962 const gfx::Point& screen_point,
963 unsigned long time) {
964 waiting_on_status_.insert(dest_window);
967 xev.xclient.type = ClientMessage;
968 xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition");
969 xev.xclient.format = 32;
970 xev.xclient.window = dest_window;
971 xev.xclient.data.l[0] = xwindow_;
972 xev.xclient.data.l[1] = 0;
973 xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y();
974 xev.xclient.data.l[3] = time;
975 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
976 SendXClientEvent(dest_window, &xev);
979 void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {
981 xev.xclient.type = ClientMessage;
982 xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop");
983 xev.xclient.format = 32;
984 xev.xclient.window = dest_window;
985 xev.xclient.data.l[0] = xwindow_;
986 xev.xclient.data.l[1] = 0;
987 xev.xclient.data.l[2] = CurrentTime;
988 xev.xclient.data.l[3] = None;
989 xev.xclient.data.l[4] = None;
990 SendXClientEvent(dest_window, &xev);
993 void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid,
995 DCHECK_EQ(ClientMessage, xev->type);
997 // Don't send messages to the X11 message queue if we can help it.
998 DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid);
1000 Atom message_type = xev->xclient.message_type;
1001 if (message_type == atom_cache_.GetAtom("XdndEnter")) {
1002 short_circuit->OnXdndEnter(xev->xclient);
1004 } else if (message_type == atom_cache_.GetAtom("XdndLeave")) {
1005 short_circuit->OnXdndLeave(xev->xclient);
1007 } else if (message_type == atom_cache_.GetAtom("XdndPosition")) {
1008 short_circuit->OnXdndPosition(xev->xclient);
1010 } else if (message_type == atom_cache_.GetAtom("XdndStatus")) {
1011 short_circuit->OnXdndStatus(xev->xclient);
1013 } else if (message_type == atom_cache_.GetAtom("XdndFinished")) {
1014 short_circuit->OnXdndFinished(xev->xclient);
1016 } else if (message_type == atom_cache_.GetAtom("XdndDrop")) {
1017 short_circuit->OnXdndDrop(xev->xclient);
1022 // I don't understand why the GTK+ code is doing what it's doing here. It
1023 // goes out of its way to send the XEvent so that it receives a callback on
1024 // success or failure, and when it fails, it then sends an internal
1025 // GdkEvent about the failed drag. (And sending this message doesn't appear
1026 // to go through normal xlib machinery, but instead passes through the low
1027 // level xProto (the x11 wire format) that I don't understand.
1029 // I'm unsure if I have to jump through those hoops, or if XSendEvent is
1031 XSendEvent(xdisplay_, xid, False, 0, xev);
1034 } // namespace views