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/views/widget/desktop_aura/desktop_root_window_host_x11.h" 6 7 #include <X11/extensions/shape.h> 8 #include <X11/extensions/XInput2.h> 9 #include <X11/Xatom.h> 10 #include <X11/Xregion.h> 11 #include <X11/Xutil.h> 12 13 #include "base/message_loop/message_pump_aurax11.h" 14 #include "base/strings/stringprintf.h" 15 #include "base/strings/utf_string_conversions.h" 16 #include "third_party/skia/include/core/SkPath.h" 17 #include "ui/aura/client/screen_position_client.h" 18 #include "ui/aura/client/user_action_client.h" 19 #include "ui/aura/focus_manager.h" 20 #include "ui/aura/root_window.h" 21 #include "ui/aura/window_property.h" 22 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h" 23 #include "ui/base/events/event_utils.h" 24 #include "ui/base/touch/touch_factory_x11.h" 25 #include "ui/base/x/x11_util.h" 26 #include "ui/gfx/insets.h" 27 #include "ui/gfx/path_x11.h" 28 #include "ui/linux_ui/linux_ui.h" 29 #include "ui/native_theme/native_theme.h" 30 #include "ui/views/corewm/compound_event_filter.h" 31 #include "ui/views/corewm/corewm_switches.h" 32 #include "ui/views/corewm/cursor_manager.h" 33 #include "ui/views/corewm/focus_controller.h" 34 #include "ui/views/ime/input_method.h" 35 #include "ui/views/widget/desktop_aura/desktop_activation_client.h" 36 #include "ui/views/widget/desktop_aura/desktop_capture_client.h" 37 #include "ui/views/widget/desktop_aura/desktop_cursor_loader_updater_aurax11.h" 38 #include "ui/views/widget/desktop_aura/desktop_dispatcher_client.h" 39 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h" 40 #include "ui/views/widget/desktop_aura/desktop_focus_rules.h" 41 #include "ui/views/widget/desktop_aura/desktop_layout_manager.h" 42 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" 43 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" 44 #include "ui/views/widget/desktop_aura/desktop_root_window_host_observer_x11.h" 45 #include "ui/views/widget/desktop_aura/desktop_screen_position_client.h" 46 #include "ui/views/widget/desktop_aura/x11_desktop_handler.h" 47 #include "ui/views/widget/desktop_aura/x11_desktop_window_move_client.h" 48 #include "ui/views/widget/desktop_aura/x11_window_event_filter.h" 49 50 namespace views { 51 52 DesktopRootWindowHostX11* DesktopRootWindowHostX11::g_current_capture = 53 NULL; 54 std::list<XID>* DesktopRootWindowHostX11::open_windows_ = NULL; 55 56 DEFINE_WINDOW_PROPERTY_KEY( 57 aura::Window*, kViewsWindowForRootWindow, NULL); 58 59 DEFINE_WINDOW_PROPERTY_KEY( 60 DesktopRootWindowHostX11*, kHostForRootWindow, NULL); 61 62 namespace { 63 64 // Standard Linux mouse buttons for going back and forward. 65 const int kBackMouseButton = 8; 66 const int kForwardMouseButton = 9; 67 68 // Constants that are part of EWMH. 69 const int k_NET_WM_STATE_ADD = 1; 70 const int k_NET_WM_STATE_REMOVE = 0; 71 72 const char* kAtomsToCache[] = { 73 "WM_DELETE_WINDOW", 74 "WM_PROTOCOLS", 75 "WM_S0", 76 "_NET_WM_PID", 77 "_NET_WM_PING", 78 "_NET_WM_STATE", 79 "_NET_WM_STATE_ABOVE", 80 "_NET_WM_STATE_FULLSCREEN", 81 "_NET_WM_STATE_HIDDEN", 82 "_NET_WM_STATE_MAXIMIZED_HORZ", 83 "_NET_WM_STATE_MAXIMIZED_VERT", 84 "_NET_WM_STATE_SKIP_TASKBAR", 85 "_NET_WM_WINDOW_OPACITY", 86 "_NET_WM_WINDOW_TYPE", 87 "_NET_WM_WINDOW_TYPE_MENU", 88 "_NET_WM_WINDOW_TYPE_NORMAL", 89 "_NET_WM_WINDOW_TYPE_TOOLTIP", 90 "XdndActionAsk", 91 "XdndActionCopy" 92 "XdndActionLink", 93 "XdndActionList", 94 "XdndActionMove", 95 "XdndActionPrivate", 96 "XdndAware", 97 "XdndDrop", 98 "XdndEnter", 99 "XdndFinished", 100 "XdndLeave", 101 "XdndPosition", 102 "XdndProxy", // Proxy windows? 103 "XdndSelection", 104 "XdndStatus", 105 "XdndTypeList", 106 NULL 107 }; 108 109 } // namespace 110 111 //////////////////////////////////////////////////////////////////////////////// 112 // DesktopRootWindowHostX11, public: 113 114 DesktopRootWindowHostX11::DesktopRootWindowHostX11( 115 internal::NativeWidgetDelegate* native_widget_delegate, 116 DesktopNativeWidgetAura* desktop_native_widget_aura, 117 const gfx::Rect& initial_bounds) 118 : close_widget_factory_(this), 119 xdisplay_(base::MessagePumpAuraX11::GetDefaultXDisplay()), 120 xwindow_(0), 121 x_root_window_(DefaultRootWindow(xdisplay_)), 122 atom_cache_(xdisplay_, kAtomsToCache), 123 window_mapped_(false), 124 focus_when_shown_(false), 125 current_cursor_(ui::kCursorNull), 126 native_widget_delegate_(native_widget_delegate), 127 desktop_native_widget_aura_(desktop_native_widget_aura) { 128 } 129 130 DesktopRootWindowHostX11::~DesktopRootWindowHostX11() { 131 root_window_->ClearProperty(kHostForRootWindow); 132 if (corewm::UseFocusControllerOnDesktop()) { 133 aura::client::SetFocusClient(root_window_, NULL); 134 aura::client::SetActivationClient(root_window_, NULL); 135 } 136 } 137 138 // static 139 aura::Window* DesktopRootWindowHostX11::GetContentWindowForXID(XID xid) { 140 aura::RootWindow* root = aura::RootWindow::GetForAcceleratedWidget(xid); 141 return root ? root->GetProperty(kViewsWindowForRootWindow) : NULL; 142 } 143 144 // static 145 DesktopRootWindowHostX11* DesktopRootWindowHostX11::GetHostForXID(XID xid) { 146 aura::RootWindow* root = aura::RootWindow::GetForAcceleratedWidget(xid); 147 return root ? root->GetProperty(kHostForRootWindow) : NULL; 148 } 149 150 // static 151 std::vector<aura::Window*> DesktopRootWindowHostX11::GetAllOpenWindows() { 152 std::vector<aura::Window*> windows(open_windows().size()); 153 std::transform(open_windows().begin(), 154 open_windows().end(), 155 windows.begin(), 156 GetContentWindowForXID); 157 return windows; 158 } 159 160 void DesktopRootWindowHostX11::HandleNativeWidgetActivationChanged( 161 bool active) { 162 if (active) 163 root_window_host_delegate_->OnHostActivated(); 164 native_widget_delegate_->OnNativeWidgetActivationChanged(active); 165 // If we're not active we need to deactivate the corresponding aura::Window. 166 // This way if a child widget is active it gets correctly deactivated (child 167 // widgets don't get native desktop activation changes, only aura activation 168 // changes). 169 if (!active) { 170 aura::client::ActivationClient* activation_client = 171 aura::client::GetActivationClient(root_window_); 172 if (activation_client) { 173 aura::Window* active_window = activation_client->GetActiveWindow(); 174 if (active_window) 175 activation_client->DeactivateWindow(active_window); 176 } 177 } 178 179 native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint(); 180 } 181 182 void DesktopRootWindowHostX11::AddObserver( 183 views::DesktopRootWindowHostObserverX11* observer) { 184 observer_list_.AddObserver(observer); 185 } 186 187 void DesktopRootWindowHostX11::RemoveObserver( 188 views::DesktopRootWindowHostObserverX11* observer) { 189 observer_list_.RemoveObserver(observer); 190 } 191 192 void DesktopRootWindowHostX11::CleanUpWindowList() { 193 delete open_windows_; 194 open_windows_ = NULL; 195 } 196 197 //////////////////////////////////////////////////////////////////////////////// 198 // DesktopRootWindowHostX11, DesktopRootWindowHost implementation: 199 200 aura::RootWindow* DesktopRootWindowHostX11::Init( 201 aura::Window* content_window, 202 const Widget::InitParams& params) { 203 content_window_ = content_window; 204 205 // TODO(erg): Check whether we *should* be building a RootWindowHost here, or 206 // whether we should be proxying requests to another DRWHL. 207 208 // In some situations, views tries to make a zero sized window, and that 209 // makes us crash. Make sure we have valid sizes. 210 Widget::InitParams sanitized_params = params; 211 if (sanitized_params.bounds.width() == 0) 212 sanitized_params.bounds.set_width(100); 213 if (sanitized_params.bounds.height() == 0) 214 sanitized_params.bounds.set_height(100); 215 216 InitX11Window(sanitized_params); 217 return InitRootWindow(sanitized_params); 218 } 219 220 void DesktopRootWindowHostX11::InitFocus(aura::Window* window) { 221 } 222 223 void DesktopRootWindowHostX11::Close() { 224 // TODO(erg): Might need to do additional hiding tasks here. 225 226 if (!close_widget_factory_.HasWeakPtrs()) { 227 // And we delay the close so that if we are called from an ATL callback, 228 // we don't destroy the window before the callback returned (as the caller 229 // may delete ourselves on destroy and the ATL callback would still 230 // dereference us when the callback returns). 231 base::MessageLoop::current()->PostTask( 232 FROM_HERE, 233 base::Bind(&DesktopRootWindowHostX11::CloseNow, 234 close_widget_factory_.GetWeakPtr())); 235 } 236 } 237 238 void DesktopRootWindowHostX11::CloseNow() { 239 if (xwindow_ == None) 240 return; 241 242 native_widget_delegate_->OnNativeWidgetDestroying(); 243 244 // Remove the event listeners we've installed. We need to remove these 245 // because otherwise we get assert during ~RootWindow(). 246 desktop_native_widget_aura_->root_window_event_filter()->RemoveHandler( 247 x11_window_event_filter_.get()); 248 249 open_windows().remove(xwindow_); 250 // Actually free our native resources. 251 base::MessagePumpAuraX11::Current()->RemoveDispatcherForWindow(xwindow_); 252 XDestroyWindow(xdisplay_, xwindow_); 253 xwindow_ = None; 254 255 desktop_native_widget_aura_->OnHostClosed(); 256 } 257 258 aura::RootWindowHost* DesktopRootWindowHostX11::AsRootWindowHost() { 259 return this; 260 } 261 262 void DesktopRootWindowHostX11::ShowWindowWithState( 263 ui::WindowShowState show_state) { 264 if (show_state != ui::SHOW_STATE_DEFAULT && 265 show_state != ui::SHOW_STATE_NORMAL) { 266 // Only forwarding to Show(). 267 NOTIMPLEMENTED(); 268 } 269 270 Show(); 271 } 272 273 void DesktopRootWindowHostX11::ShowMaximizedWithBounds( 274 const gfx::Rect& restored_bounds) { 275 restored_bounds_ = restored_bounds; 276 Maximize(); 277 Show(); 278 } 279 280 bool DesktopRootWindowHostX11::IsVisible() const { 281 return window_mapped_; 282 } 283 284 void DesktopRootWindowHostX11::SetSize(const gfx::Size& size) { 285 // TODO(erg): 286 NOTIMPLEMENTED(); 287 } 288 289 void DesktopRootWindowHostX11::CenterWindow(const gfx::Size& size) { 290 gfx::Rect parent_bounds = GetWorkAreaBoundsInScreen(); 291 292 // If |window_|'s transient parent bounds are big enough to contain |size|, 293 // use them instead. 294 if (content_window_->transient_parent()) { 295 gfx::Rect transient_parent_rect = 296 content_window_->transient_parent()->GetBoundsInScreen(); 297 if (transient_parent_rect.height() >= size.height() && 298 transient_parent_rect.width() >= size.width()) { 299 parent_bounds = transient_parent_rect; 300 } 301 } 302 303 gfx::Rect window_bounds( 304 parent_bounds.x() + (parent_bounds.width() - size.width()) / 2, 305 parent_bounds.y() + (parent_bounds.height() - size.height()) / 2, 306 size.width(), 307 size.height()); 308 // Don't size the window bigger than the parent, otherwise the user may not be 309 // able to close or move it. 310 window_bounds.AdjustToFit(parent_bounds); 311 312 SetBounds(window_bounds); 313 } 314 315 void DesktopRootWindowHostX11::GetWindowPlacement( 316 gfx::Rect* bounds, 317 ui::WindowShowState* show_state) const { 318 *bounds = bounds_; 319 320 // TODO(erg): This needs a better implementation. For now, we're just pass 321 // back the normal state until we keep track of this. 322 *show_state = ui::SHOW_STATE_NORMAL; 323 } 324 325 gfx::Rect DesktopRootWindowHostX11::GetWindowBoundsInScreen() const { 326 return bounds_; 327 } 328 329 gfx::Rect DesktopRootWindowHostX11::GetClientAreaBoundsInScreen() const { 330 // TODO(erg): The NativeWidgetAura version returns |bounds_|, claiming its 331 // needed for View::ConvertPointToScreen() to work 332 // correctly. DesktopRootWindowHostWin::GetClientAreaBoundsInScreen() just 333 // asks windows what it thinks the client rect is. 334 // 335 // Attempts to calculate the rect by asking the NonClientFrameView what it 336 // thought its GetBoundsForClientView() were broke combobox drop down 337 // placement. 338 return bounds_; 339 } 340 341 gfx::Rect DesktopRootWindowHostX11::GetRestoredBounds() const { 342 // We can't reliably track the restored bounds of a window, but we can get 343 // the 90% case down. When *chrome* is the process that requests maximizing 344 // or restoring bounds, we can record the current bounds before we request 345 // maximization, and clear it when we detect a state change. 346 if (!restored_bounds_.IsEmpty()) 347 return restored_bounds_; 348 349 return GetWindowBoundsInScreen(); 350 } 351 352 gfx::Rect DesktopRootWindowHostX11::GetWorkAreaBoundsInScreen() const { 353 std::vector<int> value; 354 if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) && 355 value.size() >= 4) { 356 return gfx::Rect(value[0], value[1], value[2], value[3]); 357 } 358 359 // Fetch the geometry of the root window. 360 Window root; 361 int x, y; 362 unsigned int width, height; 363 unsigned int border_width, depth; 364 if (!XGetGeometry(xdisplay_, x_root_window_, &root, &x, &y, 365 &width, &height, &border_width, &depth)) { 366 NOTIMPLEMENTED(); 367 return gfx::Rect(0, 0, 10, 10); 368 } 369 370 return gfx::Rect(x, y, width, height); 371 } 372 373 void DesktopRootWindowHostX11::SetShape(gfx::NativeRegion native_region) { 374 SkPath path; 375 native_region->getBoundaryPath(&path); 376 Region region = gfx::CreateRegionFromSkPath(path); 377 XShapeCombineRegion(xdisplay_, xwindow_, ShapeBounding, 0, 0, region, false); 378 XDestroyRegion(region); 379 } 380 381 void DesktopRootWindowHostX11::Activate() { 382 X11DesktopHandler::get()->ActivateWindow(xwindow_); 383 } 384 385 void DesktopRootWindowHostX11::Deactivate() { 386 // Deactivating a window means activating nothing. 387 X11DesktopHandler::get()->ActivateWindow(None); 388 } 389 390 bool DesktopRootWindowHostX11::IsActive() const { 391 return X11DesktopHandler::get()->IsActiveWindow(xwindow_); 392 } 393 394 void DesktopRootWindowHostX11::Maximize() { 395 // When we're the process requesting the maximizing, we can accurately keep 396 // track of our restored bounds instead of relying on the heuristics that are 397 // in the PropertyNotify and ConfigureNotify handlers. 398 restored_bounds_ = bounds_; 399 400 SetWMSpecState(true, 401 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"), 402 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ")); 403 } 404 405 void DesktopRootWindowHostX11::Minimize() { 406 XIconifyWindow(xdisplay_, xwindow_, 0); 407 } 408 409 void DesktopRootWindowHostX11::Restore() { 410 SetWMSpecState(false, 411 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"), 412 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ")); 413 } 414 415 bool DesktopRootWindowHostX11::IsMaximized() const { 416 return (HasWMSpecProperty("_NET_WM_STATE_MAXIMIZED_VERT") || 417 HasWMSpecProperty("_NET_WM_STATE_MAXIMIZED_HORZ")); 418 } 419 420 bool DesktopRootWindowHostX11::IsMinimized() const { 421 return HasWMSpecProperty("_NET_WM_STATE_HIDDEN"); 422 } 423 424 425 bool DesktopRootWindowHostX11::HasCapture() const { 426 return g_current_capture == this; 427 } 428 429 void DesktopRootWindowHostX11::SetAlwaysOnTop(bool always_on_top) { 430 SetWMSpecState(always_on_top, 431 atom_cache_.GetAtom("_NET_WM_STATE_ABOVE"), 432 None); 433 } 434 435 void DesktopRootWindowHostX11::SetWindowTitle(const string16& title) { 436 XStoreName(xdisplay_, xwindow_, UTF16ToUTF8(title).c_str()); 437 } 438 439 void DesktopRootWindowHostX11::ClearNativeFocus() { 440 // This method is weird and misnamed. Instead of clearing the native focus, 441 // it sets the focus to our |content_window_|, which will trigger a cascade 442 // of focus changes into views. 443 if (content_window_ && aura::client::GetFocusClient(content_window_) && 444 content_window_->Contains( 445 aura::client::GetFocusClient(content_window_)->GetFocusedWindow())) { 446 aura::client::GetFocusClient(content_window_)->FocusWindow(content_window_); 447 } 448 } 449 450 Widget::MoveLoopResult DesktopRootWindowHostX11::RunMoveLoop( 451 const gfx::Vector2d& drag_offset, 452 Widget::MoveLoopSource source) { 453 SetCapture(); 454 455 aura::client::WindowMoveSource window_move_source = 456 source == Widget::MOVE_LOOP_SOURCE_MOUSE ? 457 aura::client::WINDOW_MOVE_SOURCE_MOUSE : 458 aura::client::WINDOW_MOVE_SOURCE_TOUCH; 459 if (x11_window_move_client_->RunMoveLoop(content_window_, drag_offset, 460 window_move_source) == aura::client::MOVE_SUCCESSFUL) 461 return Widget::MOVE_LOOP_SUCCESSFUL; 462 463 return Widget::MOVE_LOOP_CANCELED; 464 } 465 466 void DesktopRootWindowHostX11::EndMoveLoop() { 467 x11_window_move_client_->EndMoveLoop(); 468 } 469 470 void DesktopRootWindowHostX11::SetVisibilityChangedAnimationsEnabled( 471 bool value) { 472 // Much like the previous NativeWidgetGtk, we don't have anything to do here. 473 } 474 475 bool DesktopRootWindowHostX11::ShouldUseNativeFrame() { 476 return false; 477 } 478 479 void DesktopRootWindowHostX11::FrameTypeChanged() { 480 // Replace the frame and layout the contents. Even though we don't have a 481 // swapable glass frame like on Windows, we still replace the frame because 482 // the button assets don't update otherwise. 483 native_widget_delegate_->AsWidget()->non_client_view()->UpdateFrame(true); 484 } 485 486 NonClientFrameView* DesktopRootWindowHostX11::CreateNonClientFrameView() { 487 return NULL; 488 } 489 490 void DesktopRootWindowHostX11::SetFullscreen(bool fullscreen) { 491 SetWMSpecState(fullscreen, 492 atom_cache_.GetAtom("_NET_WM_STATE_FULLSCREEN"), 493 None); 494 } 495 496 bool DesktopRootWindowHostX11::IsFullscreen() const { 497 return HasWMSpecProperty("_NET_WM_STATE_FULLSCREEN"); 498 } 499 500 void DesktopRootWindowHostX11::SetOpacity(unsigned char opacity) { 501 // X server opacity is in terms of 32 bit unsigned int space, and counts from 502 // the opposite direction. 503 unsigned int cardinality = opacity * 0x1010101; 504 505 if (cardinality == 0xffffffff) { 506 XDeleteProperty(xdisplay_, xwindow_, 507 atom_cache_.GetAtom("_NET_WM_WINDOW_OPACITY")); 508 } else { 509 XChangeProperty(xdisplay_, xwindow_, 510 atom_cache_.GetAtom("_NET_WM_WINDOW_OPACITY"), 511 XA_CARDINAL, 32, 512 PropModeReplace, 513 reinterpret_cast<unsigned char*>(&cardinality), 1); 514 } 515 } 516 517 void DesktopRootWindowHostX11::SetWindowIcons( 518 const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) { 519 // TODO(erg): 520 NOTIMPLEMENTED(); 521 } 522 523 void DesktopRootWindowHostX11::InitModalType(ui::ModalType modal_type) { 524 // TODO(erg): 525 NOTIMPLEMENTED(); 526 } 527 528 void DesktopRootWindowHostX11::FlashFrame(bool flash_frame) { 529 // TODO(erg): 530 NOTIMPLEMENTED(); 531 } 532 533 void DesktopRootWindowHostX11::OnNativeWidgetFocus() { 534 native_widget_delegate_->AsWidget()->GetInputMethod()->OnFocus(); 535 } 536 537 void DesktopRootWindowHostX11::OnNativeWidgetBlur() { 538 if (xwindow_) 539 native_widget_delegate_->AsWidget()->GetInputMethod()->OnBlur(); 540 } 541 542 void DesktopRootWindowHostX11::SetInactiveRenderingDisabled( 543 bool disable_inactive) { 544 } 545 546 //////////////////////////////////////////////////////////////////////////////// 547 // DesktopRootWindowHostX11, aura::RootWindowHost implementation: 548 549 void DesktopRootWindowHostX11::SetDelegate( 550 aura::RootWindowHostDelegate* delegate) { 551 root_window_host_delegate_ = delegate; 552 } 553 554 aura::RootWindow* DesktopRootWindowHostX11::GetRootWindow() { 555 return root_window_; 556 } 557 558 gfx::AcceleratedWidget DesktopRootWindowHostX11::GetAcceleratedWidget() { 559 return xwindow_; 560 } 561 562 void DesktopRootWindowHostX11::Show() { 563 if (!window_mapped_) { 564 // Before we map the window, set size hints. Otherwise, some window managers 565 // will ignore toplevel XMoveWindow commands. 566 XSizeHints size_hints; 567 size_hints.flags = PPosition; 568 size_hints.x = bounds_.x(); 569 size_hints.y = bounds_.y(); 570 XSetWMNormalHints(xdisplay_, xwindow_, &size_hints); 571 572 XMapWindow(xdisplay_, xwindow_); 573 574 // We now block until our window is mapped. Some X11 APIs will crash and 575 // burn if passed |xwindow_| before the window is mapped, and XMapWindow is 576 // asynchronous. 577 base::MessagePumpAuraX11::Current()->BlockUntilWindowMapped(xwindow_); 578 window_mapped_ = true; 579 } 580 } 581 582 void DesktopRootWindowHostX11::Hide() { 583 if (window_mapped_) { 584 XWithdrawWindow(xdisplay_, xwindow_, 0); 585 window_mapped_ = false; 586 } 587 } 588 589 void DesktopRootWindowHostX11::ToggleFullScreen() { 590 NOTIMPLEMENTED(); 591 } 592 593 gfx::Rect DesktopRootWindowHostX11::GetBounds() const { 594 return bounds_; 595 } 596 597 void DesktopRootWindowHostX11::SetBounds(const gfx::Rect& bounds) { 598 bool origin_changed = bounds_.origin() != bounds.origin(); 599 bool size_changed = bounds_.size() != bounds.size(); 600 XWindowChanges changes = {0}; 601 unsigned value_mask = 0; 602 603 if (size_changed) { 604 // X11 will send an XError at our process if have a 0 sized window. 605 DCHECK_GT(bounds.width(), 0); 606 DCHECK_GT(bounds.height(), 0); 607 608 changes.width = bounds.width(); 609 changes.height = bounds.height(); 610 value_mask |= CWHeight | CWWidth; 611 } 612 613 if (origin_changed) { 614 changes.x = bounds.x(); 615 changes.y = bounds.y(); 616 value_mask |= CWX | CWY; 617 } 618 if (value_mask) 619 XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes); 620 621 // Assume that the resize will go through as requested, which should be the 622 // case if we're running without a window manager. If there's a window 623 // manager, it can modify or ignore the request, but (per ICCCM) we'll get a 624 // (possibly synthetic) ConfigureNotify about the actual size and correct 625 // |bounds_| later. 626 bounds_ = bounds; 627 628 if (origin_changed) 629 native_widget_delegate_->AsWidget()->OnNativeWidgetMove(); 630 if (size_changed) 631 root_window_host_delegate_->OnHostResized(bounds.size()); 632 else 633 root_window_host_delegate_->OnHostPaint(gfx::Rect(bounds.size())); 634 } 635 636 gfx::Insets DesktopRootWindowHostX11::GetInsets() const { 637 return gfx::Insets(); 638 } 639 640 void DesktopRootWindowHostX11::SetInsets(const gfx::Insets& insets) { 641 } 642 643 gfx::Point DesktopRootWindowHostX11::GetLocationOnNativeScreen() const { 644 return bounds_.origin(); 645 } 646 647 void DesktopRootWindowHostX11::SetCapture() { 648 // This is vaguely based on the old NativeWidgetGtk implementation. 649 // 650 // X11's XPointerGrab() shouldn't be used for everything; it doesn't map 651 // cleanly to Windows' SetCapture(). GTK only provides a separate concept of 652 // a grab that wasn't the X11 pointer grab, but was instead a manual 653 // redirection of the event. (You need to drop into GDK if you want to 654 // perform a raw X11 grab). 655 656 if (g_current_capture) 657 g_current_capture->OnCaptureReleased(); 658 659 g_current_capture = this; 660 661 // TODO(erg): In addition to the above, NativeWidgetGtk performs a full X 662 // pointer grab when our NativeWidget is of type Menu. However, things work 663 // without it. Clicking inside a chrome window causes a release capture, and 664 // clicking outside causes an activation change. Since previous attempts at 665 // using XPointerGrab() to implement this have locked my X server, I'm going 666 // to skip this for now. 667 } 668 669 void DesktopRootWindowHostX11::ReleaseCapture() { 670 if (g_current_capture) 671 g_current_capture->OnCaptureReleased(); 672 } 673 674 void DesktopRootWindowHostX11::SetCursor(gfx::NativeCursor cursor) { 675 XDefineCursor(xdisplay_, xwindow_, cursor.platform()); 676 } 677 678 bool DesktopRootWindowHostX11::QueryMouseLocation( 679 gfx::Point* location_return) { 680 aura::client::CursorClient* cursor_client = 681 aura::client::GetCursorClient(GetRootWindow()); 682 if (cursor_client && !cursor_client->IsMouseEventsEnabled()) { 683 *location_return = gfx::Point(0, 0); 684 return false; 685 } 686 687 ::Window root_return, child_return; 688 int root_x_return, root_y_return, win_x_return, win_y_return; 689 unsigned int mask_return; 690 XQueryPointer(xdisplay_, 691 xwindow_, 692 &root_return, 693 &child_return, 694 &root_x_return, &root_y_return, 695 &win_x_return, &win_y_return, 696 &mask_return); 697 *location_return = gfx::Point( 698 std::max(0, std::min(bounds_.width(), win_x_return)), 699 std::max(0, std::min(bounds_.height(), win_y_return))); 700 return (win_x_return >= 0 && win_x_return < bounds_.width() && 701 win_y_return >= 0 && win_y_return < bounds_.height()); 702 } 703 704 bool DesktopRootWindowHostX11::ConfineCursorToRootWindow() { 705 NOTIMPLEMENTED(); 706 return false; 707 } 708 709 void DesktopRootWindowHostX11::UnConfineCursor() { 710 NOTIMPLEMENTED(); 711 } 712 713 void DesktopRootWindowHostX11::OnCursorVisibilityChanged(bool show) { 714 // TODO(erg): Conditional on us enabling touch on desktop linux builds, do 715 // the same tap-to-click disabling here that chromeos does. 716 } 717 718 void DesktopRootWindowHostX11::MoveCursorTo(const gfx::Point& location) { 719 XWarpPointer(xdisplay_, None, x_root_window_, 0, 0, 0, 0, 720 bounds_.x() + location.x(), bounds_.y() + location.y()); 721 } 722 723 void DesktopRootWindowHostX11::SetFocusWhenShown(bool focus_when_shown) { 724 static const char* k_NET_WM_USER_TIME = "_NET_WM_USER_TIME"; 725 focus_when_shown_ = focus_when_shown; 726 if (IsWindowManagerPresent() && !focus_when_shown_) { 727 ui::SetIntProperty(xwindow_, 728 k_NET_WM_USER_TIME, 729 k_NET_WM_USER_TIME, 730 0); 731 } 732 } 733 734 bool DesktopRootWindowHostX11::CopyAreaToSkCanvas( 735 const gfx::Rect& source_bounds, 736 const gfx::Point& dest_offset, 737 SkCanvas* canvas) { 738 NOTIMPLEMENTED(); 739 return false; 740 } 741 742 void DesktopRootWindowHostX11::PostNativeEvent( 743 const base::NativeEvent& native_event) { 744 DCHECK(xwindow_); 745 DCHECK(xdisplay_); 746 XEvent xevent = *native_event; 747 xevent.xany.display = xdisplay_; 748 xevent.xany.window = xwindow_; 749 750 switch (xevent.type) { 751 case EnterNotify: 752 case LeaveNotify: 753 case MotionNotify: 754 case KeyPress: 755 case KeyRelease: 756 case ButtonPress: 757 case ButtonRelease: { 758 // The fields used below are in the same place for all of events 759 // above. Using xmotion from XEvent's unions to avoid repeating 760 // the code. 761 xevent.xmotion.root = x_root_window_; 762 xevent.xmotion.time = CurrentTime; 763 764 gfx::Point point(xevent.xmotion.x, xevent.xmotion.y); 765 root_window_->ConvertPointToNativeScreen(&point); 766 xevent.xmotion.x_root = point.x(); 767 xevent.xmotion.y_root = point.y(); 768 } 769 default: 770 break; 771 } 772 XSendEvent(xdisplay_, xwindow_, False, 0, &xevent); 773 } 774 775 void DesktopRootWindowHostX11::OnDeviceScaleFactorChanged( 776 float device_scale_factor) { 777 } 778 779 void DesktopRootWindowHostX11::PrepareForShutdown() { 780 } 781 782 //////////////////////////////////////////////////////////////////////////////// 783 // DesktopRootWindowHostX11, private: 784 785 void DesktopRootWindowHostX11::InitX11Window( 786 const Widget::InitParams& params) { 787 unsigned long attribute_mask = CWBackPixmap; 788 XSetWindowAttributes swa; 789 memset(&swa, 0, sizeof(swa)); 790 swa.background_pixmap = None; 791 792 ::Atom window_type; 793 switch (params.type) { 794 case Widget::InitParams::TYPE_MENU: 795 swa.override_redirect = True; 796 attribute_mask |= CWOverrideRedirect; 797 window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_MENU"); 798 break; 799 case Widget::InitParams::TYPE_TOOLTIP: 800 window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_TOOLTIP"); 801 break; 802 default: 803 window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_NORMAL"); 804 break; 805 } 806 807 xwindow_ = XCreateWindow( 808 xdisplay_, x_root_window_, 809 params.bounds.x(), params.bounds.y(), 810 params.bounds.width(), params.bounds.height(), 811 0, // border width 812 CopyFromParent, // depth 813 InputOutput, 814 CopyFromParent, // visual 815 attribute_mask, 816 &swa); 817 base::MessagePumpAuraX11::Current()->AddDispatcherForWindow(this, xwindow_); 818 819 // TODO(erg): Maybe need to set a ViewProp here like in RWHL::RWHL(). 820 821 long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask | 822 KeyPressMask | KeyReleaseMask | 823 EnterWindowMask | LeaveWindowMask | 824 ExposureMask | VisibilityChangeMask | 825 StructureNotifyMask | PropertyChangeMask | 826 PointerMotionMask; 827 XSelectInput(xdisplay_, xwindow_, event_mask); 828 XFlush(xdisplay_); 829 830 if (base::MessagePumpForUI::HasXInput2()) 831 ui::TouchFactory::GetInstance()->SetupXI2ForXWindow(xwindow_); 832 833 // TODO(erg): We currently only request window deletion events. We also 834 // should listen for activation events and anything else that GTK+ listens 835 // for, and do something useful. 836 ::Atom protocols[2]; 837 protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW"); 838 protocols[1] = atom_cache_.GetAtom("_NET_WM_PING"); 839 XSetWMProtocols(xdisplay_, xwindow_, protocols, 2); 840 841 // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with 842 // the desktop environment. 843 XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL); 844 845 // Likewise, the X server needs to know this window's pid so it knows which 846 // program to kill if the window hangs. 847 pid_t pid = getpid(); 848 XChangeProperty(xdisplay_, 849 xwindow_, 850 atom_cache_.GetAtom("_NET_WM_PID"), 851 XA_CARDINAL, 852 32, 853 PropModeReplace, 854 reinterpret_cast<unsigned char*>(&pid), 1); 855 856 XChangeProperty(xdisplay_, 857 xwindow_, 858 atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE"), 859 XA_ATOM, 860 32, 861 PropModeReplace, 862 reinterpret_cast<unsigned char*>(&window_type), 1); 863 864 // Remove popup windows from taskbar. 865 if (params.type == Widget::InitParams::TYPE_POPUP || 866 params.type == Widget::InitParams::TYPE_BUBBLE) { 867 Atom atom = atom_cache_.GetAtom("_NET_WM_STATE_SKIP_TASKBAR"); 868 869 // Setting _NET_WM_STATE by sending a message to the root_window (with 870 // SetWMSpecState) has no effect here since the window has not yet been 871 // mapped. So we manually change the state. 872 XChangeProperty (xdisplay_, 873 xwindow_, 874 atom_cache_.GetAtom("_NET_WM_STATE"), 875 XA_ATOM, 876 32, 877 PropModeAppend, 878 reinterpret_cast<unsigned char*>(&atom), 1); 879 } 880 } 881 882 // TODO(erg): This method should basically be everything I need form 883 // RootWindowHostX11::RootWindowHostX11(). 884 aura::RootWindow* DesktopRootWindowHostX11::InitRootWindow( 885 const Widget::InitParams& params) { 886 bounds_ = params.bounds; 887 888 aura::RootWindow::CreateParams rw_params(bounds_); 889 rw_params.host = this; 890 root_window_ = new aura::RootWindow(rw_params); 891 root_window_->Init(); 892 root_window_->AddChild(content_window_); 893 root_window_->SetLayoutManager(new DesktopLayoutManager(root_window_)); 894 root_window_->SetProperty(kViewsWindowForRootWindow, content_window_); 895 root_window_->SetProperty(kHostForRootWindow, this); 896 root_window_host_delegate_ = root_window_; 897 898 // If we're given a parent, we need to mark ourselves as transient to another 899 // window. Otherwise activation gets screwy. 900 gfx::NativeView parent = params.parent; 901 if (!params.child && params.parent) 902 parent->AddTransientChild(content_window_); 903 904 native_widget_delegate_->OnNativeWidgetCreated(true); 905 906 capture_client_.reset(new views::DesktopCaptureClient(root_window_)); 907 aura::client::SetCaptureClient(root_window_, capture_client_.get()); 908 909 // Ensure that the X11DesktopHandler exists so that it dispatches activation 910 // messages to us. 911 X11DesktopHandler::get(); 912 913 if (corewm::UseFocusControllerOnDesktop()) { 914 corewm::FocusController* focus_controller = 915 new corewm::FocusController(new DesktopFocusRules); 916 focus_client_.reset(focus_controller); 917 aura::client::SetFocusClient(root_window_, focus_controller); 918 aura::client::SetActivationClient(root_window_, focus_controller); 919 root_window_->AddPreTargetHandler(focus_controller); 920 } else { 921 focus_client_.reset(new aura::FocusManager); 922 aura::client::SetFocusClient(root_window_, focus_client_.get()); 923 activation_client_.reset(new DesktopActivationClient(root_window_)); 924 } 925 926 dispatcher_client_.reset(new DesktopDispatcherClient); 927 aura::client::SetDispatcherClient(root_window_, 928 dispatcher_client_.get()); 929 930 views::DesktopNativeCursorManager* desktop_native_cursor_manager = 931 new views::DesktopNativeCursorManager( 932 root_window_, 933 scoped_ptr<DesktopCursorLoaderUpdater>( 934 new DesktopCursorLoaderUpdaterAuraX11)); 935 cursor_client_.reset( 936 new views::corewm::CursorManager( 937 scoped_ptr<corewm::NativeCursorManager>( 938 desktop_native_cursor_manager))); 939 aura::client::SetCursorClient(root_window_, 940 cursor_client_.get()); 941 942 position_client_.reset(new DesktopScreenPositionClient); 943 aura::client::SetScreenPositionClient(root_window_, 944 position_client_.get()); 945 946 desktop_native_widget_aura_->InstallInputMethodEventFilter(root_window_); 947 948 drag_drop_client_.reset(new DesktopDragDropClientAuraX11( 949 root_window_, desktop_native_cursor_manager, xdisplay_, xwindow_)); 950 aura::client::SetDragDropClient(root_window_, drag_drop_client_.get()); 951 952 // TODO(erg): Unify this code once the other consumer goes away. 953 x11_window_event_filter_.reset( 954 new X11WindowEventFilter(root_window_, activation_client_.get())); 955 x11_window_event_filter_->SetUseHostWindowBorders(false); 956 desktop_native_widget_aura_->root_window_event_filter()->AddHandler( 957 x11_window_event_filter_.get()); 958 959 x11_window_move_client_.reset(new X11DesktopWindowMoveClient); 960 aura::client::SetWindowMoveClient(root_window_, 961 x11_window_move_client_.get()); 962 963 focus_client_->FocusWindow(content_window_); 964 return root_window_; 965 } 966 967 bool DesktopRootWindowHostX11::IsWindowManagerPresent() { 968 // Per ICCCM 2.8, "Manager Selections", window managers should take ownership 969 // of WM_Sn selections (where n is a screen number). 970 return XGetSelectionOwner( 971 xdisplay_, atom_cache_.GetAtom("WM_S0")) != None; 972 } 973 974 void DesktopRootWindowHostX11::SetWMSpecState(bool enabled, 975 ::Atom state1, 976 ::Atom state2) { 977 XEvent xclient; 978 memset(&xclient, 0, sizeof(xclient)); 979 xclient.type = ClientMessage; 980 xclient.xclient.window = xwindow_; 981 xclient.xclient.message_type = atom_cache_.GetAtom("_NET_WM_STATE"); 982 xclient.xclient.format = 32; 983 xclient.xclient.data.l[0] = 984 enabled ? k_NET_WM_STATE_ADD : k_NET_WM_STATE_REMOVE; 985 xclient.xclient.data.l[1] = state1; 986 xclient.xclient.data.l[2] = state2; 987 xclient.xclient.data.l[3] = 1; 988 xclient.xclient.data.l[4] = 0; 989 990 XSendEvent(xdisplay_, x_root_window_, False, 991 SubstructureRedirectMask | SubstructureNotifyMask, 992 &xclient); 993 } 994 995 bool DesktopRootWindowHostX11::HasWMSpecProperty(const char* property) const { 996 return window_properties_.find(atom_cache_.GetAtom(property)) != 997 window_properties_.end(); 998 } 999 1000 void DesktopRootWindowHostX11::OnCaptureReleased() { 1001 native_widget_delegate_->OnMouseCaptureLost(); 1002 g_current_capture = NULL; 1003 } 1004 1005 void DesktopRootWindowHostX11::DispatchMouseEvent(ui::MouseEvent* event) { 1006 if (!g_current_capture || g_current_capture == this) { 1007 root_window_host_delegate_->OnHostMouseEvent(event); 1008 } else { 1009 // Another DesktopRootWindowHostX11 has installed itself as 1010 // capture. Translate the event's location and dispatch to the other. 1011 event->ConvertLocationToTarget(root_window_, 1012 g_current_capture->root_window_); 1013 g_current_capture->root_window_host_delegate_->OnHostMouseEvent(event); 1014 } 1015 } 1016 1017 std::list<XID>& DesktopRootWindowHostX11::open_windows() { 1018 if (!open_windows_) 1019 open_windows_ = new std::list<XID>(); 1020 return *open_windows_; 1021 } 1022 1023 //////////////////////////////////////////////////////////////////////////////// 1024 // DesktopRootWindowHostX11, MessageLoop::Dispatcher implementation: 1025 1026 bool DesktopRootWindowHostX11::Dispatch(const base::NativeEvent& event) { 1027 XEvent* xev = event; 1028 1029 // May want to factor CheckXEventForConsistency(xev); into a common location 1030 // since it is called here. 1031 switch (xev->type) { 1032 case EnterNotify: 1033 case LeaveNotify: { 1034 ui::MouseEvent mouse_event(xev); 1035 DispatchMouseEvent(&mouse_event); 1036 break; 1037 } 1038 case Expose: { 1039 gfx::Rect damage_rect(xev->xexpose.x, xev->xexpose.y, 1040 xev->xexpose.width, xev->xexpose.height); 1041 root_window_host_delegate_->OnHostPaint(damage_rect); 1042 break; 1043 } 1044 case KeyPress: { 1045 ui::KeyEvent keydown_event(xev, false); 1046 root_window_host_delegate_->OnHostKeyEvent(&keydown_event); 1047 break; 1048 } 1049 case KeyRelease: { 1050 ui::KeyEvent keyup_event(xev, false); 1051 root_window_host_delegate_->OnHostKeyEvent(&keyup_event); 1052 break; 1053 } 1054 case ButtonPress: { 1055 if (static_cast<int>(xev->xbutton.button) == kBackMouseButton || 1056 static_cast<int>(xev->xbutton.button) == kForwardMouseButton) { 1057 aura::client::UserActionClient* gesture_client = 1058 aura::client::GetUserActionClient(root_window_); 1059 if (gesture_client) { 1060 gesture_client->OnUserAction( 1061 static_cast<int>(xev->xbutton.button) == kBackMouseButton ? 1062 aura::client::UserActionClient::BACK : 1063 aura::client::UserActionClient::FORWARD); 1064 } 1065 break; 1066 } 1067 } // fallthrough 1068 case ButtonRelease: { 1069 ui::MouseEvent mouseev(xev); 1070 DispatchMouseEvent(&mouseev); 1071 break; 1072 } 1073 case FocusOut: 1074 if (xev->xfocus.mode != NotifyGrab) { 1075 ReleaseCapture(); 1076 root_window_host_delegate_->OnHostLostWindowCapture(); 1077 } else { 1078 root_window_host_delegate_->OnHostLostMouseGrab(); 1079 } 1080 break; 1081 case ConfigureNotify: { 1082 DCHECK_EQ(xwindow_, xev->xconfigure.window); 1083 DCHECK_EQ(xwindow_, xev->xconfigure.event); 1084 // It's possible that the X window may be resized by some other means than 1085 // from within aura (e.g. the X window manager can change the size). Make 1086 // sure the root window size is maintained properly. 1087 int translated_x = xev->xconfigure.x; 1088 int translated_y = xev->xconfigure.y; 1089 if (!xev->xconfigure.send_event && !xev->xconfigure.override_redirect) { 1090 Window unused; 1091 XTranslateCoordinates(xdisplay_, xwindow_, x_root_window_, 1092 0, 0, &translated_x, &translated_y, &unused); 1093 } 1094 gfx::Rect bounds(translated_x, translated_y, 1095 xev->xconfigure.width, xev->xconfigure.height); 1096 bool size_changed = bounds_.size() != bounds.size(); 1097 bool origin_changed = bounds_.origin() != bounds.origin(); 1098 previous_bounds_ = bounds_; 1099 bounds_ = bounds; 1100 if (size_changed) 1101 root_window_host_delegate_->OnHostResized(bounds.size()); 1102 if (origin_changed) 1103 root_window_host_delegate_->OnHostMoved(bounds_.origin()); 1104 break; 1105 } 1106 case GenericEvent: { 1107 ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); 1108 if (!factory->ShouldProcessXI2Event(xev)) 1109 break; 1110 1111 ui::EventType type = ui::EventTypeFromNative(xev); 1112 XEvent last_event; 1113 int num_coalesced = 0; 1114 1115 switch (type) { 1116 // case ui::ET_TOUCH_MOVED: 1117 // num_coalesced = CoalescePendingMotionEvents(xev, &last_event); 1118 // if (num_coalesced > 0) 1119 // xev = &last_event; 1120 // // fallthrough 1121 // case ui::ET_TOUCH_PRESSED: 1122 // case ui::ET_TOUCH_RELEASED: { 1123 // ui::TouchEvent touchev(xev); 1124 // root_window_host_delegate_->OnHostTouchEvent(&touchev); 1125 // break; 1126 // } 1127 case ui::ET_MOUSE_MOVED: 1128 case ui::ET_MOUSE_DRAGGED: 1129 case ui::ET_MOUSE_PRESSED: 1130 case ui::ET_MOUSE_RELEASED: 1131 case ui::ET_MOUSE_ENTERED: 1132 case ui::ET_MOUSE_EXITED: { 1133 if (type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_DRAGGED) { 1134 // If this is a motion event, we want to coalesce all pending motion 1135 // events that are at the top of the queue. 1136 num_coalesced = ui::CoalescePendingMotionEvents(xev, &last_event); 1137 if (num_coalesced > 0) 1138 xev = &last_event; 1139 } else if (type == ui::ET_MOUSE_PRESSED) { 1140 XIDeviceEvent* xievent = 1141 static_cast<XIDeviceEvent*>(xev->xcookie.data); 1142 int button = xievent->detail; 1143 if (button == kBackMouseButton || button == kForwardMouseButton) { 1144 aura::client::UserActionClient* gesture_client = 1145 aura::client::GetUserActionClient( 1146 root_window_host_delegate_->AsRootWindow()); 1147 if (gesture_client) { 1148 bool reverse_direction = 1149 ui::IsTouchpadEvent(xev) && ui::IsNaturalScrollEnabled(); 1150 gesture_client->OnUserAction( 1151 (button == kBackMouseButton && !reverse_direction) || 1152 (button == kForwardMouseButton && reverse_direction) ? 1153 aura::client::UserActionClient::BACK : 1154 aura::client::UserActionClient::FORWARD); 1155 } 1156 break; 1157 } 1158 } else if (type == ui::ET_MOUSE_RELEASED) { 1159 XIDeviceEvent* xievent = 1160 static_cast<XIDeviceEvent*>(xev->xcookie.data); 1161 int button = xievent->detail; 1162 if (button == kBackMouseButton || button == kForwardMouseButton) { 1163 // We've already passed the back/forward mouse down to the user 1164 // action client; we want to swallow the corresponding release. 1165 break; 1166 } 1167 } 1168 ui::MouseEvent mouseev(xev); 1169 DispatchMouseEvent(&mouseev); 1170 break; 1171 } 1172 case ui::ET_MOUSEWHEEL: { 1173 ui::MouseWheelEvent mouseev(xev); 1174 DispatchMouseEvent(&mouseev); 1175 break; 1176 } 1177 case ui::ET_SCROLL_FLING_START: 1178 case ui::ET_SCROLL_FLING_CANCEL: 1179 case ui::ET_SCROLL: { 1180 ui::ScrollEvent scrollev(xev); 1181 root_window_host_delegate_->OnHostScrollEvent(&scrollev); 1182 break; 1183 } 1184 case ui::ET_UNKNOWN: 1185 break; 1186 default: 1187 NOTREACHED(); 1188 } 1189 1190 // If we coalesced an event we need to free its cookie. 1191 if (num_coalesced > 0) 1192 XFreeEventData(xev->xgeneric.display, &last_event.xcookie); 1193 break; 1194 } 1195 case MapNotify: { 1196 // If there's no window manager running, we need to assign the X input 1197 // focus to our host window. 1198 if (!IsWindowManagerPresent() && focus_when_shown_) 1199 XSetInputFocus(xdisplay_, xwindow_, RevertToNone, CurrentTime); 1200 1201 FOR_EACH_OBSERVER(DesktopRootWindowHostObserverX11, 1202 observer_list_, 1203 OnWindowMapped(xwindow_)); 1204 break; 1205 } 1206 case UnmapNotify: { 1207 FOR_EACH_OBSERVER(DesktopRootWindowHostObserverX11, 1208 observer_list_, 1209 OnWindowUnmapped(xwindow_)); 1210 break; 1211 } 1212 case ClientMessage: { 1213 Atom message_type = xev->xclient.message_type; 1214 if (message_type == atom_cache_.GetAtom("WM_PROTOCOLS")) { 1215 Atom protocol = static_cast<Atom>(xev->xclient.data.l[0]); 1216 if (protocol == atom_cache_.GetAtom("WM_DELETE_WINDOW")) { 1217 // We have received a close message from the window manager. 1218 root_window_->OnRootWindowHostCloseRequested(); 1219 } else if (protocol == atom_cache_.GetAtom("_NET_WM_PING")) { 1220 XEvent reply_event = *xev; 1221 reply_event.xclient.window = x_root_window_; 1222 1223 XSendEvent(xdisplay_, 1224 reply_event.xclient.window, 1225 False, 1226 SubstructureRedirectMask | SubstructureNotifyMask, 1227 &reply_event); 1228 } 1229 } else if (message_type == atom_cache_.GetAtom("XdndEnter")) { 1230 drag_drop_client_->OnXdndEnter(xev->xclient); 1231 } else if (message_type == atom_cache_.GetAtom("XdndLeave")) { 1232 drag_drop_client_->OnXdndLeave(xev->xclient); 1233 } else if (message_type == atom_cache_.GetAtom("XdndPosition")) { 1234 drag_drop_client_->OnXdndPosition(xev->xclient); 1235 } else if (message_type == atom_cache_.GetAtom("XdndStatus")) { 1236 drag_drop_client_->OnXdndStatus(xev->xclient); 1237 } else if (message_type == atom_cache_.GetAtom("XdndFinished")) { 1238 drag_drop_client_->OnXdndFinished(xev->xclient); 1239 } else if (message_type == atom_cache_.GetAtom("XdndDrop")) { 1240 drag_drop_client_->OnXdndDrop(xev->xclient); 1241 } 1242 break; 1243 } 1244 case MappingNotify: { 1245 switch (xev->xmapping.request) { 1246 case MappingModifier: 1247 case MappingKeyboard: 1248 XRefreshKeyboardMapping(&xev->xmapping); 1249 root_window_->OnKeyboardMappingChanged(); 1250 break; 1251 case MappingPointer: 1252 ui::UpdateButtonMap(); 1253 break; 1254 default: 1255 NOTIMPLEMENTED() << " Unknown request: " << xev->xmapping.request; 1256 break; 1257 } 1258 break; 1259 } 1260 case MotionNotify: { 1261 // Discard all but the most recent motion event that targets the same 1262 // window with unchanged state. 1263 XEvent last_event; 1264 while (XPending(xev->xany.display)) { 1265 XEvent next_event; 1266 XPeekEvent(xev->xany.display, &next_event); 1267 if (next_event.type == MotionNotify && 1268 next_event.xmotion.window == xev->xmotion.window && 1269 next_event.xmotion.subwindow == xev->xmotion.subwindow && 1270 next_event.xmotion.state == xev->xmotion.state) { 1271 XNextEvent(xev->xany.display, &last_event); 1272 xev = &last_event; 1273 } else { 1274 break; 1275 } 1276 } 1277 1278 ui::MouseEvent mouseev(xev); 1279 DispatchMouseEvent(&mouseev); 1280 break; 1281 } 1282 case PropertyNotify: { 1283 // Get our new window property state if the WM has told us its changed. 1284 ::Atom state = atom_cache_.GetAtom("_NET_WM_STATE"); 1285 1286 std::vector< ::Atom> atom_list; 1287 if (xev->xproperty.atom == state && 1288 ui::GetAtomArrayProperty(xwindow_, "_NET_WM_STATE", &atom_list)) { 1289 window_properties_.clear(); 1290 std::copy(atom_list.begin(), atom_list.end(), 1291 inserter(window_properties_, window_properties_.begin())); 1292 1293 if (!restored_bounds_.IsEmpty() && !IsMaximized()) { 1294 // If we have restored bounds, but WM_STATE no longer claims to be 1295 // maximized, we should clear our restored bounds. 1296 restored_bounds_ = gfx::Rect(); 1297 } else if (IsMaximized() && restored_bounds_.IsEmpty()) { 1298 // The request that we become maximized originated from a different 1299 // process. |bounds_| already contains our maximized bounds. Do a 1300 // best effort attempt to get restored bounds by setting it to our 1301 // previously set bounds (and if we get this wrong, we aren't any 1302 // worse off since we'd otherwise be returning our maximized bounds). 1303 restored_bounds_ = previous_bounds_; 1304 } 1305 1306 // Now that we have different window properties, we may need to 1307 // relayout the window. (The windows code doesn't need this because 1308 // their window change is synchronous.) 1309 // 1310 // TODO(erg): While this does work, there's a quick flash showing the 1311 // tabstrip/toolbar/etc. when going into fullscreen mode before hiding 1312 // those parts of the UI because we receive the sizing event from the 1313 // window manager before we receive the event that changes the 1314 // fullscreen state. Unsure what to do about that. 1315 Widget* widget = native_widget_delegate_->AsWidget(); 1316 NonClientView* non_client_view = widget->non_client_view(); 1317 // non_client_view may be NULL, especially during creation. 1318 if (non_client_view) { 1319 non_client_view->client_view()->InvalidateLayout(); 1320 non_client_view->InvalidateLayout(); 1321 } 1322 widget->GetRootView()->Layout(); 1323 } 1324 break; 1325 } 1326 case SelectionNotify: { 1327 drag_drop_client_->OnSelectionNotify(xev->xselection); 1328 break; 1329 } 1330 } 1331 return true; 1332 } 1333 1334 //////////////////////////////////////////////////////////////////////////////// 1335 // DesktopRootWindowHost, public: 1336 1337 // static 1338 DesktopRootWindowHost* DesktopRootWindowHost::Create( 1339 internal::NativeWidgetDelegate* native_widget_delegate, 1340 DesktopNativeWidgetAura* desktop_native_widget_aura, 1341 const gfx::Rect& initial_bounds) { 1342 return new DesktopRootWindowHostX11(native_widget_delegate, 1343 desktop_native_widget_aura, 1344 initial_bounds); 1345 } 1346 1347 // static 1348 ui::NativeTheme* DesktopRootWindowHost::GetNativeTheme(aura::Window* window) { 1349 const ui::LinuxUI* linux_ui = ui::LinuxUI::instance(); 1350 if (linux_ui) { 1351 ui::NativeTheme* native_theme = linux_ui->GetNativeTheme(); 1352 if (native_theme) 1353 return native_theme; 1354 } 1355 1356 return ui::NativeTheme::instance(); 1357 } 1358 1359 } // namespace views 1360