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.
5 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
7 #include <X11/extensions/shape.h>
8 #include <X11/extensions/XInput2.h>
10 #include <X11/Xregion.h>
11 #include <X11/Xutil.h>
13 #include "base/basictypes.h"
14 #include "base/command_line.h"
15 #include "base/debug/trace_event.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/window.h"
22 #include "ui/aura/window_event_dispatcher.h"
23 #include "ui/aura/window_property.h"
24 #include "ui/base/dragdrop/os_exchange_data_provider_aurax11.h"
25 #include "ui/base/hit_test.h"
26 #include "ui/base/x/x11_util.h"
27 #include "ui/events/event_utils.h"
28 #include "ui/events/platform/platform_event_source.h"
29 #include "ui/events/platform/x11/x11_event_source.h"
30 #include "ui/events/x/device_data_manager.h"
31 #include "ui/events/x/device_list_cache_x.h"
32 #include "ui/events/x/touch_factory_x11.h"
33 #include "ui/gfx/image/image_skia.h"
34 #include "ui/gfx/image/image_skia_rep.h"
35 #include "ui/gfx/insets.h"
36 #include "ui/gfx/path.h"
37 #include "ui/gfx/path_x11.h"
38 #include "ui/gfx/screen.h"
39 #include "ui/native_theme/native_theme.h"
40 #include "ui/views/corewm/tooltip_aura.h"
41 #include "ui/views/ime/input_method.h"
42 #include "ui/views/linux_ui/linux_ui.h"
43 #include "ui/views/views_delegate.h"
44 #include "ui/views/views_switches.h"
45 #include "ui/views/widget/desktop_aura/desktop_dispatcher_client.h"
46 #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.h"
47 #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
48 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
49 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_observer_x11.h"
50 #include "ui/views/widget/desktop_aura/x11_desktop_handler.h"
51 #include "ui/views/widget/desktop_aura/x11_desktop_window_move_client.h"
52 #include "ui/views/widget/desktop_aura/x11_scoped_capture.h"
53 #include "ui/views/widget/desktop_aura/x11_window_event_filter.h"
54 #include "ui/wm/core/compound_event_filter.h"
55 #include "ui/wm/core/window_util.h"
59 DesktopWindowTreeHostX11* DesktopWindowTreeHostX11::g_current_capture =
61 std::list<XID>* DesktopWindowTreeHostX11::open_windows_ = NULL;
63 DEFINE_WINDOW_PROPERTY_KEY(
64 aura::Window*, kViewsWindowForRootWindow, NULL);
66 DEFINE_WINDOW_PROPERTY_KEY(
67 DesktopWindowTreeHostX11*, kHostForRootWindow, NULL);
71 // Constants that are part of EWMH.
72 const int k_NET_WM_STATE_ADD = 1;
73 const int k_NET_WM_STATE_REMOVE = 0;
75 const char* kAtomsToCache[] = {
85 "_NET_WM_STATE_ABOVE",
86 "_NET_WM_STATE_FULLSCREEN",
87 "_NET_WM_STATE_HIDDEN",
88 "_NET_WM_STATE_MAXIMIZED_HORZ",
89 "_NET_WM_STATE_MAXIMIZED_VERT",
90 "_NET_WM_STATE_SKIP_TASKBAR",
91 "_NET_WM_STATE_STICKY",
93 "_NET_WM_WINDOW_OPACITY",
94 "_NET_WM_WINDOW_TYPE",
95 "_NET_WM_WINDOW_TYPE_DND",
96 "_NET_WM_WINDOW_TYPE_MENU",
97 "_NET_WM_WINDOW_TYPE_NORMAL",
98 "_NET_WM_WINDOW_TYPE_NOTIFICATION",
99 "_NET_WM_WINDOW_TYPE_TOOLTIP",
112 "XdndProxy", // Proxy windows?
121 ////////////////////////////////////////////////////////////////////////////////
122 // DesktopWindowTreeHostX11, public:
124 DesktopWindowTreeHostX11::DesktopWindowTreeHostX11(
125 internal::NativeWidgetDelegate* native_widget_delegate,
126 DesktopNativeWidgetAura* desktop_native_widget_aura)
127 : close_widget_factory_(this),
128 xdisplay_(gfx::GetXDisplay()),
130 x_root_window_(DefaultRootWindow(xdisplay_)),
131 atom_cache_(xdisplay_, kAtomsToCache),
132 window_mapped_(false),
133 is_fullscreen_(false),
134 is_always_on_top_(false),
135 use_native_frame_(false),
136 use_argb_visual_(false),
137 drag_drop_client_(NULL),
138 current_cursor_(ui::kCursorNull),
139 native_widget_delegate_(native_widget_delegate),
140 desktop_native_widget_aura_(desktop_native_widget_aura),
141 content_window_(NULL),
142 window_parent_(NULL),
143 custom_window_shape_(NULL),
144 urgency_hint_set_(false) {
147 DesktopWindowTreeHostX11::~DesktopWindowTreeHostX11() {
148 window()->ClearProperty(kHostForRootWindow);
149 aura::client::SetWindowMoveClient(window(), NULL);
150 desktop_native_widget_aura_->OnDesktopWindowTreeHostDestroyed(this);
151 if (custom_window_shape_)
152 XDestroyRegion(custom_window_shape_);
157 aura::Window* DesktopWindowTreeHostX11::GetContentWindowForXID(XID xid) {
158 aura::WindowTreeHost* host =
159 aura::WindowTreeHost::GetForAcceleratedWidget(xid);
160 return host ? host->window()->GetProperty(kViewsWindowForRootWindow) : NULL;
164 DesktopWindowTreeHostX11* DesktopWindowTreeHostX11::GetHostForXID(XID xid) {
165 aura::WindowTreeHost* host =
166 aura::WindowTreeHost::GetForAcceleratedWidget(xid);
167 return host ? host->window()->GetProperty(kHostForRootWindow) : NULL;
171 std::vector<aura::Window*> DesktopWindowTreeHostX11::GetAllOpenWindows() {
172 std::vector<aura::Window*> windows(open_windows().size());
173 std::transform(open_windows().begin(),
174 open_windows().end(),
176 GetContentWindowForXID);
180 gfx::Rect DesktopWindowTreeHostX11::GetX11RootWindowBounds() const {
184 void DesktopWindowTreeHostX11::HandleNativeWidgetActivationChanged(
189 open_windows().remove(xwindow_);
190 open_windows().insert(open_windows().begin(), xwindow_);
193 desktop_native_widget_aura_->HandleActivationChanged(active);
195 native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint();
198 void DesktopWindowTreeHostX11::AddObserver(
199 views::DesktopWindowTreeHostObserverX11* observer) {
200 observer_list_.AddObserver(observer);
203 void DesktopWindowTreeHostX11::RemoveObserver(
204 views::DesktopWindowTreeHostObserverX11* observer) {
205 observer_list_.RemoveObserver(observer);
208 void DesktopWindowTreeHostX11::CleanUpWindowList() {
209 delete open_windows_;
210 open_windows_ = NULL;
213 ////////////////////////////////////////////////////////////////////////////////
214 // DesktopWindowTreeHostX11, DesktopWindowTreeHost implementation:
216 void DesktopWindowTreeHostX11::Init(aura::Window* content_window,
217 const Widget::InitParams& params) {
218 content_window_ = content_window;
220 // TODO(erg): Check whether we *should* be building a WindowTreeHost here, or
221 // whether we should be proxying requests to another DRWHL.
223 // In some situations, views tries to make a zero sized window, and that
224 // makes us crash. Make sure we have valid sizes.
225 Widget::InitParams sanitized_params = params;
226 if (sanitized_params.bounds.width() == 0)
227 sanitized_params.bounds.set_width(100);
228 if (sanitized_params.bounds.height() == 0)
229 sanitized_params.bounds.set_height(100);
231 InitX11Window(sanitized_params);
234 void DesktopWindowTreeHostX11::OnNativeWidgetCreated(
235 const Widget::InitParams& params) {
236 window()->SetProperty(kViewsWindowForRootWindow, content_window_);
237 window()->SetProperty(kHostForRootWindow, this);
239 // Ensure that the X11DesktopHandler exists so that it dispatches activation
241 X11DesktopHandler::get();
243 // TODO(erg): Unify this code once the other consumer goes away.
244 x11_window_event_filter_.reset(new X11WindowEventFilter(this));
245 SetUseNativeFrame(params.type == Widget::InitParams::TYPE_WINDOW &&
246 !params.remove_standard_frame);
247 desktop_native_widget_aura_->root_window_event_filter()->AddHandler(
248 x11_window_event_filter_.get());
250 x11_window_move_client_.reset(new X11DesktopWindowMoveClient);
251 aura::client::SetWindowMoveClient(window(), x11_window_move_client_.get());
253 SetWindowTransparency();
255 native_widget_delegate_->OnNativeWidgetCreated(true);
258 scoped_ptr<corewm::Tooltip> DesktopWindowTreeHostX11::CreateTooltip() {
259 return scoped_ptr<corewm::Tooltip>(
260 new corewm::TooltipAura(gfx::SCREEN_TYPE_NATIVE));
263 scoped_ptr<aura::client::DragDropClient>
264 DesktopWindowTreeHostX11::CreateDragDropClient(
265 DesktopNativeCursorManager* cursor_manager) {
266 drag_drop_client_ = new DesktopDragDropClientAuraX11(
267 window(), cursor_manager, xdisplay_, xwindow_);
268 return scoped_ptr<aura::client::DragDropClient>(drag_drop_client_).Pass();
271 void DesktopWindowTreeHostX11::Close() {
272 // TODO(erg): Might need to do additional hiding tasks here.
274 if (!close_widget_factory_.HasWeakPtrs()) {
275 // And we delay the close so that if we are called from an ATL callback,
276 // we don't destroy the window before the callback returned (as the caller
277 // may delete ourselves on destroy and the ATL callback would still
278 // dereference us when the callback returns).
279 base::MessageLoop::current()->PostTask(
281 base::Bind(&DesktopWindowTreeHostX11::CloseNow,
282 close_widget_factory_.GetWeakPtr()));
286 void DesktopWindowTreeHostX11::CloseNow() {
287 if (xwindow_ == None)
290 x11_capture_.reset();
291 native_widget_delegate_->OnNativeWidgetDestroying();
293 // If we have children, close them. Use a copy for iteration because they'll
294 // remove themselves.
295 std::set<DesktopWindowTreeHostX11*> window_children_copy = window_children_;
296 for (std::set<DesktopWindowTreeHostX11*>::iterator it =
297 window_children_copy.begin(); it != window_children_copy.end();
301 DCHECK(window_children_.empty());
303 // If we have a parent, remove ourselves from its children list.
304 if (window_parent_) {
305 window_parent_->window_children_.erase(this);
306 window_parent_ = NULL;
309 // Remove the event listeners we've installed. We need to remove these
310 // because otherwise we get assert during ~WindowEventDispatcher().
311 desktop_native_widget_aura_->root_window_event_filter()->RemoveHandler(
312 x11_window_event_filter_.get());
314 // Destroy the compositor before destroying the |xwindow_| since shutdown
315 // may try to swap, and the swap without a window causes an X error, which
316 // causes a crash with in-process renderer.
319 open_windows().remove(xwindow_);
320 // Actually free our native resources.
321 if (ui::PlatformEventSource::GetInstance())
322 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
323 XDestroyWindow(xdisplay_, xwindow_);
326 desktop_native_widget_aura_->OnHostClosed();
329 aura::WindowTreeHost* DesktopWindowTreeHostX11::AsWindowTreeHost() {
333 void DesktopWindowTreeHostX11::ShowWindowWithState(
334 ui::WindowShowState show_state) {
336 MapWindow(show_state);
338 if (show_state == ui::SHOW_STATE_NORMAL ||
339 show_state == ui::SHOW_STATE_MAXIMIZED) {
340 // Note: XFCE ignores a maximize hint given before mapping the window.
341 if (show_state == ui::SHOW_STATE_MAXIMIZED)
346 native_widget_delegate_->AsWidget()->SetInitialFocus(show_state);
349 void DesktopWindowTreeHostX11::ShowMaximizedWithBounds(
350 const gfx::Rect& restored_bounds) {
351 ShowWindowWithState(ui::SHOW_STATE_MAXIMIZED);
352 // Enforce |restored_bounds_| since calling Maximize() could have reset it.
353 restored_bounds_ = restored_bounds;
356 bool DesktopWindowTreeHostX11::IsVisible() const {
357 return window_mapped_;
360 void DesktopWindowTreeHostX11::SetSize(const gfx::Size& size) {
361 bool size_changed = bounds_.size() != size;
362 XResizeWindow(xdisplay_, xwindow_, size.width(), size.height());
363 bounds_.set_size(size);
370 void DesktopWindowTreeHostX11::StackAtTop() {
371 XRaiseWindow(xdisplay_, xwindow_);
374 void DesktopWindowTreeHostX11::CenterWindow(const gfx::Size& size) {
375 gfx::Rect parent_bounds = GetWorkAreaBoundsInScreen();
377 // If |window_|'s transient parent bounds are big enough to contain |size|,
379 if (wm::GetTransientParent(content_window_)) {
380 gfx::Rect transient_parent_rect =
381 wm::GetTransientParent(content_window_)->GetBoundsInScreen();
382 if (transient_parent_rect.height() >= size.height() &&
383 transient_parent_rect.width() >= size.width()) {
384 parent_bounds = transient_parent_rect;
388 gfx::Rect window_bounds(
389 parent_bounds.x() + (parent_bounds.width() - size.width()) / 2,
390 parent_bounds.y() + (parent_bounds.height() - size.height()) / 2,
393 // Don't size the window bigger than the parent, otherwise the user may not be
394 // able to close or move it.
395 window_bounds.AdjustToFit(parent_bounds);
397 SetBounds(window_bounds);
400 void DesktopWindowTreeHostX11::GetWindowPlacement(
402 ui::WindowShowState* show_state) const {
403 *bounds = GetRestoredBounds();
405 if (IsFullscreen()) {
406 *show_state = ui::SHOW_STATE_FULLSCREEN;
407 } else if (IsMinimized()) {
408 *show_state = ui::SHOW_STATE_MINIMIZED;
409 } else if (IsMaximized()) {
410 *show_state = ui::SHOW_STATE_MAXIMIZED;
411 } else if (!IsActive()) {
412 *show_state = ui::SHOW_STATE_INACTIVE;
414 *show_state = ui::SHOW_STATE_NORMAL;
418 gfx::Rect DesktopWindowTreeHostX11::GetWindowBoundsInScreen() const {
422 gfx::Rect DesktopWindowTreeHostX11::GetClientAreaBoundsInScreen() const {
423 // TODO(erg): The NativeWidgetAura version returns |bounds_|, claiming its
424 // needed for View::ConvertPointToScreen() to work
425 // correctly. DesktopWindowTreeHostWin::GetClientAreaBoundsInScreen() just
426 // asks windows what it thinks the client rect is.
428 // Attempts to calculate the rect by asking the NonClientFrameView what it
429 // thought its GetBoundsForClientView() were broke combobox drop down
434 gfx::Rect DesktopWindowTreeHostX11::GetRestoredBounds() const {
435 // We can't reliably track the restored bounds of a window, but we can get
436 // the 90% case down. When *chrome* is the process that requests maximizing
437 // or restoring bounds, we can record the current bounds before we request
438 // maximization, and clear it when we detect a state change.
439 if (!restored_bounds_.IsEmpty())
440 return restored_bounds_;
442 return GetWindowBoundsInScreen();
445 gfx::Rect DesktopWindowTreeHostX11::GetWorkAreaBoundsInScreen() const {
446 std::vector<int> value;
447 if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) &&
449 return gfx::Rect(value[0], value[1], value[2], value[3]);
452 // Fetch the geometry of the root window.
455 unsigned int width, height;
456 unsigned int border_width, depth;
457 if (!XGetGeometry(xdisplay_, x_root_window_, &root, &x, &y,
458 &width, &height, &border_width, &depth)) {
460 return gfx::Rect(0, 0, 10, 10);
463 return gfx::Rect(x, y, width, height);
466 void DesktopWindowTreeHostX11::SetShape(gfx::NativeRegion native_region) {
467 if (custom_window_shape_)
468 XDestroyRegion(custom_window_shape_);
469 custom_window_shape_ = gfx::CreateRegionFromSkRegion(*native_region);
471 delete native_region;
474 void DesktopWindowTreeHostX11::Activate() {
478 X11DesktopHandler::get()->ActivateWindow(xwindow_);
481 void DesktopWindowTreeHostX11::Deactivate() {
485 x11_capture_.reset();
486 XLowerWindow(xdisplay_, xwindow_);
489 bool DesktopWindowTreeHostX11::IsActive() const {
490 return X11DesktopHandler::get()->IsActiveWindow(xwindow_);
493 void DesktopWindowTreeHostX11::Maximize() {
494 // When we are in the process of requesting to maximize a window, we can
495 // accurately keep track of our restored bounds instead of relying on the
496 // heuristics that are in the PropertyNotify and ConfigureNotify handlers.
497 restored_bounds_ = bounds_;
500 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"),
501 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ"));
504 void DesktopWindowTreeHostX11::Minimize() {
505 x11_capture_.reset();
506 XIconifyWindow(xdisplay_, xwindow_, 0);
509 void DesktopWindowTreeHostX11::Restore() {
510 SetWMSpecState(false,
511 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"),
512 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ"));
515 bool DesktopWindowTreeHostX11::IsMaximized() const {
516 return (HasWMSpecProperty("_NET_WM_STATE_MAXIMIZED_VERT") &&
517 HasWMSpecProperty("_NET_WM_STATE_MAXIMIZED_HORZ"));
520 bool DesktopWindowTreeHostX11::IsMinimized() const {
521 return HasWMSpecProperty("_NET_WM_STATE_HIDDEN");
524 bool DesktopWindowTreeHostX11::HasCapture() const {
525 return g_current_capture == this;
528 void DesktopWindowTreeHostX11::SetAlwaysOnTop(bool always_on_top) {
529 is_always_on_top_ = always_on_top;
530 SetWMSpecState(always_on_top,
531 atom_cache_.GetAtom("_NET_WM_STATE_ABOVE"),
535 bool DesktopWindowTreeHostX11::IsAlwaysOnTop() const {
536 return is_always_on_top_;
539 void DesktopWindowTreeHostX11::SetVisibleOnAllWorkspaces(bool always_visible) {
540 SetWMSpecState(always_visible,
541 atom_cache_.GetAtom("_NET_WM_STATE_STICKY"),
545 bool DesktopWindowTreeHostX11::SetWindowTitle(const base::string16& title) {
546 if (window_title_ == title)
548 window_title_ = title;
549 std::string utf8str = base::UTF16ToUTF8(title);
550 XChangeProperty(xdisplay_,
552 atom_cache_.GetAtom("_NET_WM_NAME"),
553 atom_cache_.GetAtom("UTF8_STRING"),
556 reinterpret_cast<const unsigned char*>(utf8str.c_str()),
558 // TODO(erg): This is technically wrong. So XStoreName and friends expect
559 // this in Host Portable Character Encoding instead of UTF-8, which I believe
560 // is Compound Text. This shouldn't matter 90% of the time since this is the
561 // fallback to the UTF8 property above.
562 XStoreName(xdisplay_, xwindow_, utf8str.c_str());
566 void DesktopWindowTreeHostX11::ClearNativeFocus() {
567 // This method is weird and misnamed. Instead of clearing the native focus,
568 // it sets the focus to our |content_window_|, which will trigger a cascade
569 // of focus changes into views.
570 if (content_window_ && aura::client::GetFocusClient(content_window_) &&
571 content_window_->Contains(
572 aura::client::GetFocusClient(content_window_)->GetFocusedWindow())) {
573 aura::client::GetFocusClient(content_window_)->FocusWindow(content_window_);
577 Widget::MoveLoopResult DesktopWindowTreeHostX11::RunMoveLoop(
578 const gfx::Vector2d& drag_offset,
579 Widget::MoveLoopSource source,
580 Widget::MoveLoopEscapeBehavior escape_behavior) {
581 aura::client::WindowMoveSource window_move_source =
582 source == Widget::MOVE_LOOP_SOURCE_MOUSE ?
583 aura::client::WINDOW_MOVE_SOURCE_MOUSE :
584 aura::client::WINDOW_MOVE_SOURCE_TOUCH;
585 if (x11_window_move_client_->RunMoveLoop(content_window_, drag_offset,
586 window_move_source) == aura::client::MOVE_SUCCESSFUL)
587 return Widget::MOVE_LOOP_SUCCESSFUL;
589 return Widget::MOVE_LOOP_CANCELED;
592 void DesktopWindowTreeHostX11::EndMoveLoop() {
593 x11_window_move_client_->EndMoveLoop();
596 void DesktopWindowTreeHostX11::SetVisibilityChangedAnimationsEnabled(
598 // Much like the previous NativeWidgetGtk, we don't have anything to do here.
601 bool DesktopWindowTreeHostX11::ShouldUseNativeFrame() const {
602 return use_native_frame_;
605 bool DesktopWindowTreeHostX11::ShouldWindowContentsBeTransparent() const {
609 void DesktopWindowTreeHostX11::FrameTypeChanged() {
610 Widget::FrameType new_type =
611 native_widget_delegate_->AsWidget()->frame_type();
612 SetUseNativeFrame(new_type == Widget::FRAME_TYPE_FORCE_NATIVE);
613 // Replace the frame and layout the contents. Even though we don't have a
614 // swapable glass frame like on Windows, we still replace the frame because
615 // the button assets don't update otherwise.
616 native_widget_delegate_->AsWidget()->non_client_view()->UpdateFrame();
619 NonClientFrameView* DesktopWindowTreeHostX11::CreateNonClientFrameView() {
623 void DesktopWindowTreeHostX11::SetFullscreen(bool fullscreen) {
624 if (is_fullscreen_ == fullscreen)
626 is_fullscreen_ = fullscreen;
627 SetWMSpecState(fullscreen,
628 atom_cache_.GetAtom("_NET_WM_STATE_FULLSCREEN"),
630 // Try to guess the size we will have after the switch to/from fullscreen:
631 // - (may) avoid transient states
632 // - works around Flash content which expects to have the size updated
634 // See https://crbug.com/361408
636 restored_bounds_ = bounds_;
637 const gfx::Display display =
638 gfx::Screen::GetScreenFor(NULL)->GetDisplayNearestWindow(window());
639 bounds_ = display.bounds();
641 bounds_ = restored_bounds_;
643 OnHostMoved(bounds_.origin());
644 OnHostResized(bounds_.size());
647 bool DesktopWindowTreeHostX11::IsFullscreen() const {
648 return is_fullscreen_;
651 void DesktopWindowTreeHostX11::SetOpacity(unsigned char opacity) {
652 // X server opacity is in terms of 32 bit unsigned int space, and counts from
653 // the opposite direction.
654 // XChangeProperty() expects "cardinality" to be long.
655 unsigned long cardinality = opacity * 0x1010101;
657 if (cardinality == 0xffffffff) {
658 XDeleteProperty(xdisplay_, xwindow_,
659 atom_cache_.GetAtom("_NET_WM_WINDOW_OPACITY"));
661 XChangeProperty(xdisplay_, xwindow_,
662 atom_cache_.GetAtom("_NET_WM_WINDOW_OPACITY"),
665 reinterpret_cast<unsigned char*>(&cardinality), 1);
669 void DesktopWindowTreeHostX11::SetWindowIcons(
670 const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) {
671 // TODO(erg): The way we handle icons across different versions of chrome
672 // could be substantially improved. The Windows version does its own thing
673 // and only sometimes comes down this code path. The icon stuff in
674 // ChromeViewsDelegate is hard coded to use HICONs. Likewise, we're hard
675 // coded to be given two images instead of an arbitrary collection of images
676 // so that we can pass to the WM.
678 // All of this could be made much, much better.
679 std::vector<unsigned long> data;
681 if (window_icon.HasRepresentation(1.0f))
682 SerializeImageRepresentation(window_icon.GetRepresentation(1.0f), &data);
684 if (app_icon.HasRepresentation(1.0f))
685 SerializeImageRepresentation(app_icon.GetRepresentation(1.0f), &data);
688 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("_NET_WM_ICON"));
690 ui::SetAtomArrayProperty(xwindow_, "_NET_WM_ICON", "CARDINAL", data);
693 void DesktopWindowTreeHostX11::InitModalType(ui::ModalType modal_type) {
694 switch (modal_type) {
695 case ui::MODAL_TYPE_NONE:
698 // TODO(erg): Figure out under what situations |modal_type| isn't
699 // none. The comment in desktop_native_widget_aura.cc suggests that this
705 void DesktopWindowTreeHostX11::FlashFrame(bool flash_frame) {
706 if (urgency_hint_set_ == flash_frame)
709 XWMHints* hints = XGetWMHints(xdisplay_, xwindow_);
711 // The window hasn't had its hints set yet.
712 hints = XAllocWMHints();
716 hints->flags |= XUrgencyHint;
718 hints->flags &= ~XUrgencyHint;
720 XSetWMHints(xdisplay_, xwindow_, hints);
723 urgency_hint_set_ = flash_frame;
726 void DesktopWindowTreeHostX11::OnRootViewLayout() const {
731 long supplied_return;
732 XGetWMNormalHints(xdisplay_, xwindow_, &hints, &supplied_return);
734 gfx::Size minimum = native_widget_delegate_->GetMinimumSize();
735 if (minimum.IsEmpty()) {
736 hints.flags &= ~PMinSize;
738 hints.flags |= PMinSize;
739 hints.min_width = minimum.width();
740 hints.min_height = minimum.height();
743 gfx::Size maximum = native_widget_delegate_->GetMaximumSize();
744 if (maximum.IsEmpty()) {
745 hints.flags &= ~PMaxSize;
747 hints.flags |= PMaxSize;
748 hints.max_width = maximum.width();
749 hints.max_height = maximum.height();
752 XSetWMNormalHints(xdisplay_, xwindow_, &hints);
755 void DesktopWindowTreeHostX11::OnNativeWidgetFocus() {
756 native_widget_delegate_->AsWidget()->GetInputMethod()->OnFocus();
759 void DesktopWindowTreeHostX11::OnNativeWidgetBlur() {
761 x11_capture_.reset();
762 native_widget_delegate_->AsWidget()->GetInputMethod()->OnBlur();
766 bool DesktopWindowTreeHostX11::IsAnimatingClosed() const {
770 ////////////////////////////////////////////////////////////////////////////////
771 // DesktopWindowTreeHostX11, aura::WindowTreeHost implementation:
773 ui::EventSource* DesktopWindowTreeHostX11::GetEventSource() {
777 gfx::AcceleratedWidget DesktopWindowTreeHostX11::GetAcceleratedWidget() {
781 void DesktopWindowTreeHostX11::Show() {
782 ShowWindowWithState(ui::SHOW_STATE_NORMAL);
783 native_widget_delegate_->OnNativeWidgetVisibilityChanged(true);
786 void DesktopWindowTreeHostX11::Hide() {
787 if (window_mapped_) {
788 XWithdrawWindow(xdisplay_, xwindow_, 0);
789 window_mapped_ = false;
791 native_widget_delegate_->OnNativeWidgetVisibilityChanged(false);
794 gfx::Rect DesktopWindowTreeHostX11::GetBounds() const {
798 void DesktopWindowTreeHostX11::SetBounds(const gfx::Rect& bounds) {
799 bool origin_changed = bounds_.origin() != bounds.origin();
800 bool size_changed = bounds_.size() != bounds.size();
801 XWindowChanges changes = {0};
802 unsigned value_mask = 0;
805 // X11 will send an XError at our process if have a 0 sized window.
806 DCHECK_GT(bounds.width(), 0);
807 DCHECK_GT(bounds.height(), 0);
809 changes.width = bounds.width();
810 changes.height = bounds.height();
811 value_mask |= CWHeight | CWWidth;
814 if (origin_changed) {
815 changes.x = bounds.x();
816 changes.y = bounds.y();
817 value_mask |= CWX | CWY;
820 XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes);
822 // Assume that the resize will go through as requested, which should be the
823 // case if we're running without a window manager. If there's a window
824 // manager, it can modify or ignore the request, but (per ICCCM) we'll get a
825 // (possibly synthetic) ConfigureNotify about the actual size and correct
830 native_widget_delegate_->AsWidget()->OnNativeWidgetMove();
832 OnHostResized(bounds.size());
837 gfx::Point DesktopWindowTreeHostX11::GetLocationOnNativeScreen() const {
838 return bounds_.origin();
841 void DesktopWindowTreeHostX11::SetCapture() {
842 // This is vaguely based on the old NativeWidgetGtk implementation.
844 // X11's XPointerGrab() shouldn't be used for everything; it doesn't map
845 // cleanly to Windows' SetCapture(). GTK only provides a separate concept of
846 // a grab that wasn't the X11 pointer grab, but was instead a manual
847 // redirection of the event. (You need to drop into GDK if you want to
848 // perform a raw X11 grab).
850 if (g_current_capture)
851 g_current_capture->OnCaptureReleased();
853 g_current_capture = this;
854 x11_capture_.reset(new X11ScopedCapture(xwindow_));
857 void DesktopWindowTreeHostX11::ReleaseCapture() {
858 if (g_current_capture == this)
859 g_current_capture->OnCaptureReleased();
862 void DesktopWindowTreeHostX11::SetCursorNative(gfx::NativeCursor cursor) {
863 XDefineCursor(xdisplay_, xwindow_, cursor.platform());
866 void DesktopWindowTreeHostX11::MoveCursorToNative(const gfx::Point& location) {
867 XWarpPointer(xdisplay_, None, x_root_window_, 0, 0, 0, 0,
868 bounds_.x() + location.x(), bounds_.y() + location.y());
871 void DesktopWindowTreeHostX11::OnCursorVisibilityChangedNative(bool show) {
872 // TODO(erg): Conditional on us enabling touch on desktop linux builds, do
873 // the same tap-to-click disabling here that chromeos does.
876 void DesktopWindowTreeHostX11::PostNativeEvent(
877 const base::NativeEvent& native_event) {
880 XEvent xevent = *native_event;
881 xevent.xany.display = xdisplay_;
882 xevent.xany.window = xwindow_;
884 switch (xevent.type) {
891 case ButtonRelease: {
892 // The fields used below are in the same place for all of events
893 // above. Using xmotion from XEvent's unions to avoid repeating
895 xevent.xmotion.root = x_root_window_;
896 xevent.xmotion.time = CurrentTime;
898 gfx::Point point(xevent.xmotion.x, xevent.xmotion.y);
899 ConvertPointToNativeScreen(&point);
900 xevent.xmotion.x_root = point.x();
901 xevent.xmotion.y_root = point.y();
906 XSendEvent(xdisplay_, xwindow_, False, 0, &xevent);
909 void DesktopWindowTreeHostX11::OnDeviceScaleFactorChanged(
910 float device_scale_factor) {
913 ////////////////////////////////////////////////////////////////////////////////
914 // DesktopWindowTreeHostX11, ui::EventSource implementation:
916 ui::EventProcessor* DesktopWindowTreeHostX11::GetEventProcessor() {
920 ////////////////////////////////////////////////////////////////////////////////
921 // DesktopWindowTreeHostX11, private:
923 void DesktopWindowTreeHostX11::InitX11Window(
924 const Widget::InitParams& params) {
925 unsigned long attribute_mask = CWBackPixmap;
926 XSetWindowAttributes swa;
927 memset(&swa, 0, sizeof(swa));
928 swa.background_pixmap = None;
931 switch (params.type) {
932 case Widget::InitParams::TYPE_MENU:
933 swa.override_redirect = True;
934 window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_MENU");
936 case Widget::InitParams::TYPE_TOOLTIP:
937 window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_TOOLTIP");
939 case Widget::InitParams::TYPE_POPUP:
940 swa.override_redirect = True;
941 window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_NOTIFICATION");
943 case Widget::InitParams::TYPE_DRAG:
944 swa.override_redirect = True;
945 window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_DND");
948 window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_NORMAL");
951 if (swa.override_redirect)
952 attribute_mask |= CWOverrideRedirect;
954 // Detect whether we're running inside a compositing manager. If so, try to
955 // use the ARGB visual. Otherwise, just use our parent's visual.
956 Visual* visual = CopyFromParent;
957 int depth = CopyFromParent;
958 if (CommandLine::ForCurrentProcess()->HasSwitch(
959 switches::kEnableTransparentVisuals) &&
960 XGetSelectionOwner(xdisplay_,
961 atom_cache_.GetAtom("_NET_WM_CM_S0")) != None) {
962 Visual* rgba_visual = GetARGBVisual();
964 visual = rgba_visual;
967 attribute_mask |= CWColormap;
968 swa.colormap = XCreateColormap(xdisplay_, x_root_window_, visual,
971 // x.org will BadMatch if we don't set a border when the depth isn't the
972 // same as the parent depth.
973 attribute_mask |= CWBorderPixel;
974 swa.border_pixel = 0;
976 use_argb_visual_ = true;
980 bounds_ = params.bounds;
981 xwindow_ = XCreateWindow(
982 xdisplay_, x_root_window_,
983 bounds_.x(), bounds_.y(),
984 bounds_.width(), bounds_.height(),
991 if (ui::PlatformEventSource::GetInstance())
992 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
993 open_windows().push_back(xwindow_);
995 // TODO(erg): Maybe need to set a ViewProp here like in RWHL::RWHL().
997 long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
998 KeyPressMask | KeyReleaseMask |
999 EnterWindowMask | LeaveWindowMask |
1000 ExposureMask | VisibilityChangeMask |
1001 StructureNotifyMask | PropertyChangeMask |
1003 XSelectInput(xdisplay_, xwindow_, event_mask);
1006 if (ui::IsXInput2Available())
1007 ui::TouchFactory::GetInstance()->SetupXI2ForXWindow(xwindow_);
1009 // TODO(erg): We currently only request window deletion events. We also
1010 // should listen for activation events and anything else that GTK+ listens
1011 // for, and do something useful.
1012 ::Atom protocols[2];
1013 protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW");
1014 protocols[1] = atom_cache_.GetAtom("_NET_WM_PING");
1015 XSetWMProtocols(xdisplay_, xwindow_, protocols, 2);
1017 // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with
1018 // the desktop environment.
1019 XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL);
1021 // Likewise, the X server needs to know this window's pid so it knows which
1022 // program to kill if the window hangs.
1023 // XChangeProperty() expects "pid" to be long.
1024 COMPILE_ASSERT(sizeof(long) >= sizeof(pid_t), pid_t_bigger_than_long);
1025 long pid = params.net_wm_pid;
1028 XChangeProperty(xdisplay_,
1030 atom_cache_.GetAtom("_NET_WM_PID"),
1034 reinterpret_cast<unsigned char*>(&pid), 1);
1036 XChangeProperty(xdisplay_,
1038 atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE"),
1042 reinterpret_cast<unsigned char*>(&window_type), 1);
1044 // List of window state properties (_NET_WM_STATE) to set, if any.
1045 std::vector< ::Atom> state_atom_list;
1047 // Remove popup windows from taskbar unless overridden.
1048 if ((params.type == Widget::InitParams::TYPE_POPUP ||
1049 params.type == Widget::InitParams::TYPE_BUBBLE) &&
1050 !params.force_show_in_taskbar) {
1051 state_atom_list.push_back(
1052 atom_cache_.GetAtom("_NET_WM_STATE_SKIP_TASKBAR"));
1055 // If the window should stay on top of other windows, add the
1056 // _NET_WM_STATE_ABOVE property.
1057 is_always_on_top_ = params.keep_on_top;
1058 if (is_always_on_top_)
1059 state_atom_list.push_back(atom_cache_.GetAtom("_NET_WM_STATE_ABOVE"));
1061 if (params.visible_on_all_workspaces)
1062 state_atom_list.push_back(atom_cache_.GetAtom("_NET_WM_STATE_STICKY"));
1064 // Setting _NET_WM_STATE by sending a message to the root_window (with
1065 // SetWMSpecState) has no effect here since the window has not yet been
1066 // mapped. So we manually change the state.
1067 if (!state_atom_list.empty()) {
1068 ui::SetAtomArrayProperty(xwindow_,
1074 if (!params.wm_class_name.empty() || !params.wm_class_class.empty()) {
1075 ui::SetWindowClassHint(
1076 xdisplay_, xwindow_, params.wm_class_name, params.wm_class_class);
1078 if (!params.wm_role_name.empty() ||
1079 params.type == Widget::InitParams::TYPE_POPUP) {
1080 const char kX11WindowRolePopup[] = "popup";
1081 ui::SetWindowRole(xdisplay_, xwindow_, params.wm_role_name.empty() ?
1082 std::string(kX11WindowRolePopup) : params.wm_role_name);
1085 if (params.remove_standard_frame) {
1086 // Setting _GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED tells gnome-shell to not force
1087 // fullscreen on the window when it matches the desktop size.
1088 ui::SetHideTitlebarWhenMaximizedProperty(xwindow_,
1089 ui::HIDE_TITLEBAR_WHEN_MAXIMIZED);
1092 // If we have a parent, record the parent/child relationship. We use this
1093 // data during destruction to make sure that when we try to close a parent
1094 // window, we also destroy all child windows.
1095 if (params.parent && params.parent->GetHost()) {
1097 params.parent->GetHost()->GetAcceleratedWidget();
1098 window_parent_ = GetHostForXID(parent_xid);
1099 DCHECK(window_parent_);
1100 window_parent_->window_children_.insert(this);
1103 // If we have a delegate which is providing a default window icon, use that
1105 gfx::ImageSkia* window_icon = ViewsDelegate::views_delegate ?
1106 ViewsDelegate::views_delegate->GetDefaultWindowIcon() : NULL;
1108 SetWindowIcons(gfx::ImageSkia(), *window_icon);
1110 CreateCompositor(GetAcceleratedWidget());
1113 void DesktopWindowTreeHostX11::SetWMSpecState(bool enabled,
1117 memset(&xclient, 0, sizeof(xclient));
1118 xclient.type = ClientMessage;
1119 xclient.xclient.window = xwindow_;
1120 xclient.xclient.message_type = atom_cache_.GetAtom("_NET_WM_STATE");
1121 xclient.xclient.format = 32;
1122 xclient.xclient.data.l[0] =
1123 enabled ? k_NET_WM_STATE_ADD : k_NET_WM_STATE_REMOVE;
1124 xclient.xclient.data.l[1] = state1;
1125 xclient.xclient.data.l[2] = state2;
1126 xclient.xclient.data.l[3] = 1;
1127 xclient.xclient.data.l[4] = 0;
1129 XSendEvent(xdisplay_, x_root_window_, False,
1130 SubstructureRedirectMask | SubstructureNotifyMask,
1134 bool DesktopWindowTreeHostX11::HasWMSpecProperty(const char* property) const {
1135 return window_properties_.find(atom_cache_.GetAtom(property)) !=
1136 window_properties_.end();
1139 void DesktopWindowTreeHostX11::SetUseNativeFrame(bool use_native_frame) {
1140 use_native_frame_ = use_native_frame;
1141 x11_window_event_filter_->SetUseHostWindowBorders(use_native_frame);
1144 void DesktopWindowTreeHostX11::OnCaptureReleased() {
1145 x11_capture_.reset();
1146 g_current_capture = NULL;
1147 OnHostLostWindowCapture();
1148 native_widget_delegate_->OnMouseCaptureLost();
1151 void DesktopWindowTreeHostX11::DispatchMouseEvent(ui::MouseEvent* event) {
1152 // In Windows, the native events sent to chrome are separated into client
1153 // and non-client versions of events, which we record on our LocatedEvent
1154 // structures. On X11, we emulate the concept of non-client. Before we pass
1155 // this event to the cross platform event handling framework, we need to
1156 // make sure it is appropriately marked as non-client if it's in the non
1157 // client area, or otherwise, we can get into a state where the a window is
1158 // set as the |mouse_pressed_handler_| in window_event_dispatcher.cc
1159 // despite the mouse button being released.
1161 // We can't do this later in the dispatch process because we share that
1162 // with ash, and ash gets confused about event IS_NON_CLIENT-ness on
1163 // events, since ash doesn't expect this bit to be set, because it's never
1164 // been set before. (This works on ash on Windows because none of the mouse
1165 // events on the ash desktop are clicking in what Windows considers to be a
1166 // non client area.) Likewise, we won't want to do the following in any
1167 // WindowTreeHost that hosts ash.
1168 if (content_window_ && content_window_->delegate()) {
1169 int flags = event->flags();
1171 content_window_->delegate()->GetNonClientComponent(event->location());
1172 if (hit_test_code != HTCLIENT && hit_test_code != HTNOWHERE)
1173 flags |= ui::EF_IS_NON_CLIENT;
1174 event->set_flags(flags);
1177 // While we unset the urgency hint when we gain focus, we also must remove it
1178 // on mouse clicks because we can call FlashFrame() on an active window.
1179 if (event->IsAnyButton() || event->IsMouseWheelEvent())
1182 if (!g_current_capture || g_current_capture == this) {
1183 SendEventToProcessor(event);
1185 // Another DesktopWindowTreeHostX11 has installed itself as
1186 // capture. Translate the event's location and dispatch to the other.
1187 event->ConvertLocationToTarget(window(), g_current_capture->window());
1188 g_current_capture->SendEventToProcessor(event);
1192 void DesktopWindowTreeHostX11::DispatchTouchEvent(ui::TouchEvent* event) {
1193 if (g_current_capture && g_current_capture != this &&
1194 event->type() == ui::ET_TOUCH_PRESSED) {
1195 event->ConvertLocationToTarget(window(), g_current_capture->window());
1196 g_current_capture->SendEventToProcessor(event);
1198 SendEventToProcessor(event);
1202 void DesktopWindowTreeHostX11::ResetWindowRegion() {
1203 // If a custom window shape was supplied then apply it.
1204 if (custom_window_shape_) {
1205 XShapeCombineRegion(
1206 xdisplay_, xwindow_, ShapeBounding, 0, 0, custom_window_shape_, false);
1210 if (!IsMaximized()) {
1211 gfx::Path window_mask;
1212 views::Widget* widget = native_widget_delegate_->AsWidget();
1213 if (widget->non_client_view()) {
1214 // Some frame views define a custom (non-rectangular) window mask. If
1215 // so, use it to define the window shape. If not, fall through.
1216 widget->non_client_view()->GetWindowMask(bounds_.size(), &window_mask);
1217 if (window_mask.countPoints() > 0) {
1218 Region region = gfx::CreateRegionFromSkPath(window_mask);
1219 XShapeCombineRegion(xdisplay_, xwindow_, ShapeBounding,
1220 0, 0, region, false);
1221 XDestroyRegion(region);
1227 // If we didn't set the shape for any reason, reset the shaping information.
1228 // How this is done depends on the border style, due to quirks and bugs in
1229 // various window managers.
1230 if (ShouldUseNativeFrame()) {
1231 // If the window has system borders, the mask must be set to null (not a
1232 // rectangle), because several window managers (eg, KDE, XFCE, XMonad) will
1233 // not put borders on a window with a custom shape.
1234 XShapeCombineMask(xdisplay_, xwindow_, ShapeBounding, 0, 0, None, ShapeSet);
1236 // Conversely, if the window does not have system borders, the mask must be
1237 // manually set to a rectangle that covers the whole window (not null). This
1238 // is due to a bug in KWin <= 4.11.5 (KDE bug #330573) where setting a null
1239 // shape causes the hint to disable system borders to be ignored (resulting
1240 // in a double border).
1241 XRectangle r = {0, 0, static_cast<unsigned short>(bounds_.width()),
1242 static_cast<unsigned short>(bounds_.height())};
1243 XShapeCombineRectangles(
1244 xdisplay_, xwindow_, ShapeBounding, 0, 0, &r, 1, ShapeSet, YXBanded);
1248 void DesktopWindowTreeHostX11::SerializeImageRepresentation(
1249 const gfx::ImageSkiaRep& rep,
1250 std::vector<unsigned long>* data) {
1251 int width = rep.GetWidth();
1252 data->push_back(width);
1254 int height = rep.GetHeight();
1255 data->push_back(height);
1257 const SkBitmap& bitmap = rep.sk_bitmap();
1258 SkAutoLockPixels locker(bitmap);
1260 for (int y = 0; y < height; ++y)
1261 for (int x = 0; x < width; ++x)
1262 data->push_back(bitmap.getColor(x, y));
1265 Visual* DesktopWindowTreeHostX11::GetARGBVisual() {
1266 XVisualInfo visual_template;
1267 visual_template.screen = 0;
1268 Visual* to_return = NULL;
1271 XVisualInfo* visual_list = XGetVisualInfo(xdisplay_,
1273 &visual_template, &visuals_len);
1274 for (int i = 0; i < visuals_len; ++i) {
1275 // Why support only 8888 ARGB? Because it's all that GTK+ supports. In
1276 // gdkvisual-x11.cc, they look for this specific visual and use it for all
1277 // their alpha channel using needs.
1279 // TODO(erg): While the following does find a valid visual, some GL drivers
1280 // don't believe that this has an alpha channel. According to marcheu@,
1281 // this should work on open source driver though. (It doesn't work with
1282 // NVidia's binaries currently.) http://crbug.com/369209
1283 if (visual_list[i].depth == 32 &&
1284 visual_list[i].visual->red_mask == 0xff0000 &&
1285 visual_list[i].visual->green_mask == 0x00ff00 &&
1286 visual_list[i].visual->blue_mask == 0x0000ff) {
1287 to_return = visual_list[i].visual;
1298 std::list<XID>& DesktopWindowTreeHostX11::open_windows() {
1300 open_windows_ = new std::list<XID>();
1301 return *open_windows_;
1304 void DesktopWindowTreeHostX11::MapWindow(ui::WindowShowState show_state) {
1305 if (show_state != ui::SHOW_STATE_DEFAULT &&
1306 show_state != ui::SHOW_STATE_NORMAL &&
1307 show_state != ui::SHOW_STATE_INACTIVE) {
1308 // It will behave like SHOW_STATE_NORMAL.
1312 // Before we map the window, set size hints. Otherwise, some window managers
1313 // will ignore toplevel XMoveWindow commands.
1314 XSizeHints size_hints;
1315 size_hints.flags = PPosition;
1316 size_hints.x = bounds_.x();
1317 size_hints.y = bounds_.y();
1318 XSetWMNormalHints(xdisplay_, xwindow_, &size_hints);
1320 // If SHOW_STATE_INACTIVE, tell the window manager not to focus the window
1321 // when mapping. This is done by setting the _NET_WM_USER_TIME to 0. See e.g.
1322 // http://standards.freedesktop.org/wm-spec/latest/ar01s05.html
1323 if (show_state == ui::SHOW_STATE_INACTIVE) {
1324 unsigned long value = 0;
1325 XChangeProperty(xdisplay_,
1327 atom_cache_.GetAtom("_NET_WM_USER_TIME"),
1331 reinterpret_cast<const unsigned char *>(&value),
1334 // TODO(piman): if this window was created in response to an X event, we
1335 // should set the time to the server time of the event that caused this.
1336 // https://crbug.com/355667
1338 xdisplay_, xwindow_, atom_cache_.GetAtom("_NET_WM_USER_TIME"));
1341 XMapWindow(xdisplay_, xwindow_);
1343 // We now block until our window is mapped. Some X11 APIs will crash and
1344 // burn if passed |xwindow_| before the window is mapped, and XMapWindow is
1346 if (ui::X11EventSource::GetInstance())
1347 ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_);
1348 window_mapped_ = true;
1351 void DesktopWindowTreeHostX11::SetWindowTransparency() {
1352 compositor()->SetHostHasTransparentBackground(use_argb_visual_);
1353 window()->SetTransparent(use_argb_visual_);
1354 content_window_->SetTransparent(use_argb_visual_);
1357 ////////////////////////////////////////////////////////////////////////////////
1358 // DesktopWindowTreeHostX11, ui::PlatformEventDispatcher implementation:
1360 bool DesktopWindowTreeHostX11::CanDispatchEvent(
1361 const ui::PlatformEvent& event) {
1362 return event->xany.window == xwindow_ ||
1363 (event->type == GenericEvent &&
1364 static_cast<XIDeviceEvent*>(event->xcookie.data)->event == xwindow_);
1367 uint32_t DesktopWindowTreeHostX11::DispatchEvent(
1368 const ui::PlatformEvent& event) {
1369 XEvent* xev = event;
1371 TRACE_EVENT1("views", "DesktopWindowTreeHostX11::Dispatch",
1372 "event->type", event->type);
1374 // May want to factor CheckXEventForConsistency(xev); into a common location
1375 // since it is called here.
1376 switch (xev->type) {
1379 ui::MouseEvent mouse_event(xev);
1380 DispatchMouseEvent(&mouse_event);
1384 gfx::Rect damage_rect(xev->xexpose.x, xev->xexpose.y,
1385 xev->xexpose.width, xev->xexpose.height);
1386 compositor()->ScheduleRedrawRect(damage_rect);
1390 ui::KeyEvent keydown_event(xev, false);
1391 SendEventToProcessor(&keydown_event);
1395 ui::KeyEvent keyup_event(xev, false);
1396 SendEventToProcessor(&keyup_event);
1400 case ButtonRelease: {
1401 ui::EventType event_type = ui::EventTypeFromNative(xev);
1402 switch (event_type) {
1403 case ui::ET_MOUSEWHEEL: {
1404 ui::MouseWheelEvent mouseev(xev);
1405 DispatchMouseEvent(&mouseev);
1408 case ui::ET_MOUSE_PRESSED:
1409 case ui::ET_MOUSE_RELEASED: {
1410 ui::MouseEvent mouseev(xev);
1411 DispatchMouseEvent(&mouseev);
1414 case ui::ET_UNKNOWN:
1415 // No event is created for X11-release events for mouse-wheel buttons.
1418 NOTREACHED() << event_type;
1423 if (xev->xfocus.mode != NotifyGrab) {
1425 OnHostLostWindowCapture();
1426 X11DesktopHandler::get()->ProcessXEvent(xev);
1428 dispatcher()->OnHostLostMouseGrab();
1432 X11DesktopHandler::get()->ProcessXEvent(xev);
1434 case ConfigureNotify: {
1435 DCHECK_EQ(xwindow_, xev->xconfigure.window);
1436 DCHECK_EQ(xwindow_, xev->xconfigure.event);
1437 // It's possible that the X window may be resized by some other means than
1438 // from within aura (e.g. the X window manager can change the size). Make
1439 // sure the root window size is maintained properly.
1440 int translated_x = xev->xconfigure.x;
1441 int translated_y = xev->xconfigure.y;
1442 if (!xev->xconfigure.send_event && !xev->xconfigure.override_redirect) {
1444 XTranslateCoordinates(xdisplay_, xwindow_, x_root_window_,
1445 0, 0, &translated_x, &translated_y, &unused);
1447 gfx::Rect bounds(translated_x, translated_y,
1448 xev->xconfigure.width, xev->xconfigure.height);
1449 bool size_changed = bounds_.size() != bounds.size();
1450 bool origin_changed = bounds_.origin() != bounds.origin();
1451 previous_bounds_ = bounds_;
1454 OnHostResized(bounds.size());
1456 OnHostMoved(bounds_.origin());
1458 ResetWindowRegion();
1461 case GenericEvent: {
1462 ui::TouchFactory* factory = ui::TouchFactory::GetInstance();
1463 if (!factory->ShouldProcessXI2Event(xev))
1466 ui::EventType type = ui::EventTypeFromNative(xev);
1468 int num_coalesced = 0;
1471 case ui::ET_TOUCH_MOVED:
1472 num_coalesced = ui::CoalescePendingMotionEvents(xev, &last_event);
1473 if (num_coalesced > 0)
1476 case ui::ET_TOUCH_PRESSED:
1477 case ui::ET_TOUCH_RELEASED: {
1478 ui::TouchEvent touchev(xev);
1479 DispatchTouchEvent(&touchev);
1482 case ui::ET_MOUSE_MOVED:
1483 case ui::ET_MOUSE_DRAGGED:
1484 case ui::ET_MOUSE_PRESSED:
1485 case ui::ET_MOUSE_RELEASED:
1486 case ui::ET_MOUSE_ENTERED:
1487 case ui::ET_MOUSE_EXITED: {
1488 if (type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_DRAGGED) {
1489 // If this is a motion event, we want to coalesce all pending motion
1490 // events that are at the top of the queue.
1491 num_coalesced = ui::CoalescePendingMotionEvents(xev, &last_event);
1492 if (num_coalesced > 0)
1495 ui::MouseEvent mouseev(xev);
1496 DispatchMouseEvent(&mouseev);
1499 case ui::ET_MOUSEWHEEL: {
1500 ui::MouseWheelEvent mouseev(xev);
1501 DispatchMouseEvent(&mouseev);
1504 case ui::ET_SCROLL_FLING_START:
1505 case ui::ET_SCROLL_FLING_CANCEL:
1506 case ui::ET_SCROLL: {
1507 ui::ScrollEvent scrollev(xev);
1508 SendEventToProcessor(&scrollev);
1511 case ui::ET_UNKNOWN:
1517 // If we coalesced an event we need to free its cookie.
1518 if (num_coalesced > 0)
1519 XFreeEventData(xev->xgeneric.display, &last_event.xcookie);
1523 FOR_EACH_OBSERVER(DesktopWindowTreeHostObserverX11,
1525 OnWindowMapped(xwindow_));
1529 FOR_EACH_OBSERVER(DesktopWindowTreeHostObserverX11,
1531 OnWindowUnmapped(xwindow_));
1534 case ClientMessage: {
1535 Atom message_type = xev->xclient.message_type;
1536 if (message_type == atom_cache_.GetAtom("WM_PROTOCOLS")) {
1537 Atom protocol = static_cast<Atom>(xev->xclient.data.l[0]);
1538 if (protocol == atom_cache_.GetAtom("WM_DELETE_WINDOW")) {
1539 // We have received a close message from the window manager.
1540 OnHostCloseRequested();
1541 } else if (protocol == atom_cache_.GetAtom("_NET_WM_PING")) {
1542 XEvent reply_event = *xev;
1543 reply_event.xclient.window = x_root_window_;
1545 XSendEvent(xdisplay_,
1546 reply_event.xclient.window,
1548 SubstructureRedirectMask | SubstructureNotifyMask,
1551 } else if (message_type == atom_cache_.GetAtom("XdndEnter")) {
1552 drag_drop_client_->OnXdndEnter(xev->xclient);
1553 } else if (message_type == atom_cache_.GetAtom("XdndLeave")) {
1554 drag_drop_client_->OnXdndLeave(xev->xclient);
1555 } else if (message_type == atom_cache_.GetAtom("XdndPosition")) {
1556 drag_drop_client_->OnXdndPosition(xev->xclient);
1557 } else if (message_type == atom_cache_.GetAtom("XdndStatus")) {
1558 drag_drop_client_->OnXdndStatus(xev->xclient);
1559 } else if (message_type == atom_cache_.GetAtom("XdndFinished")) {
1560 drag_drop_client_->OnXdndFinished(xev->xclient);
1561 } else if (message_type == atom_cache_.GetAtom("XdndDrop")) {
1562 drag_drop_client_->OnXdndDrop(xev->xclient);
1566 case MappingNotify: {
1567 switch (xev->xmapping.request) {
1568 case MappingModifier:
1569 case MappingKeyboard:
1570 XRefreshKeyboardMapping(&xev->xmapping);
1572 case MappingPointer:
1573 ui::DeviceDataManager::GetInstance()->UpdateButtonMap();
1576 NOTIMPLEMENTED() << " Unknown request: " << xev->xmapping.request;
1581 case MotionNotify: {
1582 // Discard all but the most recent motion event that targets the same
1583 // window with unchanged state.
1585 while (XPending(xev->xany.display)) {
1587 XPeekEvent(xev->xany.display, &next_event);
1588 if (next_event.type == MotionNotify &&
1589 next_event.xmotion.window == xev->xmotion.window &&
1590 next_event.xmotion.subwindow == xev->xmotion.subwindow &&
1591 next_event.xmotion.state == xev->xmotion.state) {
1592 XNextEvent(xev->xany.display, &last_event);
1599 ui::MouseEvent mouseev(xev);
1600 DispatchMouseEvent(&mouseev);
1603 case PropertyNotify: {
1604 // Get our new window property state if the WM has told us its changed.
1605 ::Atom state = atom_cache_.GetAtom("_NET_WM_STATE");
1607 std::vector< ::Atom> atom_list;
1608 if (xev->xproperty.atom == state &&
1609 ui::GetAtomArrayProperty(xwindow_, "_NET_WM_STATE", &atom_list)) {
1610 window_properties_.clear();
1611 std::copy(atom_list.begin(), atom_list.end(),
1612 inserter(window_properties_, window_properties_.begin()));
1614 if (!restored_bounds_.IsEmpty() && !IsMaximized()) {
1615 // If we have restored bounds, but WM_STATE no longer claims to be
1616 // maximized, we should clear our restored bounds.
1617 restored_bounds_ = gfx::Rect();
1618 } else if (IsMaximized() && restored_bounds_.IsEmpty()) {
1619 // The request that we become maximized originated from a different
1620 // process. |bounds_| already contains our maximized bounds. Do a
1621 // best effort attempt to get restored bounds by setting it to our
1622 // previously set bounds (and if we get this wrong, we aren't any
1623 // worse off since we'd otherwise be returning our maximized bounds).
1624 restored_bounds_ = previous_bounds_;
1627 is_fullscreen_ = HasWMSpecProperty("_NET_WM_STATE_FULLSCREEN");
1628 is_always_on_top_ = HasWMSpecProperty("_NET_WM_STATE_ABOVE");
1630 // Now that we have different window properties, we may need to
1631 // relayout the window. (The windows code doesn't need this because
1632 // their window change is synchronous.)
1634 // TODO(erg): While this does work, there's a quick flash showing the
1635 // tabstrip/toolbar/etc. when going into fullscreen mode before hiding
1636 // those parts of the UI because we receive the sizing event from the
1637 // window manager before we receive the event that changes the
1638 // fullscreen state. Unsure what to do about that.
1639 Widget* widget = native_widget_delegate_->AsWidget();
1640 NonClientView* non_client_view = widget->non_client_view();
1641 // non_client_view may be NULL, especially during creation.
1642 if (non_client_view) {
1643 non_client_view->client_view()->InvalidateLayout();
1644 non_client_view->InvalidateLayout();
1646 widget->GetRootView()->Layout();
1647 // Refresh the window's border, which may need to be updated if we have
1648 // changed the window's maximization state.
1649 ResetWindowRegion();
1653 case SelectionNotify: {
1654 drag_drop_client_->OnSelectionNotify(xev->xselection);
1658 return ui::POST_DISPATCH_STOP_PROPAGATION;
1661 ////////////////////////////////////////////////////////////////////////////////
1662 // DesktopWindowTreeHost, public:
1665 DesktopWindowTreeHost* DesktopWindowTreeHost::Create(
1666 internal::NativeWidgetDelegate* native_widget_delegate,
1667 DesktopNativeWidgetAura* desktop_native_widget_aura) {
1668 return new DesktopWindowTreeHostX11(native_widget_delegate,
1669 desktop_native_widget_aura);
1673 ui::NativeTheme* DesktopWindowTreeHost::GetNativeTheme(aura::Window* window) {
1674 const views::LinuxUI* linux_ui = views::LinuxUI::instance();
1676 ui::NativeTheme* native_theme = linux_ui->GetNativeTheme(window);
1678 return native_theme;
1681 return ui::NativeTheme::instance();
1684 } // namespace views