Roll src/third_party/WebKit a3b4a2e:7441784 (svn 202551:202552)
[chromium-blink-merge.git] / ui / platform_window / x11 / x11_window.cc
blob91af8ed09314867b883103f9c1c032a59370bb77
1 // Copyright 2014 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/platform_window/x11/x11_window.h"
7 #include <X11/extensions/XInput2.h>
8 #include <X11/Xatom.h>
9 #include <X11/Xlib.h>
10 #include <X11/Xutil.h>
12 #include "ui/events/devices/x11/touch_factory_x11.h"
13 #include "ui/events/event.h"
14 #include "ui/events/event_utils.h"
15 #include "ui/events/platform/platform_event_dispatcher.h"
16 #include "ui/events/platform/platform_event_source.h"
17 #include "ui/events/platform/x11/x11_event_source.h"
18 #include "ui/gfx/geometry/rect.h"
19 #include "ui/gfx/x/x11_atom_cache.h"
20 #include "ui/gfx/x/x11_types.h"
21 #include "ui/platform_window/platform_window_delegate.h"
23 namespace ui {
25 namespace {
27 const char* kAtomsToCache[] = {
28 "WM_DELETE_WINDOW",
29 "_NET_WM_PING",
30 "_NET_WM_PID",
31 NULL
34 XID FindXEventTarget(XEvent* xevent) {
35 XID target = xevent->xany.window;
36 if (xevent->type == GenericEvent)
37 target = static_cast<XIDeviceEvent*>(xevent->xcookie.data)->event;
38 return target;
41 bool g_override_redirect = false;
43 } // namespace
45 X11Window::X11Window(PlatformWindowDelegate* delegate)
46 : delegate_(delegate),
47 xdisplay_(gfx::GetXDisplay()),
48 xwindow_(None),
49 xroot_window_(DefaultRootWindow(xdisplay_)),
50 atom_cache_(xdisplay_, kAtomsToCache),
51 window_mapped_(false) {
52 CHECK(delegate_);
53 TouchFactory::SetTouchDeviceListFromCommandLine();
56 X11Window::~X11Window() {
57 Destroy();
60 void X11Window::Destroy() {
61 if (xwindow_ == None)
62 return;
64 // Stop processing events.
65 PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
66 XID xwindow = xwindow_;
67 XDisplay* xdisplay = xdisplay_;
68 xwindow_ = None;
69 delegate_->OnClosed();
70 // |this| might be deleted because of the above call.
72 XDestroyWindow(xdisplay, xwindow);
75 void X11Window::ProcessXInput2Event(XEvent* xev) {
76 if (!TouchFactory::GetInstance()->ShouldProcessXI2Event(xev))
77 return;
78 EventType event_type = EventTypeFromNative(xev);
79 switch (event_type) {
80 case ET_KEY_PRESSED:
81 case ET_KEY_RELEASED: {
82 KeyEvent key_event(xev);
83 delegate_->DispatchEvent(&key_event);
84 break;
86 case ET_MOUSE_PRESSED:
87 case ET_MOUSE_MOVED:
88 case ET_MOUSE_DRAGGED:
89 case ET_MOUSE_RELEASED: {
90 MouseEvent mouse_event(xev);
91 delegate_->DispatchEvent(&mouse_event);
92 break;
94 case ET_MOUSEWHEEL: {
95 MouseWheelEvent wheel_event(xev);
96 delegate_->DispatchEvent(&wheel_event);
97 break;
99 case ET_SCROLL_FLING_START:
100 case ET_SCROLL_FLING_CANCEL:
101 case ET_SCROLL: {
102 ScrollEvent scroll_event(xev);
103 delegate_->DispatchEvent(&scroll_event);
104 break;
106 case ET_TOUCH_MOVED:
107 case ET_TOUCH_PRESSED:
108 case ET_TOUCH_CANCELLED:
109 case ET_TOUCH_RELEASED: {
110 TouchEvent touch_event(xev);
111 delegate_->DispatchEvent(&touch_event);
112 break;
114 default:
115 break;
119 void X11Window::Show() {
120 if (window_mapped_)
121 return;
123 CHECK(PlatformEventSource::GetInstance());
124 PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
126 XSetWindowAttributes swa;
127 memset(&swa, 0, sizeof(swa));
128 swa.background_pixmap = None;
129 swa.override_redirect = g_override_redirect;
130 xwindow_ = XCreateWindow(xdisplay_,
131 xroot_window_,
132 requested_bounds_.x(),
133 requested_bounds_.y(),
134 requested_bounds_.width(),
135 requested_bounds_.height(),
136 0, // border width
137 CopyFromParent, // depth
138 InputOutput,
139 CopyFromParent, // visual
140 CWBackPixmap | CWOverrideRedirect,
141 &swa);
143 long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
144 KeyPressMask | KeyReleaseMask | EnterWindowMask |
145 LeaveWindowMask | ExposureMask | VisibilityChangeMask |
146 StructureNotifyMask | PropertyChangeMask |
147 PointerMotionMask;
148 XSelectInput(xdisplay_, xwindow_, event_mask);
150 unsigned char mask[XIMaskLen(XI_LASTEVENT)];
151 memset(mask, 0, sizeof(mask));
153 XISetMask(mask, XI_TouchBegin);
154 XISetMask(mask, XI_TouchUpdate);
155 XISetMask(mask, XI_TouchEnd);
156 XISetMask(mask, XI_ButtonPress);
157 XISetMask(mask, XI_ButtonRelease);
158 XISetMask(mask, XI_Motion);
159 XISetMask(mask, XI_KeyPress);
160 XISetMask(mask, XI_KeyRelease);
162 XIEventMask evmask;
163 evmask.deviceid = XIAllDevices;
164 evmask.mask_len = sizeof(mask);
165 evmask.mask = mask;
166 XISelectEvents(xdisplay_, xwindow_, &evmask, 1);
167 XFlush(xdisplay_);
169 ::Atom protocols[2];
170 protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW");
171 protocols[1] = atom_cache_.GetAtom("_NET_WM_PING");
172 XSetWMProtocols(xdisplay_, xwindow_, protocols, 2);
174 // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with
175 // the desktop environment.
176 XSetWMProperties(
177 xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL);
179 // Likewise, the X server needs to know this window's pid so it knows which
180 // program to kill if the window hangs.
181 // XChangeProperty() expects "pid" to be long.
182 static_assert(sizeof(long) >= sizeof(pid_t),
183 "pid_t should not be larger than long");
184 long pid = getpid();
185 XChangeProperty(xdisplay_,
186 xwindow_,
187 atom_cache_.GetAtom("_NET_WM_PID"),
188 XA_CARDINAL,
190 PropModeReplace,
191 reinterpret_cast<unsigned char*>(&pid),
193 // Before we map the window, set size hints. Otherwise, some window managers
194 // will ignore toplevel XMoveWindow commands.
195 XSizeHints size_hints;
196 size_hints.flags = PPosition | PWinGravity;
197 size_hints.x = requested_bounds_.x();
198 size_hints.y = requested_bounds_.y();
199 // Set StaticGravity so that the window position is not affected by the
200 // frame width when running with window manager.
201 size_hints.win_gravity = StaticGravity;
202 XSetWMNormalHints(xdisplay_, xwindow_, &size_hints);
204 XMapWindow(xdisplay_, xwindow_);
206 // We now block until our window is mapped. Some X11 APIs will crash and
207 // burn if passed |xwindow_| before the window is mapped, and XMapWindow is
208 // asynchronous.
209 if (X11EventSource::GetInstance())
210 X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_);
211 window_mapped_ = true;
213 // TODO(sky): provide real scale factor.
214 delegate_->OnAcceleratedWidgetAvailable(xwindow_, 1.f);
217 void X11Window::Hide() {
218 if (!window_mapped_)
219 return;
220 XWithdrawWindow(xdisplay_, xwindow_, 0);
221 window_mapped_ = false;
224 void X11Window::Close() {
225 Destroy();
228 void X11Window::SetBounds(const gfx::Rect& bounds) {
229 requested_bounds_ = bounds;
230 if (!window_mapped_)
231 return;
232 XWindowChanges changes = {0};
233 unsigned value_mask = CWX | CWY | CWWidth | CWHeight;
234 changes.x = bounds.x();
235 changes.y = bounds.y();
236 changes.width = bounds.width();
237 changes.height = bounds.height();
238 XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes);
241 gfx::Rect X11Window::GetBounds() {
242 return confirmed_bounds_;
245 void X11Window::SetTitle(const base::string16& title) {}
247 void X11Window::SetCapture() {}
249 void X11Window::ReleaseCapture() {}
251 void X11Window::ToggleFullscreen() {}
253 void X11Window::Maximize() {}
255 void X11Window::Minimize() {}
257 void X11Window::Restore() {}
259 void X11Window::SetCursor(PlatformCursor cursor) {}
261 void X11Window::MoveCursorTo(const gfx::Point& location) {}
263 void X11Window::ConfineCursorToBounds(const gfx::Rect& bounds) {
266 PlatformImeController* X11Window::GetPlatformImeController() {
267 return nullptr;
270 bool X11Window::CanDispatchEvent(const PlatformEvent& event) {
271 return FindXEventTarget(event) == xwindow_;
274 uint32_t X11Window::DispatchEvent(const PlatformEvent& event) {
275 XEvent* xev = event;
276 switch (xev->type) {
277 case EnterNotify: {
278 // EnterNotify creates ET_MOUSE_MOVED. Mark as synthesized as this is
279 // not real mouse move event.
280 MouseEvent mouse_event(xev);
281 CHECK_EQ(ET_MOUSE_MOVED, mouse_event.type());
282 mouse_event.set_flags(mouse_event.flags() | EF_IS_SYNTHESIZED);
283 delegate_->DispatchEvent(&mouse_event);
284 break;
286 case LeaveNotify: {
287 MouseEvent mouse_event(xev);
288 delegate_->DispatchEvent(&mouse_event);
289 break;
292 case Expose: {
293 gfx::Rect damage_rect(xev->xexpose.x,
294 xev->xexpose.y,
295 xev->xexpose.width,
296 xev->xexpose.height);
297 delegate_->OnDamageRect(damage_rect);
298 break;
301 case KeyPress:
302 case KeyRelease: {
303 KeyEvent key_event(xev);
304 delegate_->DispatchEvent(&key_event);
305 break;
308 case ButtonPress:
309 case ButtonRelease: {
310 switch (EventTypeFromNative(xev)) {
311 case ET_MOUSEWHEEL: {
312 MouseWheelEvent mouseev(xev);
313 delegate_->DispatchEvent(&mouseev);
314 break;
316 case ET_MOUSE_PRESSED:
317 case ET_MOUSE_RELEASED: {
318 MouseEvent mouseev(xev);
319 delegate_->DispatchEvent(&mouseev);
320 break;
322 case ET_UNKNOWN:
323 // No event is created for X11-release events for mouse-wheel
324 // buttons.
325 break;
326 default:
327 NOTREACHED();
329 break;
332 case FocusOut:
333 if (xev->xfocus.mode != NotifyGrab)
334 delegate_->OnLostCapture();
335 break;
337 case ConfigureNotify: {
338 DCHECK_EQ(xwindow_, xev->xconfigure.event);
339 DCHECK_EQ(xwindow_, xev->xconfigure.window);
340 gfx::Rect bounds(xev->xconfigure.x,
341 xev->xconfigure.y,
342 xev->xconfigure.width,
343 xev->xconfigure.height);
344 if (confirmed_bounds_ != bounds) {
345 confirmed_bounds_ = bounds;
346 delegate_->OnBoundsChanged(confirmed_bounds_);
348 break;
351 case ClientMessage: {
352 Atom message = static_cast<Atom>(xev->xclient.data.l[0]);
353 if (message == atom_cache_.GetAtom("WM_DELETE_WINDOW")) {
354 delegate_->OnCloseRequest();
355 } else if (message == atom_cache_.GetAtom("_NET_WM_PING")) {
356 XEvent reply_event = *xev;
357 reply_event.xclient.window = xroot_window_;
359 XSendEvent(xdisplay_,
360 reply_event.xclient.window,
361 False,
362 SubstructureRedirectMask | SubstructureNotifyMask,
363 &reply_event);
364 XFlush(xdisplay_);
366 break;
369 case GenericEvent: {
370 ProcessXInput2Event(xev);
371 break;
374 return POST_DISPATCH_STOP_PROPAGATION;
377 namespace test {
379 void SetUseOverrideRedirectWindowByDefault(bool override_redirect) {
380 g_override_redirect = override_redirect;
383 } // namespace test
384 } // namespace ui