diff options
author | erg@chromium.org <erg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-19 13:09:56 +0000 |
---|---|---|
committer | erg@chromium.org <erg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-19 13:09:56 +0000 |
commit | 022731a384c7666aff36f8d87ec627d7bedf8995 (patch) | |
tree | 00552ba63ce3799e333cc176c78a0d30110bf4a0 /ui/views | |
parent | 85f50ec0ad933ff17c19ef33e70b38141e0bcb1a (diff) | |
download | chromium_src-022731a384c7666aff36f8d87ec627d7bedf8995.zip chromium_src-022731a384c7666aff36f8d87ec627d7bedf8995.tar.gz chromium_src-022731a384c7666aff36f8d87ec627d7bedf8995.tar.bz2 |
linux_aura: Make chrome windows drag sources on X11.
This implements the source side of the Xdnd protocol. It also includes some small changes to which object owns the drag selection so that we can do that part entirely asynchronously without spinning up a nested message loop.
This patch only handles text drags and doesn't change the cursor or show a drag image under the cursor. Both of these require some refactoring and this patch is big enough as is.
BUG=130806
Review URL: https://chromiumcodereview.appspot.com/16271006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@207219 0039d316-1c4b-4281-b951-d872f2087c98
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; |