OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/platform_window/x11/x11_window.h" | 5 #include "ui/platform_window/x11/x11_window.h" |
6 | 6 |
7 #include <X11/extensions/XInput2.h> | 7 #include <X11/extensions/XInput2.h> |
8 #include <X11/Xatom.h> | |
9 #include <X11/Xlib.h> | 8 #include <X11/Xlib.h> |
10 #include <X11/Xutil.h> | 9 #include <X11/Xutil.h> |
11 | 10 |
12 #include "base/strings/utf_string_conversions.h" | |
13 #include "ui/events/devices/x11/touch_factory_x11.h" | 11 #include "ui/events/devices/x11/touch_factory_x11.h" |
14 #include "ui/events/event.h" | 12 #include "ui/events/event.h" |
15 #include "ui/events/event_utils.h" | 13 #include "ui/events/event_utils.h" |
16 #include "ui/events/platform/platform_event_dispatcher.h" | |
17 #include "ui/events/platform/platform_event_source.h" | 14 #include "ui/events/platform/platform_event_source.h" |
18 #include "ui/events/platform/x11/x11_event_source.h" | 15 #include "ui/events/platform/x11/x11_event_source.h" |
19 #include "ui/gfx/geometry/rect.h" | |
20 #include "ui/gfx/x/x11_atom_cache.h" | |
21 #include "ui/gfx/x/x11_types.h" | |
22 #include "ui/platform_window/platform_window_delegate.h" | 16 #include "ui/platform_window/platform_window_delegate.h" |
23 | 17 |
24 namespace ui { | 18 namespace ui { |
25 | 19 |
26 namespace { | |
27 | |
28 const char* kAtomsToCache[] = { | |
29 "UTF8_STRING", | |
30 "WM_DELETE_WINDOW", | |
31 "_NET_WM_NAME", | |
32 "_NET_WM_PID", | |
33 "_NET_WM_PING", | |
34 NULL | |
35 }; | |
36 | |
37 XID FindXEventTarget(XEvent* xevent) { | |
38 XID target = xevent->xany.window; | |
39 if (xevent->type == GenericEvent) | |
40 target = static_cast<XIDeviceEvent*>(xevent->xcookie.data)->event; | |
41 return target; | |
42 } | |
43 | |
44 bool g_override_redirect = false; | |
45 | |
46 } // namespace | |
47 | |
48 X11Window::X11Window(PlatformWindowDelegate* delegate) | 20 X11Window::X11Window(PlatformWindowDelegate* delegate) |
49 : delegate_(delegate), | 21 : X11WindowBase(delegate) { |
50 xdisplay_(gfx::GetXDisplay()), | 22 DCHECK(PlatformEventSource::GetInstance()); |
51 xwindow_(None), | 23 PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); |
52 xroot_window_(DefaultRootWindow(xdisplay_)), | |
53 atom_cache_(xdisplay_, kAtomsToCache), | |
54 window_mapped_(false) { | |
55 CHECK(delegate_); | |
56 TouchFactory::SetTouchDeviceListFromCommandLine(); | |
57 } | 24 } |
58 | 25 |
59 X11Window::~X11Window() { | 26 X11Window::~X11Window() { |
60 Destroy(); | 27 PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); |
61 } | 28 } |
62 | 29 |
63 void X11Window::Destroy() { | 30 void X11Window::SetCursor(PlatformCursor cursor) { |
64 if (xwindow_ == None) | 31 XDefineCursor(xdisplay(), xwindow(), cursor); |
65 return; | |
66 | |
67 // Stop processing events. | |
68 PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); | |
69 XID xwindow = xwindow_; | |
70 XDisplay* xdisplay = xdisplay_; | |
71 xwindow_ = None; | |
72 delegate_->OnClosed(); | |
73 // |this| might be deleted because of the above call. | |
74 | |
75 XDestroyWindow(xdisplay, xwindow); | |
76 } | 32 } |
77 | 33 |
78 void X11Window::ProcessXInput2Event(XEvent* xev) { | 34 void X11Window::ProcessXInput2Event(XEvent* xev) { |
79 if (!TouchFactory::GetInstance()->ShouldProcessXI2Event(xev)) | 35 if (!TouchFactory::GetInstance()->ShouldProcessXI2Event(xev)) |
80 return; | 36 return; |
81 EventType event_type = EventTypeFromNative(xev); | 37 EventType event_type = EventTypeFromNative(xev); |
82 switch (event_type) { | 38 switch (event_type) { |
83 case ET_KEY_PRESSED: | 39 case ET_KEY_PRESSED: |
84 case ET_KEY_RELEASED: { | 40 case ET_KEY_RELEASED: { |
85 KeyEvent key_event(xev); | 41 KeyEvent key_event(xev); |
86 delegate_->DispatchEvent(&key_event); | 42 delegate()->DispatchEvent(&key_event); |
87 break; | 43 break; |
88 } | 44 } |
89 case ET_MOUSE_PRESSED: | 45 case ET_MOUSE_PRESSED: |
90 case ET_MOUSE_MOVED: | 46 case ET_MOUSE_MOVED: |
91 case ET_MOUSE_DRAGGED: | 47 case ET_MOUSE_DRAGGED: |
92 case ET_MOUSE_RELEASED: { | 48 case ET_MOUSE_RELEASED: { |
93 MouseEvent mouse_event(xev); | 49 MouseEvent mouse_event(xev); |
94 delegate_->DispatchEvent(&mouse_event); | 50 delegate()->DispatchEvent(&mouse_event); |
95 break; | 51 break; |
96 } | 52 } |
97 case ET_MOUSEWHEEL: { | 53 case ET_MOUSEWHEEL: { |
98 MouseWheelEvent wheel_event(xev); | 54 MouseWheelEvent wheel_event(xev); |
99 delegate_->DispatchEvent(&wheel_event); | 55 delegate()->DispatchEvent(&wheel_event); |
100 break; | 56 break; |
101 } | 57 } |
102 case ET_SCROLL_FLING_START: | 58 case ET_SCROLL_FLING_START: |
103 case ET_SCROLL_FLING_CANCEL: | 59 case ET_SCROLL_FLING_CANCEL: |
104 case ET_SCROLL: { | 60 case ET_SCROLL: { |
105 ScrollEvent scroll_event(xev); | 61 ScrollEvent scroll_event(xev); |
106 delegate_->DispatchEvent(&scroll_event); | 62 delegate()->DispatchEvent(&scroll_event); |
107 break; | 63 break; |
108 } | 64 } |
109 case ET_TOUCH_MOVED: | 65 case ET_TOUCH_MOVED: |
110 case ET_TOUCH_PRESSED: | 66 case ET_TOUCH_PRESSED: |
111 case ET_TOUCH_CANCELLED: | 67 case ET_TOUCH_CANCELLED: |
112 case ET_TOUCH_RELEASED: { | 68 case ET_TOUCH_RELEASED: { |
113 TouchEvent touch_event(xev); | 69 TouchEvent touch_event(xev); |
114 delegate_->DispatchEvent(&touch_event); | 70 delegate()->DispatchEvent(&touch_event); |
115 break; | 71 break; |
116 } | 72 } |
117 default: | 73 default: |
118 break; | 74 break; |
119 } | 75 } |
120 } | 76 } |
121 | 77 |
122 void X11Window::Show() { | 78 bool X11Window::CanDispatchEvent(const PlatformEvent& xev) { |
123 if (window_mapped_) | 79 return IsEventForXWindow(*xev); |
124 return; | |
125 | |
126 CHECK(PlatformEventSource::GetInstance()); | |
127 PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); | |
128 | |
129 XSetWindowAttributes swa; | |
130 memset(&swa, 0, sizeof(swa)); | |
131 swa.background_pixmap = None; | |
132 swa.bit_gravity = NorthWestGravity; | |
133 swa.override_redirect = g_override_redirect; | |
134 xwindow_ = XCreateWindow(xdisplay_, | |
135 xroot_window_, | |
136 requested_bounds_.x(), | |
137 requested_bounds_.y(), | |
138 requested_bounds_.width(), | |
139 requested_bounds_.height(), | |
140 0, // border width | |
141 CopyFromParent, // depth | |
142 InputOutput, | |
143 CopyFromParent, // visual | |
144 CWBackPixmap | CWBitGravity | CWOverrideRedirect, | |
145 &swa); | |
146 | |
147 long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask | | |
148 KeyPressMask | KeyReleaseMask | EnterWindowMask | | |
149 LeaveWindowMask | ExposureMask | VisibilityChangeMask | | |
150 StructureNotifyMask | PropertyChangeMask | | |
151 PointerMotionMask; | |
152 XSelectInput(xdisplay_, xwindow_, event_mask); | |
153 | |
154 unsigned char mask[XIMaskLen(XI_LASTEVENT)]; | |
155 memset(mask, 0, sizeof(mask)); | |
156 | |
157 XISetMask(mask, XI_TouchBegin); | |
158 XISetMask(mask, XI_TouchUpdate); | |
159 XISetMask(mask, XI_TouchEnd); | |
160 XISetMask(mask, XI_ButtonPress); | |
161 XISetMask(mask, XI_ButtonRelease); | |
162 XISetMask(mask, XI_Motion); | |
163 XISetMask(mask, XI_KeyPress); | |
164 XISetMask(mask, XI_KeyRelease); | |
165 | |
166 XIEventMask evmask; | |
167 evmask.deviceid = XIAllDevices; | |
168 evmask.mask_len = sizeof(mask); | |
169 evmask.mask = mask; | |
170 XISelectEvents(xdisplay_, xwindow_, &evmask, 1); | |
171 XFlush(xdisplay_); | |
172 | |
173 ::Atom protocols[2]; | |
174 protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); | |
175 protocols[1] = atom_cache_.GetAtom("_NET_WM_PING"); | |
176 XSetWMProtocols(xdisplay_, xwindow_, protocols, 2); | |
177 | |
178 // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with | |
179 // the desktop environment. | |
180 XSetWMProperties( | |
181 xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL); | |
182 | |
183 // Likewise, the X server needs to know this window's pid so it knows which | |
184 // program to kill if the window hangs. | |
185 // XChangeProperty() expects "pid" to be long. | |
186 static_assert(sizeof(long) >= sizeof(pid_t), | |
187 "pid_t should not be larger than long"); | |
188 long pid = getpid(); | |
189 XChangeProperty(xdisplay_, | |
190 xwindow_, | |
191 atom_cache_.GetAtom("_NET_WM_PID"), | |
192 XA_CARDINAL, | |
193 32, | |
194 PropModeReplace, | |
195 reinterpret_cast<unsigned char*>(&pid), | |
196 1); | |
197 // Before we map the window, set size hints. Otherwise, some window managers | |
198 // will ignore toplevel XMoveWindow commands. | |
199 XSizeHints size_hints; | |
200 size_hints.flags = PPosition | PWinGravity; | |
201 size_hints.x = requested_bounds_.x(); | |
202 size_hints.y = requested_bounds_.y(); | |
203 // Set StaticGravity so that the window position is not affected by the | |
204 // frame width when running with window manager. | |
205 size_hints.win_gravity = StaticGravity; | |
206 XSetWMNormalHints(xdisplay_, xwindow_, &size_hints); | |
207 | |
208 XMapWindow(xdisplay_, xwindow_); | |
209 | |
210 // We now block until our window is mapped. Some X11 APIs will crash and | |
211 // burn if passed |xwindow_| before the window is mapped, and XMapWindow is | |
212 // asynchronous. | |
213 if (X11EventSource::GetInstance()) | |
214 X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_); | |
215 window_mapped_ = true; | |
216 | |
217 // TODO(sky): provide real scale factor. | |
218 delegate_->OnAcceleratedWidgetAvailable(xwindow_, 1.f); | |
219 } | |
220 | |
221 void X11Window::Hide() { | |
222 if (!window_mapped_) | |
223 return; | |
224 XWithdrawWindow(xdisplay_, xwindow_, 0); | |
225 window_mapped_ = false; | |
226 } | |
227 | |
228 void X11Window::Close() { | |
229 Destroy(); | |
230 } | |
231 | |
232 void X11Window::SetBounds(const gfx::Rect& bounds) { | |
233 requested_bounds_ = bounds; | |
234 if (!window_mapped_) | |
235 return; | |
236 XWindowChanges changes = {0}; | |
237 unsigned value_mask = CWX | CWY | CWWidth | CWHeight; | |
238 changes.x = bounds.x(); | |
239 changes.y = bounds.y(); | |
240 changes.width = bounds.width(); | |
241 changes.height = bounds.height(); | |
242 XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes); | |
243 } | |
244 | |
245 gfx::Rect X11Window::GetBounds() { | |
246 return confirmed_bounds_; | |
247 } | |
248 | |
249 void X11Window::SetTitle(const base::string16& title) { | |
250 if (window_title_ == title) | |
251 return; | |
252 window_title_ = title; | |
253 std::string utf8str = base::UTF16ToUTF8(title); | |
254 XChangeProperty(xdisplay_, | |
255 xwindow_, | |
256 atom_cache_.GetAtom("_NET_WM_NAME"), | |
257 atom_cache_.GetAtom("UTF8_STRING"), | |
258 8, | |
259 PropModeReplace, | |
260 reinterpret_cast<const unsigned char*>(utf8str.c_str()), | |
261 utf8str.size()); | |
262 XTextProperty xtp; | |
263 char *c_utf8_str = const_cast<char *>(utf8str.c_str()); | |
264 if (Xutf8TextListToTextProperty(xdisplay_, &c_utf8_str, 1, | |
265 XUTF8StringStyle, &xtp) == Success) { | |
266 XSetWMName(xdisplay_, xwindow_, &xtp); | |
267 XFree(xtp.value); | |
268 } | |
269 } | |
270 | |
271 void X11Window::SetCapture() {} | |
272 | |
273 void X11Window::ReleaseCapture() {} | |
274 | |
275 void X11Window::ToggleFullscreen() {} | |
276 | |
277 void X11Window::Maximize() {} | |
278 | |
279 void X11Window::Minimize() {} | |
280 | |
281 void X11Window::Restore() {} | |
282 | |
283 void X11Window::SetCursor(PlatformCursor cursor) { | |
284 XDefineCursor(xdisplay_, xwindow_, cursor); | |
285 } | |
286 | |
287 void X11Window::MoveCursorTo(const gfx::Point& location) {} | |
288 | |
289 void X11Window::ConfineCursorToBounds(const gfx::Rect& bounds) { | |
290 } | |
291 | |
292 PlatformImeController* X11Window::GetPlatformImeController() { | |
293 return nullptr; | |
294 } | |
295 | |
296 bool X11Window::CanDispatchEvent(const PlatformEvent& event) { | |
297 return FindXEventTarget(event) == xwindow_; | |
298 } | 80 } |
299 | 81 |
300 uint32_t X11Window::DispatchEvent(const PlatformEvent& event) { | 82 uint32_t X11Window::DispatchEvent(const PlatformEvent& event) { |
301 XEvent* xev = event; | 83 XEvent* xev = event; |
302 switch (xev->type) { | 84 switch (xev->type) { |
303 case EnterNotify: { | 85 case EnterNotify: { |
304 // EnterNotify creates ET_MOUSE_MOVED. Mark as synthesized as this is | 86 // EnterNotify creates ET_MOUSE_MOVED. Mark as synthesized as this is |
305 // not real mouse move event. | 87 // not real mouse move event. |
306 MouseEvent mouse_event(xev); | 88 MouseEvent mouse_event(xev); |
307 CHECK_EQ(ET_MOUSE_MOVED, mouse_event.type()); | 89 CHECK_EQ(ET_MOUSE_MOVED, mouse_event.type()); |
308 mouse_event.set_flags(mouse_event.flags() | EF_IS_SYNTHESIZED); | 90 mouse_event.set_flags(mouse_event.flags() | EF_IS_SYNTHESIZED); |
309 delegate_->DispatchEvent(&mouse_event); | 91 delegate()->DispatchEvent(&mouse_event); |
310 break; | 92 break; |
311 } | 93 } |
312 case LeaveNotify: { | 94 case LeaveNotify: { |
313 MouseEvent mouse_event(xev); | 95 MouseEvent mouse_event(xev); |
314 delegate_->DispatchEvent(&mouse_event); | 96 delegate()->DispatchEvent(&mouse_event); |
315 break; | |
316 } | |
317 | |
318 case Expose: { | |
319 gfx::Rect damage_rect(xev->xexpose.x, | |
320 xev->xexpose.y, | |
321 xev->xexpose.width, | |
322 xev->xexpose.height); | |
323 delegate_->OnDamageRect(damage_rect); | |
324 break; | 97 break; |
325 } | 98 } |
326 | 99 |
327 case KeyPress: | 100 case KeyPress: |
328 case KeyRelease: { | 101 case KeyRelease: { |
329 KeyEvent key_event(xev); | 102 KeyEvent key_event(xev); |
330 delegate_->DispatchEvent(&key_event); | 103 delegate()->DispatchEvent(&key_event); |
331 break; | 104 break; |
332 } | 105 } |
333 | 106 |
334 case ButtonPress: | 107 case ButtonPress: |
335 case ButtonRelease: { | 108 case ButtonRelease: { |
336 switch (EventTypeFromNative(xev)) { | 109 switch (EventTypeFromNative(xev)) { |
337 case ET_MOUSEWHEEL: { | 110 case ET_MOUSEWHEEL: { |
338 MouseWheelEvent mouseev(xev); | 111 MouseWheelEvent mouseev(xev); |
339 delegate_->DispatchEvent(&mouseev); | 112 delegate()->DispatchEvent(&mouseev); |
340 break; | 113 break; |
341 } | 114 } |
342 case ET_MOUSE_PRESSED: | 115 case ET_MOUSE_PRESSED: |
343 case ET_MOUSE_RELEASED: { | 116 case ET_MOUSE_RELEASED: { |
344 MouseEvent mouseev(xev); | 117 MouseEvent mouseev(xev); |
345 delegate_->DispatchEvent(&mouseev); | 118 delegate()->DispatchEvent(&mouseev); |
346 break; | 119 break; |
347 } | 120 } |
348 case ET_UNKNOWN: | 121 case ET_UNKNOWN: |
349 // No event is created for X11-release events for mouse-wheel | 122 // No event is created for X11-release events for mouse-wheel |
350 // buttons. | 123 // buttons. |
351 break; | 124 break; |
352 default: | 125 default: |
353 NOTREACHED(); | 126 NOTREACHED(); |
354 } | 127 } |
355 break; | 128 break; |
356 } | 129 } |
357 | 130 |
| 131 case Expose: |
358 case FocusOut: | 132 case FocusOut: |
359 if (xev->xfocus.mode != NotifyGrab) | 133 case ConfigureNotify: |
360 delegate_->OnLostCapture(); | 134 case ClientMessage: { |
361 break; | 135 ProcessXWindowEvent(xev); |
362 | |
363 case ConfigureNotify: { | |
364 DCHECK_EQ(xwindow_, xev->xconfigure.event); | |
365 DCHECK_EQ(xwindow_, xev->xconfigure.window); | |
366 gfx::Rect bounds(xev->xconfigure.x, | |
367 xev->xconfigure.y, | |
368 xev->xconfigure.width, | |
369 xev->xconfigure.height); | |
370 if (confirmed_bounds_ != bounds) { | |
371 confirmed_bounds_ = bounds; | |
372 delegate_->OnBoundsChanged(confirmed_bounds_); | |
373 } | |
374 break; | 136 break; |
375 } | 137 } |
376 | 138 |
377 case ClientMessage: { | |
378 Atom message = static_cast<Atom>(xev->xclient.data.l[0]); | |
379 if (message == atom_cache_.GetAtom("WM_DELETE_WINDOW")) { | |
380 delegate_->OnCloseRequest(); | |
381 } else if (message == atom_cache_.GetAtom("_NET_WM_PING")) { | |
382 XEvent reply_event = *xev; | |
383 reply_event.xclient.window = xroot_window_; | |
384 | |
385 XSendEvent(xdisplay_, | |
386 reply_event.xclient.window, | |
387 False, | |
388 SubstructureRedirectMask | SubstructureNotifyMask, | |
389 &reply_event); | |
390 XFlush(xdisplay_); | |
391 } | |
392 break; | |
393 } | |
394 | |
395 case GenericEvent: { | 139 case GenericEvent: { |
396 ProcessXInput2Event(xev); | 140 ProcessXInput2Event(xev); |
397 break; | 141 break; |
398 } | 142 } |
399 } | 143 } |
400 return POST_DISPATCH_STOP_PROPAGATION; | 144 return POST_DISPATCH_STOP_PROPAGATION; |
401 } | 145 } |
402 | 146 |
403 namespace test { | |
404 | |
405 void SetUseOverrideRedirectWindowByDefault(bool override_redirect) { | |
406 g_override_redirect = override_redirect; | |
407 } | |
408 | |
409 } // namespace test | |
410 } // namespace ui | 147 } // namespace ui |
OLD | NEW |