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/message_loop/message_loop.h"
11 #include "ui/aura/client/drag_drop_client.h"
12 #include "ui/aura/client/drag_drop_delegate.h"
13 #include "ui/aura/root_window.h"
14 #include "ui/aura/window.h"
15 #include "ui/base/dragdrop/drag_drop_types.h"
16 #include "ui/base/dragdrop/drop_target_event.h"
17 #include "ui/base/dragdrop/os_exchange_data.h"
18 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
19 #include "ui/base/x/selection_utils.h"
20 #include "ui/base/x/x11_util.h"
21 #include "ui/events/event.h"
22 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
24 using aura::client::DragDropDelegate;
25 using ui::OSExchangeData;
29 const int kMinXdndVersion = 5;
31 const int kWillAcceptDrop = 1;
32 const int kWantFurtherPosEvents = 2;
34 const char kXdndActionCopy[] = "XdndActionCopy";
35 const char kXdndActionMove[] = "XdndActionMove";
36 const char kXdndActionLink[] = "XdndActionLink";
38 const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER";
39 const char kXdndSelection[] = "XdndSelection";
41 const char* kAtomsToCache[] = {
42 kChromiumDragReciever,
55 "XdndProxy", // Proxy windows?
62 // Helper class to FindWindowFor which looks for a drag target under the
64 class DragTargetWindowFinder : public ui::EnumerateWindowsDelegate {
66 DragTargetWindowFinder(XID ignored_icon_window,
67 gfx::Point screen_loc)
68 : ignored_icon_window_(ignored_icon_window),
70 screen_loc_(screen_loc) {
71 ui::EnumerateTopLevelWindows(this);
74 virtual ~DragTargetWindowFinder() {}
76 XID window() const { return output_window_; }
79 virtual bool ShouldStopIterating(XID window) OVERRIDE {
80 if (window == ignored_icon_window_)
83 if (!ui::IsWindowVisible(window))
86 if (!ui::WindowContainsPoint(window, screen_loc_))
89 if (ui::PropertyExists(window, "WM_STATE")) {
90 output_window_ = window;
98 XID ignored_icon_window_;
100 gfx::Point screen_loc_;
102 DISALLOW_COPY_AND_ASSIGN(DragTargetWindowFinder);
105 // Returns the topmost X11 window at |screen_point| if it is advertising that
106 // is supports the Xdnd protocol. Will return the window under the pointer as
107 // |mouse_window|. If there's a Xdnd aware window, it will be returned in
109 void FindWindowFor(const gfx::Point& screen_point,
110 ::Window* mouse_window, ::Window* dest_window) {
111 DragTargetWindowFinder finder(None, screen_point);
112 *mouse_window = finder.window();
115 if (finder.window() == None)
118 // Figure out which window we should test as XdndAware. If mouse_window has
119 // XdndProxy, it will set that proxy on target, and if not, |target|'s
120 // original value will remain.
121 XID target = *mouse_window;
122 ui::GetXIDProperty(*mouse_window, "XdndProxy", &target);
125 if (ui::GetIntProperty(target, "XdndAware", &version) &&
126 version >= kMinXdndVersion) {
127 *dest_window = target;
135 std::map< ::Window, DesktopDragDropClientAuraX11*>
136 DesktopDragDropClientAuraX11::g_live_client_map;
138 class DesktopDragDropClientAuraX11::X11DragContext :
139 public base::MessageLoop::Dispatcher {
141 X11DragContext(ui::X11AtomCache* atom_cache,
142 ::Window local_window,
143 const XClientMessageEvent& event);
144 virtual ~X11DragContext();
146 // When we receive an XdndPosition message, we need to have all the data
147 // copied from the other window before we process the XdndPosition
148 // message. If we have that data already, dispatch immediately. Otherwise,
149 // delay dispatching until we do.
150 void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client,
151 ::Window source_window,
152 const gfx::Point& screen_point);
154 // Called to request the next target from the source window. This is only
155 // done on the first XdndPosition; after that, we cache the data offered by
156 // the source window.
157 void RequestNextTarget();
159 // Called when XSelection data has been copied to our process.
160 void OnSelectionNotify(const XSelectionEvent& xselection);
162 // Clones the fetched targets.
163 const ui::SelectionFormatMap& fetched_targets() { return fetched_targets_; }
165 // Reads the "XdndActionList" property from |source_window| and copies it
169 // Creates a ui::DragDropTypes::DragOperation representation of the current
171 int GetDragOperation() const;
174 // Overridden from MessageLoop::Dispatcher:
175 virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
177 // The atom cache owned by our parent.
178 ui::X11AtomCache* atom_cache_;
180 // The XID of our chrome local aura window handling our events.
181 ::Window local_window_;
183 // The XID of the window that's initiated the drag.
184 unsigned long source_window_;
186 // The client we inform once we're done with requesting data.
187 DesktopDragDropClientAuraX11* drag_drop_client_;
189 // Whether we're blocking the handling of an XdndPosition message by waiting
190 // for |unfetched_targets_| to be fetched.
191 bool waiting_to_handle_position_;
193 // Where the cursor is on screen.
194 gfx::Point screen_point_;
196 // A SelectionFormatMap of data that we have in our process.
197 ui::SelectionFormatMap fetched_targets_;
199 // The names of various data types offered by the other window that we
200 // haven't fetched and put in |fetched_targets_| yet.
201 std::vector<Atom> unfetched_targets_;
204 std::vector<Atom> actions_;
206 DISALLOW_COPY_AND_ASSIGN(X11DragContext);
209 DesktopDragDropClientAuraX11::X11DragContext::X11DragContext(
210 ui::X11AtomCache* atom_cache,
211 ::Window local_window,
212 const XClientMessageEvent& event)
213 : atom_cache_(atom_cache),
214 local_window_(local_window),
215 source_window_(event.data.l[0]),
216 drag_drop_client_(NULL),
217 waiting_to_handle_position_(false) {
218 bool get_types = ((event.data.l[1] & 1) != 0);
221 if (!ui::GetAtomArrayProperty(source_window_,
223 &unfetched_targets_)) {
227 // data.l[2,3,4] contain the first three types. Unused slots can be None.
228 for (int i = 0; i < 3; ++i) {
229 if (event.data.l[2+i] != None) {
230 unfetched_targets_.push_back(event.data.l[2+i]);
235 DesktopDragDropClientAuraX11* client =
236 DesktopDragDropClientAuraX11::GetForWindow(source_window_);
238 // The window doesn't have a DesktopDragDropClientAuraX11, that means it's
239 // created by some other process. Listen for messages on it.
240 base::MessagePumpX11::Current()->AddDispatcherForWindow(
241 this, source_window_);
242 XSelectInput(gfx::GetXDisplay(), source_window_, PropertyChangeMask);
244 // We must perform a full sync here because we could be racing
246 XSync(gfx::GetXDisplay(), False);
248 // This drag originates from an aura window within our process. This means
249 // that we can shortcut the X11 server and ask the owning SelectionOwner
250 // for the data it's offering.
251 fetched_targets_ = client->GetFormatMap();
252 unfetched_targets_.clear();
258 DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() {
259 DesktopDragDropClientAuraX11* client =
260 DesktopDragDropClientAuraX11::GetForWindow(source_window_);
262 // Unsubscribe from message events.
263 base::MessagePumpX11::Current()->RemoveDispatcherForWindow(
268 void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage(
269 DesktopDragDropClientAuraX11* client,
270 ::Window source_window,
271 const gfx::Point& screen_point) {
272 DCHECK_EQ(source_window_, source_window);
274 if (!unfetched_targets_.empty()) {
275 // We have unfetched targets. That means we need to pause the handling of
276 // the position message and ask the other window for its data.
277 screen_point_ = screen_point;
278 drag_drop_client_ = client;
279 waiting_to_handle_position_ = true;
281 fetched_targets_ = ui::SelectionFormatMap();
284 client->CompleteXdndPosition(source_window, screen_point);
288 void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() {
289 ::Atom target = unfetched_targets_.back();
290 unfetched_targets_.pop_back();
292 XConvertSelection(gfx::GetXDisplay(),
293 atom_cache_->GetAtom(kXdndSelection),
295 atom_cache_->GetAtom(kChromiumDragReciever),
300 void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify(
301 const XSelectionEvent& event) {
302 DCHECK(waiting_to_handle_position_);
303 DCHECK(drag_drop_client_);
304 DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever));
306 scoped_refptr<base::RefCountedMemory> data;
308 if (ui::GetRawBytesOfProperty(local_window_, event.property,
309 &data, NULL, NULL, &type)) {
310 fetched_targets_.Insert(event.target, data);
313 if (!unfetched_targets_.empty()) {
316 waiting_to_handle_position_ = false;
317 drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_);
318 drag_drop_client_ = NULL;
322 void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() {
323 DesktopDragDropClientAuraX11* client =
324 DesktopDragDropClientAuraX11::GetForWindow(source_window_);
326 std::vector<Atom> atom_array;
327 if (!ui::GetAtomArrayProperty(source_window_,
332 actions_.swap(atom_array);
335 // We have a property notify set up for other windows in case they change
336 // their action list. Thankfully, the views interface is static and you
337 // can't change the action list after you enter StartDragAndDrop().
338 actions_ = client->GetOfferedDragOperations();
342 int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const {
343 int drag_operation = ui::DragDropTypes::DRAG_NONE;
344 for (std::vector<Atom>::const_iterator it = actions_.begin();
345 it != actions_.end(); ++it) {
346 if (*it == atom_cache_->GetAtom(kXdndActionCopy))
347 drag_operation |= ui::DragDropTypes::DRAG_COPY;
348 else if (*it == atom_cache_->GetAtom(kXdndActionMove))
349 drag_operation |= ui::DragDropTypes::DRAG_MOVE;
350 else if (*it == atom_cache_->GetAtom(kXdndActionLink))
351 drag_operation |= ui::DragDropTypes::DRAG_LINK;
354 return drag_operation;
357 bool DesktopDragDropClientAuraX11::X11DragContext::Dispatch(
358 const base::NativeEvent& event) {
359 if (event->type == PropertyNotify &&
360 event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) {
366 ///////////////////////////////////////////////////////////////////////////////
368 DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11(
369 aura::Window* root_window,
370 views::DesktopNativeCursorManager* cursor_manager,
374 root_window_(root_window),
377 atom_cache_(xdisplay_, kAtomsToCache),
378 target_window_(NULL),
379 source_provider_(NULL),
380 source_current_window_(None),
381 drag_drop_in_progress_(false),
383 resulting_operation_(0),
384 grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorGrabbing)),
385 copy_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorCopy)),
386 move_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorMove)) {
387 DCHECK(g_live_client_map.find(xwindow) == g_live_client_map.end());
388 g_live_client_map.insert(std::make_pair(xwindow, this));
390 // Mark that we are aware of drag and drop concepts.
391 unsigned long xdnd_version = kMinXdndVersion;
392 XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"),
393 XA_ATOM, 32, PropModeReplace,
394 reinterpret_cast<unsigned char*>(&xdnd_version), 1);
397 DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() {
398 g_live_client_map.erase(xwindow_);
402 DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow(
404 std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it =
405 g_live_client_map.find(window);
406 if (it == g_live_client_map.end())
411 void DesktopDragDropClientAuraX11::OnXdndEnter(
412 const XClientMessageEvent& event) {
413 DVLOG(1) << "XdndEnter";
415 int version = (event.data.l[1] & 0xff000000) >> 24;
417 LOG(ERROR) << "Received old XdndEnter message.";
421 // Make sure that we've run ~X11DragContext() before creating another one.
422 target_current_context_.reset();
423 target_current_context_.reset(
424 new X11DragContext(&atom_cache_, xwindow_, event));
426 // In the Windows implementation, we immediately call DesktopDropTargetWin::
427 // Translate(). Here, we wait until we receive an XdndPosition message
428 // because the enter message doesn't convey any positioning
432 void DesktopDragDropClientAuraX11::OnXdndLeave(
433 const XClientMessageEvent& event) {
434 DVLOG(1) << "XdndLeave";
436 target_current_context_.reset();
439 void DesktopDragDropClientAuraX11::OnXdndPosition(
440 const XClientMessageEvent& event) {
441 DVLOG(1) << "XdndPosition";
443 unsigned long source_window = event.data.l[0];
444 int x_root_window = event.data.l[2] >> 16;
445 int y_root_window = event.data.l[2] & 0xffff;
447 if (!target_current_context_.get()) {
452 // If we already have all the data from this drag, we complete it
454 target_current_context_->OnStartXdndPositionMessage(
455 this, source_window, gfx::Point(x_root_window, y_root_window));
458 void DesktopDragDropClientAuraX11::OnXdndStatus(
459 const XClientMessageEvent& event) {
460 DVLOG(1) << "XdndStatus";
462 unsigned long source_window = event.data.l[0];
463 int drag_operation = ui::DragDropTypes::DRAG_NONE;
464 if (event.data.l[1] & 1) {
465 ::Atom atom_operation = event.data.l[4];
466 negotiated_operation_[source_window] = atom_operation;
467 drag_operation = AtomToDragOperation(atom_operation);
470 switch (drag_operation) {
471 case ui::DragDropTypes::DRAG_COPY:
472 move_loop_.UpdateCursor(copy_grab_cursor_);
474 case ui::DragDropTypes::DRAG_MOVE:
475 move_loop_.UpdateCursor(move_grab_cursor_);
478 move_loop_.UpdateCursor(grab_cursor_);
482 // Note: event.data.[2,3] specify a rectangle. It is a request by the other
483 // window to not send further XdndPosition messages while the cursor is
484 // within it. However, it is considered advisory and (at least according to
485 // the spec) the other side must handle further position messages within
486 // it. GTK+ doesn't bother with this, so neither should we.
488 waiting_on_status_.erase(source_window);
490 if (ContainsKey(pending_drop_, source_window)) {
491 // We were waiting on the status message so we could send the XdndDrop.
492 SendXdndDrop(source_window);
496 NextPositionMap::iterator it = next_position_message_.find(source_window);
497 if (it != next_position_message_.end()) {
498 // We were waiting on the status message so we could send off the next
499 // position message we queued up.
500 gfx::Point p = it->second.first;
501 unsigned long time = it->second.second;
502 next_position_message_.erase(it);
504 SendXdndPosition(source_window, p, time);
508 void DesktopDragDropClientAuraX11::OnXdndFinished(
509 const XClientMessageEvent& event) {
510 DVLOG(1) << "XdndFinished";
511 resulting_operation_ = AtomToDragOperation(
512 negotiated_operation_[event.data.l[0]]);
513 move_loop_.EndMoveLoop();
516 void DesktopDragDropClientAuraX11::OnXdndDrop(
517 const XClientMessageEvent& event) {
518 DVLOG(1) << "XdndDrop";
520 unsigned long source_window = event.data.l[0];
522 int drag_operation = ui::DragDropTypes::DRAG_NONE;
523 if (target_window_) {
524 aura::client::DragDropDelegate* delegate =
525 aura::client::GetDragDropDelegate(target_window_);
527 ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11(
528 xwindow_, target_current_context_->fetched_targets()));
530 ui::DropTargetEvent event(data,
531 target_window_location_,
532 target_window_root_location_,
533 target_current_context_->GetDragOperation());
534 drag_operation = delegate->OnPerformDrop(event);
537 target_window_->RemoveObserver(this);
538 target_window_ = NULL;
542 xev.xclient.type = ClientMessage;
543 xev.xclient.message_type = atom_cache_.GetAtom("XdndFinished");
544 xev.xclient.format = 32;
545 xev.xclient.window = source_window;
546 xev.xclient.data.l[0] = xwindow_;
547 xev.xclient.data.l[1] = (drag_operation != 0) ? 1 : 0;
548 xev.xclient.data.l[2] = DragOperationToAtom(drag_operation);
550 SendXClientEvent(source_window, &xev);
553 void DesktopDragDropClientAuraX11::OnSelectionNotify(
554 const XSelectionEvent& xselection) {
555 if (!target_current_context_) {
560 target_current_context_->OnSelectionNotify(xselection);
563 int DesktopDragDropClientAuraX11::StartDragAndDrop(
564 const ui::OSExchangeData& data,
565 aura::Window* root_window,
566 aura::Window* source_window,
567 const gfx::Point& root_location,
569 ui::DragDropTypes::DragEventSource source) {
570 source_current_window_ = None;
571 drag_drop_in_progress_ = true;
572 drag_operation_ = operation;
573 resulting_operation_ = ui::DragDropTypes::DRAG_NONE;
575 const ui::OSExchangeData::Provider* provider = &data.provider();
576 source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>(
579 source_provider_->TakeOwnershipOfSelection();
581 std::vector< ::Atom> actions = GetOfferedDragOperations();
582 ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions);
584 // Windows has a specific method, DoDragDrop(), which performs the entire
585 // drag. We have to emulate this, so we spin off a nested runloop which will
586 // track all cursor movement and reroute events to a specific handler.
587 move_loop_.RunMoveLoop(source_window, grab_cursor_);
589 source_provider_ = NULL;
590 drag_drop_in_progress_ = false;
592 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList"));
594 return resulting_operation_;
597 void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target,
598 const ui::LocatedEvent& event) {
602 void DesktopDragDropClientAuraX11::Drop(aura::Window* target,
603 const ui::LocatedEvent& event) {
607 void DesktopDragDropClientAuraX11::DragCancel() {
608 move_loop_.EndMoveLoop();
611 bool DesktopDragDropClientAuraX11::IsDragDropInProgress() {
612 return drag_drop_in_progress_;
615 void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) {
616 DCHECK_EQ(target_window_, window);
617 target_window_ = NULL;
620 void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) {
621 gfx::Point screen_point(event->x_root, event->y_root);
623 // Find the current window the cursor is over.
624 ::Window mouse_window = None;
625 ::Window dest_window = None;
626 FindWindowFor(screen_point, &mouse_window, &dest_window);
628 if (source_current_window_ != dest_window) {
629 if (source_current_window_ != None)
630 SendXdndLeave(source_current_window_);
632 source_current_window_ = dest_window;
634 if (source_current_window_ != None) {
635 negotiated_operation_.erase(source_current_window_);
636 SendXdndEnter(source_current_window_);
640 if (source_current_window_ != None) {
641 if (ContainsKey(waiting_on_status_, dest_window)) {
642 next_position_message_[dest_window] =
643 std::make_pair(screen_point, event->time);
645 SendXdndPosition(dest_window, screen_point, event->time);
650 void DesktopDragDropClientAuraX11::OnMouseReleased() {
651 if (source_current_window_ != None) {
652 if (ContainsKey(waiting_on_status_, source_current_window_)) {
653 // If we are waiting for an XdndStatus message, we need to wait for it to
655 pending_drop_.insert(source_current_window_);
659 std::map< ::Window, ::Atom>::iterator it =
660 negotiated_operation_.find(source_current_window_);
661 if (it != negotiated_operation_.end() && it->second != None) {
662 // We have negotiated an action with the other end.
663 SendXdndDrop(source_current_window_);
667 SendXdndLeave(source_current_window_);
668 source_current_window_ = None;
671 move_loop_.EndMoveLoop();
674 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() {
675 target_current_context_.reset();
678 void DesktopDragDropClientAuraX11::DragTranslate(
679 const gfx::Point& root_window_location,
680 scoped_ptr<ui::OSExchangeData>* data,
681 scoped_ptr<ui::DropTargetEvent>* event,
682 aura::client::DragDropDelegate** delegate) {
683 gfx::Point root_location = root_window_location;
684 root_window_->GetDispatcher()->ConvertPointFromNativeScreen(&root_location);
685 aura::Window* target_window =
686 root_window_->GetEventHandlerForPoint(root_location);
687 bool target_window_changed = false;
688 if (target_window != target_window_) {
691 target_window_ = target_window;
693 target_window_->AddObserver(this);
694 target_window_changed = true;
699 *delegate = aura::client::GetDragDropDelegate(target_window_);
703 data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11(
704 xwindow_, target_current_context_->fetched_targets())));
705 gfx::Point location = root_location;
706 aura::Window::ConvertPointToTarget(root_window_, target_window_, &location);
708 target_window_location_ = location;
709 target_window_root_location_ = root_location;
711 event->reset(new ui::DropTargetEvent(
715 target_current_context_->GetDragOperation()));
716 if (target_window_changed)
717 (*delegate)->OnDragEntered(*event->get());
720 void DesktopDragDropClientAuraX11::NotifyDragLeave() {
723 DragDropDelegate* delegate =
724 aura::client::GetDragDropDelegate(target_window_);
726 delegate->OnDragExited();
727 target_window_->RemoveObserver(this);
728 target_window_ = NULL;
731 ::Atom DesktopDragDropClientAuraX11::DragOperationToAtom(
732 int drag_operation) {
733 if (drag_operation & ui::DragDropTypes::DRAG_COPY)
734 return atom_cache_.GetAtom(kXdndActionCopy);
735 if (drag_operation & ui::DragDropTypes::DRAG_MOVE)
736 return atom_cache_.GetAtom(kXdndActionMove);
737 if (drag_operation & ui::DragDropTypes::DRAG_LINK)
738 return atom_cache_.GetAtom(kXdndActionLink);
743 int DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
744 if (atom == atom_cache_.GetAtom(kXdndActionCopy))
745 return ui::DragDropTypes::DRAG_COPY;
746 if (atom == atom_cache_.GetAtom(kXdndActionMove))
747 return ui::DragDropTypes::DRAG_MOVE;
748 if (atom == atom_cache_.GetAtom(kXdndActionLink))
749 return ui::DragDropTypes::DRAG_LINK;
751 return ui::DragDropTypes::DRAG_NONE;
754 std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() {
755 std::vector< ::Atom> operations;
756 if (drag_operation_ & ui::DragDropTypes::DRAG_COPY)
757 operations.push_back(atom_cache_.GetAtom(kXdndActionCopy));
758 if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE)
759 operations.push_back(atom_cache_.GetAtom(kXdndActionMove));
760 if (drag_operation_ & ui::DragDropTypes::DRAG_LINK)
761 operations.push_back(atom_cache_.GetAtom(kXdndActionLink));
765 ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const {
766 return source_provider_ ? source_provider_->GetFormatMap() :
767 ui::SelectionFormatMap();
770 void DesktopDragDropClientAuraX11::CompleteXdndPosition(
771 ::Window source_window,
772 const gfx::Point& screen_point) {
773 int drag_operation = ui::DragDropTypes::DRAG_NONE;
774 scoped_ptr<ui::OSExchangeData> data;
775 scoped_ptr<ui::DropTargetEvent> drop_target_event;
776 DragDropDelegate* delegate = NULL;
777 DragTranslate(screen_point, &data, &drop_target_event, &delegate);
779 drag_operation = delegate->OnDragUpdated(*drop_target_event);
781 // Sends an XdndStatus message back to the source_window. l[2,3]
782 // theoretically represent an area in the window where the current action is
783 // the same as what we're returning, but I can't find any implementation that
784 // actually making use of this. A client can return (0, 0) and/or set the
785 // first bit of l[1] to disable the feature, and it appears that gtk neither
786 // sets this nor respects it if set.
788 xev.xclient.type = ClientMessage;
789 xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus");
790 xev.xclient.format = 32;
791 xev.xclient.window = source_window;
792 xev.xclient.data.l[0] = xwindow_;
793 xev.xclient.data.l[1] = (drag_operation != 0) ?
794 (kWantFurtherPosEvents | kWillAcceptDrop) : 0;
795 xev.xclient.data.l[2] = 0;
796 xev.xclient.data.l[3] = 0;
797 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation);
799 SendXClientEvent(source_window, &xev);
802 void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) {
804 xev.xclient.type = ClientMessage;
805 xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter");
806 xev.xclient.format = 32;
807 xev.xclient.window = dest_window;
808 xev.xclient.data.l[0] = xwindow_;
809 xev.xclient.data.l[1] = (kMinXdndVersion << 24); // The version number.
810 xev.xclient.data.l[2] = 0;
811 xev.xclient.data.l[3] = 0;
812 xev.xclient.data.l[4] = 0;
814 std::vector<Atom> targets;
815 source_provider_->RetrieveTargets(&targets);
817 if (targets.size() > 3) {
818 xev.xclient.data.l[1] |= 1;
819 ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets);
821 // Pack the targets into the enter message.
822 for (size_t i = 0; i < targets.size(); ++i)
823 xev.xclient.data.l[2 + i] = targets[i];
826 SendXClientEvent(dest_window, &xev);
829 void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) {
830 // If we're sending a leave message, don't wait for status messages anymore.
831 waiting_on_status_.erase(dest_window);
832 NextPositionMap::iterator it = next_position_message_.find(dest_window);
833 if (it != next_position_message_.end())
834 next_position_message_.erase(it);
837 xev.xclient.type = ClientMessage;
838 xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave");
839 xev.xclient.format = 32;
840 xev.xclient.window = dest_window;
841 xev.xclient.data.l[0] = xwindow_;
842 xev.xclient.data.l[1] = 0;
843 xev.xclient.data.l[2] = 0;
844 xev.xclient.data.l[3] = 0;
845 xev.xclient.data.l[4] = 0;
846 SendXClientEvent(dest_window, &xev);
849 void DesktopDragDropClientAuraX11::SendXdndPosition(
850 ::Window dest_window,
851 const gfx::Point& screen_point,
852 unsigned long time) {
853 waiting_on_status_.insert(dest_window);
856 xev.xclient.type = ClientMessage;
857 xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition");
858 xev.xclient.format = 32;
859 xev.xclient.window = dest_window;
860 xev.xclient.data.l[0] = xwindow_;
861 xev.xclient.data.l[1] = 0;
862 xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y();
863 xev.xclient.data.l[3] = time;
864 xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
865 SendXClientEvent(dest_window, &xev);
868 void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {
870 xev.xclient.type = ClientMessage;
871 xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop");
872 xev.xclient.format = 32;
873 xev.xclient.window = dest_window;
874 xev.xclient.data.l[0] = xwindow_;
875 xev.xclient.data.l[1] = 0;
876 xev.xclient.data.l[2] = CurrentTime;
877 xev.xclient.data.l[3] = None;
878 xev.xclient.data.l[4] = None;
879 SendXClientEvent(dest_window, &xev);
882 void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid,
884 DCHECK_EQ(ClientMessage, xev->type);
886 // Don't send messages to the X11 message queue if we can help it.
887 DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid);
889 Atom message_type = xev->xclient.message_type;
890 if (message_type == atom_cache_.GetAtom("XdndEnter")) {
891 short_circuit->OnXdndEnter(xev->xclient);
893 } else if (message_type == atom_cache_.GetAtom("XdndLeave")) {
894 short_circuit->OnXdndLeave(xev->xclient);
896 } else if (message_type == atom_cache_.GetAtom("XdndPosition")) {
897 short_circuit->OnXdndPosition(xev->xclient);
899 } else if (message_type == atom_cache_.GetAtom("XdndStatus")) {
900 short_circuit->OnXdndStatus(xev->xclient);
902 } else if (message_type == atom_cache_.GetAtom("XdndFinished")) {
903 short_circuit->OnXdndFinished(xev->xclient);
905 } else if (message_type == atom_cache_.GetAtom("XdndDrop")) {
906 short_circuit->OnXdndDrop(xev->xclient);
911 // I don't understand why the GTK+ code is doing what it's doing here. It
912 // goes out of its way to send the XEvent so that it receives a callback on
913 // success or failure, and when it fails, it then sends an internal
914 // GdkEvent about the failed drag. (And sending this message doesn't appear
915 // to go through normal xlib machinery, but instead passes through the low
916 // level xProto (the x11 wire format) that I don't understand.
918 // I'm unsure if I have to jump through those hoops, or if XSendEvent is
920 XSendEvent(xdisplay_, xid, False, 0, xev);