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. 4 5 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h" 6 7 #include <X11/Xatom.h> 8 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/os_exchange_data.h" 17 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" 18 #include "ui/base/events/event.h" 19 #include "ui/base/x/selection_utils.h" 20 #include "ui/base/x/x11_util.h" 21 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" 22 23 using aura::client::DragDropDelegate; 24 using ui::OSExchangeData; 25 26 namespace { 27 28 const int kMinXdndVersion = 5; 29 30 const int kWillAcceptDrop = 1; 31 const int kWantFurtherPosEvents = 2; 32 33 const char kXdndActionCopy[] = "XdndActionCopy"; 34 const char kXdndActionMove[] = "XdndActionMove"; 35 const char kXdndActionLink[] = "XdndActionLink"; 36 37 const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER"; 38 const char kXdndSelection[] = "XdndSelection"; 39 40 const char* kAtomsToCache[] = { 41 kChromiumDragReciever, 42 "XdndActionAsk", 43 kXdndActionCopy, 44 kXdndActionLink, 45 "XdndActionList", 46 kXdndActionMove, 47 "XdndActionPrivate", 48 "XdndAware", 49 "XdndDrop", 50 "XdndEnter", 51 "XdndFinished", 52 "XdndLeave", 53 "XdndPosition", 54 "XdndProxy", // Proxy windows? 55 kXdndSelection, 56 "XdndStatus", 57 "XdndTypeList", 58 NULL 59 }; 60 61 // Helper class to FindWindowFor which looks for a drag target under the 62 // cursor. 63 class DragTargetWindowFinder : public ui::EnumerateWindowsDelegate { 64 public: 65 DragTargetWindowFinder(XID ignored_icon_window, 66 gfx::Point screen_loc) 67 : ignored_icon_window_(ignored_icon_window), 68 output_window_(None), 69 screen_loc_(screen_loc) { 70 ui::EnumerateTopLevelWindows(this); 71 } 72 73 virtual ~DragTargetWindowFinder() {} 74 75 XID window() const { return output_window_; } 76 77 protected: 78 virtual bool ShouldStopIterating(XID window) OVERRIDE { 79 if (window == ignored_icon_window_) 80 return false; 81 82 if (!ui::IsWindowVisible(window)) 83 return false; 84 85 if (!ui::WindowContainsPoint(window, screen_loc_)) 86 return false; 87 88 if (ui::PropertyExists(window, "WM_STATE")) { 89 output_window_ = window; 90 return true; 91 } 92 93 return false; 94 } 95 96 private: 97 XID ignored_icon_window_; 98 XID output_window_; 99 gfx::Point screen_loc_; 100 101 DISALLOW_COPY_AND_ASSIGN(DragTargetWindowFinder); 102 }; 103 104 // Returns the topmost X11 window at |screen_point| if it is advertising that 105 // is supports the Xdnd protocol. Will return the window under the pointer as 106 // |mouse_window|. If there's a Xdnd aware window, it will be returned in 107 // |dest_window|. 108 void FindWindowFor(const gfx::Point& screen_point, 109 ::Window* mouse_window, ::Window* dest_window) { 110 DragTargetWindowFinder finder(None, screen_point); 111 *mouse_window = finder.window(); 112 *dest_window = None; 113 114 if (finder.window() == None) 115 return; 116 117 // Figure out which window we should test as XdndAware. If mouse_window has 118 // XdndProxy, it will set that proxy on target, and if not, |target|'s 119 // original value will remain. 120 XID target = *mouse_window; 121 ui::GetXIDProperty(*mouse_window, "XdndProxy", &target); 122 123 int version; 124 if (ui::GetIntProperty(target, "XdndAware", &version) && 125 version >= kMinXdndVersion) { 126 *dest_window = target; 127 } 128 } 129 130 } // namespace 131 132 namespace views { 133 134 std::map< ::Window, DesktopDragDropClientAuraX11*> 135 DesktopDragDropClientAuraX11::g_live_client_map; 136 137 class DesktopDragDropClientAuraX11::X11DragContext : 138 public base::MessageLoop::Dispatcher { 139 public: 140 X11DragContext(ui::X11AtomCache* atom_cache, 141 ::Window local_window, 142 const XClientMessageEvent& event); 143 virtual ~X11DragContext(); 144 145 // When we receive an XdndPosition message, we need to have all the data 146 // copied from the other window before we process the XdndPosition 147 // message. If we have that data already, dispatch immediately. Otherwise, 148 // delay dispatching until we do. 149 void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client, 150 ::Window source_window, 151 const gfx::Point& screen_point); 152 153 // Called to request the next target from the source window. This is only 154 // done on the first XdndPosition; after that, we cache the data offered by 155 // the source window. 156 void RequestNextTarget(); 157 158 // Called when XSelection data has been copied to our process. 159 void OnSelectionNotify(const XSelectionEvent& xselection); 160 161 // Clones the fetched targets. 162 const ui::SelectionFormatMap& fetched_targets() { return fetched_targets_; } 163 164 // Reads the "XdndActionList" property from |source_window| and copies it 165 // into |actions|. 166 void ReadActions(); 167 168 // Creates a ui::DragDropTypes::DragOperation representation of the current 169 // action list. 170 int GetDragOperation() const; 171 172 private: 173 // Overridden from MessageLoop::Dispatcher: 174 virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; 175 176 // The atom cache owned by our parent. 177 ui::X11AtomCache* atom_cache_; 178 179 // The XID of our chrome local aura window handling our events. 180 ::Window local_window_; 181 182 // The XID of the window that's initiated the drag. 183 unsigned long source_window_; 184 185 // The client we inform once we're done with requesting data. 186 DesktopDragDropClientAuraX11* drag_drop_client_; 187 188 // Whether we're blocking the handling of an XdndPosition message by waiting 189 // for |unfetched_targets_| to be fetched. 190 bool waiting_to_handle_position_; 191 192 // Where the cursor is on screen. 193 gfx::Point screen_point_; 194 195 // A SelectionFormatMap of data that we have in our process. 196 ui::SelectionFormatMap fetched_targets_; 197 198 // The names of various data types offered by the other window that we 199 // haven't fetched and put in |fetched_targets_| yet. 200 std::vector<Atom> unfetched_targets_; 201 202 // Possible actions. 203 std::vector<Atom> actions_; 204 205 DISALLOW_COPY_AND_ASSIGN(X11DragContext); 206 }; 207 208 DesktopDragDropClientAuraX11::X11DragContext::X11DragContext( 209 ui::X11AtomCache* atom_cache, 210 ::Window local_window, 211 const XClientMessageEvent& event) 212 : atom_cache_(atom_cache), 213 local_window_(local_window), 214 source_window_(event.data.l[0]), 215 drag_drop_client_(NULL), 216 waiting_to_handle_position_(false) { 217 bool get_types = ((event.data.l[1] & 1) != 0); 218 219 if (get_types) { 220 if (!ui::GetAtomArrayProperty(source_window_, 221 "XdndTypeList", 222 &unfetched_targets_)) { 223 return; 224 } 225 } else { 226 // data.l[2,3,4] contain the first three types. Unused slots can be None. 227 for (int i = 0; i < 3; ++i) { 228 if (event.data.l[2+i] != None) { 229 unfetched_targets_.push_back(event.data.l[2+i]); 230 } 231 } 232 } 233 234 DesktopDragDropClientAuraX11* client = 235 DesktopDragDropClientAuraX11::GetForWindow(source_window_); 236 if (!client) { 237 // The window doesn't have a DesktopDragDropClientAuraX11, that means it's 238 // created by some other process. Listen for messages on it. 239 base::MessagePumpAuraX11::Current()->AddDispatcherForWindow( 240 this, source_window_); 241 XSelectInput(base::MessagePumpAuraX11::GetDefaultXDisplay(), 242 source_window_, PropertyChangeMask); 243 244 // We must perform a full sync here because we could be racing 245 // |source_window_|. 246 XSync(base::MessagePumpAuraX11::GetDefaultXDisplay(), False); 247 } else { 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(); 253 } 254 255 ReadActions(); 256 } 257 258 DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() { 259 DesktopDragDropClientAuraX11* client = 260 DesktopDragDropClientAuraX11::GetForWindow(source_window_); 261 if (!client) { 262 // Unsubscribe from message events. 263 base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow( 264 source_window_); 265 } 266 } 267 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); 273 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; 280 281 fetched_targets_ = ui::SelectionFormatMap(); 282 RequestNextTarget(); 283 } else { 284 client->CompleteXdndPosition(source_window, screen_point); 285 } 286 } 287 288 void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() { 289 ::Atom target = unfetched_targets_.back(); 290 unfetched_targets_.pop_back(); 291 292 XConvertSelection(base::MessagePumpAuraX11::GetDefaultXDisplay(), 293 atom_cache_->GetAtom(kXdndSelection), 294 target, 295 atom_cache_->GetAtom(kChromiumDragReciever), 296 local_window_, 297 CurrentTime); 298 } 299 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)); 305 306 scoped_refptr<base::RefCountedMemory> data; 307 ::Atom type = None; 308 if (ui::GetRawBytesOfProperty(local_window_, event.property, 309 &data, NULL, NULL, &type)) { 310 fetched_targets_.Insert(event.target, data); 311 } 312 313 if (!unfetched_targets_.empty()) { 314 RequestNextTarget(); 315 } else { 316 waiting_to_handle_position_ = false; 317 drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_); 318 drag_drop_client_ = NULL; 319 } 320 } 321 322 void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() { 323 DesktopDragDropClientAuraX11* client = 324 DesktopDragDropClientAuraX11::GetForWindow(source_window_); 325 if (!client) { 326 std::vector<Atom> atom_array; 327 if (!ui::GetAtomArrayProperty(source_window_, 328 "XdndActionList", 329 &atom_array)) { 330 actions_.clear(); 331 } else { 332 actions_.swap(atom_array); 333 } 334 } else { 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(); 339 } 340 } 341 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; 352 } 353 354 return drag_operation; 355 } 356 357 bool DesktopDragDropClientAuraX11::X11DragContext::Dispatch( 358 const base::NativeEvent& event) { 359 if (event->type == PropertyNotify && 360 event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) { 361 ReadActions(); 362 } 363 return true; 364 } 365 366 /////////////////////////////////////////////////////////////////////////////// 367 368 DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11( 369 aura::RootWindow* root_window, 370 views::DesktopNativeCursorManager* cursor_manager, 371 Display* xdisplay, 372 ::Window xwindow) 373 : move_loop_(this), 374 root_window_(root_window), 375 xdisplay_(xdisplay), 376 xwindow_(xwindow), 377 atom_cache_(xdisplay_, kAtomsToCache), 378 target_window_(NULL), 379 source_provider_(NULL), 380 source_current_window_(None), 381 drag_drop_in_progress_(false), 382 drag_operation_(0), 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)); 389 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); 395 } 396 397 DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() { 398 g_live_client_map.erase(xwindow_); 399 } 400 401 // static 402 DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow( 403 ::Window window) { 404 std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it = 405 g_live_client_map.find(window); 406 if (it == g_live_client_map.end()) 407 return NULL; 408 return it->second; 409 } 410 411 void DesktopDragDropClientAuraX11::OnXdndEnter( 412 const XClientMessageEvent& event) { 413 DVLOG(1) << "XdndEnter"; 414 415 int version = (event.data.l[1] & 0xff000000) >> 24; 416 if (version < 3) { 417 LOG(ERROR) << "Received old XdndEnter message."; 418 return; 419 } 420 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)); 425 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 429 // information. 430 } 431 432 void DesktopDragDropClientAuraX11::OnXdndLeave( 433 const XClientMessageEvent& event) { 434 DVLOG(1) << "XdndLeave"; 435 NotifyDragLeave(); 436 target_current_context_.reset(); 437 } 438 439 void DesktopDragDropClientAuraX11::OnXdndPosition( 440 const XClientMessageEvent& event) { 441 DVLOG(1) << "XdndPosition"; 442 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; 446 447 if (!target_current_context_.get()) { 448 NOTREACHED(); 449 return; 450 } 451 452 // If we already have all the data from this drag, we complete it 453 // immediately. 454 target_current_context_->OnStartXdndPositionMessage( 455 this, source_window, gfx::Point(x_root_window, y_root_window)); 456 } 457 458 void DesktopDragDropClientAuraX11::OnXdndStatus( 459 const XClientMessageEvent& event) { 460 DVLOG(1) << "XdndStatus"; 461 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); 468 } 469 470 switch (drag_operation) { 471 case ui::DragDropTypes::DRAG_COPY: 472 move_loop_.UpdateCursor(copy_grab_cursor_); 473 break; 474 case ui::DragDropTypes::DRAG_MOVE: 475 move_loop_.UpdateCursor(move_grab_cursor_); 476 break; 477 default: 478 move_loop_.UpdateCursor(grab_cursor_); 479 break; 480 } 481 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. 487 488 waiting_on_status_.erase(source_window); 489 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); 493 return; 494 } 495 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); 503 504 SendXdndPosition(source_window, p, time); 505 } 506 } 507 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(); 514 } 515 516 void DesktopDragDropClientAuraX11::OnXdndDrop( 517 const XClientMessageEvent& event) { 518 DVLOG(1) << "XdndDrop"; 519 520 unsigned long source_window = event.data.l[0]; 521 522 int drag_operation = ui::DragDropTypes::DRAG_NONE; 523 if (target_window_) { 524 aura::client::DragDropDelegate* delegate = 525 aura::client::GetDragDropDelegate(target_window_); 526 if (delegate) { 527 ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11( 528 xwindow_, target_current_context_->fetched_targets())); 529 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); 535 } 536 537 target_window_->RemoveObserver(this); 538 target_window_ = NULL; 539 } 540 541 XEvent xev; 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); 549 550 SendXClientEvent(source_window, &xev); 551 } 552 553 void DesktopDragDropClientAuraX11::OnSelectionNotify( 554 const XSelectionEvent& xselection) { 555 if (!target_current_context_) { 556 NOTIMPLEMENTED(); 557 return; 558 } 559 560 target_current_context_->OnSelectionNotify(xselection); 561 } 562 563 int DesktopDragDropClientAuraX11::StartDragAndDrop( 564 const ui::OSExchangeData& data, 565 aura::RootWindow* root_window, 566 aura::Window* source_window, 567 const gfx::Point& root_location, 568 int operation, 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; 574 575 const ui::OSExchangeData::Provider* provider = &data.provider(); 576 source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>( 577 provider); 578 579 source_provider_->TakeOwnershipOfSelection(); 580 581 std::vector< ::Atom> actions = GetOfferedDragOperations(); 582 ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions); 583 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_); 588 589 source_provider_ = NULL; 590 drag_drop_in_progress_ = false; 591 drag_operation_ = 0; 592 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList")); 593 594 return resulting_operation_; 595 } 596 597 void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target, 598 const ui::LocatedEvent& event) { 599 NOTIMPLEMENTED(); 600 } 601 602 void DesktopDragDropClientAuraX11::Drop(aura::Window* target, 603 const ui::LocatedEvent& event) { 604 NOTIMPLEMENTED(); 605 } 606 607 void DesktopDragDropClientAuraX11::DragCancel() { 608 move_loop_.EndMoveLoop(); 609 } 610 611 bool DesktopDragDropClientAuraX11::IsDragDropInProgress() { 612 return drag_drop_in_progress_; 613 } 614 615 void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) { 616 DCHECK_EQ(target_window_, window); 617 target_window_ = NULL; 618 } 619 620 void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) { 621 gfx::Point screen_point(event->x_root, event->y_root); 622 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); 627 628 if (source_current_window_ != dest_window) { 629 if (source_current_window_ != None) 630 SendXdndLeave(source_current_window_); 631 632 source_current_window_ = dest_window; 633 634 if (source_current_window_ != None) { 635 negotiated_operation_.erase(source_current_window_); 636 SendXdndEnter(source_current_window_); 637 } 638 } 639 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); 644 } else { 645 SendXdndPosition(dest_window, screen_point, event->time); 646 } 647 } 648 } 649 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 654 // complete. 655 pending_drop_.insert(source_current_window_); 656 return; 657 } 658 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_); 664 return; 665 } 666 667 SendXdndLeave(source_current_window_); 668 source_current_window_ = None; 669 } 670 671 move_loop_.EndMoveLoop(); 672 } 673 674 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() { 675 target_current_context_.reset(); 676 } 677 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_->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_) { 689 if (target_window_) 690 NotifyDragLeave(); 691 target_window_ = target_window; 692 if (target_window_) 693 target_window_->AddObserver(this); 694 target_window_changed = true; 695 } 696 *delegate = NULL; 697 if (!target_window_) 698 return; 699 *delegate = aura::client::GetDragDropDelegate(target_window_); 700 if (!*delegate) 701 return; 702 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); 707 708 target_window_location_ = location; 709 target_window_root_location_ = root_location; 710 711 event->reset(new ui::DropTargetEvent( 712 *(data->get()), 713 location, 714 root_location, 715 target_current_context_->GetDragOperation())); 716 if (target_window_changed) 717 (*delegate)->OnDragEntered(*event->get()); 718 } 719 720 void DesktopDragDropClientAuraX11::NotifyDragLeave() { 721 if (!target_window_) 722 return; 723 DragDropDelegate* delegate = 724 aura::client::GetDragDropDelegate(target_window_); 725 if (delegate) 726 delegate->OnDragExited(); 727 target_window_->RemoveObserver(this); 728 target_window_ = NULL; 729 } 730 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); 739 740 return None; 741 } 742 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; 750 751 return ui::DragDropTypes::DRAG_NONE; 752 } 753 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)); 762 return operations; 763 } 764 765 ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const { 766 return source_provider_ ? source_provider_->GetFormatMap() : 767 ui::SelectionFormatMap(); 768 } 769 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); 778 if (delegate) 779 drag_operation = delegate->OnDragUpdated(*drop_target_event); 780 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. 787 XEvent xev; 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); 798 799 SendXClientEvent(source_window, &xev); 800 } 801 802 void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) { 803 XEvent xev; 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; 813 814 std::vector<Atom> targets; 815 source_provider_->RetrieveTargets(&targets); 816 817 if (targets.size() > 3) { 818 xev.xclient.data.l[1] |= 1; 819 ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets); 820 } else { 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]; 824 } 825 826 SendXClientEvent(dest_window, &xev); 827 } 828 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); 835 836 XEvent xev; 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); 847 } 848 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); 854 855 XEvent xev; 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); 866 } 867 868 void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) { 869 XEvent xev; 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); 880 } 881 882 void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid, 883 XEvent* xev) { 884 DCHECK_EQ(ClientMessage, xev->type); 885 886 // Don't send messages to the X11 message queue if we can help it. 887 DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid); 888 if (short_circuit) { 889 Atom message_type = xev->xclient.message_type; 890 if (message_type == atom_cache_.GetAtom("XdndEnter")) { 891 short_circuit->OnXdndEnter(xev->xclient); 892 return; 893 } else if (message_type == atom_cache_.GetAtom("XdndLeave")) { 894 short_circuit->OnXdndLeave(xev->xclient); 895 return; 896 } else if (message_type == atom_cache_.GetAtom("XdndPosition")) { 897 short_circuit->OnXdndPosition(xev->xclient); 898 return; 899 } else if (message_type == atom_cache_.GetAtom("XdndStatus")) { 900 short_circuit->OnXdndStatus(xev->xclient); 901 return; 902 } else if (message_type == atom_cache_.GetAtom("XdndFinished")) { 903 short_circuit->OnXdndFinished(xev->xclient); 904 return; 905 } else if (message_type == atom_cache_.GetAtom("XdndDrop")) { 906 short_circuit->OnXdndDrop(xev->xclient); 907 return; 908 } 909 } 910 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. 917 // 918 // I'm unsure if I have to jump through those hoops, or if XSendEvent is 919 // sufficient. 920 XSendEvent(xdisplay_, xid, False, 0, xev); 921 } 922 923 } // namespace views 924