diff options
Diffstat (limited to 'ui/views')
10 files changed, 671 insertions, 154 deletions
diff --git a/ui/views/controls/textfield/native_textfield_views_unittest.cc b/ui/views/controls/textfield/native_textfield_views_unittest.cc index 2af45cc..56cd1d6 100644 --- a/ui/views/controls/textfield/native_textfield_views_unittest.cc +++ b/ui/views/controls/textfield/native_textfield_views_unittest.cc @@ -934,14 +934,7 @@ TEST_F(NativeTextfieldViewsTest, DragAndDrop_AcceptDrop) { } #endif -// TODO(erg): Disabled while the other half of drag and drop is being written. -// http://crbug.com/130806 -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) -#define MAYBE_DragAndDrop_InitiateDrag DISABLED_DragAndDrop_InitiateDrag -#else -#define MAYBE_DragAndDrop_InitiateDrag DragAndDrop_InitiateDrag -#endif -TEST_F(NativeTextfieldViewsTest, MAYBE_DragAndDrop_InitiateDrag) { +TEST_F(NativeTextfieldViewsTest, DragAndDrop_InitiateDrag) { InitTextfield(Textfield::STYLE_DEFAULT); textfield_->SetText(ASCIIToUTF16("hello string world")); @@ -987,14 +980,7 @@ TEST_F(NativeTextfieldViewsTest, MAYBE_DragAndDrop_InitiateDrag) { textfield_view_->GetDragOperationsForView(textfield_view_, kStringPoint)); } -// TODO(erg): Disabled while the other half of drag and drop is being written. -// http://crbug.com/130806 -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) -#define MAYBE_DragAndDrop_ToTheRight DISABLED_DragAndDrop_ToTheRight -#else -#define MAYBE_DragAndDrop_ToTheRight DragAndDrop_ToTheRight -#endif -TEST_F(NativeTextfieldViewsTest, MAYBE_DragAndDrop_ToTheRight) { +TEST_F(NativeTextfieldViewsTest, DragAndDrop_ToTheRight) { InitTextfield(Textfield::STYLE_DEFAULT); textfield_->SetText(ASCIIToUTF16("hello world")); @@ -1049,14 +1035,7 @@ TEST_F(NativeTextfieldViewsTest, MAYBE_DragAndDrop_ToTheRight) { EXPECT_STR_EQ("h welloorld", textfield_->text()); } -// TODO(erg): Disabled while the other half of drag and drop is being written. -// http://crbug.com/130806 -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) -#define MAYBE_DragAndDrop_ToTheLeft DISABLED_DragAndDrop_ToTheLeft -#else -#define MAYBE_DragAndDrop_ToTheLeft DragAndDrop_ToTheLeft -#endif -TEST_F(NativeTextfieldViewsTest, MAYBE_DragAndDrop_ToTheLeft) { +TEST_F(NativeTextfieldViewsTest, DragAndDrop_ToTheLeft) { InitTextfield(Textfield::STYLE_DEFAULT); textfield_->SetText(ASCIIToUTF16("hello world")); @@ -1111,14 +1090,7 @@ TEST_F(NativeTextfieldViewsTest, MAYBE_DragAndDrop_ToTheLeft) { EXPECT_STR_EQ("h worlellod", textfield_->text()); } -// TODO(erg): Disabled while the other half of drag and drop is being written. -// http://crbug.com/130806 -#if defined(OS_LINUX) && !defined(OS_CHROMEOS) -#define MAYBE_DragAndDrop_Canceled DISABLED_DragAndDrop_Canceled -#else -#define MAYBE_DragAndDrop_Canceled DragAndDrop_Canceled -#endif -TEST_F(NativeTextfieldViewsTest, MAYBE_DragAndDrop_Canceled) { +TEST_F(NativeTextfieldViewsTest, DragAndDrop_Canceled) { InitTextfield(Textfield::STYLE_DEFAULT); textfield_->SetText(ASCIIToUTF16("hello world")); diff --git a/ui/views/views.gyp b/ui/views/views.gyp index 318f8db..4022df3 100644 --- a/ui/views/views.gyp +++ b/ui/views/views.gyp @@ -532,6 +532,7 @@ }], ['use_aura==1 and OS=="linux" and chromeos==0', { 'dependencies': [ + '../ui.gyp:shell_dialogs', '../linux_ui/linux_ui.gyp:linux_ui', ], }], diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc index ef2d6f8..004c19c 100644 --- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc +++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc @@ -16,6 +16,7 @@ #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" #include "ui/base/events/event.h" +#include "ui/base/x/selection_utils.h" #include "ui/base/x/x11_util.h" #include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h" @@ -24,11 +25,20 @@ using ui::OSExchangeData; namespace { +const int kMinXdndVersion = 5; + +const int kWillAcceptDrop = 1; +const int kWantFurtherPosEvents = 2; + const char kXdndActionCopy[] = "XdndActionCopy"; const char kXdndActionMove[] = "XdndActionMove"; const char kXdndActionLink[] = "XdndActionLink"; +const char kChromiumDragReciever[] = "_CHROMIUM_DRAG_RECEIVER"; +const char kXdndSelection[] = "XdndSelection"; + const char* kAtomsToCache[] = { + kChromiumDragReciever, "XdndActionAsk", kXdndActionCopy, kXdndActionLink, @@ -42,24 +52,117 @@ const char* kAtomsToCache[] = { "XdndLeave", "XdndPosition", "XdndProxy", // Proxy windows? - "XdndSelection", + kXdndSelection, "XdndStatus", "XdndTypeList", NULL }; +// Helper class to FindWindowFor which looks for a drag target under the +// cursor. +class DragTargetWindowFinder : public ui::EnumerateWindowsDelegate { + public: + DragTargetWindowFinder(XID ignored_icon_window, + gfx::Point screen_loc) + : ignored_icon_window_(ignored_icon_window), + output_window_(None), + screen_loc_(screen_loc) { + ui::EnumerateTopLevelWindows(this); + } + + virtual ~DragTargetWindowFinder() {} + + XID window() const { return output_window_; } + + protected: + virtual bool ShouldStopIterating(XID window) OVERRIDE { + if (window == ignored_icon_window_) + return false; + + if (!ui::IsWindowVisible(window)) + return false; + + if (!ui::WindowContainsPoint(window, screen_loc_)) + return false; + + if (ui::PropertyExists(window, "WM_STATE")) { + output_window_ = window; + return true; + } + + return false; + } + + private: + XID ignored_icon_window_; + XID output_window_; + gfx::Point screen_loc_; + + DISALLOW_COPY_AND_ASSIGN(DragTargetWindowFinder); +}; + +// Returns the topmost X11 window at |screen_point| if it is advertising that +// is supports the Xdnd protocol. Will return the window under the pointer as +// |mouse_window|. If there's a Xdnd aware window, it will be returned in +// |dest_window|. +void FindWindowFor(const gfx::Point& screen_point, + ::Window* mouse_window, ::Window* dest_window) { + DragTargetWindowFinder finder(None, screen_point); + *mouse_window = finder.window(); + *dest_window = None; + + if (finder.window() == None) + return; + + // Figure out which window we should test as XdndAware. If mouse_window has + // XdndProxy, it will set that proxy on target, and if not, |target|'s + // original value will remain. + XID target = *mouse_window; + ui::GetXIDProperty(*mouse_window, "XdndProxy", &target); + + int version; + if (ui::GetIntProperty(target, "XdndAware", &version) && + version >= kMinXdndVersion) { + *dest_window = target; + } +} + } // namespace namespace views { +std::map< ::Window, DesktopDragDropClientAuraX11*> + DesktopDragDropClientAuraX11::g_live_client_map; + class DesktopDragDropClientAuraX11::X11DragContext : public base::MessageLoop::Dispatcher { public: X11DragContext(ui::X11AtomCache* atom_cache, + ::Window local_window, const XClientMessageEvent& event); virtual ~X11DragContext(); - const std::vector<Atom>& targets() { return targets_; } + // When we receive an XdndPosition message, we need to have all the data + // copied from the other window before we process the XdndPosition + // message. If we have that data already, dispatch immediately. Otherwise, + // delay dispatching until we do. + void OnStartXdndPositionMessage(DesktopDragDropClientAuraX11* client, + ::Window source_window, + const gfx::Point& screen_point); + + // Called to request the next target from the source window. This is only + // done on the first XdndPosition; after that, we cache the data offered by + // the source window. + void RequestNextTarget(); + + // Called when XSelection data has been copied to our process. + void OnSelectionNotify(const XSelectionEvent& xselection); + + // Clones the fetched targets. + scoped_ptr<ui::SelectionFormatMap> CloneFetchedTargets() { + DCHECK(fetched_targets_); + return fetched_targets_->Clone(); + } // Reads the "XdndActionList" property from |source_window| and copies it // into |actions|. @@ -76,13 +179,30 @@ class DesktopDragDropClientAuraX11::X11DragContext : // The atom cache owned by our parent. ui::X11AtomCache* atom_cache_; + // The XID of our chrome local aura window handling our events. + ::Window local_window_; + // The XID of the window that's initiated the drag. unsigned long source_window_; - // targets. - std::vector<Atom> targets_; + // The client we inform once we're done with requesting data. + DesktopDragDropClientAuraX11* drag_drop_client_; + + // Whether we're blocking the handling of an XdndPosition message by waiting + // for |unfetched_targets_| to be fetched. + bool waiting_to_handle_position_; + + // Where the cursor is on screen. + gfx::Point screen_point_; - // supplied actions + // A SelectionFormatMap of data that we have in our process. + scoped_ptr<ui::SelectionFormatMap> fetched_targets_; + + // The names of various data types offered by the other window that we + // haven't fetched and put in |fetched_targets_| yet. + std::vector<Atom> unfetched_targets_; + + // Possible actions. std::vector<Atom> actions_; DISALLOW_COPY_AND_ASSIGN(X11DragContext); @@ -90,60 +210,139 @@ class DesktopDragDropClientAuraX11::X11DragContext : DesktopDragDropClientAuraX11::X11DragContext::X11DragContext( ui::X11AtomCache* atom_cache, + ::Window local_window, const XClientMessageEvent& event) : atom_cache_(atom_cache), - source_window_(event.data.l[0]) { + local_window_(local_window), + source_window_(event.data.l[0]), + drag_drop_client_(NULL), + waiting_to_handle_position_(false) { bool get_types = ((event.data.l[1] & 1) != 0); if (get_types) { if (!ui::GetAtomArrayProperty(source_window_, "XdndTypeList", - &targets_)) { + &unfetched_targets_)) { return; } } else { // data.l[2,3,4] contain the first three types. Unused slots can be None. for (int i = 0; i < 3; ++i) { if (event.data.l[2+i] != None) { - targets_.push_back(event.data.l[2+i]); + unfetched_targets_.push_back(event.data.l[2+i]); } } } - // TODO(erg): If this window is part of our process, don't listen through the - // MessagePump, but instead listen to the DesktopDragDropClientAuraX11 - // associated with that window. - base::MessagePumpAuraX11::Current()->AddDispatcherForWindow( - this, source_window_); - XSelectInput(base::MessagePumpAuraX11::GetDefaultXDisplay(), - source_window_, PropertyChangeMask); - - // We must perform a full sync here because we could be racing - // |source_window_|. - XSync(base::MessagePumpAuraX11::GetDefaultXDisplay(), False); + DesktopDragDropClientAuraX11* client = + DesktopDragDropClientAuraX11::GetForWindow(source_window_); + if (!client) { + // The window doesn't have a DesktopDragDropClientAuraX11, that means it's + // created by some other process. Listen for messages on it. + base::MessagePumpAuraX11::Current()->AddDispatcherForWindow( + this, source_window_); + XSelectInput(base::MessagePumpAuraX11::GetDefaultXDisplay(), + source_window_, PropertyChangeMask); + + // We must perform a full sync here because we could be racing + // |source_window_|. + XSync(base::MessagePumpAuraX11::GetDefaultXDisplay(), False); + } else { + // This drag originates from an aura window within our process. This means + // that we can shortcut the X11 server and ask the owning SelectionOwner + // for the data it's offering. + fetched_targets_ = client->CloneFormatMap(); + unfetched_targets_.clear(); + } ReadActions(); } DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() { - // Unsubscribe to message events. - base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow( - source_window_); + DesktopDragDropClientAuraX11* client = + DesktopDragDropClientAuraX11::GetForWindow(source_window_); + if (!client) { + // Unsubscribe from message events. + base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow( + source_window_); + } } -void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() { - std::vector<Atom> atom_array; +void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage( + DesktopDragDropClientAuraX11* client, + ::Window source_window, + const gfx::Point& screen_point) { + DCHECK_EQ(source_window_, source_window); + + if (!unfetched_targets_.empty()) { + // We have unfetched targets. That means we need to pause the handling of + // the position message and ask the other window for its data. + screen_point_ = screen_point; + drag_drop_client_ = client; + waiting_to_handle_position_ = true; + + fetched_targets_.reset(new ui::SelectionFormatMap); + RequestNextTarget(); + } else { + client->CompleteXdndPosition(source_window, screen_point); + } +} + +void DesktopDragDropClientAuraX11::X11DragContext::RequestNextTarget() { + ::Atom target = unfetched_targets_.back(); + unfetched_targets_.pop_back(); + + XConvertSelection(base::MessagePumpAuraX11::GetDefaultXDisplay(), + atom_cache_->GetAtom(kXdndSelection), + target, + atom_cache_->GetAtom(kChromiumDragReciever), + local_window_, + CurrentTime); +} + +void DesktopDragDropClientAuraX11::X11DragContext::OnSelectionNotify( + const XSelectionEvent& event) { + DCHECK(waiting_to_handle_position_); + DCHECK(drag_drop_client_); + DCHECK_EQ(event.property, atom_cache_->GetAtom(kChromiumDragReciever)); + + unsigned char* data = NULL; + size_t data_bytes = 0; + ::Atom type = None; + if (ui::GetRawBytesOfProperty(local_window_, event.property, + &data, &data_bytes, NULL, &type)) { + char* copied_data = new char[data_bytes]; + memcpy(copied_data, data, data_bytes); + fetched_targets_->Insert(event.target, copied_data, data_bytes); + XFree(data); + } - // TODO(erg): The GTK+ code has a fast path that short circuits talking over - // X11 for local windows. When we become a drop source, we should have a - // similar fast path. + if (!unfetched_targets_.empty()) { + RequestNextTarget(); + } else { + waiting_to_handle_position_ = false; + drag_drop_client_->CompleteXdndPosition(source_window_, screen_point_); + drag_drop_client_ = NULL; + } +} - if (!ui::GetAtomArrayProperty(source_window_, - "XdndActionList", - &atom_array)) { - actions_.clear(); +void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() { + DesktopDragDropClientAuraX11* client = + DesktopDragDropClientAuraX11::GetForWindow(source_window_); + if (!client) { + std::vector<Atom> atom_array; + if (!ui::GetAtomArrayProperty(source_window_, + "XdndActionList", + &atom_array)) { + actions_.clear(); + } else { + actions_.swap(atom_array); + } } else { - actions_.swap(atom_array); + // We have a property notify set up for other windows in case they change + // their action list. Thankfully, the views interface is static and you + // can't change the action list after you enter StartDragAndDrop(). + actions_ = client->GetOfferedDragOperations(); } } @@ -178,22 +377,40 @@ DesktopDragDropClientAuraX11::DesktopDragDropClientAuraX11( aura::RootWindow* root_window, Display* xdisplay, ::Window xwindow) - : root_window_host_(root_window_host), + : move_loop_(this), + root_window_host_(root_window_host), root_window_(root_window), xdisplay_(xdisplay), xwindow_(xwindow), atom_cache_(xdisplay_, kAtomsToCache), target_window_(NULL), + source_provider_(NULL), + source_current_window_(None), drag_drop_in_progress_(false), - drag_operation_(0) { + drag_operation_(0), + resulting_operation_(0) { + DCHECK(g_live_client_map.find(xwindow) == g_live_client_map.end()); + g_live_client_map.insert(std::make_pair(xwindow, this)); + // Mark that we are aware of drag and drop concepts. - unsigned long xdnd_version = 5; + unsigned long xdnd_version = kMinXdndVersion; XChangeProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndAware"), XA_ATOM, 32, PropModeReplace, reinterpret_cast<unsigned char*>(&xdnd_version), 1); } DesktopDragDropClientAuraX11::~DesktopDragDropClientAuraX11() { + g_live_client_map.erase(xwindow_); +} + +// static +DesktopDragDropClientAuraX11* DesktopDragDropClientAuraX11::GetForWindow( + ::Window window) { + std::map< ::Window, DesktopDragDropClientAuraX11*>::const_iterator it = + g_live_client_map.find(window); + if (it == g_live_client_map.end()) + return NULL; + return it->second; } void DesktopDragDropClientAuraX11::OnXdndEnter( @@ -207,8 +424,9 @@ void DesktopDragDropClientAuraX11::OnXdndEnter( } // Make sure that we've run ~X11DragContext() before creating another one. - current_context_.reset(); - current_context_.reset(new X11DragContext(&atom_cache_, event)); + target_current_context_.reset(); + target_current_context_.reset( + new X11DragContext(&atom_cache_, xwindow_, event)); // In the Windows implementation, we immediately call DesktopDropTargetWin:: // Translate(). Here, we wait until we receive an XdndPosition message @@ -218,8 +436,9 @@ void DesktopDragDropClientAuraX11::OnXdndEnter( void DesktopDragDropClientAuraX11::OnXdndLeave( const XClientMessageEvent& event) { + DVLOG(1) << "XdndLeave"; NotifyDragLeave(); - current_context_.reset(); + target_current_context_.reset(); } void DesktopDragDropClientAuraX11::OnXdndPosition( @@ -230,48 +449,60 @@ void DesktopDragDropClientAuraX11::OnXdndPosition( int x_root_window = event.data.l[2] >> 16; int y_root_window = event.data.l[2] & 0xffff; - if (!current_context_.get()) { + if (!target_current_context_.get()) { NOTREACHED(); return; } - int drag_operation = ui::DragDropTypes::DRAG_NONE; - scoped_ptr<ui::OSExchangeData> data; - scoped_ptr<ui::DropTargetEvent> drop_target_event; - DragDropDelegate* delegate; - DragTranslate(gfx::Point(x_root_window, y_root_window), - &data, &drop_target_event, &delegate); - if (delegate) - drag_operation = delegate->OnDragUpdated(*drop_target_event); - - // Sends an XdndStatus message back to the source_window. l[2,3] - // theoretically represent an area in the window where the current action is - // the same as what we're returning, but I can't find any implementation that - // actually making use of this. A client can return (0, 0) and/or set the - // first bit of l[1] to disable the feature, and it appears that gtk neither - // sets this nor respects it if set. - XEvent xev; - xev.xclient.type = ClientMessage; - xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus"); - xev.xclient.format = 32; - xev.xclient.window = source_window; - xev.xclient.data.l[0] = xwindow_; - xev.xclient.data.l[1] = (drag_operation != 0) ? (2 | 1) : 0; - xev.xclient.data.l[2] = 0; - xev.xclient.data.l[3] = 0; - xev.xclient.data.l[4] = DragOperationToAtom(drag_operation); - - SendXClientEvent(source_window, &xev); + // If we already have all the data from this drag, we complete it + // immediately. + target_current_context_->OnStartXdndPositionMessage( + this, source_window, gfx::Point(x_root_window, y_root_window)); } void DesktopDragDropClientAuraX11::OnXdndStatus( const XClientMessageEvent& event) { DVLOG(1) << "XdndStatus"; + + unsigned long source_window = event.data.l[0]; + if (event.data.l[1] & 1) + negotiated_operation_[source_window] = event.data.l[4]; + + // Note: event.data.[2,3] specify a rectangle. It is a request by the other + // window to not send further XdndPosition messages while the cursor is + // within it. However, it is considered advisory and (at least according to + // the spec) the other side must handle further position messages within + // it. GTK+ doesn't bother with this, so neither should we. + + waiting_on_status_.erase(source_window); + + // TODO(erg): We should be using the response to try to update the cursor or + // something. + + if (ContainsKey(pending_drop_, source_window)) { + // We were waiting on the status message so we could send the XdndDrop. + SendXdndDrop(source_window); + return; + } + + NextPositionMap::iterator it = next_position_message_.find(source_window); + if (it != next_position_message_.end()) { + // We were waiting on the status message so we could send off the next + // position message we queued up. + gfx::Point p = it->second.first; + unsigned long time = it->second.second; + next_position_message_.erase(it); + + SendXdndPosition(source_window, p, time); + } } void DesktopDragDropClientAuraX11::OnXdndFinished( const XClientMessageEvent& event) { DVLOG(1) << "XdndFinished"; + resulting_operation_ = AtomToDragOperation( + negotiated_operation_[event.data.l[0]]); + move_loop_.EndMoveLoop(); } void DesktopDragDropClientAuraX11::OnXdndDrop( @@ -286,11 +517,12 @@ void DesktopDragDropClientAuraX11::OnXdndDrop( aura::client::GetDragDropDelegate(target_window_); if (delegate) { ui::OSExchangeData data(new ui::OSExchangeDataProviderAuraX11( - root_window_host_, xwindow_, current_context_->targets())); + xwindow_, target_current_context_->CloneFetchedTargets())); + ui::DropTargetEvent event(data, target_window_location_, target_window_root_location_, - current_context_->GetDragOperation()); + target_current_context_->GetDragOperation()); drag_operation = delegate->OnPerformDrop(event); } @@ -310,6 +542,16 @@ void DesktopDragDropClientAuraX11::OnXdndDrop( SendXClientEvent(source_window, &xev); } +void DesktopDragDropClientAuraX11::OnSelectionNotify( + const XSelectionEvent& xselection) { + if (!target_current_context_) { + NOTIMPLEMENTED(); + return; + } + + target_current_context_->OnSelectionNotify(xselection); +} + int DesktopDragDropClientAuraX11::StartDragAndDrop( const ui::OSExchangeData& data, aura::RootWindow* root_window, @@ -317,16 +559,31 @@ int DesktopDragDropClientAuraX11::StartDragAndDrop( const gfx::Point& root_location, int operation, ui::DragDropTypes::DragEventSource source) { + source_current_window_ = None; + drag_drop_in_progress_ = true; + drag_operation_ = operation; + resulting_operation_ = ui::DragDropTypes::DRAG_NONE; + + const ui::OSExchangeData::Provider* provider = &data.provider(); + source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>( + provider); + + source_provider_->TakeOwnershipOfSelection(); + + std::vector< ::Atom> actions = GetOfferedDragOperations(); + ui::SetAtomArrayProperty(xwindow_, "XdndActionList", "ATOM", actions); + // Windows has a specific method, DoDragDrop(), which performs the entire // drag. We have to emulate this, so we spin off a nested runloop which will // track all cursor movement and reroute events to a specific handler. + move_loop_.RunMoveLoop(source_window); - NOTIMPLEMENTED(); - - // TODO(erg): Once this is implemented, make sure to reenable the - // NativeTextfieldViewsTest_DragAndDrop* tests. + source_provider_ = NULL; + drag_drop_in_progress_ = false; + drag_operation_ = 0; + XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList")); - return ui::DragDropTypes::DRAG_NONE; + return resulting_operation_; } void DesktopDragDropClientAuraX11::DragUpdate(aura::Window* target, @@ -340,7 +597,7 @@ void DesktopDragDropClientAuraX11::Drop(aura::Window* target, } void DesktopDragDropClientAuraX11::DragCancel() { - NOTIMPLEMENTED(); + move_loop_.EndMoveLoop(); } bool DesktopDragDropClientAuraX11::IsDragDropInProgress() { @@ -352,6 +609,64 @@ void DesktopDragDropClientAuraX11::OnWindowDestroyed(aura::Window* window) { target_window_ = NULL; } +void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) { + gfx::Point screen_point(event->x_root, event->y_root); + + // Find the current window the cursor is over. + ::Window mouse_window = None; + ::Window dest_window = None; + FindWindowFor(screen_point, &mouse_window, &dest_window); + + if (source_current_window_ != dest_window) { + if (source_current_window_ != None) + SendXdndLeave(source_current_window_); + + source_current_window_ = dest_window; + + if (source_current_window_ != None) { + negotiated_operation_.erase(source_current_window_); + SendXdndEnter(source_current_window_); + } + } + + if (source_current_window_ != None) { + if (ContainsKey(waiting_on_status_, dest_window)) { + next_position_message_[dest_window] = + std::make_pair(screen_point, event->time); + } else { + SendXdndPosition(dest_window, screen_point, event->time); + } + } +} + +void DesktopDragDropClientAuraX11::OnMouseReleased() { + if (source_current_window_ != None) { + if (ContainsKey(waiting_on_status_, source_current_window_)) { + // If we are waiting for an XdndStatus message, we need to wait for it to + // complete. + pending_drop_.insert(source_current_window_); + return; + } + + std::map< ::Window, ::Atom>::iterator it = + negotiated_operation_.find(source_current_window_); + if (it != negotiated_operation_.end() && it->second != None) { + // We have negotiated an action with the other end. + SendXdndDrop(source_current_window_); + return; + } + + SendXdndLeave(source_current_window_); + source_current_window_ = None; + } + + move_loop_.EndMoveLoop(); +} + +void DesktopDragDropClientAuraX11::OnMoveLoopEnded() { + target_current_context_.reset(); +} + void DesktopDragDropClientAuraX11::DragTranslate( const gfx::Point& root_window_location, scoped_ptr<ui::OSExchangeData>* data, @@ -378,7 +693,7 @@ void DesktopDragDropClientAuraX11::DragTranslate( return; data->reset(new OSExchangeData(new ui::OSExchangeDataProviderAuraX11( - root_window_host_, xwindow_, current_context_->targets()))); + xwindow_, target_current_context_->CloneFetchedTargets()))); gfx::Point location = root_location; aura::Window::ConvertPointToTarget(root_window_, target_window_, &location); @@ -389,7 +704,7 @@ void DesktopDragDropClientAuraX11::DragTranslate( *(data->get()), location, root_location, - current_context_->GetDragOperation())); + target_current_context_->GetDragOperation())); if (target_window_changed) (*delegate)->OnDragEntered(*event->get()); } @@ -405,7 +720,7 @@ void DesktopDragDropClientAuraX11::NotifyDragLeave() { target_window_ = NULL; } -unsigned long DesktopDragDropClientAuraX11::DragOperationToAtom( +::Atom DesktopDragDropClientAuraX11::DragOperationToAtom( int drag_operation) { if (drag_operation & ui::DragDropTypes::DRAG_COPY) return atom_cache_.GetAtom(kXdndActionCopy); @@ -417,24 +732,184 @@ unsigned long DesktopDragDropClientAuraX11::DragOperationToAtom( return None; } -void DesktopDragDropClientAuraX11::SendXClientEvent(unsigned long xid, +int DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) { + if (atom == atom_cache_.GetAtom(kXdndActionCopy)) + return ui::DragDropTypes::DRAG_COPY; + if (atom == atom_cache_.GetAtom(kXdndActionMove)) + return ui::DragDropTypes::DRAG_MOVE; + if (atom == atom_cache_.GetAtom(kXdndActionLink)) + return ui::DragDropTypes::DRAG_LINK; + + return ui::DragDropTypes::DRAG_NONE; +} + +std::vector< ::Atom> DesktopDragDropClientAuraX11::GetOfferedDragOperations() { + std::vector< ::Atom> operations; + if (drag_operation_ & ui::DragDropTypes::DRAG_COPY) + operations.push_back(atom_cache_.GetAtom(kXdndActionCopy)); + if (drag_operation_ & ui::DragDropTypes::DRAG_MOVE) + operations.push_back(atom_cache_.GetAtom(kXdndActionMove)); + if (drag_operation_ & ui::DragDropTypes::DRAG_LINK) + operations.push_back(atom_cache_.GetAtom(kXdndActionLink)); + return operations; +} + +scoped_ptr<ui::SelectionFormatMap> +DesktopDragDropClientAuraX11::CloneFormatMap() const { + return source_provider_ ? source_provider_->CloneFormatMap() : + scoped_ptr<ui::SelectionFormatMap>(); +} + +void DesktopDragDropClientAuraX11::CompleteXdndPosition( + ::Window source_window, + const gfx::Point& screen_point) { + int drag_operation = ui::DragDropTypes::DRAG_NONE; + scoped_ptr<ui::OSExchangeData> data; + scoped_ptr<ui::DropTargetEvent> drop_target_event; + DragDropDelegate* delegate = NULL; + DragTranslate(screen_point, &data, &drop_target_event, &delegate); + if (delegate) + drag_operation = delegate->OnDragUpdated(*drop_target_event); + + // Sends an XdndStatus message back to the source_window. l[2,3] + // theoretically represent an area in the window where the current action is + // the same as what we're returning, but I can't find any implementation that + // actually making use of this. A client can return (0, 0) and/or set the + // first bit of l[1] to disable the feature, and it appears that gtk neither + // sets this nor respects it if set. + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = atom_cache_.GetAtom("XdndStatus"); + xev.xclient.format = 32; + xev.xclient.window = source_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = (drag_operation != 0) ? + (kWantFurtherPosEvents | kWillAcceptDrop) : 0; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = DragOperationToAtom(drag_operation); + + SendXClientEvent(source_window, &xev); +} + +void DesktopDragDropClientAuraX11::SendXdndEnter(::Window dest_window) { + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = atom_cache_.GetAtom("XdndEnter"); + xev.xclient.format = 32; + xev.xclient.window = dest_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = (kMinXdndVersion << 24); // The version number. + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + + std::vector<Atom> targets; + source_provider_->RetrieveTargets(&targets); + + if (targets.size() > 3) { + xev.xclient.data.l[1] |= 1; + ui::SetAtomArrayProperty(xwindow_, "XdndTypeList", "ATOM", targets); + } else { + // Pack the targets into the enter message. + for (size_t i = 0; i < targets.size(); ++i) + xev.xclient.data.l[2 + i] = targets[i]; + } + + SendXClientEvent(dest_window, &xev); +} + +void DesktopDragDropClientAuraX11::SendXdndLeave(::Window dest_window) { + // If we're sending a leave message, don't wait for status messages anymore. + waiting_on_status_.erase(dest_window); + NextPositionMap::iterator it = next_position_message_.find(dest_window); + if (it != next_position_message_.end()) + next_position_message_.erase(it); + + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = atom_cache_.GetAtom("XdndLeave"); + xev.xclient.format = 32; + xev.xclient.window = dest_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + SendXClientEvent(dest_window, &xev); +} + +void DesktopDragDropClientAuraX11::SendXdndPosition( + ::Window dest_window, + const gfx::Point& screen_point, + unsigned long time) { + waiting_on_status_.insert(dest_window); + + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = atom_cache_.GetAtom("XdndPosition"); + xev.xclient.format = 32; + xev.xclient.window = dest_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = (screen_point.x() << 16) | screen_point.y(); + xev.xclient.data.l[3] = time; + xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_); + SendXClientEvent(dest_window, &xev); +} + +void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) { + XEvent xev; + xev.xclient.type = ClientMessage; + xev.xclient.message_type = atom_cache_.GetAtom("XdndDrop"); + xev.xclient.format = 32; + xev.xclient.window = dest_window; + xev.xclient.data.l[0] = xwindow_; + xev.xclient.data.l[1] = 0; + xev.xclient.data.l[2] = CurrentTime; + xev.xclient.data.l[3] = None; + xev.xclient.data.l[4] = None; + SendXClientEvent(dest_window, &xev); +} + +void DesktopDragDropClientAuraX11::SendXClientEvent(::Window xid, XEvent* xev) { DCHECK_EQ(ClientMessage, xev->type); - // TODO(erg): When I get drag receiving working, shortcut messages to the X - // server and call the receivers DesktopDragDropClientAuraX11 instance - // instead. - // + // Don't send messages to the X11 message queue if we can help it. + DesktopDragDropClientAuraX11* short_circuit = GetForWindow(xid); + if (short_circuit) { + Atom message_type = xev->xclient.message_type; + if (message_type == atom_cache_.GetAtom("XdndEnter")) { + short_circuit->OnXdndEnter(xev->xclient); + return; + } else if (message_type == atom_cache_.GetAtom("XdndLeave")) { + short_circuit->OnXdndLeave(xev->xclient); + return; + } else if (message_type == atom_cache_.GetAtom("XdndPosition")) { + short_circuit->OnXdndPosition(xev->xclient); + return; + } else if (message_type == atom_cache_.GetAtom("XdndStatus")) { + short_circuit->OnXdndStatus(xev->xclient); + return; + } else if (message_type == atom_cache_.GetAtom("XdndFinished")) { + short_circuit->OnXdndFinished(xev->xclient); + return; + } else if (message_type == atom_cache_.GetAtom("XdndDrop")) { + short_circuit->OnXdndDrop(xev->xclient); + return; + } + } + // I don't understand why the GTK+ code is doing what it's doing here. It // goes out of its way to send the XEvent so that it receives a callback on - // success or failure, and when it fails, it then sends an internal GdkEvent - // about the failed drag. (And sending this message doesn't appear to go - // through normal xlib machinery, but instead passes through the low level - // xProto (the x11 wire format) that I don't understand. + // success or failure, and when it fails, it then sends an internal + // GdkEvent about the failed drag. (And sending this message doesn't appear + // to go through normal xlib machinery, but instead passes through the low + // level xProto (the x11 wire format) that I don't understand. // // I'm unsure if I have to jump through those hoops, or if XSendEvent is // sufficient. - XSendEvent(xdisplay_, xid, False, 0, xev); } diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h index 47c4f2b..4152778 100644 --- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h +++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h @@ -10,6 +10,8 @@ // Get rid of a macro from Xlib.h that conflicts with Aura's RootWindow class. #undef RootWindow +#include <set> + #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" @@ -18,6 +20,8 @@ #include "ui/base/x/x11_atom_cache.h" #include "ui/gfx/point.h" #include "ui/views/views_export.h" +#include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h" +#include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h" namespace aura { class RootWindow; @@ -34,7 +38,9 @@ namespace ui { class DragSource; class DropTargetEvent; class OSExchangeData; +class OSExchangeDataProviderAuraX11; class RootWindow; +class SelectionFormatMap; } namespace views { @@ -45,7 +51,8 @@ class DesktopRootWindowHostX11; // handles the views drag events. class VIEWS_EXPORT DesktopDragDropClientAuraX11 : public aura::client::DragDropClient, - public aura::WindowObserver { + public aura::WindowObserver, + public X11WholeScreenMoveLoopDelegate { public: DesktopDragDropClientAuraX11( views::DesktopRootWindowHostX11* root_window_host, @@ -54,6 +61,11 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 ::Window xwindow); virtual ~DesktopDragDropClientAuraX11(); + // We maintain a mapping of live DesktopDragDropClientAuraX11 objects to + // their ::Windows. We do this so that we're able to short circuit sending + // X11 messages to windows in our process. + static DesktopDragDropClientAuraX11* GetForWindow(::Window window); + // These methods handle the various X11 client messages from the platform. void OnXdndEnter(const XClientMessageEvent& event); void OnXdndLeave(const XClientMessageEvent& event); @@ -62,6 +74,9 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 void OnXdndFinished(const XClientMessageEvent& event); void OnXdndDrop(const XClientMessageEvent& event); + // Called when XSelection data has been copied to our process. + void OnSelectionNotify(const XSelectionEvent& xselection); + // Overridden from aura::client::DragDropClient: virtual int StartDragAndDrop( const ui::OSExchangeData& data, @@ -77,10 +92,18 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 virtual void DragCancel() OVERRIDE; virtual bool IsDragDropInProgress() OVERRIDE; - // aura::WindowObserver implementation: + // Overridden from aura::WindowObserver: virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE; + // Overridden from X11WholeScreenMoveLoopDelegate: + virtual void OnMouseMovement(XMotionEvent* event) OVERRIDE; + virtual void OnMouseReleased() OVERRIDE; + virtual void OnMoveLoopEnded() OVERRIDE; + private: + typedef std::map< ::Window, std::pair<gfx::Point, unsigned long> > + NextPositionMap; + // When we receive an position x11 message, we need to translate that into // the underlying aura::Window representation, as moves internal to the X11 // window can cause internal drag leave and enter messages. @@ -95,11 +118,41 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 // Converts our bitfield of actions into an Atom that represents what action // we're most likely to take on drop. - unsigned long DragOperationToAtom(int drag_operation); + ::Atom DragOperationToAtom(int drag_operation); + + // Converts a single action atom to a drag operation. + int AtomToDragOperation(::Atom atom); + + // During the blocking StartDragAndDrop() call, this converts the views-style + // |drag_operation_| bitfield into a vector of Atoms to offer to other + // processes. + std::vector< ::Atom> GetOfferedDragOperations(); + + // This returns a newly allocated copy of the data we're offering in this + // drag. This is done to bypass an asynchronous roundtrip with the X11 + // server. + scoped_ptr<ui::SelectionFormatMap> CloneFormatMap() const; + + // Handling XdndPosition can be paused while waiting for more data; this is + // called either synchronously from OnXdndPosition, or asynchronously after + // we've received data requested from the other window. + void CompleteXdndPosition(::Window source_window, + const gfx::Point& screen_point); + + void SendXdndEnter(::Window dest_window); + void SendXdndLeave(::Window dest_window); + void SendXdndPosition(::Window dest_window, + const gfx::Point& screen_point, + unsigned long time); + void SendXdndDrop(::Window dest_window); // Sends |xev| to |xid|, optionally short circuiting the round trip to the X // server. - void SendXClientEvent(unsigned long xid, XEvent* xev); + void SendXClientEvent(::Window xid, XEvent* xev); + + // A nested message loop that notifies this object of events through the + // X11WholeScreenMoveLoopDelegate interface. + X11WholeScreenMoveLoop move_loop_; views::DesktopRootWindowHostX11* root_window_host_; aura::RootWindow* root_window_; @@ -110,9 +163,8 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 ui::X11AtomCache atom_cache_; // Target side information. - class X11DragContext; - scoped_ptr<X11DragContext> current_context_; + scoped_ptr<X11DragContext> target_current_context_; // The Aura window that is currently under the cursor. We need to manually // keep track of this because Windows will only call our drag enter method @@ -127,10 +179,45 @@ class VIEWS_EXPORT DesktopDragDropClientAuraX11 gfx::Point target_window_location_; gfx::Point target_window_root_location_; + // In the Xdnd protocol, we aren't supposed to send another XdndPosition + // message until we have received a confirming XdndStatus message. + std::set< ::Window> waiting_on_status_; + + // If we would send an XdndPosition message while we're waiting for an + // XdndStatus response, we need to cache the latest details we'd send. + NextPositionMap next_position_message_; + + // Source side information. + ui::OSExchangeDataProviderAuraX11 const* source_provider_; + ::Window source_current_window_; + bool drag_drop_in_progress_; + // The operation bitfield as requested by StartDragAndDrop. int drag_operation_; + // The operation performed. Is initialized to None at the start of + // StartDragAndDrop(), and is set only during the asynchronous XdndFinished + // message. + int resulting_operation_; + + // This window will be receiving a drop as soon as we receive an XdndStatus + // from it. + std::set< ::Window> pending_drop_; + + // We offer the other window a list of possible operations, + // XdndActionsList. This is the requested action from the other window. This + // is None if we haven't sent out an XdndPosition message yet, haven't yet + // received an XdndStatus or if the other window has told us that there's no + // action that we can agree on. + // + // This is a map instead of a simple variable because of the case where we + // put an XdndLeave in the queue at roughly the same time that the other + // window responds to an XdndStatus. + std::map< ::Window, ::Atom> negotiated_operation_; + + static std::map< ::Window, DesktopDragDropClientAuraX11*> g_live_client_map; + DISALLOW_COPY_AND_ASSIGN(DesktopDragDropClientAuraX11); }; diff --git a/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc b/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc index 4606a6f..ea98a9c 100644 --- a/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc +++ b/ui/views/widget/desktop_aura/desktop_root_window_host_x11.cc @@ -111,8 +111,7 @@ DesktopRootWindowHostX11::DesktopRootWindowHostX11( focus_when_shown_(false), current_cursor_(ui::kCursorNull), native_widget_delegate_(native_widget_delegate), - desktop_native_widget_aura_(desktop_native_widget_aura), - drop_handler_(NULL) { + desktop_native_widget_aura_(desktop_native_widget_aura) { } DesktopRootWindowHostX11::~DesktopRootWindowHostX11() { @@ -907,20 +906,6 @@ void DesktopRootWindowHostX11::PrepareForShutdown() { } //////////////////////////////////////////////////////////////////////////////// -// DesktopRootWindowHostX11, ui::DesktopSelectionProviderAuraX11 implementation: - -void DesktopRootWindowHostX11::SetDropHandler( - ui::OSExchangeDataProviderAuraX11* handler) { - if (handler) { - DCHECK(!drop_handler_); - drop_handler_ = handler; - } else { - DCHECK(drop_handler_); - drop_handler_ = NULL; - } -} - -//////////////////////////////////////////////////////////////////////////////// // DesktopRootWindowHostX11, MessageLoop::Dispatcher implementation: bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { @@ -1194,8 +1179,7 @@ bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { break; } case SelectionNotify: { - if (drop_handler_) - drop_handler_->OnSelectionNotify(xev->xselection); + drag_drop_client_->OnSelectionNotify(xev->xselection); break; } } diff --git a/ui/views/widget/desktop_aura/desktop_root_window_host_x11.h b/ui/views/widget/desktop_aura/desktop_root_window_host_x11.h index 8cd7aa3..ca178ba 100644 --- a/ui/views/widget/desktop_aura/desktop_root_window_host_x11.h +++ b/ui/views/widget/desktop_aura/desktop_root_window_host_x11.h @@ -15,7 +15,6 @@ #include "ui/aura/client/cursor_client.h" #include "ui/aura/root_window_host.h" #include "ui/base/cursor/cursor_loader_x11.h" -#include "ui/base/dragdrop/desktop_selection_provider_aurax11.h" #include "ui/base/x/x11_atom_cache.h" #include "ui/gfx/rect.h" #include "ui/views/views_export.h" @@ -43,7 +42,6 @@ class CursorManager; class VIEWS_EXPORT DesktopRootWindowHostX11 : public DesktopRootWindowHost, public aura::RootWindowHost, - public ui::DesktopSelectionProviderAuraX11, public base::MessageLoop::Dispatcher { public: DesktopRootWindowHostX11( @@ -172,10 +170,6 @@ class VIEWS_EXPORT DesktopRootWindowHostX11 : virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE; virtual void PrepareForShutdown() OVERRIDE; - // Overridden from DesktopSelectionProviderAuraX11: - virtual void SetDropHandler( - ui::OSExchangeDataProviderAuraX11* handler) OVERRIDE; - // Overridden from Dispatcher: virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE; @@ -230,9 +224,6 @@ class VIEWS_EXPORT DesktopRootWindowHostX11 : aura::RootWindowHostDelegate* root_window_host_delegate_; aura::Window* content_window_; - // We forward drop related messages to this object. - ui::OSExchangeDataProviderAuraX11* drop_handler_; - // The current root window host that has capture. While X11 has something // like Windows SetCapture()/ReleaseCapture(), it is entirely implicit and // there are no notifications when this changes. We need to track this so we diff --git a/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc b/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc index e90cb64..986e945 100644 --- a/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc +++ b/ui/views/widget/desktop_aura/x11_desktop_window_move_client.cc @@ -34,6 +34,10 @@ void X11DesktopWindowMoveClient::OnMouseMovement(XMotionEvent* event) { system_loc, root_window_->GetHostSize())); } +void X11DesktopWindowMoveClient::OnMouseReleased() { + EndMoveLoop(); +} + void X11DesktopWindowMoveClient::OnMoveLoopEnded() { root_window_ = NULL; } diff --git a/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h b/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h index 0238a25..d4f2d8e 100644 --- a/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h +++ b/ui/views/widget/desktop_aura/x11_desktop_window_move_client.h @@ -35,6 +35,7 @@ class VIEWS_EXPORT X11DesktopWindowMoveClient : // Overridden from X11WholeScreenMoveLoopDelegate: virtual void OnMouseMovement(XMotionEvent* event) OVERRIDE; + virtual void OnMouseReleased() OVERRIDE; virtual void OnMoveLoopEnded() OVERRIDE; // Overridden from aura::client::WindowMoveClient: diff --git a/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc b/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc index 7053432..9cdce13 100644 --- a/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc +++ b/ui/views/widget/desktop_aura/x11_whole_screen_move_loop.cc @@ -43,9 +43,8 @@ bool X11WholeScreenMoveLoop::Dispatch(const base::NativeEvent& event) { delegate_->OnMouseMovement(&xev->xmotion); break; } - case ButtonPress: case ButtonRelease: { - EndMoveLoop(); + delegate_->OnMouseReleased(); break; } } diff --git a/ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h b/ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h index f6a569c..69f63be 100644 --- a/ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h +++ b/ui/views/widget/desktop_aura/x11_whole_screen_move_loop_delegate.h @@ -18,6 +18,9 @@ class X11WholeScreenMoveLoopDelegate { // Called when we receive a motion event. virtual void OnMouseMovement(XMotionEvent* event) = 0; + // Called when the mouse button is released. + virtual void OnMouseReleased() = 0; + // Called when the user has released the mouse button. The move loop will // release the grab after this has been called. virtual void OnMoveLoopEnded() = 0; |