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/root_window_host_x11.h" 6 7 #include <strings.h> 8 #include <X11/cursorfont.h> 9 #include <X11/extensions/Xfixes.h> 10 #include <X11/extensions/XInput2.h> 11 #include <X11/extensions/Xrandr.h> 12 #include <X11/Xatom.h> 13 #include <X11/Xcursor/Xcursor.h> 14 #include <X11/Xlib.h> 15 16 #include <algorithm> 17 #include <limits> 18 #include <string> 19 20 #include "base/basictypes.h" 21 #include "base/command_line.h" 22 #include "base/debug/trace_event.h" 23 #include "base/message_loop/message_loop.h" 24 #include "base/message_loop/message_pump_x11.h" 25 #include "base/stl_util.h" 26 #include "base/strings/string_number_conversions.h" 27 #include "base/strings/string_util.h" 28 #include "base/strings/stringprintf.h" 29 #include "base/sys_info.h" 30 #include "ui/aura/client/cursor_client.h" 31 #include "ui/aura/client/screen_position_client.h" 32 #include "ui/aura/client/user_action_client.h" 33 #include "ui/aura/env.h" 34 #include "ui/aura/root_window.h" 35 #include "ui/base/cursor/cursor.h" 36 #include "ui/base/ui_base_switches.h" 37 #include "ui/base/view_prop.h" 38 #include "ui/base/x/x11_util.h" 39 #include "ui/compositor/dip_util.h" 40 #include "ui/compositor/layer.h" 41 #include "ui/events/event.h" 42 #include "ui/events/event_utils.h" 43 #include "ui/events/keycodes/keyboard_codes.h" 44 #include "ui/events/x/device_data_manager.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 // Standard Linux mouse buttons for going back and forward. 57 const int kBackMouseButton = 8; 58 const int kForwardMouseButton = 9; 59 60 const char* kAtomsToCache[] = { 61 "WM_DELETE_WINDOW", 62 "_NET_WM_PING", 63 "_NET_WM_PID", 64 "WM_S0", 65 #if defined(OS_CHROMEOS) 66 "Tap Paused", // Defined in the gestures library. 67 #endif 68 NULL 69 }; 70 71 ::Window FindEventTarget(const base::NativeEvent& xev) { 72 ::Window target = xev->xany.window; 73 if (xev->type == GenericEvent) 74 target = static_cast<XIDeviceEvent*>(xev->xcookie.data)->event; 75 return target; 76 } 77 78 #if defined(USE_XI2_MT) 79 bool IsSideBezelsEnabled() { 80 static bool side_bezels_enabled = 81 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 82 switches::kTouchSideBezels) != "0"; 83 return side_bezels_enabled; 84 } 85 #endif 86 87 void SelectXInput2EventsForRootWindow(XDisplay* display, ::Window root_window) { 88 CHECK(ui::IsXInput2Available()); 89 unsigned char mask[XIMaskLen(XI_LASTEVENT)] = {}; 90 memset(mask, 0, sizeof(mask)); 91 92 XISetMask(mask, XI_HierarchyChanged); 93 XISetMask(mask, XI_KeyPress); 94 XISetMask(mask, XI_KeyRelease); 95 96 XIEventMask evmask; 97 evmask.deviceid = XIAllDevices; 98 evmask.mask_len = sizeof(mask); 99 evmask.mask = mask; 100 XISelectEvents(display, root_window, &evmask, 1); 101 102 #if defined(OS_CHROMEOS) 103 if (base::SysInfo::IsRunningOnChromeOS()) { 104 // It is necessary to listen for touch events on the root window for proper 105 // touch event calibration on Chrome OS, but this is not currently necessary 106 // on the desktop. This seems to fail in some cases (e.g. when logging 107 // in incognito). So select for non-touch events first, and then select for 108 // touch-events (but keep the other events in the mask, i.e. do not memset 109 // |mask| back to 0). 110 // TODO(sad): Figure out why this happens. http://crbug.com/153976 111 XISetMask(mask, XI_TouchBegin); 112 XISetMask(mask, XI_TouchUpdate); 113 XISetMask(mask, XI_TouchEnd); 114 XISelectEvents(display, root_window, &evmask, 1); 115 } 116 #endif 117 } 118 119 bool default_override_redirect = false; 120 121 } // namespace 122 123 namespace internal { 124 125 // Accomplishes 2 tasks concerning touch event calibration: 126 // 1. Being a message-pump observer, 127 // routes all the touch events to the X root window, 128 // where they can be calibrated later. 129 // 2. Has the Calibrate method that does the actual bezel calibration, 130 // when invoked from X root window's event dispatcher. 131 class TouchEventCalibrate : public base::MessagePumpObserver { 132 public: 133 TouchEventCalibrate() 134 : left_(0), 135 right_(0), 136 top_(0), 137 bottom_(0) { 138 base::MessageLoopForUI::current()->AddObserver(this); 139 #if defined(USE_XI2_MT) 140 std::vector<std::string> parts; 141 if (Tokenize(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 142 switches::kTouchCalibration), ",", &parts) >= 4) { 143 if (!base::StringToInt(parts[0], &left_)) 144 DLOG(ERROR) << "Incorrect left border calibration value passed."; 145 if (!base::StringToInt(parts[1], &right_)) 146 DLOG(ERROR) << "Incorrect right border calibration value passed."; 147 if (!base::StringToInt(parts[2], &top_)) 148 DLOG(ERROR) << "Incorrect top border calibration value passed."; 149 if (!base::StringToInt(parts[3], &bottom_)) 150 DLOG(ERROR) << "Incorrect bottom border calibration value passed."; 151 } 152 #endif // defined(USE_XI2_MT) 153 } 154 155 virtual ~TouchEventCalibrate() { 156 base::MessageLoopForUI::current()->RemoveObserver(this); 157 } 158 159 #if defined(USE_XI2_MT) 160 bool IsEventOnSideBezels( 161 const base::NativeEvent& xev, 162 const gfx::Rect& bounds) { 163 if (!left_ && !right_) 164 return false; 165 166 gfx::Point location = ui::EventLocationFromNative(xev); 167 int x = location.x(); 168 return x < left_ || x > bounds.width() - right_; 169 } 170 #endif // defined(USE_XI2_MT) 171 172 // Modify the location of the |event|, 173 // expanding it from |bounds| to (|bounds| + bezels). 174 // Required when touchscreen is bigger than screen (i.e. has bezels), 175 // because we receive events in touchscreen coordinates, 176 // which need to be expanded when converting to screen coordinates, 177 // so that location on bezels will be outside of screen area. 178 void Calibrate(ui::TouchEvent* event, const gfx::Rect& bounds) { 179 #if defined(USE_XI2_MT) 180 int x = event->x(); 181 int y = event->y(); 182 183 if (!left_ && !right_ && !top_ && !bottom_) 184 return; 185 186 const int resolution_x = bounds.width(); 187 const int resolution_y = bounds.height(); 188 // The "grace area" (10% in this case) is to make it easier for the user to 189 // navigate to the corner. 190 const double kGraceAreaFraction = 0.1; 191 if (left_ || right_) { 192 // Offset the x position to the real 193 x -= left_; 194 // Check if we are in the grace area of the left side. 195 // Note: We might not want to do this when the gesture is locked? 196 if (x < 0 && x > -left_ * kGraceAreaFraction) 197 x = 0; 198 // Check if we are in the grace area of the right side. 199 // Note: We might not want to do this when the gesture is locked? 200 if (x > resolution_x - left_ && 201 x < resolution_x - left_ + right_ * kGraceAreaFraction) 202 x = resolution_x - left_; 203 // Scale the screen area back to the full resolution of the screen. 204 x = (x * resolution_x) / (resolution_x - (right_ + left_)); 205 } 206 if (top_ || bottom_) { 207 // When there is a top bezel we add our border, 208 y -= top_; 209 210 // Check if we are in the grace area of the top side. 211 // Note: We might not want to do this when the gesture is locked? 212 if (y < 0 && y > -top_ * kGraceAreaFraction) 213 y = 0; 214 215 // Check if we are in the grace area of the bottom side. 216 // Note: We might not want to do this when the gesture is locked? 217 if (y > resolution_y - top_ && 218 y < resolution_y - top_ + bottom_ * kGraceAreaFraction) 219 y = resolution_y - top_; 220 // Scale the screen area back to the full resolution of the screen. 221 y = (y * resolution_y) / (resolution_y - (bottom_ + top_)); 222 } 223 224 // Set the modified coordinate back to the event. 225 if (event->root_location() == event->location()) { 226 // Usually those will be equal, 227 // if not, I am not sure what the correct value should be. 228 event->set_root_location(gfx::Point(x, y)); 229 } 230 event->set_location(gfx::Point(x, y)); 231 #endif // defined(USE_XI2_MT) 232 } 233 234 private: 235 // Overridden from base::MessagePumpObserver: 236 virtual base::EventStatus WillProcessEvent( 237 const base::NativeEvent& event) OVERRIDE { 238 #if defined(USE_XI2_MT) 239 if (event->type == GenericEvent && 240 (event->xgeneric.evtype == XI_TouchBegin || 241 event->xgeneric.evtype == XI_TouchUpdate || 242 event->xgeneric.evtype == XI_TouchEnd)) { 243 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event->xcookie.data); 244 xievent->event = xievent->root; 245 xievent->event_x = xievent->root_x; 246 xievent->event_y = xievent->root_y; 247 } 248 #endif // defined(USE_XI2_MT) 249 return base::EVENT_CONTINUE; 250 } 251 252 virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE { 253 } 254 255 // The difference in screen's native resolution pixels between 256 // the border of the touchscreen and the border of the screen, 257 // aka bezel sizes. 258 int left_; 259 int right_; 260 int top_; 261 int bottom_; 262 263 DISALLOW_COPY_AND_ASSIGN(TouchEventCalibrate); 264 }; 265 266 } // namespace internal 267 268 //////////////////////////////////////////////////////////////////////////////// 269 // RootWindowHostX11::MouseMoveFilter filters out the move events that 270 // jump back and forth between two points. This happens when sub pixel mouse 271 // move is enabled and mouse move events could be jumping between two neighbor 272 // pixels, e.g. move(0,0), move(1,0), move(0,0), move(1,0) and on and on. 273 // The filtering is done by keeping track of the last two event locations and 274 // provides a Filter method to find out whether a mouse event is in a different 275 // location and should be processed. 276 277 class RootWindowHostX11::MouseMoveFilter { 278 public: 279 MouseMoveFilter() : insert_index_(0) { 280 for (size_t i = 0; i < kMaxEvents; ++i) { 281 const int int_max = std::numeric_limits<int>::max(); 282 recent_locations_[i] = gfx::Point(int_max, int_max); 283 } 284 } 285 ~MouseMoveFilter() {} 286 287 // Returns true if |event| is known and should be ignored. 288 bool Filter(const base::NativeEvent& event) { 289 const gfx::Point& location = ui::EventLocationFromNative(event); 290 for (size_t i = 0; i < kMaxEvents; ++i) { 291 if (location == recent_locations_[i]) 292 return true; 293 } 294 295 recent_locations_[insert_index_] = location; 296 insert_index_ = (insert_index_ + 1) % kMaxEvents; 297 return false; 298 } 299 300 private: 301 static const size_t kMaxEvents = 2; 302 303 gfx::Point recent_locations_[kMaxEvents]; 304 size_t insert_index_; 305 306 DISALLOW_COPY_AND_ASSIGN(MouseMoveFilter); 307 }; 308 309 //////////////////////////////////////////////////////////////////////////////// 310 // RootWindowHostX11 311 312 RootWindowHostX11::RootWindowHostX11(const gfx::Rect& bounds) 313 : xdisplay_(gfx::GetXDisplay()), 314 xwindow_(0), 315 x_root_window_(DefaultRootWindow(xdisplay_)), 316 current_cursor_(ui::kCursorNull), 317 window_mapped_(false), 318 bounds_(bounds), 319 is_internal_display_(false), 320 touch_calibrate_(new internal::TouchEventCalibrate), 321 mouse_move_filter_(new MouseMoveFilter), 322 atom_cache_(xdisplay_, kAtomsToCache), 323 bezel_tracking_ids_(0) { 324 XSetWindowAttributes swa; 325 memset(&swa, 0, sizeof(swa)); 326 swa.background_pixmap = None; 327 swa.override_redirect = default_override_redirect; 328 xwindow_ = XCreateWindow( 329 xdisplay_, x_root_window_, 330 bounds.x(), bounds.y(), bounds.width(), bounds.height(), 331 0, // border width 332 CopyFromParent, // depth 333 InputOutput, 334 CopyFromParent, // visual 335 CWBackPixmap | CWOverrideRedirect, 336 &swa); 337 base::MessagePumpX11::Current()->AddDispatcherForWindow(this, xwindow_); 338 base::MessagePumpX11::Current()->AddDispatcherForRootWindow(this); 339 340 long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask | 341 KeyPressMask | KeyReleaseMask | 342 EnterWindowMask | LeaveWindowMask | 343 ExposureMask | VisibilityChangeMask | 344 StructureNotifyMask | PropertyChangeMask | 345 PointerMotionMask; 346 XSelectInput(xdisplay_, xwindow_, event_mask); 347 XFlush(xdisplay_); 348 349 if (ui::IsXInput2Available()) { 350 ui::TouchFactory::GetInstance()->SetupXI2ForXWindow(xwindow_); 351 SelectXInput2EventsForRootWindow(xdisplay_, x_root_window_); 352 } 353 354 // TODO(erg): We currently only request window deletion events. We also 355 // should listen for activation events and anything else that GTK+ listens 356 // for, and do something useful. 357 ::Atom protocols[2]; 358 protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); 359 protocols[1] = atom_cache_.GetAtom("_NET_WM_PING"); 360 XSetWMProtocols(xdisplay_, xwindow_, protocols, 2); 361 362 // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with 363 // the desktop environment. 364 XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL); 365 366 // Likewise, the X server needs to know this window's pid so it knows which 367 // program to kill if the window hangs. 368 // XChangeProperty() expects "pid" to be long. 369 COMPILE_ASSERT(sizeof(long) >= sizeof(pid_t), pid_t_bigger_than_long); 370 long pid = getpid(); 371 XChangeProperty(xdisplay_, 372 xwindow_, 373 atom_cache_.GetAtom("_NET_WM_PID"), 374 XA_CARDINAL, 375 32, 376 PropModeReplace, 377 reinterpret_cast<unsigned char*>(&pid), 1); 378 379 XRRSelectInput(xdisplay_, x_root_window_, 380 RRScreenChangeNotifyMask | RROutputChangeNotifyMask); 381 Env::GetInstance()->AddObserver(this); 382 } 383 384 RootWindowHostX11::~RootWindowHostX11() { 385 Env::GetInstance()->RemoveObserver(this); 386 base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this); 387 base::MessagePumpX11::Current()->RemoveDispatcherForWindow(xwindow_); 388 389 UnConfineCursor(); 390 391 XDestroyWindow(xdisplay_, xwindow_); 392 } 393 394 bool RootWindowHostX11::Dispatch(const base::NativeEvent& event) { 395 XEvent* xev = event; 396 397 if (FindEventTarget(event) == x_root_window_) 398 return DispatchEventForRootWindow(event); 399 400 switch (xev->type) { 401 case EnterNotify: { 402 aura::Window* root_window = GetRootWindow()->window(); 403 client::CursorClient* cursor_client = 404 client::GetCursorClient(root_window); 405 if (cursor_client) { 406 const gfx::Display display = gfx::Screen::GetScreenFor(root_window)-> 407 GetDisplayNearestWindow(root_window); 408 cursor_client->SetDisplay(display); 409 } 410 ui::MouseEvent mouse_event(xev); 411 // EnterNotify creates ET_MOUSE_MOVE. Mark as synthesized as this is not 412 // real mouse move event. 413 mouse_event.set_flags(mouse_event.flags() | ui::EF_IS_SYNTHESIZED); 414 TranslateAndDispatchMouseEvent(&mouse_event); 415 break; 416 } 417 case LeaveNotify: { 418 ui::MouseEvent mouse_event(xev); 419 TranslateAndDispatchMouseEvent(&mouse_event); 420 break; 421 } 422 case Expose: { 423 gfx::Rect damage_rect(xev->xexpose.x, xev->xexpose.y, 424 xev->xexpose.width, xev->xexpose.height); 425 delegate_->AsRootWindow()->ScheduleRedrawRect(damage_rect); 426 break; 427 } 428 case KeyPress: { 429 ui::KeyEvent keydown_event(xev, false); 430 delegate_->OnHostKeyEvent(&keydown_event); 431 break; 432 } 433 case KeyRelease: { 434 ui::KeyEvent keyup_event(xev, false); 435 delegate_->OnHostKeyEvent(&keyup_event); 436 break; 437 } 438 case ButtonPress: { 439 if (static_cast<int>(xev->xbutton.button) == kBackMouseButton || 440 static_cast<int>(xev->xbutton.button) == kForwardMouseButton) { 441 client::UserActionClient* gesture_client = 442 client::GetUserActionClient(delegate_->AsRootWindow()->window()); 443 if (gesture_client) { 444 gesture_client->OnUserAction( 445 static_cast<int>(xev->xbutton.button) == kBackMouseButton ? 446 client::UserActionClient::BACK : 447 client::UserActionClient::FORWARD); 448 } 449 break; 450 } 451 } // fallthrough 452 case ButtonRelease: { 453 switch (ui::EventTypeFromNative(xev)) { 454 case ui::ET_MOUSEWHEEL: { 455 ui::MouseWheelEvent mouseev(xev); 456 TranslateAndDispatchMouseEvent(&mouseev); 457 break; 458 } 459 case ui::ET_MOUSE_PRESSED: 460 case ui::ET_MOUSE_RELEASED: { 461 ui::MouseEvent mouseev(xev); 462 TranslateAndDispatchMouseEvent(&mouseev); 463 break; 464 } 465 case ui::ET_UNKNOWN: 466 // No event is created for X11-release events for mouse-wheel buttons. 467 break; 468 default: 469 NOTREACHED(); 470 } 471 break; 472 } 473 case FocusOut: 474 if (xev->xfocus.mode != NotifyGrab) 475 delegate_->OnHostLostWindowCapture(); 476 break; 477 case ConfigureNotify: { 478 DCHECK_EQ(xwindow_, xev->xconfigure.event); 479 DCHECK_EQ(xwindow_, xev->xconfigure.window); 480 // It's possible that the X window may be resized by some other means 481 // than from within aura (e.g. the X window manager can change the 482 // size). Make sure the root window size is maintained properly. 483 gfx::Rect bounds(xev->xconfigure.x, xev->xconfigure.y, 484 xev->xconfigure.width, xev->xconfigure.height); 485 bool size_changed = bounds_.size() != bounds.size(); 486 bool origin_changed = bounds_.origin() != bounds.origin(); 487 bounds_ = bounds; 488 UpdateIsInternalDisplay(); 489 // Always update barrier and mouse location because |bounds_| might 490 // have already been updated in |SetBounds|. 491 if (pointer_barriers_) { 492 UnConfineCursor(); 493 ConfineCursorToRootWindow(); 494 } 495 if (size_changed) 496 delegate_->OnHostResized(bounds.size()); 497 if (origin_changed) 498 delegate_->OnHostMoved(bounds_.origin()); 499 break; 500 } 501 case GenericEvent: 502 DispatchXI2Event(event); 503 break; 504 case ClientMessage: { 505 Atom message_type = static_cast<Atom>(xev->xclient.data.l[0]); 506 if (message_type == atom_cache_.GetAtom("WM_DELETE_WINDOW")) { 507 // We have received a close message from the window manager. 508 delegate_->AsRootWindow()->OnRootWindowHostCloseRequested(); 509 } else if (message_type == atom_cache_.GetAtom("_NET_WM_PING")) { 510 XEvent reply_event = *xev; 511 reply_event.xclient.window = x_root_window_; 512 513 XSendEvent(xdisplay_, 514 reply_event.xclient.window, 515 False, 516 SubstructureRedirectMask | SubstructureNotifyMask, 517 &reply_event); 518 } 519 break; 520 } 521 case MappingNotify: { 522 switch (xev->xmapping.request) { 523 case MappingModifier: 524 case MappingKeyboard: 525 XRefreshKeyboardMapping(&xev->xmapping); 526 delegate_->AsRootWindow()->OnKeyboardMappingChanged(); 527 break; 528 case MappingPointer: 529 ui::DeviceDataManager::GetInstance()->UpdateButtonMap(); 530 break; 531 default: 532 NOTIMPLEMENTED() << " Unknown request: " << xev->xmapping.request; 533 break; 534 } 535 break; 536 } 537 case MotionNotify: { 538 // Discard all but the most recent motion event that targets the same 539 // window with unchanged state. 540 XEvent last_event; 541 while (XPending(xev->xany.display)) { 542 XEvent next_event; 543 XPeekEvent(xev->xany.display, &next_event); 544 if (next_event.type == MotionNotify && 545 next_event.xmotion.window == xev->xmotion.window && 546 next_event.xmotion.subwindow == xev->xmotion.subwindow && 547 next_event.xmotion.state == xev->xmotion.state) { 548 XNextEvent(xev->xany.display, &last_event); 549 xev = &last_event; 550 } else { 551 break; 552 } 553 } 554 555 ui::MouseEvent mouseev(xev); 556 TranslateAndDispatchMouseEvent(&mouseev); 557 break; 558 } 559 } 560 return true; 561 } 562 563 RootWindow* RootWindowHostX11::GetRootWindow() { 564 return delegate_->AsRootWindow(); 565 } 566 567 gfx::AcceleratedWidget RootWindowHostX11::GetAcceleratedWidget() { 568 return xwindow_; 569 } 570 571 void RootWindowHostX11::Show() { 572 if (!window_mapped_) { 573 // Before we map the window, set size hints. Otherwise, some window managers 574 // will ignore toplevel XMoveWindow commands. 575 XSizeHints size_hints; 576 size_hints.flags = PPosition | PWinGravity; 577 size_hints.x = bounds_.x(); 578 size_hints.y = bounds_.y(); 579 // Set StaticGravity so that the window position is not affected by the 580 // frame width when running with window manager. 581 size_hints.win_gravity = StaticGravity; 582 XSetWMNormalHints(xdisplay_, xwindow_, &size_hints); 583 584 XMapWindow(xdisplay_, xwindow_); 585 586 // We now block until our window is mapped. Some X11 APIs will crash and 587 // burn if passed |xwindow_| before the window is mapped, and XMapWindow is 588 // asynchronous. 589 base::MessagePumpX11::Current()->BlockUntilWindowMapped(xwindow_); 590 window_mapped_ = true; 591 } 592 } 593 594 void RootWindowHostX11::Hide() { 595 if (window_mapped_) { 596 XWithdrawWindow(xdisplay_, xwindow_, 0); 597 window_mapped_ = false; 598 } 599 } 600 601 void RootWindowHostX11::ToggleFullScreen() { 602 NOTIMPLEMENTED(); 603 } 604 605 gfx::Rect RootWindowHostX11::GetBounds() const { 606 return bounds_; 607 } 608 609 void RootWindowHostX11::SetBounds(const gfx::Rect& bounds) { 610 // Even if the host window's size doesn't change, aura's root window 611 // size, which is in DIP, changes when the scale changes. 612 float current_scale = delegate_->GetDeviceScaleFactor(); 613 float new_scale = gfx::Screen::GetScreenFor( 614 delegate_->AsRootWindow()->window())->GetDisplayNearestWindow( 615 delegate_->AsRootWindow()->window()).device_scale_factor(); 616 bool origin_changed = bounds_.origin() != bounds.origin(); 617 bool size_changed = bounds_.size() != bounds.size(); 618 XWindowChanges changes = {0}; 619 unsigned value_mask = 0; 620 621 if (size_changed) { 622 changes.width = bounds.width(); 623 changes.height = bounds.height(); 624 value_mask = CWHeight | CWWidth; 625 } 626 627 if (origin_changed) { 628 changes.x = bounds.x(); 629 changes.y = bounds.y(); 630 value_mask |= CWX | CWY; 631 } 632 if (value_mask) 633 XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes); 634 635 // Assume that the resize will go through as requested, which should be the 636 // case if we're running without a window manager. If there's a window 637 // manager, it can modify or ignore the request, but (per ICCCM) we'll get a 638 // (possibly synthetic) ConfigureNotify about the actual size and correct 639 // |bounds_| later. 640 bounds_ = bounds; 641 UpdateIsInternalDisplay(); 642 if (origin_changed) 643 delegate_->OnHostMoved(bounds.origin()); 644 if (size_changed || current_scale != new_scale) { 645 delegate_->OnHostResized(bounds.size()); 646 } else { 647 delegate_->AsRootWindow()->window()->SchedulePaintInRect( 648 delegate_->AsRootWindow()->window()->bounds()); 649 } 650 } 651 652 gfx::Insets RootWindowHostX11::GetInsets() const { 653 return insets_; 654 } 655 656 void RootWindowHostX11::SetInsets(const gfx::Insets& insets) { 657 insets_ = insets; 658 if (pointer_barriers_) { 659 UnConfineCursor(); 660 ConfineCursorToRootWindow(); 661 } 662 } 663 664 gfx::Point RootWindowHostX11::GetLocationOnNativeScreen() const { 665 return bounds_.origin(); 666 } 667 668 void RootWindowHostX11::SetCapture() { 669 // TODO(oshima): Grab x input. 670 } 671 672 void RootWindowHostX11::ReleaseCapture() { 673 // TODO(oshima): Release x input. 674 } 675 676 void RootWindowHostX11::SetCursor(gfx::NativeCursor cursor) { 677 if (cursor == current_cursor_) 678 return; 679 current_cursor_ = cursor; 680 SetCursorInternal(cursor); 681 } 682 683 bool RootWindowHostX11::QueryMouseLocation(gfx::Point* location_return) { 684 client::CursorClient* cursor_client = 685 client::GetCursorClient(GetRootWindow()->window()); 686 if (cursor_client && !cursor_client->IsMouseEventsEnabled()) { 687 *location_return = gfx::Point(0, 0); 688 return false; 689 } 690 691 ::Window root_return, child_return; 692 int root_x_return, root_y_return, win_x_return, win_y_return; 693 unsigned int mask_return; 694 XQueryPointer(xdisplay_, 695 xwindow_, 696 &root_return, 697 &child_return, 698 &root_x_return, &root_y_return, 699 &win_x_return, &win_y_return, 700 &mask_return); 701 *location_return = gfx::Point(max(0, min(bounds_.width(), win_x_return)), 702 max(0, min(bounds_.height(), win_y_return))); 703 return (win_x_return >= 0 && win_x_return < bounds_.width() && 704 win_y_return >= 0 && win_y_return < bounds_.height()); 705 } 706 707 bool RootWindowHostX11::ConfineCursorToRootWindow() { 708 #if XFIXES_MAJOR >= 5 709 DCHECK(!pointer_barriers_.get()); 710 if (pointer_barriers_) 711 return false; 712 pointer_barriers_.reset(new XID[4]); 713 gfx::Rect bounds(bounds_); 714 bounds.Inset(insets_); 715 // Horizontal, top barriers. 716 pointer_barriers_[0] = XFixesCreatePointerBarrier( 717 xdisplay_, x_root_window_, 718 bounds.x(), bounds.y(), bounds.right(), bounds.y(), 719 BarrierPositiveY, 720 0, XIAllDevices); 721 // Horizontal, bottom barriers. 722 pointer_barriers_[1] = XFixesCreatePointerBarrier( 723 xdisplay_, x_root_window_, 724 bounds.x(), bounds.bottom(), bounds.right(), bounds.bottom(), 725 BarrierNegativeY, 726 0, XIAllDevices); 727 // Vertical, left barriers. 728 pointer_barriers_[2] = XFixesCreatePointerBarrier( 729 xdisplay_, x_root_window_, 730 bounds.x(), bounds.y(), bounds.x(), bounds.bottom(), 731 BarrierPositiveX, 732 0, XIAllDevices); 733 // Vertical, right barriers. 734 pointer_barriers_[3] = XFixesCreatePointerBarrier( 735 xdisplay_, x_root_window_, 736 bounds.right(), bounds.y(), bounds.right(), bounds.bottom(), 737 BarrierNegativeX, 738 0, XIAllDevices); 739 #endif 740 return true; 741 } 742 743 void RootWindowHostX11::UnConfineCursor() { 744 #if XFIXES_MAJOR >= 5 745 if (pointer_barriers_) { 746 XFixesDestroyPointerBarrier(xdisplay_, pointer_barriers_[0]); 747 XFixesDestroyPointerBarrier(xdisplay_, pointer_barriers_[1]); 748 XFixesDestroyPointerBarrier(xdisplay_, pointer_barriers_[2]); 749 XFixesDestroyPointerBarrier(xdisplay_, pointer_barriers_[3]); 750 pointer_barriers_.reset(); 751 } 752 #endif 753 } 754 755 void RootWindowHostX11::OnCursorVisibilityChanged(bool show) { 756 SetCrOSTapPaused(!show); 757 } 758 759 void RootWindowHostX11::MoveCursorTo(const gfx::Point& location) { 760 XWarpPointer(xdisplay_, None, x_root_window_, 0, 0, 0, 0, 761 bounds_.x() + location.x(), 762 bounds_.y() + location.y()); 763 } 764 765 void RootWindowHostX11::PostNativeEvent( 766 const base::NativeEvent& native_event) { 767 DCHECK(xwindow_); 768 DCHECK(xdisplay_); 769 XEvent xevent = *native_event; 770 xevent.xany.display = xdisplay_; 771 xevent.xany.window = xwindow_; 772 773 switch (xevent.type) { 774 case EnterNotify: 775 case LeaveNotify: 776 case MotionNotify: 777 case KeyPress: 778 case KeyRelease: 779 case ButtonPress: 780 case ButtonRelease: { 781 // The fields used below are in the same place for all of events 782 // above. Using xmotion from XEvent's unions to avoid repeating 783 // the code. 784 xevent.xmotion.root = x_root_window_; 785 xevent.xmotion.time = CurrentTime; 786 787 gfx::Point point(xevent.xmotion.x, xevent.xmotion.y); 788 delegate_->AsRootWindow()->host()->ConvertPointToNativeScreen(&point); 789 xevent.xmotion.x_root = point.x(); 790 xevent.xmotion.y_root = point.y(); 791 } 792 default: 793 break; 794 } 795 XSendEvent(xdisplay_, xwindow_, False, 0, &xevent); 796 } 797 798 void RootWindowHostX11::OnDeviceScaleFactorChanged( 799 float device_scale_factor) { 800 } 801 802 void RootWindowHostX11::PrepareForShutdown() { 803 base::MessagePumpX11::Current()->RemoveDispatcherForWindow(xwindow_); 804 } 805 806 void RootWindowHostX11::OnWindowInitialized(Window* window) { 807 } 808 809 void RootWindowHostX11::OnRootWindowInitialized(RootWindow* root_window) { 810 // UpdateIsInternalDisplay relies on: 811 // 1. delegate_ pointing to RootWindow - available after SetDelegate. 812 // 2. RootWindow's kDisplayIdKey property set - available by the time 813 // RootWindow::Init is called. 814 // (set in DisplayManager::CreateRootWindowForDisplay) 815 // Ready when NotifyRootWindowInitialized is called from RootWindow::Init. 816 if (!delegate_ || root_window != GetRootWindow()) 817 return; 818 UpdateIsInternalDisplay(); 819 820 // We have to enable Tap-to-click by default because the cursor is set to 821 // visible in Shell::InitRootWindowController. 822 SetCrOSTapPaused(false); 823 } 824 825 ui::EventProcessor* RootWindowHostX11::GetEventProcessor() { 826 return delegate_->GetEventProcessor(); 827 } 828 829 bool RootWindowHostX11::DispatchEventForRootWindow( 830 const base::NativeEvent& event) { 831 switch (event->type) { 832 case GenericEvent: 833 DispatchXI2Event(event); 834 break; 835 } 836 837 return true; 838 } 839 840 void RootWindowHostX11::DispatchXI2Event(const base::NativeEvent& event) { 841 ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); 842 XEvent* xev = event; 843 if (!factory->ShouldProcessXI2Event(xev)) 844 return; 845 846 TRACE_EVENT1("input", "RootWindowHostX11::DispatchXI2Event", 847 "event_latency_us", 848 (ui::EventTimeForNow() - ui::EventTimeFromNative(event)). 849 InMicroseconds()); 850 851 ui::EventType type = ui::EventTypeFromNative(xev); 852 XEvent last_event; 853 int num_coalesced = 0; 854 855 switch (type) { 856 case ui::ET_TOUCH_MOVED: 857 case ui::ET_TOUCH_PRESSED: 858 case ui::ET_TOUCH_CANCELLED: 859 case ui::ET_TOUCH_RELEASED: { 860 #if defined(OS_CHROMEOS) 861 // Bail out early before generating a ui::TouchEvent if this event 862 // is not within the range of this RootWindow. Converting an xevent 863 // to ui::TouchEvent might change the state of the global touch tracking 864 // state, e.g. touch release event can remove the touch id from the 865 // record, and doing this multiple time when there are multiple 866 // RootWindow will cause problem. So only generate the ui::TouchEvent 867 // when we are sure it belongs to this RootWindow. 868 if (base::SysInfo::IsRunningOnChromeOS() && 869 !bounds_.Contains(ui::EventLocationFromNative(xev))) 870 break; 871 #endif // defined(OS_CHROMEOS) 872 ui::TouchEvent touchev(xev); 873 #if defined(USE_XI2_MT) 874 // Ignore touch events with touch press happening on the side bezel. 875 if (!IsSideBezelsEnabled()) { 876 uint32 tracking_id = (1 << touchev.touch_id()); 877 if (type == ui::ET_TOUCH_PRESSED && 878 touch_calibrate_->IsEventOnSideBezels(xev, bounds_)) 879 bezel_tracking_ids_ |= tracking_id; 880 if (bezel_tracking_ids_ & tracking_id) { 881 if (type == ui::ET_TOUCH_CANCELLED || type == ui::ET_TOUCH_RELEASED) 882 bezel_tracking_ids_ = 883 (bezel_tracking_ids_ | tracking_id) ^ tracking_id; 884 return; 885 } 886 } 887 #endif // defined(USE_XI2_MT) 888 #if defined(OS_CHROMEOS) 889 if (base::SysInfo::IsRunningOnChromeOS()) { 890 // X maps the touch-surface to the size of the X root-window. 891 // In multi-monitor setup, Coordinate Transformation Matrix 892 // repositions the touch-surface onto part of X root-window 893 // containing aura root-window corresponding to the touchscreen. 894 // However, if aura root-window has non-zero origin, 895 // we need to relocate the event into aura root-window coordinates. 896 touchev.Relocate(bounds_.origin()); 897 #if defined(USE_XI2_MT) 898 if (is_internal_display_) 899 touch_calibrate_->Calibrate(&touchev, bounds_); 900 #endif // defined(USE_XI2_MT) 901 } 902 #endif // defined(OS_CHROMEOS) 903 delegate_->OnHostTouchEvent(&touchev); 904 break; 905 } 906 case ui::ET_MOUSE_MOVED: 907 case ui::ET_MOUSE_DRAGGED: 908 case ui::ET_MOUSE_PRESSED: 909 case ui::ET_MOUSE_RELEASED: 910 case ui::ET_MOUSE_ENTERED: 911 case ui::ET_MOUSE_EXITED: { 912 if (type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_DRAGGED) { 913 // If this is a motion event, we want to coalesce all pending motion 914 // events that are at the top of the queue. 915 num_coalesced = ui::CoalescePendingMotionEvents(xev, &last_event); 916 if (num_coalesced > 0) 917 xev = &last_event; 918 919 if (mouse_move_filter_ && mouse_move_filter_->Filter(xev)) 920 break; 921 } else if (type == ui::ET_MOUSE_PRESSED || 922 type == ui::ET_MOUSE_RELEASED) { 923 XIDeviceEvent* xievent = 924 static_cast<XIDeviceEvent*>(xev->xcookie.data); 925 int button = xievent->detail; 926 if (button == kBackMouseButton || button == kForwardMouseButton) { 927 if (type == ui::ET_MOUSE_RELEASED) 928 break; 929 client::UserActionClient* gesture_client = 930 client::GetUserActionClient(delegate_->AsRootWindow()->window()); 931 if (gesture_client) { 932 bool reverse_direction = 933 ui::IsTouchpadEvent(xev) && ui::IsNaturalScrollEnabled(); 934 gesture_client->OnUserAction( 935 (button == kBackMouseButton && !reverse_direction) || 936 (button == kForwardMouseButton && reverse_direction) ? 937 client::UserActionClient::BACK : 938 client::UserActionClient::FORWARD); 939 } 940 break; 941 } 942 } 943 ui::MouseEvent mouseev(xev); 944 TranslateAndDispatchMouseEvent(&mouseev); 945 break; 946 } 947 case ui::ET_MOUSEWHEEL: { 948 ui::MouseWheelEvent mouseev(xev); 949 TranslateAndDispatchMouseEvent(&mouseev); 950 break; 951 } 952 case ui::ET_SCROLL_FLING_START: 953 case ui::ET_SCROLL_FLING_CANCEL: 954 case ui::ET_SCROLL: { 955 ui::ScrollEvent scrollev(xev); 956 delegate_->OnHostScrollEvent(&scrollev); 957 break; 958 } 959 case ui::ET_UMA_DATA: 960 break; 961 case ui::ET_UNKNOWN: 962 break; 963 default: 964 NOTREACHED(); 965 } 966 967 // If we coalesced an event we need to free its cookie. 968 if (num_coalesced > 0) 969 XFreeEventData(xev->xgeneric.display, &last_event.xcookie); 970 } 971 972 bool RootWindowHostX11::IsWindowManagerPresent() { 973 // Per ICCCM 2.8, "Manager Selections", window managers should take ownership 974 // of WM_Sn selections (where n is a screen number). 975 return XGetSelectionOwner( 976 xdisplay_, atom_cache_.GetAtom("WM_S0")) != None; 977 } 978 979 void RootWindowHostX11::SetCursorInternal(gfx::NativeCursor cursor) { 980 XDefineCursor(xdisplay_, xwindow_, cursor.platform()); 981 } 982 983 void RootWindowHostX11::TranslateAndDispatchMouseEvent( 984 ui::MouseEvent* event) { 985 Window* root_window = GetRootWindow()->window(); 986 client::ScreenPositionClient* screen_position_client = 987 client::GetScreenPositionClient(root_window); 988 gfx::Rect local(bounds_.size()); 989 990 if (screen_position_client && !local.Contains(event->location())) { 991 gfx::Point location(event->location()); 992 // In order to get the correct point in screen coordinates 993 // during passive grab, we first need to find on which host window 994 // the mouse is on, and find out the screen coordinates on that 995 // host window, then convert it back to this host window's coordinate. 996 screen_position_client->ConvertHostPointToScreen(root_window, &location); 997 screen_position_client->ConvertPointFromScreen(root_window, &location); 998 root_window->GetDispatcher()->ConvertPointToHost(&location); 999 event->set_location(location); 1000 event->set_root_location(location); 1001 } 1002 delegate_->OnHostMouseEvent(event); 1003 } 1004 1005 void RootWindowHostX11::UpdateIsInternalDisplay() { 1006 Window* root_window = GetRootWindow()->window(); 1007 gfx::Screen* screen = gfx::Screen::GetScreenFor(root_window); 1008 gfx::Display display = screen->GetDisplayNearestWindow(root_window); 1009 is_internal_display_ = display.IsInternal(); 1010 } 1011 1012 void RootWindowHostX11::SetCrOSTapPaused(bool state) { 1013 #if defined(OS_CHROMEOS) 1014 if (!ui::IsXInput2Available()) 1015 return; 1016 // Temporarily pause tap-to-click when the cursor is hidden. 1017 Atom prop = atom_cache_.GetAtom("Tap Paused"); 1018 unsigned char value = state; 1019 XIDeviceList dev_list = 1020 ui::DeviceListCacheX::GetInstance()->GetXI2DeviceList(xdisplay_); 1021 1022 // Only slave pointer devices could possibly have tap-paused property. 1023 for (int i = 0; i < dev_list.count; i++) { 1024 if (dev_list[i].use == XISlavePointer) { 1025 Atom old_type; 1026 int old_format; 1027 unsigned long old_nvalues, bytes; 1028 unsigned char* data; 1029 int result = XIGetProperty(xdisplay_, dev_list[i].deviceid, prop, 0, 0, 1030 False, AnyPropertyType, &old_type, &old_format, 1031 &old_nvalues, &bytes, &data); 1032 if (result != Success) 1033 continue; 1034 XFree(data); 1035 XIChangeProperty(xdisplay_, dev_list[i].deviceid, prop, XA_INTEGER, 8, 1036 PropModeReplace, &value, 1); 1037 } 1038 } 1039 #endif 1040 } 1041 1042 // static 1043 RootWindowHost* RootWindowHost::Create(const gfx::Rect& bounds) { 1044 return new RootWindowHostX11(bounds); 1045 } 1046 1047 // static 1048 gfx::Size RootWindowHost::GetNativeScreenSize() { 1049 ::XDisplay* xdisplay = gfx::GetXDisplay(); 1050 return gfx::Size(DisplayWidth(xdisplay, 0), DisplayHeight(xdisplay, 0)); 1051 } 1052 1053 namespace test { 1054 1055 void SetUseOverrideRedirectWindowByDefault(bool override_redirect) { 1056 default_override_redirect = override_redirect; 1057 } 1058 1059 } // namespace test 1060 } // namespace aura 1061