Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h" | 5 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h" |
| 6 | 6 |
| 7 #include <X11/Xatom.h> | 7 #include <X11/Xatom.h> |
| 8 | 8 |
| 9 #include "base/event_types.h" | 9 #include "base/event_types.h" |
| 10 #include "base/lazy_instance.h" | 10 #include "base/lazy_instance.h" |
| 11 #include "base/message_loop/message_loop.h" | 11 #include "base/message_loop/message_loop.h" |
| 12 #include "ui/aura/window.h" | 12 #include "ui/aura/window.h" |
| 13 #include "ui/aura/window_tree_host.h" | 13 #include "ui/aura/window_tree_host.h" |
| 14 #include "ui/base/clipboard/clipboard.h" | 14 #include "ui/base/clipboard/clipboard.h" |
| 15 #include "ui/base/dragdrop/drop_target_event.h" | 15 #include "ui/base/dragdrop/drop_target_event.h" |
| 16 #include "ui/base/dragdrop/os_exchange_data.h" | 16 #include "ui/base/dragdrop/os_exchange_data.h" |
| 17 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" | 17 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" |
| 18 #include "ui/base/x/selection_utils.h" | 18 #include "ui/base/x/selection_utils.h" |
| 19 #include "ui/base/x/x11_util.h" | 19 #include "ui/base/x/x11_util.h" |
| 20 #include "ui/events/event.h" | 20 #include "ui/events/event.h" |
| 21 #include "ui/events/platform/platform_event_source.h" | 21 #include "ui/events/platform/platform_event_source.h" |
| 22 #include "ui/gfx/screen.h" | |
| 23 #include "ui/views/controls/image_view.h" | |
| 22 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" | 24 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" |
| 23 #include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h" | 25 #include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h" |
| 26 #include "ui/views/widget/widget.h" | |
| 24 #include "ui/wm/public/drag_drop_client.h" | 27 #include "ui/wm/public/drag_drop_client.h" |
| 25 #include "ui/wm/public/drag_drop_delegate.h" | 28 #include "ui/wm/public/drag_drop_delegate.h" |
| 26 | 29 |
| 27 using aura::client::DragDropDelegate; | 30 using aura::client::DragDropDelegate; |
| 28 using ui::OSExchangeData; | 31 using ui::OSExchangeData; |
| 29 | 32 |
| 30 namespace { | 33 namespace { |
| 31 | 34 |
| 35 // The minimum alpha before we declare a pixel transparent when searching in | |
| 36 // our source image. | |
| 37 const uint32 kMinAlpha = 32; | |
| 38 const unsigned char kDragWidgetOpacity = 0xc0; | |
| 39 | |
| 40 // Drag widget is positioned off screen when drag image is not set or is | |
| 41 // completely transparent. | |
| 42 const gfx::Size kNullDragWidgetSize = gfx::Size(1, 1); | |
| 43 const gfx::Point kNullDragWidgetLocation = gfx::Point(-100, -100); | |
|
pkotwicz
2014/05/12 16:04:29
I do not know if there is much precedent for non p
varkha
2014/05/20 17:13:35
Good catch. https://engdoc.corp.google.com/eng/doc
| |
| 44 | |
| 45 bool CheckIfIconValid(const gfx::ImageSkia& drag_image) { | |
|
pkotwicz
2014/05/12 16:04:29
Is CheckIfIconValid() still needed in a world wher
varkha
2014/05/20 17:13:35
Done.
| |
| 46 // Because we need a GL context per window, we do a quick check so that we | |
| 47 // don't make another context if the window would just be displaying a mostly | |
| 48 // transparent image. | |
| 49 const SkBitmap* in_bitmap = drag_image.bitmap(); | |
| 50 SkAutoLockPixels in_lock(*in_bitmap); | |
| 51 for (int y = 0; y < in_bitmap->height(); ++y) { | |
| 52 uint32* in_row = in_bitmap->getAddr32(0, y); | |
| 53 | |
| 54 for (int x = 0; x < in_bitmap->width(); ++x) { | |
| 55 if (SkColorGetA(in_row[x]) > kMinAlpha) | |
| 56 return true; | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 return false; | |
| 61 } | |
| 62 | |
| 32 const int kMinXdndVersion = 5; | 63 const int kMinXdndVersion = 5; |
| 33 | 64 |
| 34 const int kWillAcceptDrop = 1; | 65 const int kWillAcceptDrop = 1; |
| 35 const int kWantFurtherPosEvents = 2; | 66 const int kWantFurtherPosEvents = 2; |
| 36 | 67 |
| 37 const char kXdndActionCopy[] = "XdndActionCopy"; | 68 const char kXdndActionCopy[] = "XdndActionCopy"; |
| 38 const char kXdndActionMove[] = "XdndActionMove"; | 69 const char kXdndActionMove[] = "XdndActionMove"; |
| 39 const char kXdndActionLink[] = "XdndActionLink"; | 70 const char kXdndActionLink[] = "XdndActionLink"; |
| 40 const char kXdndActionDirectSave[] = "XdndActionDirectSave"; | 71 const char kXdndActionDirectSave[] = "XdndActionDirectSave"; |
| 41 | 72 |
| (...skipping 455 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 497 // We were waiting on the status message so we could send the XdndDrop. | 528 // We were waiting on the status message so we could send the XdndDrop. |
| 498 if (negotiated_operation_ == ui::DragDropTypes::DRAG_NONE) { | 529 if (negotiated_operation_ == ui::DragDropTypes::DRAG_NONE) { |
| 499 move_loop_.EndMoveLoop(); | 530 move_loop_.EndMoveLoop(); |
| 500 return; | 531 return; |
| 501 } | 532 } |
| 502 source_state_ = SOURCE_STATE_DROPPED; | 533 source_state_ = SOURCE_STATE_DROPPED; |
| 503 SendXdndDrop(source_window); | 534 SendXdndDrop(source_window); |
| 504 return; | 535 return; |
| 505 } | 536 } |
| 506 | 537 |
| 507 switch (negotiated_operation_) { | 538 UpdateCursor(); |
| 508 case ui::DragDropTypes::DRAG_COPY: | |
| 509 move_loop_.UpdateCursor(copy_grab_cursor_); | |
| 510 break; | |
| 511 case ui::DragDropTypes::DRAG_MOVE: | |
| 512 move_loop_.UpdateCursor(move_grab_cursor_); | |
| 513 break; | |
| 514 default: | |
| 515 move_loop_.UpdateCursor(grab_cursor_); | |
| 516 break; | |
| 517 } | |
| 518 | 539 |
| 519 // Note: event.data.[2,3] specify a rectangle. It is a request by the other | 540 // Note: event.data.[2,3] specify a rectangle. It is a request by the other |
| 520 // window to not send further XdndPosition messages while the cursor is | 541 // window to not send further XdndPosition messages while the cursor is |
| 521 // within it. However, it is considered advisory and (at least according to | 542 // within it. However, it is considered advisory and (at least according to |
| 522 // the spec) the other side must handle further position messages within | 543 // the spec) the other side must handle further position messages within |
| 523 // it. GTK+ doesn't bother with this, so neither should we. | 544 // it. GTK+ doesn't bother with this, so neither should we. |
| 524 | 545 |
| 525 if (next_position_message_.get()) { | 546 if (next_position_message_.get()) { |
| 526 // We were waiting on the status message so we could send off the next | 547 // We were waiting on the status message so we could send off the next |
| 527 // position message we queued up. | 548 // position message we queued up. |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 623 source_provider_->file_contents_name().AsUTF8Unsafe()); | 644 source_provider_->file_contents_name().AsUTF8Unsafe()); |
| 624 } | 645 } |
| 625 ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions); | 646 ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions); |
| 626 | 647 |
| 627 // It is possible for the DesktopWindowTreeHostX11 to be destroyed during the | 648 // It is possible for the DesktopWindowTreeHostX11 to be destroyed during the |
| 628 // move loop, which would also destroy this drag-client. So keep track of | 649 // move loop, which would also destroy this drag-client. So keep track of |
| 629 // whether it is alive after the drag ends. | 650 // whether it is alive after the drag ends. |
| 630 base::WeakPtr<DesktopDragDropClientAuraX11> alive( | 651 base::WeakPtr<DesktopDragDropClientAuraX11> alive( |
| 631 weak_ptr_factory_.GetWeakPtr()); | 652 weak_ptr_factory_.GetWeakPtr()); |
| 632 | 653 |
| 654 SetDragImage(source_provider_->GetDragImage(), | |
| 655 source_provider_->GetDragImageOffset()); | |
| 656 CreateDragImageWindow(); | |
| 633 // Windows has a specific method, DoDragDrop(), which performs the entire | 657 // Windows has a specific method, DoDragDrop(), which performs the entire |
| 634 // drag. We have to emulate this, so we spin off a nested runloop which will | 658 // drag. We have to emulate this, so we spin off a nested runloop which will |
| 635 // track all cursor movement and reroute events to a specific handler. | 659 // track all cursor movement and reroute events to a specific handler. |
| 636 move_loop_.SetDragImage(source_provider_->GetDragImage(), | 660 move_loop_.RunMoveLoop(); |
| 637 source_provider_->GetDragImageOffset()); | |
| 638 move_loop_.RunMoveLoop(source_window, grab_cursor_); | |
| 639 | 661 |
| 640 if (alive) { | 662 if (alive) { |
| 641 move_loop_.SetDragImage(gfx::ImageSkia(), gfx::Vector2dF()); | 663 SetDragImage(gfx::ImageSkia(), gfx::Vector2dF()); |
| 642 | 664 |
| 643 source_provider_ = NULL; | 665 source_provider_ = NULL; |
| 644 g_current_drag_drop_client = NULL; | 666 g_current_drag_drop_client = NULL; |
| 645 drag_operation_ = 0; | 667 drag_operation_ = 0; |
| 646 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList")); | 668 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList")); |
| 647 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom(kXdndDirectSave0)); | 669 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom(kXdndDirectSave0)); |
| 648 | 670 |
| 649 return negotiated_operation_; | 671 return negotiated_operation_; |
| 650 } | 672 } |
| 651 return ui::DragDropTypes::DRAG_NONE; | 673 return ui::DragDropTypes::DRAG_NONE; |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 715 // an XdndFinished message. | 737 // an XdndFinished message. |
| 716 StartEndMoveLoopTimer(); | 738 StartEndMoveLoopTimer(); |
| 717 return; | 739 return; |
| 718 } | 740 } |
| 719 } | 741 } |
| 720 | 742 |
| 721 move_loop_.EndMoveLoop(); | 743 move_loop_.EndMoveLoop(); |
| 722 } | 744 } |
| 723 | 745 |
| 724 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() { | 746 void DesktopDragDropClientAuraX11::OnMoveLoopEnded() { |
| 747 drag_widget_.reset(); | |
| 725 if (source_current_window_ != None) { | 748 if (source_current_window_ != None) { |
| 726 SendXdndLeave(source_current_window_); | 749 SendXdndLeave(source_current_window_); |
| 727 source_current_window_ = None; | 750 source_current_window_ = None; |
| 728 } | 751 } |
| 729 target_current_context_.reset(); | 752 target_current_context_.reset(); |
| 730 repeat_mouse_move_timer_.Stop(); | 753 repeat_mouse_move_timer_.Stop(); |
| 731 end_move_loop_timer_.Stop(); | 754 end_move_loop_timer_.Stop(); |
| 732 } | 755 } |
| 733 | 756 |
| 734 void DesktopDragDropClientAuraX11::ProcessMouseMove( | 757 void DesktopDragDropClientAuraX11::ProcessMouseMove( |
| 735 const gfx::Point& screen_point, | 758 const gfx::Point& screen_point, |
| 736 unsigned long event_time) { | 759 unsigned long event_time) { |
| 737 if (source_state_ != SOURCE_STATE_OTHER) | 760 if (source_state_ != SOURCE_STATE_OTHER) |
| 738 return; | 761 return; |
| 739 | 762 |
| 763 if (drag_widget_.get()) { | |
|
pkotwicz
2014/05/12 16:04:29
If we intentionally moved |drag_widget_| offscreen
varkha
2014/05/20 17:13:35
I am using drag_image_.isNull() for that (and rese
| |
| 764 gfx::Point location = drag_image_.isNull() ? | |
| 765 kNullDragWidgetLocation : | |
| 766 gfx::ToFlooredPoint(screen_point - drag_offset_); | |
| 767 drag_widget_->SetBounds(gfx::Rect( | |
| 768 location, | |
| 769 drag_image_.isNull() ? kNullDragWidgetSize : drag_image_.size())); | |
| 770 drag_widget_->StackAtTop(); | |
| 771 } | |
| 772 | |
| 740 // Find the current window the cursor is over. | 773 // Find the current window the cursor is over. |
| 741 ::Window mouse_window = None; | 774 ::Window mouse_window = None; |
| 742 ::Window dest_window = None; | 775 ::Window dest_window = None; |
| 743 FindWindowFor(screen_point, &mouse_window, &dest_window); | 776 FindWindowFor(screen_point, &mouse_window, &dest_window); |
| 744 | 777 |
| 745 if (source_current_window_ != dest_window) { | 778 if (source_current_window_ != dest_window) { |
| 746 if (source_current_window_ != None) | 779 if (source_current_window_ != None) |
| 747 SendXdndLeave(source_current_window_); | 780 SendXdndLeave(source_current_window_); |
| 748 | 781 |
| 749 source_current_window_ = dest_window; | 782 source_current_window_ = dest_window; |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 864 if (drag_operation_ & ui::DragDropTypes::DRAG_LINK) | 897 if (drag_operation_ & ui::DragDropTypes::DRAG_LINK) |
| 865 operations.push_back(atom_cache_.GetAtom(kXdndActionLink)); | 898 operations.push_back(atom_cache_.GetAtom(kXdndActionLink)); |
| 866 return operations; | 899 return operations; |
| 867 } | 900 } |
| 868 | 901 |
| 869 ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const { | 902 ui::SelectionFormatMap DesktopDragDropClientAuraX11::GetFormatMap() const { |
| 870 return source_provider_ ? source_provider_->GetFormatMap() : | 903 return source_provider_ ? source_provider_->GetFormatMap() : |
| 871 ui::SelectionFormatMap(); | 904 ui::SelectionFormatMap(); |
| 872 } | 905 } |
| 873 | 906 |
| 907 void DesktopDragDropClientAuraX11::UpdateCursor() { | |
| 908 gfx::NativeCursor cursor = grab_cursor_; | |
| 909 switch (negotiated_operation_) { | |
| 910 case ui::DragDropTypes::DRAG_COPY: | |
| 911 cursor = copy_grab_cursor_; | |
| 912 break; | |
| 913 case ui::DragDropTypes::DRAG_MOVE: | |
| 914 cursor = move_grab_cursor_; | |
| 915 break; | |
| 916 default: | |
| 917 break; | |
| 918 } | |
| 919 | |
| 920 // Update the pointer with the new cursor if a grab is active. | |
| 921 XChangeActivePointerGrab( | |
| 922 xdisplay_, | |
| 923 ButtonPressMask | ButtonReleaseMask | PointerMotionMask, | |
| 924 cursor.platform(), | |
| 925 CurrentTime); | |
| 926 } | |
| 927 | |
| 874 void DesktopDragDropClientAuraX11::CompleteXdndPosition( | 928 void DesktopDragDropClientAuraX11::CompleteXdndPosition( |
| 875 ::Window source_window, | 929 ::Window source_window, |
| 876 const gfx::Point& screen_point) { | 930 const gfx::Point& screen_point) { |
| 877 int drag_operation = ui::DragDropTypes::DRAG_NONE; | 931 int drag_operation = ui::DragDropTypes::DRAG_NONE; |
| 878 scoped_ptr<ui::OSExchangeData> data; | 932 scoped_ptr<ui::OSExchangeData> data; |
| 879 scoped_ptr<ui::DropTargetEvent> drop_target_event; | 933 scoped_ptr<ui::DropTargetEvent> drop_target_event; |
| 880 DragDropDelegate* delegate = NULL; | 934 DragDropDelegate* delegate = NULL; |
| 881 DragTranslate(screen_point, &data, &drop_target_event, &delegate); | 935 DragTranslate(screen_point, &data, &drop_target_event, &delegate); |
| 882 if (delegate) | 936 if (delegate) |
| 883 drag_operation = delegate->OnDragUpdated(*drop_target_event); | 937 drag_operation = delegate->OnDragUpdated(*drop_target_event); |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1022 // success or failure, and when it fails, it then sends an internal | 1076 // success or failure, and when it fails, it then sends an internal |
| 1023 // GdkEvent about the failed drag. (And sending this message doesn't appear | 1077 // GdkEvent about the failed drag. (And sending this message doesn't appear |
| 1024 // to go through normal xlib machinery, but instead passes through the low | 1078 // to go through normal xlib machinery, but instead passes through the low |
| 1025 // level xProto (the x11 wire format) that I don't understand. | 1079 // level xProto (the x11 wire format) that I don't understand. |
| 1026 // | 1080 // |
| 1027 // I'm unsure if I have to jump through those hoops, or if XSendEvent is | 1081 // I'm unsure if I have to jump through those hoops, or if XSendEvent is |
| 1028 // sufficient. | 1082 // sufficient. |
| 1029 XSendEvent(xdisplay_, xid, False, 0, xev); | 1083 XSendEvent(xdisplay_, xid, False, 0, xev); |
| 1030 } | 1084 } |
| 1031 | 1085 |
| 1086 void DesktopDragDropClientAuraX11::SetDragImage(const gfx::ImageSkia& image, | |
| 1087 gfx::Vector2dF offset) { | |
| 1088 /*if (drag_image_.isNull() || !CheckIfIconValid(drag_image_))*/ { | |
|
pkotwicz
2014/05/12 16:04:29
Remove the if block entirely. It is not needed.
varkha
2014/05/20 17:13:35
Done.
| |
| 1089 drag_image_ = gfx::ImageSkia(); | |
| 1090 drag_offset_ = gfx::Vector2dF(); | |
| 1091 return; | |
| 1092 } | |
| 1093 drag_image_ = image; | |
| 1094 drag_offset_ = offset; | |
| 1095 // Reset the Y offset, so that the drag-image is always just below the cursor, | |
| 1096 // so that it is possible to see where the cursor is going. | |
| 1097 drag_offset_.set_y(0.f); | |
| 1098 } | |
| 1099 | |
| 1100 void DesktopDragDropClientAuraX11::CreateDragImageWindow() { | |
| 1101 Widget* widget = new Widget; | |
| 1102 Widget::InitParams params(Widget::InitParams::TYPE_DRAG); | |
| 1103 params.opacity = Widget::InitParams::OPAQUE_WINDOW; | |
| 1104 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 1105 params.accept_events = false; | |
| 1106 | |
| 1107 gfx::Point location = drag_image_.isNull() ? | |
|
pkotwicz
2014/05/12 16:04:29
I will defer to sadrul@/erg@ as to whether they li
varkha
2014/05/20 17:13:35
Right. Keeping the drag widget offscreen matches t
| |
| 1108 kNullDragWidgetLocation : | |
| 1109 gfx::ToFlooredPoint( | |
| 1110 gfx::Screen::GetNativeScreen()->GetCursorScreenPoint() - | |
| 1111 drag_offset_); | |
| 1112 params.bounds = gfx::Rect( | |
| 1113 location, | |
| 1114 drag_image_.isNull() ? kNullDragWidgetSize : drag_image_.size()); | |
| 1115 widget->set_focus_on_creation(false); | |
| 1116 widget->set_frame_type(Widget::FRAME_TYPE_FORCE_NATIVE); | |
| 1117 widget->Init(params); | |
| 1118 widget->SetOpacity(kDragWidgetOpacity); | |
| 1119 widget->GetNativeWindow()->SetName("DragWindow"); | |
| 1120 | |
| 1121 if (!drag_image_.isNull()) { | |
| 1122 ImageView* image = new ImageView(); | |
| 1123 image->SetImage(drag_image_); | |
| 1124 image->SetBounds(0, 0, drag_image_.width(), drag_image_.height()); | |
| 1125 widget->SetContentsView(image); | |
| 1126 } | |
| 1127 | |
| 1128 widget->Show(); | |
| 1129 widget->GetNativeWindow()->layer()->SetFillsBoundsOpaquely(false); | |
|
pkotwicz
2014/05/12 16:04:29
We should have a comment as to why we set capture
varkha
2014/05/20 17:13:35
Done.
| |
| 1130 widget->GetNativeWindow()->SetCapture(); | |
| 1131 drag_widget_.reset(widget); | |
| 1132 } | |
| 1133 | |
| 1032 } // namespace views | 1134 } // namespace views |
| OLD | NEW |