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 = 1000;
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 if (!waiting_to_handle_position_) {
339 // A misbehaved window may send SelectionNotify without us requesting data
340 // via XConvertSelection().
343 DCHECK(drag_drop_client_);
344 DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever));
346 scoped_refptr<base::RefCountedMemory> data;
348 if (ui::GetRawBytesOfProperty(local_window_, event.property,
349 &data, NULL, NULL, &type)) {
350 fetched_targets_.Insert(event.target, data);
353 if (!unfetched_targets_.empty()) {
356 waiting_to_handle_position_ = false;
357 drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_);
358 drag_drop_client_ = NULL;
362 void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
363 DesktopDragDropClientAuraX11* client =
364 DesktopDragDropClientAuraX11::GetForWindow(source_window_);
366 std::vector<Atom> atom_array;
367 if (!ui::GetAtomArrayProperty(source_window_,
372 actions_.swap(atom_array);
375 // We have a property notify set up for other windows in case they change
376 // their action list. Thankfully, the views interface is static and you
377 // can't change the action list after you enter StartDragAndDrop().
378 actions_ = client->GetOfferedDragOperations();
382 int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const {
383 int drag_operation = ui::DragDropTypes::DRAG_NONE;
384 for (std::vector<Atom>::const_iterator it = actions_.begin();
385 it != actions_.end(); ++it) {
386 MaskOpeartion(*it, &drag_operation);
389 MaskOpeartion(suggested_action_, &drag_operation);
391 return drag_operation;
394 void DesktopDragDropClientAuraX11::X11DragContext::MaskOpeartion(
395 ::Atom xdnd_operation,
396 int* drag_operation) const {
397 if (xdnd_operation == atom_cache_->GetAtom(kXdndActionCopy))
398 *drag_operation |= ui::DragDropTypes::DRAG_COPY;
399 else if (xdnd_operation == atom_cache_->GetAtom(kXdndActionMove))
400 *drag_operation |= ui::DragDropTypes::DRAG_MOVE;
401 else if (xdnd_operation == atom_cache_->GetAtom(kXdndActionLink))
402 *drag_operation |= ui::DragDropTypes::DRAG_LINK;
405 uint32_t DesktopDragDropClientAuraX11::X11DragContext::Dispatch(
406 const base::NativeEvent& event) {
407 if (event->type == PropertyNotify &&
408 event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) {
411 return POST_DISPATCH_NONE;
414 ///////////////////////////////////////////////////////////////////////////////
416 DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
417 aura::Window* root_window,
418 views::DesktopNativeCursorManager* cursor_manager,
422 root_window_(root_window),
425 atom_cache_(xdisplay_, kAtomsToCache),
426 target_window_(NULL),
427 source_provider_(NULL),
428 source_current_window_(None),
429 source_state_(SOURCE_STATE_OTHER),
431 resulting_operation_(0),
432 grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorGrabbing)),
433 copy_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorCopy)),
434 move_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorMove)),
435 weak_ptr_factory_(this) {
436 DCHECK(g_live_client_map.Get().find(xwindow) ==
437 g_live_client_map.Get().end());
438 g_live_client_map.Get().insert(std::make_pair(xwindow, this));
440 // Mark that we are aware of drag and drop concepts.
441 unsigned long xdnd_version = kMinXdndVersion;
442 XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"),
443 XA_ATOM, 32, PropModeReplace,
444 reinterpret_cast<unsigned char*>(&xdnd_version), 1);
447 DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() {
448 g_live_client_map.Get().erase(xwindow_);
449 // Make sure that all observers are unregistered from source and target
450 // windows. This may be necessary when the parent native widget gets destroyed
451 // while a drag operation is in progress.
456 DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow(
458 std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it =
459 g_live_client_map.Get().find(window);
460 if (it == g_live_client_map.Get().end())
465 void DesktopDragDropClientAuraX11::OnXdndEnter(
466 const XClientMessageEvent& event) {
467 DVLOG(1) << "XdndEnter";
469 int version = (event.data.l[1] & 0xff000000) >> 24;
471 LOG(ERROR) << "Received old XdndEnter message.";
475 // Make sure that we've run ~X11DragContext() before creating another one.
476 target_current_context_.reset();
477 target_current_context_.reset(
478 new X11DragContext(&atom_cache_, xwindow_, event));
480 // In the Windows implementation, we immediately call DesktopDropTargetWin::
481 // Translate(). Here, we wait until we receive an XdndPosition message
482 // because the enter message doesn't convey any positioning
486 void DesktopDragDropClientAuraX11::OnXdndLeave(
487 const XClientMessageEvent& event) {
488 DVLOG(1) << "XdndLeave";
490 target_current_context_.reset();
493 void DesktopDragDropClientAuraX11::OnXdndPosition(
494 const XClientMessageEvent& event) {
495 DVLOG(1) << "XdndPosition";
497 unsigned long source_window = event.data.l[0];
498 int x_root_window = event.data.l[2] >> 16;
499 int y_root_window = event.data.l[2] & 0xffff;
500 ::Atom suggested_action = event.data.l[4];
502 if (!target_current_context_.get()) {
507 // If we already have all the data from this drag, we complete it
509 target_current_context_->OnStartXdndPositionMessage(
510 this, suggested_action, source_window,
511 gfx::Point(x_root_window, y_root_window));
514 void DesktopDragDropClientAuraX11::OnXdndStatus(
515 const XClientMessageEvent& event) {
516 DVLOG(1) << "XdndStatus";
518 unsigned long source_window = event.data.l[0];
520 waiting_on_status_.erase(source_window);
521 if (source_window != source_current_window_)
524 int drag_operation = ui::DragDropTypes::DRAG_NONE;
525 if (event.data.l[1] & 1) {
526 ::Atom atom_operation = event.data.l[4];
527 negotiated_operation_[source_window] = atom_operation;
528 drag_operation = AtomToDragOperation(atom_operation);
531 switch (drag_operation) {
532 case ui::DragDropTypes::DRAG_COPY:
533 move_loop_.UpdateCursor(copy_grab_cursor_);
535 case ui::DragDropTypes::DRAG_MOVE:
536 move_loop_.UpdateCursor(move_grab_cursor_);
539 move_loop_.UpdateCursor(grab_cursor_);
543 // Note: event.data.[2,3] specify a rectangle. It is a request by the other
544 // window to not send further XdndPosition messages while the cursor is
545 // within it. However, it is considered advisory and (at least according to
546 // the spec) the other side must handle further position messages within
547 // it. GTK+ doesn't bother with this, so neither should we.
549 if (source_state_ == SOURCE_STATE_PENDING_DROP) {
550 // We were waiting on the status message so we could send the XdndDrop.
551 source_state_ = SOURCE_STATE_DROPPED;
552 SendXdndDrop(source_window);
556 NextPositionMap::iterator it = next_position_message_.find(source_window);
557 if (source_state_ == SOURCE_STATE_OTHER &&
558 it != next_position_message_.end()) {
559 // We were waiting on the status message so we could send off the next
560 // position message we queued up.
561 gfx::Point p = it->second.first;
562 unsigned long time = it->second.second;
563 next_position_message_.erase(it);
565 SendXdndPosition(source_window, p, time);
569 void DesktopDragDropClientAuraX11::OnXdndFinished(
570 const XClientMessageEvent& event) {
571 DVLOG(1) << "XdndFinished";
572 resulting_operation_ = AtomToDragOperation(
573 negotiated_operation_[event.data.l[0]]);
574 move_loop_.EndMoveLoop();
577 void DesktopDragDropClientAuraX11::OnXdndDrop(
578 const XClientMessageEvent& event) {
579 DVLOG(1) << "XdndDrop";
581 unsigned long source_window = event.data.l[0];
583 int drag_operation = ui::DragDropTypes::DRAG_NONE;
584 if (target_window_) {
585 aura::client::DragDropDelegate* delegate =
586 aura::client::GetDragDropDelegate(target_window_);
588 ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11(
589 xwindow_, target_current_context_->fetched_targets()));
591 ui::DropTargetEvent event(data,
592 target_window_location_,
593 target_window_root_location_,
594 target_current_context_->GetDragOperation());
595 drag_operation = delegate->OnPerformDrop(event);
598 target_window_->RemoveObserver(this);
599 target_window_ = NULL;
603 xev.xclient.type = ClientMessage;
604 xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished");
605 xev.xclient.format = 32;
606 xev.xclient.window = source_window;
607 xev.xclient.data.l[0] = xwindow_;
608 xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0;
609 xev.xclient.data.l[2] = DragOperationToAtom(drag_operation);
611 SendXClientEvent(source_window, &xev);
614 void DesktopDragDropClientAuraX11::OnSelectionNotify(
615 const XSelectionEvent& xselection) {
616 if (target_current_context_)
617 target_current_context_->OnSelectionNotify(xselection);
619 // ICCCM requires us to delete the property passed into SelectionNotify.
620 if (xselection.property != None)
621 XDeleteProperty(xdisplay_, xwindow_, xselection.property);
624 int DesktopDragDropClientAuraX11::StartDragAndDrop(
625 const ui::OSExchangeData& data,
626 aura::Window* root_window,
627 aura::Window* source_window,
628 const gfx::Point& root_location,
630 ui::DragDropTypes::DragEventSource source) {
631 source_current_window_ = None;
632 DCHECK(!g_current_drag_drop_client);
633 g_current_drag_drop_client = this;
634 waiting_on_status_.clear();
635 source_state_ = SOURCE_STATE_OTHER;
636 drag_operation_ = operation;
637 resulting_operation_ = ui::DragDropTypes::DRAG_NONE;
639 const ui::OSExchangeData::Provider* provider = &data.provider();
640 source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>(
643 source_provider_->TakeOwnershipOfSelection();
645 std::vector< ::Atom> actions = GetOfferedDragOperations();
646 if (!source_provider_->file_contents_name().empty()) {
647 actions.push_back(atom_cache_.GetAtom(kXdndActionDirectSave));
648 ui::SetStringProperty(
650 atom_cache_.GetAtom(kXdndDirectSave0),
651 atom_cache_.GetAtom(ui::Clipboard::kMimeTypeText),
652 source_provider_->file_contents_name().AsUTF8Unsafe());
654 ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions);
656 // It is possible for the DesktopWindowTreeHostX11 to be destroyed during the
657 // move loop, which would also destroy this drag-client. So keep track of
658 // whether it is alive after the drag ends.
659 base::WeakPtr<DesktopDragDropClientAuraX11> alive(
660 weak_ptr_factory_.GetWeakPtr());
662 // Windows has a specific method, DoDragDrop(), which performs the entire
663 // drag. We have to emulate this, so we spin off a nested runloop which will
664 // track all cursor movement and reroute events to a specific handler.
665 move_loop_.SetDragImage(source_provider_->GetDragImage(),
666 source_provider_->GetDragImageOffset());
667 move_loop_.RunMoveLoop(source_window, grab_cursor_);
670 move_loop_.SetDragImage(gfx::ImageSkia(), gfx::Vector2dF());
672 source_provider_ = NULL;
673 g_current_drag_drop_client = NULL;
675 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList"));
676 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom(kXdndDirectSave0));
678 return resulting_operation_;
680 return ui::DragDropTypes::DRAG_NONE;
683 void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target,
684 const ui::LocatedEvent& event) {
688 void DesktopDragDropClientAuraX11::Drop(aura::Window* target,
689 const ui::LocatedEvent& event) {
693 void DesktopDragDropClientAuraX11::DragCancel() {
694 move_loop_.EndMoveLoop();
697 bool DesktopDragDropClientAuraX11::IsDragDropInProgress() {
698 return !!g_current_drag_drop_client;
701 void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
702 DCHECK_EQ(target_window_, window);
703 target_window_ = NULL;
706 void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) {
707 gfx::Point screen_point(event->x_root, event->y_root);
709 if (source_state_ != SOURCE_STATE_OTHER)
712 // Find the current window the cursor is over.
713 ::Window mouse_window = None;
714 ::Window dest_window = None;
715 FindWindowFor(screen_point, &mouse_window, &dest_window,
716 atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_MENU"));
718 if (source_current_window_ != dest_window) {
719 if (source_current_window_ != None)
720 SendXdndLeave(source_current_window_);
722 source_current_window_ = dest_window;
724 if (source_current_window_ != None) {
725 negotiated_operation_.erase(source_current_window_);
726 SendXdndEnter(source_current_window_);
730 if (source_current_window_ != None) {
731 if (ContainsKey(waiting_on_status_, dest_window)) {
732 next_position_message_[dest_window] =
733 std::make_pair(screen_point, event->time);
735 SendXdndPosition(dest_window, screen_point, event->time);
740 void DesktopDragDropClientAuraX11::OnMouseReleased() {
741 if (source_state_ != SOURCE_STATE_OTHER) {
742 // The user has previously released the mouse and is clicking in
744 move_loop_.EndMoveLoop();
748 if (source_current_window_ != None) {
749 if (ContainsKey(waiting_on_status_, source_current_window_)) {
750 // If we are waiting for an XdndStatus message, we need to wait for it to
752 source_state_ = SOURCE_STATE_PENDING_DROP;
754 // Start timer to end the move loop if the target takes too long to send
755 // the XdndStatus and XdndFinished messages.
756 StartEndMoveLoopTimer();
760 std::map< ::Window, ::Atom>::iterator it =
761 negotiated_operation_.find(source_current_window_);
762 if (it != negotiated_operation_.end() && it->second != None) {
763 // We have negotiated an action with the other end.
764 source_state_ = SOURCE_STATE_DROPPED;
765 SendXdndDrop(source_current_window_);
767 // Start timer to end the move loop if the target takes too long to send
768 // an XdndFinished message.
769 StartEndMoveLoopTimer();
773 SendXdndLeave(source_current_window_);
774 source_current_window_ = None;
777 move_loop_.EndMoveLoop();
780 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() {
781 if (source_current_window_ != None)
782 SendXdndLeave(source_current_window_);
783 target_current_context_.reset();
784 source_state_ = SOURCE_STATE_OTHER;
785 end_move_loop_timer_.Stop();
788 void DesktopDragDropClientAuraX11::StartEndMoveLoopTimer() {
789 end_move_loop_timer_.Start(FROM_HERE,
790 base::TimeDelta::FromMilliseconds(
791 kEndMoveLoopTimeoutMs),
793 &DesktopDragDropClientAuraX11::EndMoveLoop);
796 void DesktopDragDropClientAuraX11::EndMoveLoop() {
797 move_loop_.EndMoveLoop();
800 void DesktopDragDropClientAuraX11::DragTranslate(
801 const gfx::Point& root_window_location,
802 scoped_ptr<ui::OSExchangeData>* data,
803 scoped_ptr<ui::DropTargetEvent>* event,
804 aura::client::DragDropDelegate** delegate) {
805 gfx::Point root_location = root_window_location;
806 root_window_->GetHost()->ConvertPointFromNativeScreen(&root_location);
807 aura::Window* target_window =
808 root_window_->GetEventHandlerForPoint(root_location);
809 bool target_window_changed = false;
810 if (target_window != target_window_) {
813 target_window_ = target_window;
815 target_window_->AddObserver(this);
816 target_window_changed = true;
821 *delegate = aura::client::GetDragDropDelegate(target_window_);
825 data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11(
826 xwindow_, target_current_context_->fetched_targets())));
827 gfx::Point location = root_location;
828 aura::Window::ConvertPointToTarget(root_window_, target_window_, &location);
830 target_window_location_ = location;
831 target_window_root_location_ = root_location;
833 event->reset(new ui::DropTargetEvent(
837 target_current_context_->GetDragOperation()));
838 if (target_window_changed)
839 (*delegate)->OnDragEntered(*event->get());
842 void DesktopDragDropClientAuraX11::NotifyDragLeave() {
845 DragDropDelegate* delegate =
846 aura::client::GetDragDropDelegate(target_window_);
848 delegate->OnDragExited();
849 target_window_->RemoveObserver(this);
850 target_window_ = NULL;
853 ::Atom DesktopDragDropClientAuraX11::DragOperationToAtom(
854 int drag_operation) {
855 if (drag_operation & ui::DragDropTypes::DRAG_COPY)
856 return atom_cache_.GetAtom(kXdndActionCopy);
857 if (drag_operation & ui::DragDropTypes::DRAG_MOVE)
858 return atom_cache_.GetAtom(kXdndActionMove);
859 if (drag_operation & ui::DragDropTypes::DRAG_LINK)
860 return atom_cache_.GetAtom(kXdndActionLink);
865 int DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
866 if (atom == atom_cache_.GetAtom(kXdndActionCopy))
867 return ui::DragDropTypes::DRAG_COPY;
868 if (atom == atom_cache_.GetAtom(kXdndActionMove))
869 return ui::DragDropTypes::DRAG_MOVE;
870 if (atom == atom_cache_.GetAtom(kXdndActionLink))
871 return ui::DragDropTypes::DRAG_LINK;
873 return ui::DragDropTypes::DRAG_NONE;
876 std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() {
877 std::vector< ::Atom> operations;
878 if (drag_operation_ & ui::DragDropTypes::DRAG_COPY)
879 operations.push_back(atom_cache_.GetAtom(kXdndActionCopy));
880 if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE)
881 operations.push_back(atom_cache_.GetAtom(kXdndActionMove));
882 if (drag_operation_ & ui::DragDropTypes::DRAG_LINK)
883 operations.push_back(atom_cache_.GetAtom(kXdndActionLink));
887 ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const {
888 return source_provider_ ? source_provider_->GetFormatMap() :
889 ui::SelectionFormatMap();
892 void DesktopDragDropClientAuraX11::CompleteXdndPosition(
893 ::Window source_window,
894 const gfx::Point& screen_point) {
895 int drag_operation = ui::DragDropTypes::DRAG_NONE;
896 scoped_ptr<ui::OSExchangeData> data;
897 scoped_ptr<ui::DropTargetEvent> drop_target_event;
898 DragDropDelegate* delegate = NULL;
899 DragTranslate(screen_point, &data, &drop_target_event, &delegate);
901 drag_operation = delegate->OnDragUpdated(*drop_target_event);
903 // Sends an XdndStatus message back to the source_window. l[2,3]
904 // theoretically represent an area in the window where the current action is
905 // the same as what we're returning, but I can't find any implementation that
906 // actually making use of this. A client can return (0, 0) and/or set the
907 // first bit of l[1] to disable the feature, and it appears that gtk neither
908 // sets this nor respects it if set.
910 xev.xclient.type = ClientMessage;
911 xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus");
912 xev.xclient.format = 32;
913 xev.xclient.window = source_window;
914 xev.xclient.data.l[0] = xwindow_;
915 xev.xclient.data.l[1] = (drag_operation != 0) ?
916 (kWantFurtherPosEvents | kWillAcceptDrop) : 0;
917 xev.xclient.data.l[2] = 0;
918 xev.xclient.data.l[3] = 0;
919 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation);
921 SendXClientEvent(source_window, &xev);
924 void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) {
926 xev.xclient.type = ClientMessage;
927 xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter");
928 xev.xclient.format = 32;
929 xev.xclient.window = dest_window;
930 xev.xclient.data.l[0] = xwindow_;
931 xev.xclient.data.l[1] = (kMinXdndVersion << 24); // The version number.
932 xev.xclient.data.l[2] = 0;
933 xev.xclient.data.l[3] = 0;
934 xev.xclient.data.l[4] = 0;
936 std::vector<Atom> targets;
937 source_provider_->RetrieveTargets(&targets);
939 if (targets.size() > 3) {
940 xev.xclient.data.l[1] |= 1;
941 ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets);
943 // Pack the targets into the enter message.
944 for (size_t i = 0; i < targets.size(); ++i)
945 xev.xclient.data.l[2 + i] = targets[i];
948 SendXClientEvent(dest_window, &xev);
951 void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
952 // If we're sending a leave message, don't wait for status messages anymore.
953 waiting_on_status_.erase(dest_window);
954 NextPositionMap::iterator it = next_position_message_.find(dest_window);
955 if (it != next_position_message_.end())
956 next_position_message_.erase(it);
959 xev.xclient.type = ClientMessage;
960 xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave");
961 xev.xclient.format = 32;
962 xev.xclient.window = dest_window;
963 xev.xclient.data.l[0] = xwindow_;
964 xev.xclient.data.l[1] = 0;
965 xev.xclient.data.l[2] = 0;
966 xev.xclient.data.l[3] = 0;
967 xev.xclient.data.l[4] = 0;
968 SendXClientEvent(dest_window, &xev);
971 void DesktopDragDropClientAuraX11::SendXdndPosition(
972 ::Window dest_window,
973 const gfx::Point& screen_point,
974 unsigned long time) {
975 waiting_on_status_.insert(dest_window);
978 xev.xclient.type = ClientMessage;
979 xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition");
980 xev.xclient.format = 32;
981 xev.xclient.window = dest_window;
982 xev.xclient.data.l[0] = xwindow_;
983 xev.xclient.data.l[1] = 0;
984 xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y();
985 xev.xclient.data.l[3] = time;
986 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
987 SendXClientEvent(dest_window, &xev);
990 void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {
992 xev.xclient.type = ClientMessage;
993 xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop");
994 xev.xclient.format = 32;
995 xev.xclient.window = dest_window;
996 xev.xclient.data.l[0] = xwindow_;
997 xev.xclient.data.l[1] = 0;
998 xev.xclient.data.l[2] = CurrentTime;
999 xev.xclient.data.l[3] = None;
1000 xev.xclient.data.l[4] = None;
1001 SendXClientEvent(dest_window, &xev);
1004 void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid,
1006 DCHECK_EQ(ClientMessage, xev->type);
1008 // Don't send messages to the X11 message queue if we can help it.
1009 DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid);
1010 if (short_circuit) {
1011 Atom message_type = xev->xclient.message_type;
1012 if (message_type == atom_cache_.GetAtom("XdndEnter")) {
1013 short_circuit->OnXdndEnter(xev->xclient);
1015 } else if (message_type == atom_cache_.GetAtom("XdndLeave")) {
1016 short_circuit->OnXdndLeave(xev->xclient);
1018 } else if (message_type == atom_cache_.GetAtom("XdndPosition")) {
1019 short_circuit->OnXdndPosition(xev->xclient);
1021 } else if (message_type == atom_cache_.GetAtom("XdndStatus")) {
1022 short_circuit->OnXdndStatus(xev->xclient);
1024 } else if (message_type == atom_cache_.GetAtom("XdndFinished")) {
1025 short_circuit->OnXdndFinished(xev->xclient);
1027 } else if (message_type == atom_cache_.GetAtom("XdndDrop")) {
1028 short_circuit->OnXdndDrop(xev->xclient);
1033 // I don't understand why the GTK+ code is doing what it's doing here. It
1034 // goes out of its way to send the XEvent so that it receives a callback on
1035 // success or failure, and when it fails, it then sends an internal
1036 // GdkEvent about the failed drag. (And sending this message doesn't appear
1037 // to go through normal xlib machinery, but instead passes through the low
1038 // level xProto (the x11 wire format) that I don't understand.
1040 // I'm unsure if I have to jump through those hoops, or if XSendEvent is
1042 XSendEvent(xdisplay_, xid, False, 0, xev);
1045 } // namespace views