| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "ui/aura/window_tree_host_x11.h" | |
| 6 | |
| 7 #include <strings.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> | |
| 13 #include <X11/Xlib.h> | |
| 14 | |
| 15 #include <algorithm> | |
| 16 #include <limits> | |
| 17 #include <string> | |
| 18 | |
| 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/platform_event_observer.h" | |
| 43 #include "ui/events/platform/x11/x11_event_source.h" | |
| 44 #include "ui/events/x/device_data_manager_x11.h" | |
| 45 #include "ui/events/x/device_list_cache_x.h" | |
| 46 #include "ui/events/x/touch_factory_x11.h" | |
| 47 #include "ui/gfx/screen.h" | |
| 48 | |
| 49 using std::max; | |
| 50 using std::min; | |
| 51 | |
| 52 namespace aura { | |
| 53 | |
| 54 namespace { | |
| 55 | |
| 56 const char* kAtomsToCache[] = { | |
| 57 "WM_DELETE_WINDOW", | |
| 58 "_NET_WM_PING", | |
| 59 "_NET_WM_PID", | |
| 60 NULL | |
| 61 }; | |
| 62 | |
| 63 ::Window FindEventTarget(const base::NativeEvent& xev) { | |
| 64 ::Window target = xev->xany.window; | |
| 65 if (xev->type == GenericEvent) | |
| 66 target = static_cast<XIDeviceEvent*>(xev->xcookie.data)->event; | |
| 67 return target; | |
| 68 } | |
| 69 | |
| 70 void SelectXInput2EventsForRootWindow(XDisplay* display, ::Window root_window) { | |
| 71 CHECK(ui::IsXInput2Available()); | |
| 72 unsigned char mask[XIMaskLen(XI_LASTEVENT)] = {}; | |
| 73 memset(mask, 0, sizeof(mask)); | |
| 74 | |
| 75 XISetMask(mask, XI_HierarchyChanged); | |
| 76 | |
| 77 XIEventMask evmask; | |
| 78 evmask.deviceid = XIAllDevices; | |
| 79 evmask.mask_len = sizeof(mask); | |
| 80 evmask.mask = mask; | |
| 81 XISelectEvents(display, root_window, &evmask, 1); | |
| 82 | |
| 83 #if defined(OS_CHROMEOS) | |
| 84 if (base::SysInfo::IsRunningOnChromeOS()) { | |
| 85 // It is necessary to listen for touch events on the root window for proper | |
| 86 // touch event calibration on Chrome OS, but this is not currently necessary | |
| 87 // on the desktop. This seems to fail in some cases (e.g. when logging | |
| 88 // in incognito). So select for non-touch events first, and then select for | |
| 89 // touch-events (but keep the other events in the mask, i.e. do not memset | |
| 90 // |mask| back to 0). | |
| 91 // TODO(sad): Figure out why this happens. http://crbug.com/153976 | |
| 92 XISetMask(mask, XI_TouchBegin); | |
| 93 XISetMask(mask, XI_TouchUpdate); | |
| 94 XISetMask(mask, XI_TouchEnd); | |
| 95 XISelectEvents(display, root_window, &evmask, 1); | |
| 96 } | |
| 97 #endif | |
| 98 } | |
| 99 | |
| 100 bool default_override_redirect = false; | |
| 101 | |
| 102 } // namespace | |
| 103 | |
| 104 namespace internal { | |
| 105 | |
| 106 // TODO(miletus) : Move this into DeviceDataManager. | |
| 107 // Accomplishes 2 tasks concerning touch event calibration: | |
| 108 // 1. Being a message-pump observer, | |
| 109 // routes all the touch events to the X root window, | |
| 110 // where they can be calibrated later. | |
| 111 // 2. Has the Calibrate method that does the actual bezel calibration, | |
| 112 // when invoked from X root window's event dispatcher. | |
| 113 class TouchEventCalibrate : public ui::PlatformEventObserver { | |
| 114 public: | |
| 115 TouchEventCalibrate() : left_(0), right_(0), top_(0), bottom_(0) { | |
| 116 if (ui::PlatformEventSource::GetInstance()) | |
| 117 ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this); | |
| 118 #if defined(USE_XI2_MT) | |
| 119 std::vector<std::string> parts; | |
| 120 if (Tokenize(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
| 121 switches::kTouchCalibration), | |
| 122 ",", | |
| 123 &parts) >= 4) { | |
| 124 if (!base::StringToInt(parts[0], &left_)) | |
| 125 DLOG(ERROR) << "Incorrect left border calibration value passed."; | |
| 126 if (!base::StringToInt(parts[1], &right_)) | |
| 127 DLOG(ERROR) << "Incorrect right border calibration value passed."; | |
| 128 if (!base::StringToInt(parts[2], &top_)) | |
| 129 DLOG(ERROR) << "Incorrect top border calibration value passed."; | |
| 130 if (!base::StringToInt(parts[3], &bottom_)) | |
| 131 DLOG(ERROR) << "Incorrect bottom border calibration value passed."; | |
| 132 } | |
| 133 #endif // defined(USE_XI2_MT) | |
| 134 } | |
| 135 | |
| 136 virtual ~TouchEventCalibrate() { | |
| 137 if (ui::PlatformEventSource::GetInstance()) | |
| 138 ui::PlatformEventSource::GetInstance()->RemovePlatformEventObserver(this); | |
| 139 } | |
| 140 | |
| 141 // Modify the location of the |event|, | |
| 142 // expanding it from |bounds| to (|bounds| + bezels). | |
| 143 // Required when touchscreen is bigger than screen (i.e. has bezels), | |
| 144 // because we receive events in touchscreen coordinates, | |
| 145 // which need to be expanded when converting to screen coordinates, | |
| 146 // so that location on bezels will be outside of screen area. | |
| 147 void Calibrate(ui::TouchEvent* event, const gfx::Rect& bounds) { | |
| 148 #if defined(USE_XI2_MT) | |
| 149 int x = event->x(); | |
| 150 int y = event->y(); | |
| 151 | |
| 152 if (!left_ && !right_ && !top_ && !bottom_) | |
| 153 return; | |
| 154 | |
| 155 const int resolution_x = bounds.width(); | |
| 156 const int resolution_y = bounds.height(); | |
| 157 // The "grace area" (10% in this case) is to make it easier for the user to | |
| 158 // navigate to the corner. | |
| 159 const double kGraceAreaFraction = 0.1; | |
| 160 if (left_ || right_) { | |
| 161 // Offset the x position to the real | |
| 162 x -= left_; | |
| 163 // Check if we are in the grace area of the left side. | |
| 164 // Note: We might not want to do this when the gesture is locked? | |
| 165 if (x < 0 && x > -left_ * kGraceAreaFraction) | |
| 166 x = 0; | |
| 167 // Check if we are in the grace area of the right side. | |
| 168 // Note: We might not want to do this when the gesture is locked? | |
| 169 if (x > resolution_x - left_ && | |
| 170 x < resolution_x - left_ + right_ * kGraceAreaFraction) | |
| 171 x = resolution_x - left_; | |
| 172 // Scale the screen area back to the full resolution of the screen. | |
| 173 x = (x * resolution_x) / (resolution_x - (right_ + left_)); | |
| 174 } | |
| 175 if (top_ || bottom_) { | |
| 176 // When there is a top bezel we add our border, | |
| 177 y -= top_; | |
| 178 | |
| 179 // Check if we are in the grace area of the top side. | |
| 180 // Note: We might not want to do this when the gesture is locked? | |
| 181 if (y < 0 && y > -top_ * kGraceAreaFraction) | |
| 182 y = 0; | |
| 183 | |
| 184 // Check if we are in the grace area of the bottom side. | |
| 185 // Note: We might not want to do this when the gesture is locked? | |
| 186 if (y > resolution_y - top_ && | |
| 187 y < resolution_y - top_ + bottom_ * kGraceAreaFraction) | |
| 188 y = resolution_y - top_; | |
| 189 // Scale the screen area back to the full resolution of the screen. | |
| 190 y = (y * resolution_y) / (resolution_y - (bottom_ + top_)); | |
| 191 } | |
| 192 | |
| 193 // Set the modified coordinate back to the event. | |
| 194 if (event->root_location() == event->location()) { | |
| 195 // Usually those will be equal, | |
| 196 // if not, I am not sure what the correct value should be. | |
| 197 event->set_root_location(gfx::Point(x, y)); | |
| 198 } | |
| 199 event->set_location(gfx::Point(x, y)); | |
| 200 #endif // defined(USE_XI2_MT) | |
| 201 } | |
| 202 | |
| 203 private: | |
| 204 // ui::PlatformEventObserver: | |
| 205 virtual void WillProcessEvent(const ui::PlatformEvent& event) override { | |
| 206 #if defined(USE_XI2_MT) | |
| 207 if (event->type == GenericEvent && | |
| 208 (event->xgeneric.evtype == XI_TouchBegin || | |
| 209 event->xgeneric.evtype == XI_TouchUpdate || | |
| 210 event->xgeneric.evtype == XI_TouchEnd)) { | |
| 211 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event->xcookie.data); | |
| 212 xievent->event = xievent->root; | |
| 213 xievent->event_x = xievent->root_x; | |
| 214 xievent->event_y = xievent->root_y; | |
| 215 } | |
| 216 #endif // defined(USE_XI2_MT) | |
| 217 } | |
| 218 | |
| 219 virtual void DidProcessEvent(const ui::PlatformEvent& event) override {} | |
| 220 | |
| 221 // The difference in screen's native resolution pixels between | |
| 222 // the border of the touchscreen and the border of the screen, | |
| 223 // aka bezel sizes. | |
| 224 int left_; | |
| 225 int right_; | |
| 226 int top_; | |
| 227 int bottom_; | |
| 228 | |
| 229 DISALLOW_COPY_AND_ASSIGN(TouchEventCalibrate); | |
| 230 }; | |
| 231 | |
| 232 } // namespace internal | |
| 233 | |
| 234 //////////////////////////////////////////////////////////////////////////////// | |
| 235 // WindowTreeHostX11 | |
| 236 | |
| 237 WindowTreeHostX11::WindowTreeHostX11(const gfx::Rect& bounds) | |
| 238 : xdisplay_(gfx::GetXDisplay()), | |
| 239 xwindow_(0), | |
| 240 x_root_window_(DefaultRootWindow(xdisplay_)), | |
| 241 current_cursor_(ui::kCursorNull), | |
| 242 window_mapped_(false), | |
| 243 bounds_(bounds), | |
| 244 touch_calibrate_(new internal::TouchEventCalibrate), | |
| 245 atom_cache_(xdisplay_, kAtomsToCache) { | |
| 246 XSetWindowAttributes swa; | |
| 247 memset(&swa, 0, sizeof(swa)); | |
| 248 swa.background_pixmap = None; | |
| 249 swa.override_redirect = default_override_redirect; | |
| 250 xwindow_ = XCreateWindow( | |
| 251 xdisplay_, x_root_window_, | |
| 252 bounds.x(), bounds.y(), bounds.width(), bounds.height(), | |
| 253 0, // border width | |
| 254 CopyFromParent, // depth | |
| 255 InputOutput, | |
| 256 CopyFromParent, // visual | |
| 257 CWBackPixmap | CWOverrideRedirect, | |
| 258 &swa); | |
| 259 if (ui::PlatformEventSource::GetInstance()) | |
| 260 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); | |
| 261 | |
| 262 long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask | | |
| 263 KeyPressMask | KeyReleaseMask | | |
| 264 EnterWindowMask | LeaveWindowMask | | |
| 265 ExposureMask | VisibilityChangeMask | | |
| 266 StructureNotifyMask | PropertyChangeMask | | |
| 267 PointerMotionMask; | |
| 268 XSelectInput(xdisplay_, xwindow_, event_mask); | |
| 269 XFlush(xdisplay_); | |
| 270 | |
| 271 if (ui::IsXInput2Available()) { | |
| 272 ui::TouchFactory::GetInstance()->SetupXI2ForXWindow(xwindow_); | |
| 273 SelectXInput2EventsForRootWindow(xdisplay_, x_root_window_); | |
| 274 } | |
| 275 | |
| 276 // TODO(erg): We currently only request window deletion events. We also | |
| 277 // should listen for activation events and anything else that GTK+ listens | |
| 278 // for, and do something useful. | |
| 279 ::Atom protocols[2]; | |
| 280 protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); | |
| 281 protocols[1] = atom_cache_.GetAtom("_NET_WM_PING"); | |
| 282 XSetWMProtocols(xdisplay_, xwindow_, protocols, 2); | |
| 283 | |
| 284 // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with | |
| 285 // the desktop environment. | |
| 286 XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL); | |
| 287 | |
| 288 // Likewise, the X server needs to know this window's pid so it knows which | |
| 289 // program to kill if the window hangs. | |
| 290 // XChangeProperty() expects "pid" to be long. | |
| 291 COMPILE_ASSERT(sizeof(long) >= sizeof(pid_t), pid_t_bigger_than_long); | |
| 292 long pid = getpid(); | |
| 293 XChangeProperty(xdisplay_, | |
| 294 xwindow_, | |
| 295 atom_cache_.GetAtom("_NET_WM_PID"), | |
| 296 XA_CARDINAL, | |
| 297 32, | |
| 298 PropModeReplace, | |
| 299 reinterpret_cast<unsigned char*>(&pid), 1); | |
| 300 | |
| 301 // Allow subclasses to create and cache additional atoms. | |
| 302 atom_cache_.allow_uncached_atoms(); | |
| 303 | |
| 304 XRRSelectInput(xdisplay_, x_root_window_, | |
| 305 RRScreenChangeNotifyMask | RROutputChangeNotifyMask); | |
| 306 CreateCompositor(GetAcceleratedWidget()); | |
| 307 } | |
| 308 | |
| 309 WindowTreeHostX11::~WindowTreeHostX11() { | |
| 310 if (ui::PlatformEventSource::GetInstance()) | |
| 311 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); | |
| 312 | |
| 313 DestroyCompositor(); | |
| 314 DestroyDispatcher(); | |
| 315 XDestroyWindow(xdisplay_, xwindow_); | |
| 316 } | |
| 317 | |
| 318 bool WindowTreeHostX11::CanDispatchEvent(const ui::PlatformEvent& event) { | |
| 319 ::Window target = FindEventTarget(event); | |
| 320 return target == xwindow_ || target == x_root_window_; | |
| 321 } | |
| 322 | |
| 323 uint32_t WindowTreeHostX11::DispatchEvent(const ui::PlatformEvent& event) { | |
| 324 XEvent* xev = event; | |
| 325 if (FindEventTarget(xev) == x_root_window_) { | |
| 326 if (xev->type == GenericEvent) | |
| 327 DispatchXI2Event(xev); | |
| 328 return ui::POST_DISPATCH_NONE; | |
| 329 } | |
| 330 | |
| 331 if (xev->type == MotionNotify) { | |
| 332 // Discard all but the most recent motion event that targets the same | |
| 333 // window with unchanged state. | |
| 334 XEvent last_event; | |
| 335 while (XPending(xev->xany.display)) { | |
| 336 XEvent next_event; | |
| 337 XPeekEvent(xev->xany.display, &next_event); | |
| 338 if (next_event.type == MotionNotify && | |
| 339 next_event.xmotion.window == xev->xmotion.window && | |
| 340 next_event.xmotion.subwindow == xev->xmotion.subwindow && | |
| 341 next_event.xmotion.state == xev->xmotion.state) { | |
| 342 XNextEvent(xev->xany.display, &last_event); | |
| 343 xev = &last_event; | |
| 344 } else { | |
| 345 break; | |
| 346 } | |
| 347 } | |
| 348 } | |
| 349 | |
| 350 if ((xev->type == EnterNotify || xev->type == LeaveNotify) && | |
| 351 xev->xcrossing.detail == NotifyInferior) { | |
| 352 // Ignore EventNotify and LeaveNotify events from children of |xwindow_|. | |
| 353 // NativeViewGLSurfaceGLX adds a child to |xwindow_|. | |
| 354 // TODO(pkotwicz|tdanderson): Figure out whether the suppression is | |
| 355 // necessary. crbug.com/385716 | |
| 356 return ui::POST_DISPATCH_STOP_PROPAGATION; | |
| 357 } | |
| 358 | |
| 359 if (xev->type == EnterNotify || | |
| 360 xev->type == LeaveNotify || | |
| 361 xev->type == KeyPress || | |
| 362 xev->type == KeyRelease || | |
| 363 xev->type == ButtonPress || | |
| 364 xev->type == ButtonRelease || | |
| 365 xev->type == MotionNotify) { | |
| 366 switch (ui::EventTypeFromNative(xev)) { | |
| 367 case ui::ET_KEY_PRESSED: | |
| 368 case ui::ET_KEY_RELEASED: { | |
| 369 ui::KeyEvent keydown_event(xev); | |
| 370 SendEventToProcessor(&keydown_event); | |
| 371 break; | |
| 372 } | |
| 373 case ui::ET_MOUSE_MOVED: | |
| 374 case ui::ET_MOUSE_DRAGGED: | |
| 375 case ui::ET_MOUSE_ENTERED: | |
| 376 case ui::ET_MOUSE_EXITED: | |
| 377 case ui::ET_MOUSE_PRESSED: | |
| 378 case ui::ET_MOUSE_RELEASED: { | |
| 379 ui::MouseEvent mouse_event(xev); | |
| 380 if (xev->type == EnterNotify) { | |
| 381 aura::Window* root_window = window(); | |
| 382 client::CursorClient* cursor_client = | |
| 383 client::GetCursorClient(root_window); | |
| 384 if (cursor_client) { | |
| 385 const gfx::Display display = gfx::Screen::GetScreenFor( | |
| 386 root_window)->GetDisplayNearestWindow(root_window); | |
| 387 cursor_client->SetDisplay(display); | |
| 388 } | |
| 389 // EnterNotify creates ET_MOUSE_MOVE. Mark as synthesized as this is | |
| 390 // not a real mouse move event. | |
| 391 mouse_event.set_flags(mouse_event.flags() | ui::EF_IS_SYNTHESIZED); | |
| 392 } | |
| 393 | |
| 394 TranslateAndDispatchLocatedEvent(&mouse_event); | |
| 395 break; | |
| 396 } | |
| 397 case ui::ET_MOUSEWHEEL: { | |
| 398 ui::MouseWheelEvent mouseev(xev); | |
| 399 TranslateAndDispatchLocatedEvent(&mouseev); | |
| 400 break; | |
| 401 } | |
| 402 case ui::ET_UNKNOWN: | |
| 403 // No event is created for X11-release events for mouse-wheel buttons. | |
| 404 break; | |
| 405 default: | |
| 406 NOTREACHED(); | |
| 407 } | |
| 408 return ui::POST_DISPATCH_STOP_PROPAGATION; | |
| 409 } | |
| 410 | |
| 411 switch (xev->type) { | |
| 412 case Expose: { | |
| 413 gfx::Rect damage_rect(xev->xexpose.x, xev->xexpose.y, | |
| 414 xev->xexpose.width, xev->xexpose.height); | |
| 415 compositor()->ScheduleRedrawRect(damage_rect); | |
| 416 break; | |
| 417 } | |
| 418 case FocusOut: | |
| 419 if (xev->xfocus.mode != NotifyGrab) | |
| 420 OnHostLostWindowCapture(); | |
| 421 break; | |
| 422 case ConfigureNotify: { | |
| 423 DCHECK_EQ(xwindow_, xev->xconfigure.event); | |
| 424 DCHECK_EQ(xwindow_, xev->xconfigure.window); | |
| 425 // It's possible that the X window may be resized by some other means | |
| 426 // than from within aura (e.g. the X window manager can change the | |
| 427 // size). Make sure the root window size is maintained properly. | |
| 428 gfx::Rect bounds(xev->xconfigure.x, xev->xconfigure.y, | |
| 429 xev->xconfigure.width, xev->xconfigure.height); | |
| 430 bool size_changed = bounds_.size() != bounds.size(); | |
| 431 bool origin_changed = bounds_.origin() != bounds.origin(); | |
| 432 bounds_ = bounds; | |
| 433 OnConfigureNotify(); | |
| 434 if (size_changed) | |
| 435 OnHostResized(bounds.size()); | |
| 436 if (origin_changed) | |
| 437 OnHostMoved(bounds_.origin()); | |
| 438 break; | |
| 439 } | |
| 440 case GenericEvent: | |
| 441 DispatchXI2Event(xev); | |
| 442 break; | |
| 443 case ClientMessage: { | |
| 444 Atom message_type = static_cast<Atom>(xev->xclient.data.l[0]); | |
| 445 if (message_type == atom_cache_.GetAtom("WM_DELETE_WINDOW")) { | |
| 446 // We have received a close message from the window manager. | |
| 447 OnHostCloseRequested(); | |
| 448 } else if (message_type == atom_cache_.GetAtom("_NET_WM_PING")) { | |
| 449 XEvent reply_event = *xev; | |
| 450 reply_event.xclient.window = x_root_window_; | |
| 451 | |
| 452 XSendEvent(xdisplay_, | |
| 453 reply_event.xclient.window, | |
| 454 False, | |
| 455 SubstructureRedirectMask | SubstructureNotifyMask, | |
| 456 &reply_event); | |
| 457 XFlush(xdisplay_); | |
| 458 } | |
| 459 break; | |
| 460 } | |
| 461 case MappingNotify: { | |
| 462 switch (xev->xmapping.request) { | |
| 463 case MappingModifier: | |
| 464 case MappingKeyboard: | |
| 465 XRefreshKeyboardMapping(&xev->xmapping); | |
| 466 break; | |
| 467 case MappingPointer: | |
| 468 ui::DeviceDataManagerX11::GetInstance()->UpdateButtonMap(); | |
| 469 break; | |
| 470 default: | |
| 471 NOTIMPLEMENTED() << " Unknown request: " << xev->xmapping.request; | |
| 472 break; | |
| 473 } | |
| 474 break; | |
| 475 } | |
| 476 } | |
| 477 return ui::POST_DISPATCH_STOP_PROPAGATION; | |
| 478 } | |
| 479 | |
| 480 ui::EventSource* WindowTreeHostX11::GetEventSource() { | |
| 481 return this; | |
| 482 } | |
| 483 | |
| 484 gfx::AcceleratedWidget WindowTreeHostX11::GetAcceleratedWidget() { | |
| 485 return xwindow_; | |
| 486 } | |
| 487 | |
| 488 void WindowTreeHostX11::Show() { | |
| 489 if (!window_mapped_) { | |
| 490 // Before we map the window, set size hints. Otherwise, some window managers | |
| 491 // will ignore toplevel XMoveWindow commands. | |
| 492 XSizeHints size_hints; | |
| 493 size_hints.flags = PPosition | PWinGravity; | |
| 494 size_hints.x = bounds_.x(); | |
| 495 size_hints.y = bounds_.y(); | |
| 496 // Set StaticGravity so that the window position is not affected by the | |
| 497 // frame width when running with window manager. | |
| 498 size_hints.win_gravity = StaticGravity; | |
| 499 XSetWMNormalHints(xdisplay_, xwindow_, &size_hints); | |
| 500 | |
| 501 XMapWindow(xdisplay_, xwindow_); | |
| 502 | |
| 503 // We now block until our window is mapped. Some X11 APIs will crash and | |
| 504 // burn if passed |xwindow_| before the window is mapped, and XMapWindow is | |
| 505 // asynchronous. | |
| 506 if (ui::X11EventSource::GetInstance()) | |
| 507 ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_); | |
| 508 window_mapped_ = true; | |
| 509 } | |
| 510 } | |
| 511 | |
| 512 void WindowTreeHostX11::Hide() { | |
| 513 if (window_mapped_) { | |
| 514 XWithdrawWindow(xdisplay_, xwindow_, 0); | |
| 515 window_mapped_ = false; | |
| 516 } | |
| 517 } | |
| 518 | |
| 519 gfx::Rect WindowTreeHostX11::GetBounds() const { | |
| 520 return bounds_; | |
| 521 } | |
| 522 | |
| 523 void WindowTreeHostX11::SetBounds(const gfx::Rect& bounds) { | |
| 524 // Even if the host window's size doesn't change, aura's root window | |
| 525 // size, which is in DIP, changes when the scale changes. | |
| 526 float current_scale = compositor()->device_scale_factor(); | |
| 527 float new_scale = gfx::Screen::GetScreenFor(window())-> | |
| 528 GetDisplayNearestWindow(window()).device_scale_factor(); | |
| 529 bool origin_changed = bounds_.origin() != bounds.origin(); | |
| 530 bool size_changed = bounds_.size() != bounds.size(); | |
| 531 XWindowChanges changes = {0}; | |
| 532 unsigned value_mask = 0; | |
| 533 | |
| 534 if (size_changed) { | |
| 535 changes.width = bounds.width(); | |
| 536 changes.height = bounds.height(); | |
| 537 value_mask = CWHeight | CWWidth; | |
| 538 } | |
| 539 | |
| 540 if (origin_changed) { | |
| 541 changes.x = bounds.x(); | |
| 542 changes.y = bounds.y(); | |
| 543 value_mask |= CWX | CWY; | |
| 544 } | |
| 545 if (value_mask) | |
| 546 XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes); | |
| 547 | |
| 548 // Assume that the resize will go through as requested, which should be the | |
| 549 // case if we're running without a window manager. If there's a window | |
| 550 // manager, it can modify or ignore the request, but (per ICCCM) we'll get a | |
| 551 // (possibly synthetic) ConfigureNotify about the actual size and correct | |
| 552 // |bounds_| later. | |
| 553 bounds_ = bounds; | |
| 554 if (origin_changed) | |
| 555 OnHostMoved(bounds.origin()); | |
| 556 if (size_changed || current_scale != new_scale) { | |
| 557 OnHostResized(bounds.size()); | |
| 558 } else { | |
| 559 window()->SchedulePaintInRect(window()->bounds()); | |
| 560 } | |
| 561 } | |
| 562 | |
| 563 gfx::Point WindowTreeHostX11::GetLocationOnNativeScreen() const { | |
| 564 return bounds_.origin(); | |
| 565 } | |
| 566 | |
| 567 void WindowTreeHostX11::SetCapture() { | |
| 568 // TODO(oshima): Grab x input. | |
| 569 } | |
| 570 | |
| 571 void WindowTreeHostX11::ReleaseCapture() { | |
| 572 // TODO(oshima): Release x input. | |
| 573 } | |
| 574 | |
| 575 void WindowTreeHostX11::PostNativeEvent( | |
| 576 const base::NativeEvent& native_event) { | |
| 577 DCHECK(xwindow_); | |
| 578 DCHECK(xdisplay_); | |
| 579 XEvent xevent = *native_event; | |
| 580 xevent.xany.display = xdisplay_; | |
| 581 xevent.xany.window = xwindow_; | |
| 582 | |
| 583 switch (xevent.type) { | |
| 584 case EnterNotify: | |
| 585 case LeaveNotify: | |
| 586 case MotionNotify: | |
| 587 case KeyPress: | |
| 588 case KeyRelease: | |
| 589 case ButtonPress: | |
| 590 case ButtonRelease: { | |
| 591 // The fields used below are in the same place for all of events | |
| 592 // above. Using xmotion from XEvent's unions to avoid repeating | |
| 593 // the code. | |
| 594 xevent.xmotion.root = x_root_window_; | |
| 595 xevent.xmotion.time = CurrentTime; | |
| 596 | |
| 597 gfx::Point point(xevent.xmotion.x, xevent.xmotion.y); | |
| 598 ConvertPointToNativeScreen(&point); | |
| 599 xevent.xmotion.x_root = point.x(); | |
| 600 xevent.xmotion.y_root = point.y(); | |
| 601 } | |
| 602 default: | |
| 603 break; | |
| 604 } | |
| 605 XSendEvent(xdisplay_, xwindow_, False, 0, &xevent); | |
| 606 XFlush(xdisplay_); | |
| 607 } | |
| 608 | |
| 609 void WindowTreeHostX11::SetCursorNative(gfx::NativeCursor cursor) { | |
| 610 if (cursor == current_cursor_) | |
| 611 return; | |
| 612 current_cursor_ = cursor; | |
| 613 SetCursorInternal(cursor); | |
| 614 } | |
| 615 | |
| 616 void WindowTreeHostX11::MoveCursorToNative(const gfx::Point& location) { | |
| 617 XWarpPointer(xdisplay_, None, x_root_window_, 0, 0, 0, 0, | |
| 618 bounds_.x() + location.x(), | |
| 619 bounds_.y() + location.y()); | |
| 620 } | |
| 621 | |
| 622 void WindowTreeHostX11::OnCursorVisibilityChangedNative(bool show) { | |
| 623 } | |
| 624 | |
| 625 ui::EventProcessor* WindowTreeHostX11::GetEventProcessor() { | |
| 626 return dispatcher(); | |
| 627 } | |
| 628 | |
| 629 void WindowTreeHostX11::DispatchXI2Event(const base::NativeEvent& event) { | |
| 630 ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); | |
| 631 XEvent* xev = event; | |
| 632 XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev->xcookie.data); | |
| 633 if (!factory->ShouldProcessXI2Event(xev)) | |
| 634 return; | |
| 635 | |
| 636 TRACE_EVENT1("input", "WindowTreeHostX11::DispatchXI2Event", | |
| 637 "event_latency_us", | |
| 638 (ui::EventTimeForNow() - ui::EventTimeFromNative(event)). | |
| 639 InMicroseconds()); | |
| 640 | |
| 641 int num_coalesced = 0; | |
| 642 XEvent last_event; | |
| 643 if (xev->xgeneric.evtype == XI_Motion) { | |
| 644 // If this is a motion event, we want to coalesce all pending motion | |
| 645 // events that are at the top of the queue. Note, we don't coalesce | |
| 646 // touch update events here. | |
| 647 num_coalesced = ui::CoalescePendingMotionEvents(xev, &last_event); | |
| 648 if (num_coalesced > 0) | |
| 649 xev = &last_event; | |
| 650 } | |
| 651 ui::EventType type = ui::EventTypeFromNative(xev); | |
| 652 | |
| 653 switch (type) { | |
| 654 case ui::ET_TOUCH_MOVED: | |
| 655 case ui::ET_TOUCH_PRESSED: | |
| 656 case ui::ET_TOUCH_CANCELLED: | |
| 657 case ui::ET_TOUCH_RELEASED: { | |
| 658 ui::TouchEvent touchev(xev); | |
| 659 if (ui::DeviceDataManagerX11::GetInstance()->TouchEventNeedsCalibrate( | |
| 660 xiev->deviceid)) { | |
| 661 touch_calibrate_->Calibrate(&touchev, bounds_); | |
| 662 } | |
| 663 TranslateAndDispatchLocatedEvent(&touchev); | |
| 664 break; | |
| 665 } | |
| 666 case ui::ET_MOUSE_MOVED: | |
| 667 case ui::ET_MOUSE_DRAGGED: | |
| 668 case ui::ET_MOUSE_PRESSED: | |
| 669 case ui::ET_MOUSE_RELEASED: | |
| 670 case ui::ET_MOUSE_ENTERED: | |
| 671 case ui::ET_MOUSE_EXITED: { | |
| 672 ui::MouseEvent mouseev(xev); | |
| 673 TranslateAndDispatchLocatedEvent(&mouseev); | |
| 674 break; | |
| 675 } | |
| 676 case ui::ET_MOUSEWHEEL: { | |
| 677 ui::MouseWheelEvent mouseev(xev); | |
| 678 TranslateAndDispatchLocatedEvent(&mouseev); | |
| 679 break; | |
| 680 } | |
| 681 case ui::ET_SCROLL_FLING_START: | |
| 682 case ui::ET_SCROLL_FLING_CANCEL: | |
| 683 case ui::ET_SCROLL: { | |
| 684 ui::ScrollEvent scrollev(xev); | |
| 685 SendEventToProcessor(&scrollev); | |
| 686 break; | |
| 687 } | |
| 688 case ui::ET_KEY_PRESSED: | |
| 689 case ui::ET_KEY_RELEASED: { | |
| 690 ui::KeyEvent key_event(xev); | |
| 691 SendEventToProcessor(&key_event); | |
| 692 break; | |
| 693 } | |
| 694 case ui::ET_UMA_DATA: | |
| 695 break; | |
| 696 case ui::ET_UNKNOWN: | |
| 697 break; | |
| 698 default: | |
| 699 NOTREACHED(); | |
| 700 } | |
| 701 | |
| 702 // If we coalesced an event we need to free its cookie. | |
| 703 if (num_coalesced > 0) | |
| 704 XFreeEventData(xev->xgeneric.display, &last_event.xcookie); | |
| 705 } | |
| 706 | |
| 707 void WindowTreeHostX11::SetCursorInternal(gfx::NativeCursor cursor) { | |
| 708 XDefineCursor(xdisplay_, xwindow_, cursor.platform()); | |
| 709 } | |
| 710 | |
| 711 void WindowTreeHostX11::OnConfigureNotify() {} | |
| 712 | |
| 713 void WindowTreeHostX11::TranslateAndDispatchLocatedEvent( | |
| 714 ui::LocatedEvent* event) { | |
| 715 SendEventToProcessor(event); | |
| 716 } | |
| 717 | |
| 718 // static | |
| 719 WindowTreeHost* WindowTreeHost::Create(const gfx::Rect& bounds) { | |
| 720 return new WindowTreeHostX11(bounds); | |
| 721 } | |
| 722 | |
| 723 // static | |
| 724 gfx::Size WindowTreeHost::GetNativeScreenSize() { | |
| 725 ::XDisplay* xdisplay = gfx::GetXDisplay(); | |
| 726 return gfx::Size(DisplayWidth(xdisplay, 0), DisplayHeight(xdisplay, 0)); | |
| 727 } | |
| 728 | |
| 729 namespace test { | |
| 730 | |
| 731 void SetUseOverrideRedirectWindowByDefault(bool override_redirect) { | |
| 732 default_override_redirect = override_redirect; | |
| 733 } | |
| 734 | |
| 735 } // namespace test | |
| 736 } // namespace aura | |
| OLD | NEW |