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/client/drag_drop_client.h"
14 #include "ui/aura/client/drag_drop_delegate.h"
15 #include "ui/aura/root_window.h"
16 #include "ui/aura/window.h"
17 #include "ui/base/dragdrop/drag_drop_types.h"
18 #include "ui/base/dragdrop/drop_target_event.h"
19 #include "ui/base/dragdrop/os_exchange_data.h"
20 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
21 #include "ui/base/x/selection_utils.h"
22 #include "ui/base/x/x11_util.h"
23 #include "ui/events/event.h"
24 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
26 using aura::client::DragDropDelegate;
27 using ui::OSExchangeData;
31 const int kMinXdndVersion = 5;
33 const int kWillAcceptDrop = 1;
34 const int kWantFurtherPosEvents = 2;
36 const char kXdndActionCopy[] = "XdndActionCopy";
37 const char kXdndActionMove[] = "XdndActionMove";
38 const char kXdndActionLink[] = "XdndActionLink";
40 const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER";
41 const char kXdndSelection[] = "XdndSelection";
43 const char* kAtomsToCache[] = {
44 kChromiumDragReciever,
57 "XdndProxy", // Proxy windows?
64 static base::LazyInstance<
65 std::map< ::Window, views::DesktopDragDropClientAuraX11*> >::Leaky
66 g_live_client_map = LAZY_INSTANCE_INITIALIZER;
68 // Helper class to FindWindowFor which looks for a drag target under the
70 class DragTargetWindowFinder : public ui::EnumerateWindowsDelegate {
72 DragTargetWindowFinder(XID ignored_icon_window,
73 gfx::Point screen_loc)
74 : ignored_icon_window_(ignored_icon_window),
76 screen_loc_(screen_loc) {
77 ui::EnumerateTopLevelWindows(this);
80 virtual ~DragTargetWindowFinder() {}
82 XID window() const { return output_window_; }
85 virtual bool ShouldStopIterating(XID window) OVERRIDE {
86 if (window == ignored_icon_window_)
89 if (!ui::IsWindowVisible(window))
92 if (!ui::WindowContainsPoint(window, screen_loc_))
95 if (ui::PropertyExists(window, "WM_STATE")) {
96 output_window_ = window;
104 XID ignored_icon_window_;
106 gfx::Point screen_loc_;
108 DISALLOW_COPY_AND_ASSIGN(DragTargetWindowFinder);
111 // Returns the topmost X11 window at |screen_point| if it is advertising that
112 // is supports the Xdnd protocol. Will return the window under the pointer as
113 // |mouse_window|. If there's a Xdnd aware window, it will be returned in
115 void FindWindowFor(const gfx::Point& screen_point,
116 ::Window* mouse_window, ::Window* dest_window) {
117 DragTargetWindowFinder finder(None, screen_point);
118 *mouse_window = finder.window();
121 if (finder.window() == None)
124 // Figure out which window we should test as XdndAware. If mouse_window has
125 // XdndProxy, it will set that proxy on target, and if not, |target|'s
126 // original value will remain.
127 XID target = *mouse_window;
128 ui::GetXIDProperty(*mouse_window, "XdndProxy", &target);
131 if (ui::GetIntProperty(target, "XdndAware", &version) &&
132 version >= kMinXdndVersion) {
133 *dest_window = target;
141 DesktopDragDropClientAuraX11*
142 DesktopDragDropClientAuraX11::g_current_drag_drop_client = NULL;
144 class DesktopDragDropClientAuraX11::X11DragContext :
145 public base::MessagePumpDispatcher {
147 X11DragContext(ui::X11AtomCache* atom_cache,
148 ::Window local_window,
149 const XClientMessageEvent& event);
150 virtual ~X11DragContext();
152 // When we receive an XdndPosition message, we need to have all the data
153 // copied from the other window before we process the XdndPosition
154 // message. If we have that data already, dispatch immediately. Otherwise,
155 // delay dispatching until we do.
156 void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client,
157 ::Window source_window,
158 const gfx::Point& screen_point);
160 // Called to request the next target from the source window. This is only
161 // done on the first XdndPosition; after that, we cache the data offered by
162 // the source window.
163 void RequestNextTarget();
165 // Called when XSelection data has been copied to our process.
166 void OnSelectionNotify(const XSelectionEvent& xselection);
168 // Clones the fetched targets.
169 const ui::SelectionFormatMap& fetched_targets() { return fetched_targets_; }
171 // Reads the "XdndActionList" property from |source_window| and copies it
175 // Creates a ui::DragDropTypes::DragOperation representation of the current
177 int GetDragOperation() const;
180 // Overridden from MessagePumpDispatcher:
181 virtual uint32_t Dispatch(const base::NativeEvent& event) OVERRIDE;
183 // The atom cache owned by our parent.
184 ui::X11AtomCache* atom_cache_;
186 // The XID of our chrome local aura window handling our events.
187 ::Window local_window_;
189 // The XID of the window that's initiated the drag.
190 unsigned long source_window_;
192 // The client we inform once we're done with requesting data.
193 DesktopDragDropClientAuraX11* drag_drop_client_;
195 // Whether we're blocking the handling of an XdndPosition message by waiting
196 // for |unfetched_targets_| to be fetched.
197 bool waiting_to_handle_position_;
199 // Where the cursor is on screen.
200 gfx::Point screen_point_;
202 // A SelectionFormatMap of data that we have in our process.
203 ui::SelectionFormatMap fetched_targets_;
205 // The names of various data types offered by the other window that we
206 // haven't fetched and put in |fetched_targets_| yet.
207 std::vector<Atom> unfetched_targets_;
210 std::vector<Atom> actions_;
212 DISALLOW_COPY_AND_ASSIGN(X11DragContext);
215 DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
216 ui::X11AtomCache* atom_cache,
217 ::Window local_window,
218 const XClientMessageEvent& event)
219 : atom_cache_(atom_cache),
220 local_window_(local_window),
221 source_window_(event.data.l[0]),
222 drag_drop_client_(NULL),
223 waiting_to_handle_position_(false) {
224 bool get_types = ((event.data.l[1] & 1) != 0);
227 if (!ui::GetAtomArrayProperty(source_window_,
229 &unfetched_targets_)) {
233 // data.l[2,3,4] contain the first three types. Unused slots can be None.
234 for (int i = 0; i < 3; ++i) {
235 if (event.data.l[2+i] != None) {
236 unfetched_targets_.push_back(event.data.l[2+i]);
241 DesktopDragDropClientAuraX11* client =
242 DesktopDragDropClientAuraX11::GetForWindow(source_window_);
244 // The window doesn't have a DesktopDragDropClientAuraX11, that means it's
245 // created by some other process. Listen for messages on it.
246 base::MessagePumpX11::Current()->AddDispatcherForWindow(
247 this, source_window_);
248 XSelectInput(gfx::GetXDisplay(), source_window_, PropertyChangeMask);
250 // We must perform a full sync here because we could be racing
252 XSync(gfx::GetXDisplay(), False);
254 // This drag originates from an aura window within our process. This means
255 // that we can shortcut the X11 server and ask the owning SelectionOwner
256 // for the data it's offering.
257 fetched_targets_ = client->GetFormatMap();
258 unfetched_targets_.clear();
264 DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() {
265 DesktopDragDropClientAuraX11* client =
266 DesktopDragDropClientAuraX11::GetForWindow(source_window_);
268 // Unsubscribe from message events.
269 base::MessagePumpX11::Current()->RemoveDispatcherForWindow(
274 void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage(
275 DesktopDragDropClientAuraX11* client,
276 ::Window source_window,
277 const gfx::Point& screen_point) {
278 DCHECK_EQ(source_window_, source_window);
280 if (!unfetched_targets_.empty()) {
281 // We have unfetched targets. That means we need to pause the handling of
282 // the position message and ask the other window for its data.
283 screen_point_ = screen_point;
284 drag_drop_client_ = client;
285 waiting_to_handle_position_ = true;
287 fetched_targets_ = ui::SelectionFormatMap();
290 client->CompleteXdndPosition(source_window, screen_point);
294 void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() {
295 ::Atom target = unfetched_targets_.back();
296 unfetched_targets_.pop_back();
298 XConvertSelection(gfx::GetXDisplay(),
299 atom_cache_->GetAtom(kXdndSelection),
301 atom_cache_->GetAtom(kChromiumDragReciever),
306 void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify(
307 const XSelectionEvent& event) {
308 DCHECK(waiting_to_handle_position_);
309 DCHECK(drag_drop_client_);
310 DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever));
312 scoped_refptr<base::RefCountedMemory> data;
314 if (ui::GetRawBytesOfProperty(local_window_, event.property,
315 &data, NULL, NULL, &type)) {
316 fetched_targets_.Insert(event.target, data);
319 if (!unfetched_targets_.empty()) {
322 waiting_to_handle_position_ = false;
323 drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_);
324 drag_drop_client_ = NULL;
328 void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
329 DesktopDragDropClientAuraX11* client =
330 DesktopDragDropClientAuraX11::GetForWindow(source_window_);
332 std::vector<Atom> atom_array;
333 if (!ui::GetAtomArrayProperty(source_window_,
338 actions_.swap(atom_array);
341 // We have a property notify set up for other windows in case they change
342 // their action list. Thankfully, the views interface is static and you
343 // can't change the action list after you enter StartDragAndDrop().
344 actions_ = client->GetOfferedDragOperations();
348 int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const {
349 int drag_operation = ui::DragDropTypes::DRAG_NONE;
350 for (std::vector<Atom>::const_iterator it = actions_.begin();
351 it != actions_.end(); ++it) {
352 if (*it == atom_cache_->GetAtom(kXdndActionCopy))
353 drag_operation |= ui::DragDropTypes::DRAG_COPY;
354 else if (*it == atom_cache_->GetAtom(kXdndActionMove))
355 drag_operation |= ui::DragDropTypes::DRAG_MOVE;
356 else if (*it == atom_cache_->GetAtom(kXdndActionLink))
357 drag_operation |= ui::DragDropTypes::DRAG_LINK;
360 return drag_operation;
363 uint32_t DesktopDragDropClientAuraX11::X11DragContext::Dispatch(
364 const base::NativeEvent& event) {
365 if (event->type == PropertyNotify &&
366 event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) {
369 return POST_DISPATCH_NONE;
372 ///////////////////////////////////////////////////////////////////////////////
374 DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
375 aura::Window* root_window,
376 views::DesktopNativeCursorManager* cursor_manager,
380 root_window_(root_window),
383 atom_cache_(xdisplay_, kAtomsToCache),
384 target_window_(NULL),
385 source_provider_(NULL),
386 source_current_window_(None),
388 resulting_operation_(0),
389 grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorGrabbing)),
390 copy_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorCopy)),
391 move_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorMove)) {
392 DCHECK(g_live_client_map.Get().find(xwindow) ==
393 g_live_client_map.Get().end());
394 g_live_client_map.Get().insert(std::make_pair(xwindow, this));
396 // Mark that we are aware of drag and drop concepts.
397 unsigned long xdnd_version = kMinXdndVersion;
398 XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"),
399 XA_ATOM, 32, PropModeReplace,
400 reinterpret_cast<unsigned char*>(&xdnd_version), 1);
403 DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() {
404 g_live_client_map.Get().erase(xwindow_);
408 DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow(
410 std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it =
411 g_live_client_map.Get().find(window);
412 if (it == g_live_client_map.Get().end())
417 void DesktopDragDropClientAuraX11::OnXdndEnter(
418 const XClientMessageEvent& event) {
419 DVLOG(1) << "XdndEnter";
421 int version = (event.data.l[1] & 0xff000000) >> 24;
423 LOG(ERROR) << "Received old XdndEnter message.";
427 // Make sure that we've run ~X11DragContext() before creating another one.
428 target_current_context_.reset();
429 target_current_context_.reset(
430 new X11DragContext(&atom_cache_, xwindow_, event));
432 // In the Windows implementation, we immediately call DesktopDropTargetWin::
433 // Translate(). Here, we wait until we receive an XdndPosition message
434 // because the enter message doesn't convey any positioning
438 void DesktopDragDropClientAuraX11::OnXdndLeave(
439 const XClientMessageEvent& event) {
440 DVLOG(1) << "XdndLeave";
442 target_current_context_.reset();
445 void DesktopDragDropClientAuraX11::OnXdndPosition(
446 const XClientMessageEvent& event) {
447 DVLOG(1) << "XdndPosition";
449 unsigned long source_window = event.data.l[0];
450 int x_root_window = event.data.l[2] >> 16;
451 int y_root_window = event.data.l[2] & 0xffff;
453 if (!target_current_context_.get()) {
458 // If we already have all the data from this drag, we complete it
460 target_current_context_->OnStartXdndPositionMessage(
461 this, source_window, gfx::Point(x_root_window, y_root_window));
464 void DesktopDragDropClientAuraX11::OnXdndStatus(
465 const XClientMessageEvent& event) {
466 DVLOG(1) << "XdndStatus";
468 unsigned long source_window = event.data.l[0];
469 int drag_operation = ui::DragDropTypes::DRAG_NONE;
470 if (event.data.l[1] & 1) {
471 ::Atom atom_operation = event.data.l[4];
472 negotiated_operation_[source_window] = atom_operation;
473 drag_operation = AtomToDragOperation(atom_operation);
476 switch (drag_operation) {
477 case ui::DragDropTypes::DRAG_COPY:
478 move_loop_.UpdateCursor(copy_grab_cursor_);
480 case ui::DragDropTypes::DRAG_MOVE:
481 move_loop_.UpdateCursor(move_grab_cursor_);
484 move_loop_.UpdateCursor(grab_cursor_);
488 // Note: event.data.[2,3] specify a rectangle. It is a request by the other
489 // window to not send further XdndPosition messages while the cursor is
490 // within it. However, it is considered advisory and (at least according to
491 // the spec) the other side must handle further position messages within
492 // it. GTK+ doesn't bother with this, so neither should we.
494 waiting_on_status_.erase(source_window);
496 if (ContainsKey(pending_drop_, source_window)) {
497 // We were waiting on the status message so we could send the XdndDrop.
498 SendXdndDrop(source_window);
502 NextPositionMap::iterator it = next_position_message_.find(source_window);
503 if (it != next_position_message_.end()) {
504 // We were waiting on the status message so we could send off the next
505 // position message we queued up.
506 gfx::Point p = it->second.first;
507 unsigned long time = it->second.second;
508 next_position_message_.erase(it);
510 SendXdndPosition(source_window, p, time);
514 void DesktopDragDropClientAuraX11::OnXdndFinished(
515 const XClientMessageEvent& event) {
516 DVLOG(1) << "XdndFinished";
517 resulting_operation_ = AtomToDragOperation(
518 negotiated_operation_[event.data.l[0]]);
519 move_loop_.EndMoveLoop();
522 void DesktopDragDropClientAuraX11::OnXdndDrop(
523 const XClientMessageEvent& event) {
524 DVLOG(1) << "XdndDrop";
526 unsigned long source_window = event.data.l[0];
528 int drag_operation = ui::DragDropTypes::DRAG_NONE;
529 if (target_window_) {
530 aura::client::DragDropDelegate* delegate =
531 aura::client::GetDragDropDelegate(target_window_);
533 ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11(
534 xwindow_, target_current_context_->fetched_targets()));
536 ui::DropTargetEvent event(data,
537 target_window_location_,
538 target_window_root_location_,
539 target_current_context_->GetDragOperation());
540 drag_operation = delegate->OnPerformDrop(event);
543 target_window_->RemoveObserver(this);
544 target_window_ = NULL;
548 xev.xclient.type = ClientMessage;
549 xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished");
550 xev.xclient.format = 32;
551 xev.xclient.window = source_window;
552 xev.xclient.data.l[0] = xwindow_;
553 xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0;
554 xev.xclient.data.l[2] = DragOperationToAtom(drag_operation);
556 SendXClientEvent(source_window, &xev);
559 void DesktopDragDropClientAuraX11::OnSelectionNotify(
560 const XSelectionEvent& xselection) {
561 if (!target_current_context_) {
566 target_current_context_->OnSelectionNotify(xselection);
569 int DesktopDragDropClientAuraX11::StartDragAndDrop(
570 const ui::OSExchangeData& data,
571 aura::Window* root_window,
572 aura::Window* source_window,
573 const gfx::Point& root_location,
575 ui::DragDropTypes::DragEventSource source) {
576 source_current_window_ = None;
577 DCHECK(!g_current_drag_drop_client);
578 g_current_drag_drop_client = this;
579 drag_operation_ = operation;
580 resulting_operation_ = ui::DragDropTypes::DRAG_NONE;
582 const ui::OSExchangeData::Provider* provider = &data.provider();
583 source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>(
586 source_provider_->TakeOwnershipOfSelection();
588 std::vector< ::Atom> actions = GetOfferedDragOperations();
589 ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions);
591 // Windows has a specific method, DoDragDrop(), which performs the entire
592 // drag. We have to emulate this, so we spin off a nested runloop which will
593 // track all cursor movement and reroute events to a specific handler.
594 move_loop_.SetDragImage(source_provider_->GetDragImage(),
595 source_provider_->GetDragImageOffset());
596 move_loop_.RunMoveLoop(source_window, grab_cursor_);
597 move_loop_.SetDragImage(gfx::ImageSkia(), gfx::Vector2dF());
599 source_provider_ = NULL;
600 g_current_drag_drop_client = NULL;
602 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList"));
604 return resulting_operation_;
607 void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target,
608 const ui::LocatedEvent& event) {
612 void DesktopDragDropClientAuraX11::Drop(aura::Window* target,
613 const ui::LocatedEvent& event) {
617 void DesktopDragDropClientAuraX11::DragCancel() {
618 move_loop_.EndMoveLoop();
621 bool DesktopDragDropClientAuraX11::IsDragDropInProgress() {
622 return !!g_current_drag_drop_client;
625 void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
626 DCHECK_EQ(target_window_, window);
627 target_window_ = NULL;
630 void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) {
631 gfx::Point screen_point(event->x_root, event->y_root);
633 // Find the current window the cursor is over.
634 ::Window mouse_window = None;
635 ::Window dest_window = None;
636 FindWindowFor(screen_point, &mouse_window, &dest_window);
638 if (source_current_window_ != dest_window) {
639 if (source_current_window_ != None)
640 SendXdndLeave(source_current_window_);
642 source_current_window_ = dest_window;
644 if (source_current_window_ != None) {
645 negotiated_operation_.erase(source_current_window_);
646 SendXdndEnter(source_current_window_);
650 if (source_current_window_ != None) {
651 if (ContainsKey(waiting_on_status_, dest_window)) {
652 next_position_message_[dest_window] =
653 std::make_pair(screen_point, event->time);
655 SendXdndPosition(dest_window, screen_point, event->time);
660 void DesktopDragDropClientAuraX11::OnMouseReleased() {
661 if (source_current_window_ != None) {
662 if (ContainsKey(waiting_on_status_, source_current_window_)) {
663 // If we are waiting for an XdndStatus message, we need to wait for it to
665 pending_drop_.insert(source_current_window_);
669 std::map< ::Window, ::Atom>::iterator it =
670 negotiated_operation_.find(source_current_window_);
671 if (it != negotiated_operation_.end() && it->second != None) {
672 // We have negotiated an action with the other end.
673 SendXdndDrop(source_current_window_);
677 SendXdndLeave(source_current_window_);
678 source_current_window_ = None;
681 move_loop_.EndMoveLoop();
684 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() {
685 target_current_context_.reset();
688 void DesktopDragDropClientAuraX11::DragTranslate(
689 const gfx::Point& root_window_location,
690 scoped_ptr<ui::OSExchangeData>* data,
691 scoped_ptr<ui::DropTargetEvent>* event,
692 aura::client::DragDropDelegate** delegate) {
693 gfx::Point root_location = root_window_location;
694 root_window_->GetDispatcher()->host()->ConvertPointFromNativeScreen(
696 aura::Window* target_window =
697 root_window_->GetEventHandlerForPoint(root_location);
698 bool target_window_changed = false;
699 if (target_window != target_window_) {
702 target_window_ = target_window;
704 target_window_->AddObserver(this);
705 target_window_changed = true;
710 *delegate = aura::client::GetDragDropDelegate(target_window_);
714 data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11(
715 xwindow_, target_current_context_->fetched_targets())));
716 gfx::Point location = root_location;
717 aura::Window::ConvertPointToTarget(root_window_, target_window_, &location);
719 target_window_location_ = location;
720 target_window_root_location_ = root_location;
722 event->reset(new ui::DropTargetEvent(
726 target_current_context_->GetDragOperation()));
727 if (target_window_changed)
728 (*delegate)->OnDragEntered(*event->get());
731 void DesktopDragDropClientAuraX11::NotifyDragLeave() {
734 DragDropDelegate* delegate =
735 aura::client::GetDragDropDelegate(target_window_);
737 delegate->OnDragExited();
738 target_window_->RemoveObserver(this);
739 target_window_ = NULL;
742 ::Atom DesktopDragDropClientAuraX11::DragOperationToAtom(
743 int drag_operation) {
744 if (drag_operation & ui::DragDropTypes::DRAG_COPY)
745 return atom_cache_.GetAtom(kXdndActionCopy);
746 if (drag_operation & ui::DragDropTypes::DRAG_MOVE)
747 return atom_cache_.GetAtom(kXdndActionMove);
748 if (drag_operation & ui::DragDropTypes::DRAG_LINK)
749 return atom_cache_.GetAtom(kXdndActionLink);
754 int DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
755 if (atom == atom_cache_.GetAtom(kXdndActionCopy))
756 return ui::DragDropTypes::DRAG_COPY;
757 if (atom == atom_cache_.GetAtom(kXdndActionMove))
758 return ui::DragDropTypes::DRAG_MOVE;
759 if (atom == atom_cache_.GetAtom(kXdndActionLink))
760 return ui::DragDropTypes::DRAG_LINK;
762 return ui::DragDropTypes::DRAG_NONE;
765 std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() {
766 std::vector< ::Atom> operations;
767 if (drag_operation_ & ui::DragDropTypes::DRAG_COPY)
768 operations.push_back(atom_cache_.GetAtom(kXdndActionCopy));
769 if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE)
770 operations.push_back(atom_cache_.GetAtom(kXdndActionMove));
771 if (drag_operation_ & ui::DragDropTypes::DRAG_LINK)
772 operations.push_back(atom_cache_.GetAtom(kXdndActionLink));
776 ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const {
777 return source_provider_ ? source_provider_->GetFormatMap() :
778 ui::SelectionFormatMap();
781 void DesktopDragDropClientAuraX11::CompleteXdndPosition(
782 ::Window source_window,
783 const gfx::Point& screen_point) {
784 int drag_operation = ui::DragDropTypes::DRAG_NONE;
785 scoped_ptr<ui::OSExchangeData> data;
786 scoped_ptr<ui::DropTargetEvent> drop_target_event;
787 DragDropDelegate* delegate = NULL;
788 DragTranslate(screen_point, &data, &drop_target_event, &delegate);
790 drag_operation = delegate->OnDragUpdated(*drop_target_event);
792 // Sends an XdndStatus message back to the source_window. l[2,3]
793 // theoretically represent an area in the window where the current action is
794 // the same as what we're returning, but I can't find any implementation that
795 // actually making use of this. A client can return (0, 0) and/or set the
796 // first bit of l[1] to disable the feature, and it appears that gtk neither
797 // sets this nor respects it if set.
799 xev.xclient.type = ClientMessage;
800 xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus");
801 xev.xclient.format = 32;
802 xev.xclient.window = source_window;
803 xev.xclient.data.l[0] = xwindow_;
804 xev.xclient.data.l[1] = (drag_operation != 0) ?
805 (kWantFurtherPosEvents | kWillAcceptDrop) : 0;
806 xev.xclient.data.l[2] = 0;
807 xev.xclient.data.l[3] = 0;
808 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation);
810 SendXClientEvent(source_window, &xev);
813 void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) {
815 xev.xclient.type = ClientMessage;
816 xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter");
817 xev.xclient.format = 32;
818 xev.xclient.window = dest_window;
819 xev.xclient.data.l[0] = xwindow_;
820 xev.xclient.data.l[1] = (kMinXdndVersion << 24); // The version number.
821 xev.xclient.data.l[2] = 0;
822 xev.xclient.data.l[3] = 0;
823 xev.xclient.data.l[4] = 0;
825 std::vector<Atom> targets;
826 source_provider_->RetrieveTargets(&targets);
828 if (targets.size() > 3) {
829 xev.xclient.data.l[1] |= 1;
830 ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets);
832 // Pack the targets into the enter message.
833 for (size_t i = 0; i < targets.size(); ++i)
834 xev.xclient.data.l[2 + i] = targets[i];
837 SendXClientEvent(dest_window, &xev);
840 void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
841 // If we're sending a leave message, don't wait for status messages anymore.
842 waiting_on_status_.erase(dest_window);
843 NextPositionMap::iterator it = next_position_message_.find(dest_window);
844 if (it != next_position_message_.end())
845 next_position_message_.erase(it);
848 xev.xclient.type = ClientMessage;
849 xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave");
850 xev.xclient.format = 32;
851 xev.xclient.window = dest_window;
852 xev.xclient.data.l[0] = xwindow_;
853 xev.xclient.data.l[1] = 0;
854 xev.xclient.data.l[2] = 0;
855 xev.xclient.data.l[3] = 0;
856 xev.xclient.data.l[4] = 0;
857 SendXClientEvent(dest_window, &xev);
860 void DesktopDragDropClientAuraX11::SendXdndPosition(
861 ::Window dest_window,
862 const gfx::Point& screen_point,
863 unsigned long time) {
864 waiting_on_status_.insert(dest_window);
867 xev.xclient.type = ClientMessage;
868 xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition");
869 xev.xclient.format = 32;
870 xev.xclient.window = dest_window;
871 xev.xclient.data.l[0] = xwindow_;
872 xev.xclient.data.l[1] = 0;
873 xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y();
874 xev.xclient.data.l[3] = time;
875 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
876 SendXClientEvent(dest_window, &xev);
879 void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {
881 xev.xclient.type = ClientMessage;
882 xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop");
883 xev.xclient.format = 32;
884 xev.xclient.window = dest_window;
885 xev.xclient.data.l[0] = xwindow_;
886 xev.xclient.data.l[1] = 0;
887 xev.xclient.data.l[2] = CurrentTime;
888 xev.xclient.data.l[3] = None;
889 xev.xclient.data.l[4] = None;
890 SendXClientEvent(dest_window, &xev);
893 void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid,
895 DCHECK_EQ(ClientMessage, xev->type);
897 // Don't send messages to the X11 message queue if we can help it.
898 DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid);
900 Atom message_type = xev->xclient.message_type;
901 if (message_type == atom_cache_.GetAtom("XdndEnter")) {
902 short_circuit->OnXdndEnter(xev->xclient);
904 } else if (message_type == atom_cache_.GetAtom("XdndLeave")) {
905 short_circuit->OnXdndLeave(xev->xclient);
907 } else if (message_type == atom_cache_.GetAtom("XdndPosition")) {
908 short_circuit->OnXdndPosition(xev->xclient);
910 } else if (message_type == atom_cache_.GetAtom("XdndStatus")) {
911 short_circuit->OnXdndStatus(xev->xclient);
913 } else if (message_type == atom_cache_.GetAtom("XdndFinished")) {
914 short_circuit->OnXdndFinished(xev->xclient);
916 } else if (message_type == atom_cache_.GetAtom("XdndDrop")) {
917 short_circuit->OnXdndDrop(xev->xclient);
922 // I don't understand why the GTK+ code is doing what it's doing here. It
923 // goes out of its way to send the XEvent so that it receives a callback on
924 // success or failure, and when it fails, it then sends an internal
925 // GdkEvent about the failed drag. (And sending this message doesn't appear
926 // to go through normal xlib machinery, but instead passes through the low
927 // level xProto (the x11 wire format) that I don't understand.
929 // I'm unsure if I have to jump through those hoops, or if XSendEvent is
931 XSendEvent(xdisplay_, xid, False, 0, xev);