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