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/aura/window_tree_host_x11.h"
8 #include <X11/cursorfont.h>
9 #include <X11/extensions/XInput2.h>
10 #include <X11/extensions/Xrandr.h>
11 #include <X11/Xatom.h>
12 #include <X11/Xcursor/Xcursor.h>
19 #include "base/basictypes.h"
20 #include "base/command_line.h"
21 #include "base/debug/trace_event.h"
22 #include "base/stl_util.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/sys_info.h"
27 #include "ui/aura/client/cursor_client.h"
28 #include "ui/aura/env.h"
29 #include "ui/aura/window.h"
30 #include "ui/aura/window_event_dispatcher.h"
31 #include "ui/base/cursor/cursor.h"
32 #include "ui/base/ui_base_switches.h"
33 #include "ui/base/view_prop.h"
34 #include "ui/base/x/x11_util.h"
35 #include "ui/compositor/compositor.h"
36 #include "ui/compositor/dip_util.h"
37 #include "ui/compositor/layer.h"
38 #include "ui/events/event.h"
39 #include "ui/events/event_switches.h"
40 #include "ui/events/event_utils.h"
41 #include "ui/events/keycodes/keyboard_codes.h"
42 #include "ui/events/platform/x11/x11_event_source.h"
43 #include "ui/events/x/device_data_manager.h"
44 #include "ui/events/x/device_list_cache_x.h"
45 #include "ui/events/x/touch_factory_x11.h"
46 #include "ui/gfx/screen.h"
55 const char* kAtomsToCache[] = {
62 ::Window FindEventTarget(const base::NativeEvent& xev) {
63 ::Window target = xev->xany.window;
64 if (xev->type == GenericEvent)
65 target = static_cast<XIDeviceEvent*>(xev->xcookie.data)->event;
69 void SelectXInput2EventsForRootWindow(XDisplay* display, ::Window root_window) {
70 CHECK(ui::IsXInput2Available());
71 unsigned char mask[XIMaskLen(XI_LASTEVENT)] = {};
72 memset(mask, 0, sizeof(mask));
74 XISetMask(mask, XI_HierarchyChanged);
75 XISetMask(mask, XI_KeyPress);
76 XISetMask(mask, XI_KeyRelease);
79 evmask.deviceid = XIAllDevices;
80 evmask.mask_len = sizeof(mask);
82 XISelectEvents(display, root_window, &evmask, 1);
84 #if defined(OS_CHROMEOS)
85 if (base::SysInfo::IsRunningOnChromeOS()) {
86 // It is necessary to listen for touch events on the root window for proper
87 // touch event calibration on Chrome OS, but this is not currently necessary
88 // on the desktop. This seems to fail in some cases (e.g. when logging
89 // in incognito). So select for non-touch events first, and then select for
90 // touch-events (but keep the other events in the mask, i.e. do not memset
92 // TODO(sad): Figure out why this happens. http://crbug.com/153976
93 XISetMask(mask, XI_TouchBegin);
94 XISetMask(mask, XI_TouchUpdate);
95 XISetMask(mask, XI_TouchEnd);
96 XISelectEvents(display, root_window, &evmask, 1);
101 bool default_override_redirect = false;
105 ////////////////////////////////////////////////////////////////////////////////
108 WindowTreeHostX11::WindowTreeHostX11(const gfx::Rect& bounds)
109 : xdisplay_(gfx::GetXDisplay()),
111 x_root_window_(DefaultRootWindow(xdisplay_)),
112 current_cursor_(ui::kCursorNull),
113 window_mapped_(false),
115 atom_cache_(xdisplay_, kAtomsToCache) {
116 XSetWindowAttributes swa;
117 memset(&swa, 0, sizeof(swa));
118 swa.background_pixmap = None;
119 swa.override_redirect = default_override_redirect;
120 xwindow_ = XCreateWindow(
121 xdisplay_, x_root_window_,
122 bounds.x(), bounds.y(), bounds.width(), bounds.height(),
124 CopyFromParent, // depth
126 CopyFromParent, // visual
127 CWBackPixmap | CWOverrideRedirect,
129 if (ui::PlatformEventSource::GetInstance())
130 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
132 long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
133 KeyPressMask | KeyReleaseMask |
134 EnterWindowMask | LeaveWindowMask |
135 ExposureMask | VisibilityChangeMask |
136 StructureNotifyMask | PropertyChangeMask |
138 XSelectInput(xdisplay_, xwindow_, event_mask);
141 if (ui::IsXInput2Available()) {
142 ui::TouchFactory::GetInstance()->SetupXI2ForXWindow(xwindow_);
143 SelectXInput2EventsForRootWindow(xdisplay_, x_root_window_);
146 // TODO(erg): We currently only request window deletion events. We also
147 // should listen for activation events and anything else that GTK+ listens
148 // for, and do something useful.
150 protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW");
151 protocols[1] = atom_cache_.GetAtom("_NET_WM_PING");
152 XSetWMProtocols(xdisplay_, xwindow_, protocols, 2);
154 // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with
155 // the desktop environment.
156 XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL);
158 // Likewise, the X server needs to know this window's pid so it knows which
159 // program to kill if the window hangs.
160 // XChangeProperty() expects "pid" to be long.
161 COMPILE_ASSERT(sizeof(long) >= sizeof(pid_t), pid_t_bigger_than_long);
163 XChangeProperty(xdisplay_,
165 atom_cache_.GetAtom("_NET_WM_PID"),
169 reinterpret_cast<unsigned char*>(&pid), 1);
171 // Allow subclasses to create and cache additional atoms.
172 atom_cache_.allow_uncached_atoms();
174 XRRSelectInput(xdisplay_, x_root_window_,
175 RRScreenChangeNotifyMask | RROutputChangeNotifyMask);
176 CreateCompositor(GetAcceleratedWidget());
179 WindowTreeHostX11::~WindowTreeHostX11() {
180 if (ui::PlatformEventSource::GetInstance())
181 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
185 XDestroyWindow(xdisplay_, xwindow_);
188 bool WindowTreeHostX11::CanDispatchEvent(const ui::PlatformEvent& event) {
189 ::Window target = FindEventTarget(event);
190 return target == xwindow_ || target == x_root_window_;
193 uint32_t WindowTreeHostX11::DispatchEvent(const ui::PlatformEvent& event) {
195 if (FindEventTarget(xev) == x_root_window_) {
196 if (xev->type == GenericEvent)
197 DispatchXI2Event(xev);
198 return ui::POST_DISPATCH_NONE;
203 aura::Window* root_window = window();
204 client::CursorClient* cursor_client =
205 client::GetCursorClient(root_window);
207 const gfx::Display display = gfx::Screen::GetScreenFor(root_window)->
208 GetDisplayNearestWindow(root_window);
209 cursor_client->SetDisplay(display);
211 ui::MouseEvent mouse_event(xev);
212 // EnterNotify creates ET_MOUSE_MOVE. Mark as synthesized as this is not
213 // real mouse move event.
214 mouse_event.set_flags(mouse_event.flags() | ui::EF_IS_SYNTHESIZED);
215 TranslateAndDispatchLocatedEvent(&mouse_event);
219 ui::MouseEvent mouse_event(xev);
220 TranslateAndDispatchLocatedEvent(&mouse_event);
224 gfx::Rect damage_rect(xev->xexpose.x, xev->xexpose.y,
225 xev->xexpose.width, xev->xexpose.height);
226 compositor()->ScheduleRedrawRect(damage_rect);
230 ui::KeyEvent keydown_event(xev, false);
231 SendEventToProcessor(&keydown_event);
235 ui::KeyEvent keyup_event(xev, false);
236 SendEventToProcessor(&keyup_event);
240 case ButtonRelease: {
241 switch (ui::EventTypeFromNative(xev)) {
242 case ui::ET_MOUSEWHEEL: {
243 ui::MouseWheelEvent mouseev(xev);
244 TranslateAndDispatchLocatedEvent(&mouseev);
247 case ui::ET_MOUSE_PRESSED:
248 case ui::ET_MOUSE_RELEASED: {
249 ui::MouseEvent mouseev(xev);
250 TranslateAndDispatchLocatedEvent(&mouseev);
254 // No event is created for X11-release events for mouse-wheel buttons.
262 if (xev->xfocus.mode != NotifyGrab)
263 OnHostLostWindowCapture();
265 case ConfigureNotify: {
266 DCHECK_EQ(xwindow_, xev->xconfigure.event);
267 DCHECK_EQ(xwindow_, xev->xconfigure.window);
268 // It's possible that the X window may be resized by some other means
269 // than from within aura (e.g. the X window manager can change the
270 // size). Make sure the root window size is maintained properly.
271 gfx::Rect bounds(xev->xconfigure.x, xev->xconfigure.y,
272 xev->xconfigure.width, xev->xconfigure.height);
273 bool size_changed = bounds_.size() != bounds.size();
274 bool origin_changed = bounds_.origin() != bounds.origin();
278 OnHostResized(bounds.size());
280 OnHostMoved(bounds_.origin());
284 DispatchXI2Event(xev);
286 case ClientMessage: {
287 Atom message_type = static_cast<Atom>(xev->xclient.data.l[0]);
288 if (message_type == atom_cache_.GetAtom("WM_DELETE_WINDOW")) {
289 // We have received a close message from the window manager.
290 OnHostCloseRequested();
291 } else if (message_type == atom_cache_.GetAtom("_NET_WM_PING")) {
292 XEvent reply_event = *xev;
293 reply_event.xclient.window = x_root_window_;
295 XSendEvent(xdisplay_,
296 reply_event.xclient.window,
298 SubstructureRedirectMask | SubstructureNotifyMask,
304 case MappingNotify: {
305 switch (xev->xmapping.request) {
306 case MappingModifier:
307 case MappingKeyboard:
308 XRefreshKeyboardMapping(&xev->xmapping);
311 ui::DeviceDataManager::GetInstance()->UpdateButtonMap();
314 NOTIMPLEMENTED() << " Unknown request: " << xev->xmapping.request;
320 // Discard all but the most recent motion event that targets the same
321 // window with unchanged state.
323 while (XPending(xev->xany.display)) {
325 XPeekEvent(xev->xany.display, &next_event);
326 if (next_event.type == MotionNotify &&
327 next_event.xmotion.window == xev->xmotion.window &&
328 next_event.xmotion.subwindow == xev->xmotion.subwindow &&
329 next_event.xmotion.state == xev->xmotion.state) {
330 XNextEvent(xev->xany.display, &last_event);
337 ui::MouseEvent mouseev(xev);
338 TranslateAndDispatchLocatedEvent(&mouseev);
342 return ui::POST_DISPATCH_STOP_PROPAGATION;
345 ui::EventSource* WindowTreeHostX11::GetEventSource() {
349 gfx::AcceleratedWidget WindowTreeHostX11::GetAcceleratedWidget() {
353 void WindowTreeHostX11::Show() {
354 if (!window_mapped_) {
355 // Before we map the window, set size hints. Otherwise, some window managers
356 // will ignore toplevel XMoveWindow commands.
357 XSizeHints size_hints;
358 size_hints.flags = PPosition | PWinGravity;
359 size_hints.x = bounds_.x();
360 size_hints.y = bounds_.y();
361 // Set StaticGravity so that the window position is not affected by the
362 // frame width when running with window manager.
363 size_hints.win_gravity = StaticGravity;
364 XSetWMNormalHints(xdisplay_, xwindow_, &size_hints);
366 XMapWindow(xdisplay_, xwindow_);
368 // We now block until our window is mapped. Some X11 APIs will crash and
369 // burn if passed |xwindow_| before the window is mapped, and XMapWindow is
371 if (ui::X11EventSource::GetInstance())
372 ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_);
373 window_mapped_ = true;
377 void WindowTreeHostX11::Hide() {
378 if (window_mapped_) {
379 XWithdrawWindow(xdisplay_, xwindow_, 0);
380 window_mapped_ = false;
384 gfx::Rect WindowTreeHostX11::GetBounds() const {
388 void WindowTreeHostX11::SetBounds(const gfx::Rect& bounds) {
389 // Even if the host window's size doesn't change, aura's root window
390 // size, which is in DIP, changes when the scale changes.
391 float current_scale = compositor()->device_scale_factor();
392 float new_scale = gfx::Screen::GetScreenFor(window())->
393 GetDisplayNearestWindow(window()).device_scale_factor();
394 bool origin_changed = bounds_.origin() != bounds.origin();
395 bool size_changed = bounds_.size() != bounds.size();
396 XWindowChanges changes = {0};
397 unsigned value_mask = 0;
400 changes.width = bounds.width();
401 changes.height = bounds.height();
402 value_mask = CWHeight | CWWidth;
405 if (origin_changed) {
406 changes.x = bounds.x();
407 changes.y = bounds.y();
408 value_mask |= CWX | CWY;
411 XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes);
413 // Assume that the resize will go through as requested, which should be the
414 // case if we're running without a window manager. If there's a window
415 // manager, it can modify or ignore the request, but (per ICCCM) we'll get a
416 // (possibly synthetic) ConfigureNotify about the actual size and correct
420 OnHostMoved(bounds.origin());
421 if (size_changed || current_scale != new_scale) {
422 OnHostResized(bounds.size());
424 window()->SchedulePaintInRect(window()->bounds());
428 gfx::Point WindowTreeHostX11::GetLocationOnNativeScreen() const {
429 return bounds_.origin();
432 void WindowTreeHostX11::SetCapture() {
433 // TODO(oshima): Grab x input.
436 void WindowTreeHostX11::ReleaseCapture() {
437 // TODO(oshima): Release x input.
440 void WindowTreeHostX11::PostNativeEvent(
441 const base::NativeEvent& native_event) {
444 XEvent xevent = *native_event;
445 xevent.xany.display = xdisplay_;
446 xevent.xany.window = xwindow_;
448 switch (xevent.type) {
455 case ButtonRelease: {
456 // The fields used below are in the same place for all of events
457 // above. Using xmotion from XEvent's unions to avoid repeating
459 xevent.xmotion.root = x_root_window_;
460 xevent.xmotion.time = CurrentTime;
462 gfx::Point point(xevent.xmotion.x, xevent.xmotion.y);
463 ConvertPointToNativeScreen(&point);
464 xevent.xmotion.x_root = point.x();
465 xevent.xmotion.y_root = point.y();
470 XSendEvent(xdisplay_, xwindow_, False, 0, &xevent);
474 void WindowTreeHostX11::OnDeviceScaleFactorChanged(
475 float device_scale_factor) {
478 void WindowTreeHostX11::SetCursorNative(gfx::NativeCursor cursor) {
479 if (cursor == current_cursor_)
481 current_cursor_ = cursor;
482 SetCursorInternal(cursor);
485 void WindowTreeHostX11::MoveCursorToNative(const gfx::Point& location) {
486 XWarpPointer(xdisplay_, None, x_root_window_, 0, 0, 0, 0,
487 bounds_.x() + location.x(),
488 bounds_.y() + location.y());
491 void WindowTreeHostX11::OnCursorVisibilityChangedNative(bool show) {
494 ui::EventProcessor* WindowTreeHostX11::GetEventProcessor() {
498 void WindowTreeHostX11::DispatchXI2Event(const base::NativeEvent& event) {
499 ui::TouchFactory* factory = ui::TouchFactory::GetInstance();
501 if (!factory->ShouldProcessXI2Event(xev))
504 TRACE_EVENT1("input", "WindowTreeHostX11::DispatchXI2Event",
506 (ui::EventTimeForNow() - ui::EventTimeFromNative(event)).
509 ui::EventType type = ui::EventTypeFromNative(xev);
511 int num_coalesced = 0;
514 case ui::ET_TOUCH_MOVED:
515 case ui::ET_TOUCH_PRESSED:
516 case ui::ET_TOUCH_CANCELLED:
517 case ui::ET_TOUCH_RELEASED: {
518 #if defined(OS_CHROMEOS)
519 // Bail out early before generating a ui::TouchEvent if this event
520 // is not within the range of this RootWindow. Converting an xevent
521 // to ui::TouchEvent might change the state of the global touch tracking
522 // state, e.g. touch release event can remove the touch id from the
523 // record, and doing this multiple time when there are multiple
524 // RootWindow will cause problem. So only generate the ui::TouchEvent
525 // when we are sure it belongs to this RootWindow.
526 if (base::SysInfo::IsRunningOnChromeOS() &&
527 !bounds().Contains(ui::EventLocationFromNative(xev)))
530 ui::TouchEvent touchev(xev);
531 TranslateAndDispatchLocatedEvent(&touchev);
534 case ui::ET_MOUSE_MOVED:
535 case ui::ET_MOUSE_DRAGGED:
536 case ui::ET_MOUSE_PRESSED:
537 case ui::ET_MOUSE_RELEASED:
538 case ui::ET_MOUSE_ENTERED:
539 case ui::ET_MOUSE_EXITED: {
540 if (type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_DRAGGED) {
541 // If this is a motion event, we want to coalesce all pending motion
542 // events that are at the top of the queue.
543 num_coalesced = ui::CoalescePendingMotionEvents(xev, &last_event);
544 if (num_coalesced > 0)
547 ui::MouseEvent mouseev(xev);
548 TranslateAndDispatchLocatedEvent(&mouseev);
551 case ui::ET_MOUSEWHEEL: {
552 ui::MouseWheelEvent mouseev(xev);
553 TranslateAndDispatchLocatedEvent(&mouseev);
556 case ui::ET_SCROLL_FLING_START:
557 case ui::ET_SCROLL_FLING_CANCEL:
558 case ui::ET_SCROLL: {
559 ui::ScrollEvent scrollev(xev);
560 SendEventToProcessor(&scrollev);
563 case ui::ET_UMA_DATA:
571 // If we coalesced an event we need to free its cookie.
572 if (num_coalesced > 0)
573 XFreeEventData(xev->xgeneric.display, &last_event.xcookie);
576 void WindowTreeHostX11::SetCursorInternal(gfx::NativeCursor cursor) {
577 XDefineCursor(xdisplay_, xwindow_, cursor.platform());
580 void WindowTreeHostX11::OnConfigureNotify() {}
582 void WindowTreeHostX11::TranslateAndDispatchLocatedEvent(
583 ui::LocatedEvent* event) {
584 SendEventToProcessor(event);
588 WindowTreeHost* WindowTreeHost::Create(const gfx::Rect& bounds) {
589 return new WindowTreeHostX11(bounds);
593 gfx::Size WindowTreeHost::GetNativeScreenSize() {
594 ::XDisplay* xdisplay = gfx::GetXDisplay();
595 return gfx::Size(DisplayWidth(xdisplay, 0), DisplayHeight(xdisplay, 0));
600 void SetUseOverrideRedirectWindowByDefault(bool override_redirect) {
601 default_override_redirect = override_redirect;