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