#include "base/event_types.h"
#include "base/lazy_instance.h"
#include "base/message_loop/message_loop.h"
-#include "base/message_loop/message_pump_dispatcher.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/clipboard/clipboard.h"
-#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/drop_target_event.h"
#include "ui/base/dragdrop/os_exchange_data.h"
#include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
#include "ui/base/x/selection_utils.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/event.h"
+#include "ui/events/platform/platform_event_source.h"
#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
+#include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h"
#include "ui/wm/public/drag_drop_client.h"
#include "ui/wm/public/drag_drop_delegate.h"
"XdndStatus",
"XdndTypeList",
ui::Clipboard::kMimeTypeText,
- "_NET_WM_WINDOW_TYPE_MENU",
NULL
};
// mouse button before ending the move loop.
const int kEndMoveLoopTimeoutMs = 1000;
+// The time to wait since sending the last XdndPosition message before
+// reprocessing the most recent mouse move event in case that the window
+// stacking order has changed and |source_current_window_| needs to be updated.
+const int kRepeatMouseMoveTimeoutMs = 350;
+
static base::LazyInstance<
std::map< ::Window, views::DesktopDragDropClientAuraX11*> >::Leaky
g_live_client_map = LAZY_INSTANCE_INITIALIZER;
-// Helper class to FindWindowFor which looks for a drag target under the
-// cursor.
-class DragTargetWindowFinder : public ui::EnumerateWindowsDelegate {
- public:
- DragTargetWindowFinder(XID ignored_icon_window,
- Atom menu_type_atom,
- gfx::Point screen_loc)
- : ignored_icon_window_(ignored_icon_window),
- output_window_(None),
- menu_type_atom_(menu_type_atom),
- 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;
-
- int value = 0;
- if (ui::PropertyExists(window, "WM_STATE") ||
- (ui::GetIntProperty(window, "_NET_WM_WINDOW_TYPE", &value) &&
- static_cast<Atom>(value) == menu_type_atom_)) {
- output_window_ = window;
- return true;
- }
-
- return false;
- }
-
- private:
- XID ignored_icon_window_;
- XID output_window_;
- const Atom menu_type_atom_;
- 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,
- Atom menu_type_atom) {
- DragTargetWindowFinder finder(None, menu_type_atom, screen_point);
- *mouse_window = finder.window();
+ ::Window* mouse_window,
+ ::Window* dest_window) {
+ views::X11TopmostWindowFinder finder;
+ *mouse_window = finder.FindWindowAt(screen_point);
*dest_window = None;
- if (finder.window() == None)
+ if (*mouse_window == None)
return;
// Figure out which window we should test as XdndAware. If mouse_window has
DesktopDragDropClientAuraX11*
DesktopDragDropClientAuraX11::g_current_drag_drop_client = NULL;
-class DesktopDragDropClientAuraX11::X11DragContext :
- public base::MessagePumpDispatcher {
+class DesktopDragDropClientAuraX11::X11DragContext
+ : public ui::PlatformEventDispatcher {
public:
X11DragContext(ui::X11AtomCache* atom_cache,
::Window local_window,
// |drag_operation|.
void MaskOpeartion(::Atom xdnd_operation, int* drag_operation) const;
- // Overridden from MessagePumpDispatcher:
- virtual uint32_t Dispatch(const base::NativeEvent& event) OVERRIDE;
+ // ui::PlatformEventDispatcher:
+ virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE;
+ virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE;
// The atom cache owned by our parent.
ui::X11AtomCache* atom_cache_;
if (!client) {
// The window doesn't have a DesktopDragDropClientAuraX11, that means it's
// created by some other process. Listen for messages on it.
- base::MessagePumpX11::Current()->AddDispatcherForWindow(
- this, source_window_);
+ ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
XSelectInput(gfx::GetXDisplay(), source_window_, PropertyChangeMask);
// We must perform a full sync here because we could be racing
}
DesktopDragDropClientAuraX11::X11DragContext::~X11DragContext() {
- DesktopDragDropClientAuraX11* client =
- DesktopDragDropClientAuraX11::GetForWindow(source_window_);
- if (!client) {
- // Unsubscribe from message events.
- base::MessagePumpX11::Current()->RemoveDispatcherForWindow(
- source_window_);
- }
+ // Unsubscribe from message events.
+ ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
}
void DesktopDragDropClientAuraX11::X11DragContext::OnStartXdndPositionMessage(
*drag_operation |= ui::DragDropTypes::DRAG_LINK;
}
-uint32_t DesktopDragDropClientAuraX11::X11DragContext::Dispatch(
- const base::NativeEvent& event) {
+bool DesktopDragDropClientAuraX11::X11DragContext::CanDispatchEvent(
+ const ui::PlatformEvent& event) {
+ return event->xany.window == source_window_;
+}
+
+uint32_t DesktopDragDropClientAuraX11::X11DragContext::DispatchEvent(
+ const ui::PlatformEvent& event) {
if (event->type == PropertyNotify &&
event->xproperty.atom == atom_cache_->GetAtom("XdndActionList")) {
ReadActions();
+ return ui::POST_DISPATCH_STOP_PROPAGATION;
}
- return POST_DISPATCH_NONE;
+ return ui::POST_DISPATCH_NONE;
}
///////////////////////////////////////////////////////////////////////////////
xwindow_(xwindow),
atom_cache_(xdisplay_, kAtomsToCache),
target_window_(NULL),
+ waiting_on_status_(false),
+ status_received_since_enter_(false),
source_provider_(NULL),
source_current_window_(None),
source_state_(SOURCE_STATE_OTHER),
drag_operation_(0),
- resulting_operation_(0),
+ negotiated_operation_(ui::DragDropTypes::DRAG_NONE),
grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorGrabbing)),
copy_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorCopy)),
move_grab_cursor_(cursor_manager->GetInitializedCursor(ui::kCursorMove)),
unsigned long source_window = event.data.l[0];
- waiting_on_status_.erase(source_window);
if (source_window != source_current_window_)
return;
- int drag_operation = ui::DragDropTypes::DRAG_NONE;
+ if (source_state_ != SOURCE_STATE_PENDING_DROP &&
+ source_state_ != SOURCE_STATE_OTHER) {
+ return;
+ }
+
+ waiting_on_status_ = false;
+ status_received_since_enter_ = true;
+
if (event.data.l[1] & 1) {
::Atom atom_operation = event.data.l[4];
- negotiated_operation_[source_window] = atom_operation;
- drag_operation = AtomToDragOperation(atom_operation);
+ negotiated_operation_ = AtomToDragOperation(atom_operation);
+ } else {
+ negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
}
- switch (drag_operation) {
+ if (source_state_ == SOURCE_STATE_PENDING_DROP) {
+ // We were waiting on the status message so we could send the XdndDrop.
+ if (negotiated_operation_ == ui::DragDropTypes::DRAG_NONE) {
+ move_loop_.EndMoveLoop();
+ return;
+ }
+ source_state_ = SOURCE_STATE_DROPPED;
+ SendXdndDrop(source_window);
+ return;
+ }
+
+ switch (negotiated_operation_) {
case ui::DragDropTypes::DRAG_COPY:
move_loop_.UpdateCursor(copy_grab_cursor_);
break;
// the spec) the other side must handle further position messages within
// it. GTK+ doesn't bother with this, so neither should we.
- if (source_state_ == SOURCE_STATE_PENDING_DROP) {
- // We were waiting on the status message so we could send the XdndDrop.
- source_state_ = SOURCE_STATE_DROPPED;
- SendXdndDrop(source_window);
- return;
- }
-
- NextPositionMap::iterator it = next_position_message_.find(source_window);
- if (source_state_ == SOURCE_STATE_OTHER &&
- it != next_position_message_.end()) {
+ if (next_position_message_.get()) {
// 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);
+ gfx::Point p = next_position_message_->first;
+ unsigned long event_time = next_position_message_->second;
+ next_position_message_.reset();
- SendXdndPosition(source_window, p, time);
+ SendXdndPosition(source_window, p, event_time);
}
}
void DesktopDragDropClientAuraX11::OnXdndFinished(
const XClientMessageEvent& event) {
DVLOG(1) << "XdndFinished";
- resulting_operation_ = AtomToDragOperation(
- negotiated_operation_[event.data.l[0]]);
+
+ // Clear |source_current_window_| to avoid sending XdndLeave upon ending the
+ // move loop.
+ source_current_window_ = None;
move_loop_.EndMoveLoop();
}
source_current_window_ = None;
DCHECK(!g_current_drag_drop_client);
g_current_drag_drop_client = this;
- waiting_on_status_.clear();
+ waiting_on_status_ = false;
+ next_position_message_.reset();
+ status_received_since_enter_ = false;
source_state_ = SOURCE_STATE_OTHER;
drag_operation_ = operation;
- resulting_operation_ = ui::DragDropTypes::DRAG_NONE;
+ negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
const ui::OSExchangeData::Provider* provider = &data.provider();
source_provider_ = static_cast<const ui::OSExchangeDataProviderAuraX11*>(
XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("XdndActionList"));
XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom(kXdndDirectSave0));
- return resulting_operation_;
+ return negotiated_operation_;
}
return ui::DragDropTypes::DRAG_NONE;
}
}
void DesktopDragDropClientAuraX11::OnMouseMovement(XMotionEvent* event) {
- gfx::Point screen_point(event->x_root, event->y_root);
-
- if (source_state_ != SOURCE_STATE_OTHER)
- return;
-
- // Find the current window the cursor is over.
- ::Window mouse_window = None;
- ::Window dest_window = None;
- FindWindowFor(screen_point, &mouse_window, &dest_window,
- atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_MENU"));
-
- 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);
- }
- }
+ repeat_mouse_move_timer_.Stop();
+ ProcessMouseMove(gfx::Point(event->x_root, event->y_root), event->time);
}
void DesktopDragDropClientAuraX11::OnMouseReleased() {
+ repeat_mouse_move_timer_.Stop();
+
if (source_state_ != SOURCE_STATE_OTHER) {
// The user has previously released the mouse and is clicking in
// frustration.
}
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.
- source_state_ = SOURCE_STATE_PENDING_DROP;
+ if (waiting_on_status_) {
+ if (status_received_since_enter_) {
+ // If we are waiting for an XdndStatus message, we need to wait for it
+ // to complete.
+ source_state_ = SOURCE_STATE_PENDING_DROP;
+
+ // Start timer to end the move loop if the target takes too long to send
+ // the XdndStatus and XdndFinished messages.
+ StartEndMoveLoopTimer();
+ return;
+ }
- // Start timer to end the move loop if the target takes too long to send
- // the XdndStatus and XdndFinished messages.
- StartEndMoveLoopTimer();
+ move_loop_.EndMoveLoop();
return;
}
- std::map< ::Window, ::Atom>::iterator it =
- negotiated_operation_.find(source_current_window_);
- if (it != negotiated_operation_.end() && it->second != None) {
+ if (negotiated_operation_ != ui::DragDropTypes::DRAG_NONE) {
// We have negotiated an action with the other end.
source_state_ = SOURCE_STATE_DROPPED;
SendXdndDrop(source_current_window_);
StartEndMoveLoopTimer();
return;
}
-
- SendXdndLeave(source_current_window_);
- source_current_window_ = None;
}
move_loop_.EndMoveLoop();
}
void DesktopDragDropClientAuraX11::OnMoveLoopEnded() {
- if (source_current_window_ != None)
+ if (source_current_window_ != None) {
SendXdndLeave(source_current_window_);
+ source_current_window_ = None;
+ }
target_current_context_.reset();
- source_state_ = SOURCE_STATE_OTHER;
+ repeat_mouse_move_timer_.Stop();
end_move_loop_timer_.Stop();
}
+void DesktopDragDropClientAuraX11::ProcessMouseMove(
+ const gfx::Point& screen_point,
+ unsigned long event_time) {
+ if (source_state_ != SOURCE_STATE_OTHER)
+ return;
+
+ // 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;
+ waiting_on_status_ = false;
+ next_position_message_.reset();
+ status_received_since_enter_ = false;
+ negotiated_operation_ = ui::DragDropTypes::DRAG_NONE;
+
+ if (source_current_window_ != None)
+ SendXdndEnter(source_current_window_);
+ }
+
+ if (source_current_window_ != None) {
+ if (waiting_on_status_) {
+ next_position_message_.reset(
+ new std::pair<gfx::Point, unsigned long>(screen_point, event_time));
+ } else {
+ SendXdndPosition(dest_window, screen_point, event_time);
+ }
+ }
+}
+
void DesktopDragDropClientAuraX11::StartEndMoveLoopTimer() {
end_move_loop_timer_.Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(
return None;
}
-int DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
+ui::DragDropTypes::DragOperation
+DesktopDragDropClientAuraX11::AtomToDragOperation(::Atom atom) {
if (atom == atom_cache_.GetAtom(kXdndActionCopy))
return ui::DragDropTypes::DRAG_COPY;
if (atom == atom_cache_.GetAtom(kXdndActionMove))
}
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");
void DesktopDragDropClientAuraX11::SendXdndPosition(
::Window dest_window,
const gfx::Point& screen_point,
- unsigned long time) {
- waiting_on_status_.insert(dest_window);
+ unsigned long event_time) {
+ waiting_on_status_ = true;
XEvent xev;
xev.xclient.type = ClientMessage;
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[3] = event_time;
xev.xclient.data.l[4] = DragOperationToAtom(drag_operation_);
SendXClientEvent(dest_window, &xev);
+
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html and
+ // the Xdnd protocol both recommend that drag events should be sent
+ // periodically.
+ repeat_mouse_move_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kRepeatMouseMoveTimeoutMs),
+ base::Bind(&DesktopDragDropClientAuraX11::ProcessMouseMove,
+ base::Unretained(this),
+ screen_point,
+ event_time));
}
void DesktopDragDropClientAuraX11::SendXdndDrop(::Window dest_window) {