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>
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"
27 const char* kAtomsToCache
[] = {
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
;
41 bool g_override_redirect
= false;
45 X11Window::X11Window(PlatformWindowDelegate
* delegate
)
46 : delegate_(delegate
),
47 xdisplay_(gfx::GetXDisplay()),
49 xroot_window_(DefaultRootWindow(xdisplay_
)),
50 atom_cache_(xdisplay_
, kAtomsToCache
),
51 window_mapped_(false) {
53 TouchFactory::SetTouchDeviceListFromCommandLine();
56 X11Window::~X11Window() {
60 void X11Window::Destroy() {
64 // Stop processing events.
65 PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
66 XID xwindow
= xwindow_
;
67 XDisplay
* xdisplay
= xdisplay_
;
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
))
78 EventType event_type
= EventTypeFromNative(xev
);
81 case ET_KEY_RELEASED
: {
82 KeyEvent
key_event(xev
);
83 delegate_
->DispatchEvent(&key_event
);
86 case ET_MOUSE_PRESSED
:
88 case ET_MOUSE_DRAGGED
:
89 case ET_MOUSE_RELEASED
: {
90 MouseEvent
mouse_event(xev
);
91 delegate_
->DispatchEvent(&mouse_event
);
95 MouseWheelEvent
wheel_event(xev
);
96 delegate_
->DispatchEvent(&wheel_event
);
99 case ET_SCROLL_FLING_START
:
100 case ET_SCROLL_FLING_CANCEL
:
102 ScrollEvent
scroll_event(xev
);
103 delegate_
->DispatchEvent(&scroll_event
);
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
);
119 void X11Window::Show() {
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_
,
132 requested_bounds_
.x(),
133 requested_bounds_
.y(),
134 requested_bounds_
.width(),
135 requested_bounds_
.height(),
137 CopyFromParent
, // depth
139 CopyFromParent
, // visual
140 CWBackPixmap
| CWOverrideRedirect
,
143 long event_mask
= ButtonPressMask
| ButtonReleaseMask
| FocusChangeMask
|
144 KeyPressMask
| KeyReleaseMask
| EnterWindowMask
|
145 LeaveWindowMask
| ExposureMask
| VisibilityChangeMask
|
146 StructureNotifyMask
| PropertyChangeMask
|
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
);
163 evmask
.deviceid
= XIAllDevices
;
164 evmask
.mask_len
= sizeof(mask
);
166 XISelectEvents(xdisplay_
, xwindow_
, &evmask
, 1);
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.
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");
185 XChangeProperty(xdisplay_
,
187 atom_cache_
.GetAtom("_NET_WM_PID"),
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
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() {
220 XWithdrawWindow(xdisplay_
, xwindow_
, 0);
221 window_mapped_
= false;
224 void X11Window::Close() {
228 void X11Window::SetBounds(const gfx::Rect
& bounds
) {
229 requested_bounds_
= bounds
;
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() {
270 bool X11Window::CanDispatchEvent(const PlatformEvent
& event
) {
271 return FindXEventTarget(event
) == xwindow_
;
274 uint32_t X11Window::DispatchEvent(const PlatformEvent
& event
) {
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
);
287 MouseEvent
mouse_event(xev
);
288 delegate_
->DispatchEvent(&mouse_event
);
293 gfx::Rect
damage_rect(xev
->xexpose
.x
,
296 xev
->xexpose
.height
);
297 delegate_
->OnDamageRect(damage_rect
);
303 KeyEvent
key_event(xev
);
304 delegate_
->DispatchEvent(&key_event
);
309 case ButtonRelease
: {
310 switch (EventTypeFromNative(xev
)) {
311 case ET_MOUSEWHEEL
: {
312 MouseWheelEvent
mouseev(xev
);
313 delegate_
->DispatchEvent(&mouseev
);
316 case ET_MOUSE_PRESSED
:
317 case ET_MOUSE_RELEASED
: {
318 MouseEvent
mouseev(xev
);
319 delegate_
->DispatchEvent(&mouseev
);
323 // No event is created for X11-release events for mouse-wheel
333 if (xev
->xfocus
.mode
!= NotifyGrab
)
334 delegate_
->OnLostCapture();
337 case ConfigureNotify
: {
338 DCHECK_EQ(xwindow_
, xev
->xconfigure
.event
);
339 DCHECK_EQ(xwindow_
, xev
->xconfigure
.window
);
340 gfx::Rect
bounds(xev
->xconfigure
.x
,
342 xev
->xconfigure
.width
,
343 xev
->xconfigure
.height
);
344 if (confirmed_bounds_
!= bounds
) {
345 confirmed_bounds_
= bounds
;
346 delegate_
->OnBoundsChanged(confirmed_bounds_
);
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
,
362 SubstructureRedirectMask
| SubstructureNotifyMask
,
370 ProcessXInput2Event(xev
);
374 return POST_DISPATCH_STOP_PROPAGATION
;
379 void SetUseOverrideRedirectWindowByDefault(bool override_redirect
) {
380 g_override_redirect
= override_redirect
;