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/event.h"
13 #include "ui/events/event_utils.h"
14 #include "ui/events/platform/platform_event_dispatcher.h"
15 #include "ui/events/platform/platform_event_source.h"
16 #include "ui/events/platform/x11/x11_event_source.h"
17 #include "ui/gfx/geometry/rect.h"
18 #include "ui/gfx/x/x11_atom_cache.h"
19 #include "ui/gfx/x/x11_types.h"
20 #include "ui/platform_window/platform_window_delegate.h"
26 const char* kAtomsToCache
[] = {
33 XID
FindXEventTarget(XEvent
* xevent
) {
34 XID target
= xevent
->xany
.window
;
35 if (xevent
->type
== GenericEvent
)
36 target
= static_cast<XIDeviceEvent
*>(xevent
->xcookie
.data
)->event
;
42 X11Window::X11Window(PlatformWindowDelegate
* delegate
)
43 : delegate_(delegate
),
44 xdisplay_(gfx::GetXDisplay()),
46 xroot_window_(DefaultRootWindow(xdisplay_
)),
47 atom_cache_(xdisplay_
, kAtomsToCache
),
48 window_mapped_(false) {
52 X11Window::~X11Window() {
56 void X11Window::Destroy() {
60 // Stop processing events.
61 PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
62 XDestroyWindow(xdisplay_
, xwindow_
);
66 void X11Window::Show() {
70 CHECK(PlatformEventSource::GetInstance());
71 PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
73 XSetWindowAttributes swa
;
74 memset(&swa
, 0, sizeof(swa
));
75 swa
.background_pixmap
= None
;
76 swa
.override_redirect
= False
;
77 xwindow_
= XCreateWindow(xdisplay_
,
79 requested_bounds_
.x(),
80 requested_bounds_
.y(),
81 requested_bounds_
.width(),
82 requested_bounds_
.height(),
84 CopyFromParent
, // depth
86 CopyFromParent
, // visual
87 CWBackPixmap
| CWOverrideRedirect
,
90 long event_mask
= ButtonPressMask
| ButtonReleaseMask
| FocusChangeMask
|
91 KeyPressMask
| KeyReleaseMask
| EnterWindowMask
|
92 LeaveWindowMask
| ExposureMask
| VisibilityChangeMask
|
93 StructureNotifyMask
| PropertyChangeMask
|
95 XSelectInput(xdisplay_
, xwindow_
, event_mask
);
97 unsigned char mask
[XIMaskLen(XI_LASTEVENT
)];
98 memset(mask
, 0, sizeof(mask
));
100 XISetMask(mask
, XI_TouchBegin
);
101 XISetMask(mask
, XI_TouchUpdate
);
102 XISetMask(mask
, XI_TouchEnd
);
103 XISetMask(mask
, XI_ButtonPress
);
104 XISetMask(mask
, XI_ButtonRelease
);
105 XISetMask(mask
, XI_Motion
);
108 evmask
.deviceid
= XIAllDevices
;
109 evmask
.mask_len
= sizeof(mask
);
111 XISelectEvents(xdisplay_
, xwindow_
, &evmask
, 1);
115 protocols
[0] = atom_cache_
.GetAtom("WM_DELETE_WINDOW");
116 protocols
[1] = atom_cache_
.GetAtom("_NET_WM_PING");
117 XSetWMProtocols(xdisplay_
, xwindow_
, protocols
, 2);
119 // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with
120 // the desktop environment.
122 xdisplay_
, xwindow_
, NULL
, NULL
, NULL
, 0, NULL
, NULL
, NULL
);
124 // Likewise, the X server needs to know this window's pid so it knows which
125 // program to kill if the window hangs.
126 // XChangeProperty() expects "pid" to be long.
127 COMPILE_ASSERT(sizeof(long) >= sizeof(pid_t
), pid_t_bigger_than_long
);
129 XChangeProperty(xdisplay_
,
131 atom_cache_
.GetAtom("_NET_WM_PID"),
135 reinterpret_cast<unsigned char*>(&pid
),
137 // Before we map the window, set size hints. Otherwise, some window managers
138 // will ignore toplevel XMoveWindow commands.
139 XSizeHints size_hints
;
140 size_hints
.flags
= PPosition
| PWinGravity
;
141 size_hints
.x
= requested_bounds_
.x();
142 size_hints
.y
= requested_bounds_
.y();
143 // Set StaticGravity so that the window position is not affected by the
144 // frame width when running with window manager.
145 size_hints
.win_gravity
= StaticGravity
;
146 XSetWMNormalHints(xdisplay_
, xwindow_
, &size_hints
);
148 delegate_
->OnAcceleratedWidgetAvailable(xwindow_
);
150 XMapWindow(xdisplay_
, xwindow_
);
152 // We now block until our window is mapped. Some X11 APIs will crash and
153 // burn if passed |xwindow_| before the window is mapped, and XMapWindow is
155 if (X11EventSource::GetInstance())
156 X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_
);
157 window_mapped_
= true;
160 void X11Window::Hide() {
163 XWithdrawWindow(xdisplay_
, xwindow_
, 0);
164 window_mapped_
= false;
167 void X11Window::Close() {
171 void X11Window::SetBounds(const gfx::Rect
& bounds
) {
172 requested_bounds_
= bounds
;
175 XWindowChanges changes
= {0};
176 unsigned value_mask
= CWX
| CWY
| CWWidth
| CWHeight
;
177 changes
.x
= bounds
.x();
178 changes
.y
= bounds
.y();
179 changes
.width
= bounds
.width();
180 changes
.height
= bounds
.height();
181 XConfigureWindow(xdisplay_
, xwindow_
, value_mask
, &changes
);
184 gfx::Rect
X11Window::GetBounds() {
185 return confirmed_bounds_
;
188 void X11Window::SetCapture() {}
190 void X11Window::ReleaseCapture() {}
192 void X11Window::ToggleFullscreen() {}
194 void X11Window::Maximize() {}
196 void X11Window::Minimize() {}
198 void X11Window::Restore() {}
200 bool X11Window::CanDispatchEvent(const PlatformEvent
& event
) {
201 return FindXEventTarget(event
) == xwindow_
;
204 uint32_t X11Window::DispatchEvent(const PlatformEvent
& event
) {
208 // EnterNotify creates ET_MOUSE_MOVED. Mark as synthesized as this is
209 // not real mouse move event.
210 MouseEvent
mouse_event(xev
);
211 CHECK_EQ(ET_MOUSE_MOVED
, mouse_event
.type());
212 mouse_event
.set_flags(mouse_event
.flags() | EF_IS_SYNTHESIZED
);
213 delegate_
->DispatchEvent(&mouse_event
);
217 MouseEvent
mouse_event(xev
);
218 delegate_
->DispatchEvent(&mouse_event
);
223 gfx::Rect
damage_rect(xev
->xexpose
.x
,
226 xev
->xexpose
.height
);
227 delegate_
->OnDamageRect(damage_rect
);
233 KeyEvent
key_event(xev
, false);
234 delegate_
->DispatchEvent(&key_event
);
239 case ButtonRelease
: {
240 switch (EventTypeFromNative(xev
)) {
241 case ET_MOUSEWHEEL
: {
242 MouseWheelEvent
mouseev(xev
);
243 delegate_
->DispatchEvent(&mouseev
);
246 case ET_MOUSE_PRESSED
:
247 case ET_MOUSE_RELEASED
: {
248 MouseEvent
mouseev(xev
);
249 delegate_
->DispatchEvent(&mouseev
);
253 // No event is created for X11-release events for mouse-wheel
263 if (xev
->xfocus
.mode
!= NotifyGrab
)
264 delegate_
->OnLostCapture();
267 case ConfigureNotify
: {
268 DCHECK_EQ(xwindow_
, xev
->xconfigure
.event
);
269 DCHECK_EQ(xwindow_
, xev
->xconfigure
.window
);
270 gfx::Rect
bounds(xev
->xconfigure
.x
,
272 xev
->xconfigure
.width
,
273 xev
->xconfigure
.height
);
274 if (confirmed_bounds_
!= bounds
) {
275 confirmed_bounds_
= bounds
;
276 delegate_
->OnBoundsChanged(confirmed_bounds_
);
281 case ClientMessage
: {
282 Atom message
= static_cast<Atom
>(xev
->xclient
.data
.l
[0]);
283 if (message
== atom_cache_
.GetAtom("WM_DELETE_WINDOW")) {
284 delegate_
->OnCloseRequest();
285 } else if (message
== atom_cache_
.GetAtom("_NET_WM_PING")) {
286 XEvent reply_event
= *xev
;
287 reply_event
.xclient
.window
= xroot_window_
;
289 XSendEvent(xdisplay_
,
290 reply_event
.xclient
.window
,
292 SubstructureRedirectMask
| SubstructureNotifyMask
,
299 return POST_DISPATCH_STOP_PROPAGATION
;