Roll src/third_party/WebKit 6f84130:7353389 (svn 184386:184391)
[chromium-blink-merge.git] / ui / views / widget / desktop_aura / x11_whole_screen_move_loop.cc
blobe0840cc43e84fc5bd0ac1abb2094c692dc7ed1bf
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/views/widget/desktop_aura/x11_whole_screen_move_loop.h"
7 #include <X11/keysym.h>
8 #include <X11/Xlib.h>
10 #include "base/bind.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/run_loop.h"
13 #include "ui/aura/client/capture_client.h"
14 #include "ui/aura/env.h"
15 #include "ui/aura/window.h"
16 #include "ui/aura/window_event_dispatcher.h"
17 #include "ui/aura/window_tree_host.h"
18 #include "ui/base/x/x11_util.h"
19 #include "ui/events/event.h"
20 #include "ui/events/event_utils.h"
21 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
22 #include "ui/events/platform/scoped_event_dispatcher.h"
23 #include "ui/events/platform/x11/x11_event_source.h"
25 namespace views {
27 // XGrabKey requires the modifier mask to explicitly be specified.
28 const unsigned int kModifiersMasks[] = {
29 0, // No additional modifier.
30 Mod2Mask, // Num lock
31 LockMask, // Caps lock
32 Mod5Mask, // Scroll lock
33 Mod2Mask | LockMask,
34 Mod2Mask | Mod5Mask,
35 LockMask | Mod5Mask,
36 Mod2Mask | LockMask | Mod5Mask
39 X11WholeScreenMoveLoop::X11WholeScreenMoveLoop(X11MoveLoopDelegate* delegate)
40 : delegate_(delegate),
41 in_move_loop_(false),
42 initial_cursor_(ui::kCursorNull),
43 should_reset_mouse_flags_(false),
44 grab_input_window_(None),
45 grabbed_pointer_(false),
46 canceled_(false),
47 weak_factory_(this) {
48 last_xmotion_.type = LASTEvent;
51 X11WholeScreenMoveLoop::~X11WholeScreenMoveLoop() {}
53 void X11WholeScreenMoveLoop::DispatchMouseMovement() {
54 if (!weak_factory_.HasWeakPtrs())
55 return;
56 weak_factory_.InvalidateWeakPtrs();
57 DCHECK_EQ(MotionNotify, last_xmotion_.type);
58 delegate_->OnMouseMovement(&last_xmotion_);
59 last_xmotion_.type = LASTEvent;
62 ////////////////////////////////////////////////////////////////////////////////
63 // DesktopWindowTreeHostLinux, ui::PlatformEventDispatcher implementation:
65 bool X11WholeScreenMoveLoop::CanDispatchEvent(const ui::PlatformEvent& event) {
66 return in_move_loop_;
69 uint32_t X11WholeScreenMoveLoop::DispatchEvent(const ui::PlatformEvent& event) {
70 // This method processes all events while the move loop is active.
71 if (!in_move_loop_)
72 return ui::POST_DISPATCH_PERFORM_DEFAULT;
74 XEvent* xev = event;
75 switch (xev->type) {
76 case MotionNotify: {
77 last_xmotion_ = xev->xmotion;
78 if (!weak_factory_.HasWeakPtrs()) {
79 // Post a task to dispatch mouse movement event when control returns to
80 // the message loop. This allows smoother dragging since the events are
81 // dispatched without waiting for the drag widget updates.
82 base::MessageLoopForUI::current()->PostTask(
83 FROM_HERE,
84 base::Bind(&X11WholeScreenMoveLoop::DispatchMouseMovement,
85 weak_factory_.GetWeakPtr()));
87 return ui::POST_DISPATCH_NONE;
89 case ButtonRelease: {
90 if (xev->xbutton.button == Button1) {
91 // Assume that drags are being done with the left mouse button. Only
92 // break the drag if the left mouse button was released.
93 DispatchMouseMovement();
94 delegate_->OnMouseReleased();
96 if (!grabbed_pointer_) {
97 // If the source widget had capture prior to the move loop starting,
98 // it may be relying on views::Widget getting the mouse release and
99 // releasing capture in Widget::OnMouseEvent().
100 return ui::POST_DISPATCH_PERFORM_DEFAULT;
103 return ui::POST_DISPATCH_NONE;
105 case KeyPress: {
106 if (ui::KeyboardCodeFromXKeyEvent(xev) == ui::VKEY_ESCAPE) {
107 canceled_ = true;
108 EndMoveLoop();
109 return ui::POST_DISPATCH_NONE;
111 break;
113 case GenericEvent: {
114 ui::EventType type = ui::EventTypeFromNative(xev);
115 switch (type) {
116 case ui::ET_MOUSE_MOVED:
117 case ui::ET_MOUSE_DRAGGED:
118 case ui::ET_MOUSE_RELEASED: {
119 XEvent xevent = {0};
120 if (type == ui::ET_MOUSE_RELEASED) {
121 xevent.type = ButtonRelease;
122 xevent.xbutton.button = ui::EventButtonFromNative(xev);
123 } else {
124 xevent.type = MotionNotify;
126 xevent.xany.display = xev->xgeneric.display;
127 xevent.xany.window = grab_input_window_;
128 // The fields used below are in the same place for all of events
129 // above. Using xmotion from XEvent's unions to avoid repeating
130 // the code.
131 xevent.xmotion.root = DefaultRootWindow(xev->xgeneric.display);
132 xevent.xmotion.time = ui::EventTimeFromNative(xev).InMilliseconds();
133 gfx::Point point(ui::EventSystemLocationFromNative(xev));
134 xevent.xmotion.x_root = point.x();
135 xevent.xmotion.y_root = point.y();
136 return DispatchEvent(&xevent);
138 default:
139 break;
144 return ui::POST_DISPATCH_PERFORM_DEFAULT;
147 bool X11WholeScreenMoveLoop::RunMoveLoop(aura::Window* source,
148 gfx::NativeCursor cursor) {
149 DCHECK(!in_move_loop_); // Can only handle one nested loop at a time.
151 // Query the mouse cursor prior to the move loop starting so that it can be
152 // restored when the move loop finishes.
153 initial_cursor_ = source->GetHost()->last_cursor();
155 grab_input_window_ = CreateDragInputWindow(gfx::GetXDisplay());
157 // Only grab mouse capture of |grab_input_window_| if |source| does not have
158 // capture.
159 // - The caller may intend to transfer capture to a different aura::Window
160 // when the move loop ends and not release capture.
161 // - Releasing capture and X window destruction are both asynchronous. We drop
162 // events targeted at |grab_input_window_| in the time between the move
163 // loop ends and |grab_input_window_| loses capture.
164 grabbed_pointer_ = false;
165 if (!source->HasCapture()) {
166 aura::client::CaptureClient* capture_client =
167 aura::client::GetCaptureClient(source->GetRootWindow());
168 CHECK(capture_client->GetGlobalCaptureWindow() == NULL);
169 grabbed_pointer_ = GrabPointer(cursor);
170 if (!grabbed_pointer_) {
171 XDestroyWindow(gfx::GetXDisplay(), grab_input_window_);
172 return false;
176 GrabEscKey();
178 scoped_ptr<ui::ScopedEventDispatcher> old_dispatcher =
179 nested_dispatcher_.Pass();
180 nested_dispatcher_ =
181 ui::PlatformEventSource::GetInstance()->OverrideDispatcher(this);
183 // We are handling a mouse drag outside of the aura::Window system. We must
184 // manually make aura think that the mouse button is pressed so that we don't
185 // draw extraneous tooltips.
186 aura::Env* env = aura::Env::GetInstance();
187 if (!env->IsMouseButtonDown()) {
188 env->set_mouse_button_flags(ui::EF_LEFT_MOUSE_BUTTON);
189 should_reset_mouse_flags_ = true;
192 in_move_loop_ = true;
193 canceled_ = false;
194 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
195 base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop);
196 base::RunLoop run_loop;
197 quit_closure_ = run_loop.QuitClosure();
198 run_loop.Run();
199 nested_dispatcher_ = old_dispatcher.Pass();
200 return !canceled_;
203 void X11WholeScreenMoveLoop::UpdateCursor(gfx::NativeCursor cursor) {
204 if (in_move_loop_) {
205 // We cannot call GrabPointer() because we do not want to change the
206 // "owner_events" property of the active pointer grab.
207 XChangeActivePointerGrab(
208 gfx::GetXDisplay(),
209 ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
210 cursor.platform(),
211 CurrentTime);
215 void X11WholeScreenMoveLoop::EndMoveLoop() {
216 if (!in_move_loop_)
217 return;
219 // Prevent DispatchMouseMovement from dispatching any posted motion event.
220 weak_factory_.InvalidateWeakPtrs();
221 last_xmotion_.type = LASTEvent;
223 // We undo our emulated mouse click from RunMoveLoop();
224 if (should_reset_mouse_flags_) {
225 aura::Env::GetInstance()->set_mouse_button_flags(0);
226 should_reset_mouse_flags_ = false;
229 // TODO(erg): Is this ungrab the cause of having to click to give input focus
230 // on drawn out windows? Not ungrabbing here screws the X server until I kill
231 // the chrome process.
233 // Ungrab before we let go of the window.
234 XDisplay* display = gfx::GetXDisplay();
235 if (grabbed_pointer_)
236 XUngrabPointer(display, CurrentTime);
237 else
238 UpdateCursor(initial_cursor_);
240 unsigned int esc_keycode = XKeysymToKeycode(display, XK_Escape);
241 for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) {
242 XUngrabKey(display, esc_keycode, kModifiersMasks[i], grab_input_window_);
245 // Restore the previous dispatcher.
246 nested_dispatcher_.reset();
247 delegate_->OnMoveLoopEnded();
248 XDestroyWindow(display, grab_input_window_);
249 grab_input_window_ = None;
251 in_move_loop_ = false;
252 quit_closure_.Run();
255 bool X11WholeScreenMoveLoop::GrabPointer(gfx::NativeCursor cursor) {
256 XDisplay* display = gfx::GetXDisplay();
257 XGrabServer(display);
259 // Pass "owner_events" as false so that X sends all mouse events to
260 // |grab_input_window_|.
261 int ret = XGrabPointer(
262 display,
263 grab_input_window_,
264 False, // owner_events
265 ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
266 GrabModeAsync,
267 GrabModeAsync,
268 None,
269 cursor.platform(),
270 CurrentTime);
271 if (ret != GrabSuccess) {
272 DLOG(ERROR) << "Grabbing pointer for dragging failed: "
273 << ui::GetX11ErrorString(display, ret);
275 XUngrabServer(display);
276 XFlush(display);
277 return ret == GrabSuccess;
280 void X11WholeScreenMoveLoop::GrabEscKey() {
281 XDisplay* display = gfx::GetXDisplay();
282 unsigned int esc_keycode = XKeysymToKeycode(display, XK_Escape);
283 for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) {
284 XGrabKey(display, esc_keycode, kModifiersMasks[i], grab_input_window_,
285 False, GrabModeAsync, GrabModeAsync);
289 Window X11WholeScreenMoveLoop::CreateDragInputWindow(XDisplay* display) {
290 unsigned long attribute_mask = CWEventMask | CWOverrideRedirect;
291 XSetWindowAttributes swa;
292 memset(&swa, 0, sizeof(swa));
293 swa.event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
294 KeyPressMask | KeyReleaseMask | StructureNotifyMask;
295 swa.override_redirect = True;
296 Window window = XCreateWindow(display,
297 DefaultRootWindow(display),
298 -100, -100, 10, 10,
299 0, CopyFromParent, InputOnly, CopyFromParent,
300 attribute_mask, &swa);
301 XMapRaised(display, window);
302 ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(window);
303 return window;
306 } // namespace views