Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / ui / views / widget / desktop_aura / x11_whole_screen_move_loop.cc
blobde39a04867757a01fd19152f4ff88ca869212c50
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"
24 #include "ui/views/widget/desktop_aura/x11_pointer_grab.h"
26 namespace views {
28 // XGrabKey requires the modifier mask to explicitly be specified.
29 const unsigned int kModifiersMasks[] = {
30 0, // No additional modifier.
31 Mod2Mask, // Num lock
32 LockMask, // Caps lock
33 Mod5Mask, // Scroll lock
34 Mod2Mask | LockMask,
35 Mod2Mask | Mod5Mask,
36 LockMask | Mod5Mask,
37 Mod2Mask | LockMask | Mod5Mask
40 X11WholeScreenMoveLoop::X11WholeScreenMoveLoop(X11MoveLoopDelegate* delegate)
41 : delegate_(delegate),
42 in_move_loop_(false),
43 initial_cursor_(ui::kCursorNull),
44 should_reset_mouse_flags_(false),
45 grab_input_window_(None),
46 grabbed_pointer_(false),
47 canceled_(false),
48 weak_factory_(this) {
51 X11WholeScreenMoveLoop::~X11WholeScreenMoveLoop() {}
53 void X11WholeScreenMoveLoop::DispatchMouseMovement() {
54 if (!last_motion_in_screen_)
55 return;
56 delegate_->OnMouseMovement(last_motion_in_screen_->location(),
57 last_motion_in_screen_->flags(),
58 last_motion_in_screen_->time_stamp());
59 last_motion_in_screen_.reset();
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 ui::EventType type = ui::EventTypeFromNative(xev);
76 switch (type) {
77 case ui::ET_MOUSE_MOVED:
78 case ui::ET_MOUSE_DRAGGED: {
79 bool dispatch_mouse_event = !last_motion_in_screen_.get();
80 last_motion_in_screen_.reset(
81 static_cast<ui::MouseEvent*>(ui::EventFromNative(xev).release()));
82 last_motion_in_screen_->set_location(
83 ui::EventSystemLocationFromNative(xev));
84 if (dispatch_mouse_event) {
85 // Post a task to dispatch mouse movement event when control returns to
86 // the message loop. This allows smoother dragging since the events are
87 // dispatched without waiting for the drag widget updates.
88 base::MessageLoopForUI::current()->PostTask(
89 FROM_HERE,
90 base::Bind(&X11WholeScreenMoveLoop::DispatchMouseMovement,
91 weak_factory_.GetWeakPtr()));
93 return ui::POST_DISPATCH_NONE;
95 case ui::ET_MOUSE_RELEASED: {
96 int button = (xev->type == ButtonRelease)
97 ? xev->xbutton.button
98 : ui::EventButtonFromNative(xev);
99 if (button == Button1) {
100 // Assume that drags are being done with the left mouse button. Only
101 // break the drag if the left mouse button was released.
102 DispatchMouseMovement();
103 delegate_->OnMouseReleased();
105 if (!grabbed_pointer_) {
106 // If the source widget had capture prior to the move loop starting,
107 // it may be relying on views::Widget getting the mouse release and
108 // releasing capture in Widget::OnMouseEvent().
109 return ui::POST_DISPATCH_PERFORM_DEFAULT;
112 return ui::POST_DISPATCH_NONE;
114 case ui::ET_KEY_PRESSED:
115 if (ui::KeyboardCodeFromXKeyEvent(xev) == ui::VKEY_ESCAPE) {
116 canceled_ = true;
117 EndMoveLoop();
118 return ui::POST_DISPATCH_NONE;
120 break;
121 default:
122 break;
124 return ui::POST_DISPATCH_PERFORM_DEFAULT;
127 bool X11WholeScreenMoveLoop::RunMoveLoop(aura::Window* source,
128 gfx::NativeCursor cursor) {
129 DCHECK(!in_move_loop_); // Can only handle one nested loop at a time.
131 // Query the mouse cursor prior to the move loop starting so that it can be
132 // restored when the move loop finishes.
133 initial_cursor_ = source->GetHost()->last_cursor();
135 grab_input_window_ = CreateDragInputWindow(gfx::GetXDisplay());
137 // Only grab mouse capture of |grab_input_window_| if |source| does not have
138 // capture.
139 // - The caller may intend to transfer capture to a different aura::Window
140 // when the move loop ends and not release capture.
141 // - Releasing capture and X window destruction are both asynchronous. We drop
142 // events targeted at |grab_input_window_| in the time between the move
143 // loop ends and |grab_input_window_| loses capture.
144 grabbed_pointer_ = false;
145 if (!source->HasCapture()) {
146 aura::client::CaptureClient* capture_client =
147 aura::client::GetCaptureClient(source->GetRootWindow());
148 CHECK(capture_client->GetGlobalCaptureWindow() == NULL);
149 grabbed_pointer_ = GrabPointer(cursor);
150 if (!grabbed_pointer_) {
151 XDestroyWindow(gfx::GetXDisplay(), grab_input_window_);
152 return false;
156 GrabEscKey();
158 scoped_ptr<ui::ScopedEventDispatcher> old_dispatcher =
159 nested_dispatcher_.Pass();
160 nested_dispatcher_ =
161 ui::PlatformEventSource::GetInstance()->OverrideDispatcher(this);
163 // We are handling a mouse drag outside of the aura::Window system. We must
164 // manually make aura think that the mouse button is pressed so that we don't
165 // draw extraneous tooltips.
166 aura::Env* env = aura::Env::GetInstance();
167 if (!env->IsMouseButtonDown()) {
168 env->set_mouse_button_flags(ui::EF_LEFT_MOUSE_BUTTON);
169 should_reset_mouse_flags_ = true;
172 base::WeakPtr<X11WholeScreenMoveLoop> alive(weak_factory_.GetWeakPtr());
174 in_move_loop_ = true;
175 canceled_ = false;
176 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
177 base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop);
178 base::RunLoop run_loop;
179 quit_closure_ = run_loop.QuitClosure();
180 run_loop.Run();
182 if (!alive)
183 return false;
185 nested_dispatcher_ = old_dispatcher.Pass();
186 return !canceled_;
189 void X11WholeScreenMoveLoop::UpdateCursor(gfx::NativeCursor cursor) {
190 if (in_move_loop_)
191 ChangeActivePointerGrabCursor(cursor.platform());
194 void X11WholeScreenMoveLoop::EndMoveLoop() {
195 if (!in_move_loop_)
196 return;
198 // Prevent DispatchMouseMovement from dispatching any posted motion event.
199 last_motion_in_screen_.reset();
201 // We undo our emulated mouse click from RunMoveLoop();
202 if (should_reset_mouse_flags_) {
203 aura::Env::GetInstance()->set_mouse_button_flags(0);
204 should_reset_mouse_flags_ = false;
207 // TODO(erg): Is this ungrab the cause of having to click to give input focus
208 // on drawn out windows? Not ungrabbing here screws the X server until I kill
209 // the chrome process.
211 // Ungrab before we let go of the window.
212 if (grabbed_pointer_)
213 UngrabPointer();
214 else
215 UpdateCursor(initial_cursor_);
217 XDisplay* display = gfx::GetXDisplay();
218 unsigned int esc_keycode = XKeysymToKeycode(display, XK_Escape);
219 for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) {
220 XUngrabKey(display, esc_keycode, kModifiersMasks[i], grab_input_window_);
223 // Restore the previous dispatcher.
224 nested_dispatcher_.reset();
225 delegate_->OnMoveLoopEnded();
226 XDestroyWindow(display, grab_input_window_);
227 grab_input_window_ = None;
229 in_move_loop_ = false;
230 quit_closure_.Run();
233 bool X11WholeScreenMoveLoop::GrabPointer(gfx::NativeCursor cursor) {
234 XDisplay* display = gfx::GetXDisplay();
235 XGrabServer(display);
237 // Pass "owner_events" as false so that X sends all mouse events to
238 // |grab_input_window_|.
239 int ret = ::views::GrabPointer(grab_input_window_, false, cursor.platform());
240 if (ret != GrabSuccess) {
241 DLOG(ERROR) << "Grabbing pointer for dragging failed: "
242 << ui::GetX11ErrorString(display, ret);
244 XUngrabServer(display);
245 XFlush(display);
246 return ret == GrabSuccess;
249 void X11WholeScreenMoveLoop::GrabEscKey() {
250 XDisplay* display = gfx::GetXDisplay();
251 unsigned int esc_keycode = XKeysymToKeycode(display, XK_Escape);
252 for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) {
253 XGrabKey(display, esc_keycode, kModifiersMasks[i], grab_input_window_,
254 False, GrabModeAsync, GrabModeAsync);
258 Window X11WholeScreenMoveLoop::CreateDragInputWindow(XDisplay* display) {
259 unsigned long attribute_mask = CWEventMask | CWOverrideRedirect;
260 XSetWindowAttributes swa;
261 memset(&swa, 0, sizeof(swa));
262 swa.event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
263 KeyPressMask | KeyReleaseMask | StructureNotifyMask;
264 swa.override_redirect = True;
265 Window window = XCreateWindow(display,
266 DefaultRootWindow(display),
267 -100, -100, 10, 10,
268 0, CopyFromParent, InputOnly, CopyFromParent,
269 attribute_mask, &swa);
270 XMapRaised(display, window);
271 ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(window);
272 return window;
275 } // namespace views