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_window_tree_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/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"
56
57 namespace views {
58
59 DesktopWindowTreeHostX11* DesktopWindowTreeHostX11::g_current_capture =
60 NULL;
61 std::list<XID>* DesktopWindowTreeHostX11::open_windows_ = NULL;
62
63 DEFINE_WINDOW_PROPERTY_KEY(
64 aura::Window*, kViewsWindowForRootWindow, NULL);
65
66 DEFINE_WINDOW_PROPERTY_KEY(
67 DesktopWindowTreeHostX11*, kHostForRootWindow, NULL);
68
69 namespace {
70
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;
74
75 // Special value of the _NET_WM_DESKTOP property which indicates that the window
76 // should appear on all desktops.
77 const int kAllDesktops = 0xFFFFFFFF;
78
79 const char* kAtomsToCache[] = {
80 "UTF8_STRING",
81 "WM_DELETE_WINDOW",
82 "WM_PROTOCOLS",
83 "_NET_FRAME_EXTENTS",
84 "_NET_WM_CM_S0",
85 "_NET_WM_DESKTOP",
86 "_NET_WM_ICON",
87 "_NET_WM_NAME",
88 "_NET_WM_PID",
89 "_NET_WM_PING",
90 "_NET_WM_STATE",
91 "_NET_WM_STATE_ABOVE",
92 "_NET_WM_STATE_FULLSCREEN",
93 "_NET_WM_STATE_HIDDEN",
94 "_NET_WM_STATE_MAXIMIZED_HORZ",
95 "_NET_WM_STATE_MAXIMIZED_VERT",
96 "_NET_WM_STATE_SKIP_TASKBAR",
97 "_NET_WM_STATE_STICKY",
98 "_NET_WM_USER_TIME",
99 "_NET_WM_WINDOW_OPACITY",
100 "_NET_WM_WINDOW_TYPE",
101 "_NET_WM_WINDOW_TYPE_DND",
102 "_NET_WM_WINDOW_TYPE_MENU",
103 "_NET_WM_WINDOW_TYPE_NORMAL",
104 "_NET_WM_WINDOW_TYPE_NOTIFICATION",
105 "_NET_WM_WINDOW_TYPE_TOOLTIP",
106 "XdndActionAsk",
107 "XdndActionCopy"
108 "XdndActionLink",
109 "XdndActionList",
110 "XdndActionMove",
111 "XdndActionPrivate",
112 "XdndAware",
113 "XdndDrop",
114 "XdndEnter",
115 "XdndFinished",
116 "XdndLeave",
117 "XdndPosition",
118 "XdndProxy", // Proxy windows?
119 "XdndSelection",
120 "XdndStatus",
121 "XdndTypeList",
122 NULL
123 };
124
125 } // namespace
126
127 ////////////////////////////////////////////////////////////////////////////////
128 // DesktopWindowTreeHostX11, public:
129
DesktopWindowTreeHostX11(internal::NativeWidgetDelegate * native_widget_delegate,DesktopNativeWidgetAura * desktop_native_widget_aura)130 DesktopWindowTreeHostX11::DesktopWindowTreeHostX11(
131 internal::NativeWidgetDelegate* native_widget_delegate,
132 DesktopNativeWidgetAura* desktop_native_widget_aura)
133 : close_widget_factory_(this),
134 xdisplay_(gfx::GetXDisplay()),
135 xwindow_(0),
136 x_root_window_(DefaultRootWindow(xdisplay_)),
137 atom_cache_(xdisplay_, kAtomsToCache),
138 window_mapped_(false),
139 is_fullscreen_(false),
140 is_always_on_top_(false),
141 use_native_frame_(false),
142 use_argb_visual_(false),
143 drag_drop_client_(NULL),
144 current_cursor_(ui::kCursorNull),
145 native_widget_delegate_(native_widget_delegate),
146 desktop_native_widget_aura_(desktop_native_widget_aura),
147 content_window_(NULL),
148 window_parent_(NULL),
149 window_shape_(NULL),
150 custom_window_shape_(false),
151 urgency_hint_set_(false) {
152 }
153
~DesktopWindowTreeHostX11()154 DesktopWindowTreeHostX11::~DesktopWindowTreeHostX11() {
155 window()->ClearProperty(kHostForRootWindow);
156 aura::client::SetWindowMoveClient(window(), NULL);
157 desktop_native_widget_aura_->OnDesktopWindowTreeHostDestroyed(this);
158 if (window_shape_)
159 XDestroyRegion(window_shape_);
160 DestroyDispatcher();
161 }
162
163 // static
GetContentWindowForXID(XID xid)164 aura::Window* DesktopWindowTreeHostX11::GetContentWindowForXID(XID xid) {
165 aura::WindowTreeHost* host =
166 aura::WindowTreeHost::GetForAcceleratedWidget(xid);
167 return host ? host->window()->GetProperty(kViewsWindowForRootWindow) : NULL;
168 }
169
170 // static
GetHostForXID(XID xid)171 DesktopWindowTreeHostX11* DesktopWindowTreeHostX11::GetHostForXID(XID xid) {
172 aura::WindowTreeHost* host =
173 aura::WindowTreeHost::GetForAcceleratedWidget(xid);
174 return host ? host->window()->GetProperty(kHostForRootWindow) : NULL;
175 }
176
177 // static
GetAllOpenWindows()178 std::vector<aura::Window*> DesktopWindowTreeHostX11::GetAllOpenWindows() {
179 std::vector<aura::Window*> windows(open_windows().size());
180 std::transform(open_windows().begin(),
181 open_windows().end(),
182 windows.begin(),
183 GetContentWindowForXID);
184 return windows;
185 }
186
GetX11RootWindowBounds() const187 gfx::Rect DesktopWindowTreeHostX11::GetX11RootWindowBounds() const {
188 return bounds_;
189 }
190
GetX11RootWindowOuterBounds() const191 gfx::Rect DesktopWindowTreeHostX11::GetX11RootWindowOuterBounds() const {
192 gfx::Rect outer_bounds(bounds_);
193 outer_bounds.Inset(-native_window_frame_borders_);
194 return outer_bounds;
195 }
196
GetWindowShape() const197 ::Region DesktopWindowTreeHostX11::GetWindowShape() const {
198 return window_shape_;
199 }
200
HandleNativeWidgetActivationChanged(bool active)201 void DesktopWindowTreeHostX11::HandleNativeWidgetActivationChanged(
202 bool active) {
203 if (active) {
204 FlashFrame(false);
205 OnHostActivated();
206 open_windows().remove(xwindow_);
207 open_windows().insert(open_windows().begin(), xwindow_);
208 }
209
210 desktop_native_widget_aura_->HandleActivationChanged(active);
211
212 native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint();
213 }
214
AddObserver(views::DesktopWindowTreeHostObserverX11 * observer)215 void DesktopWindowTreeHostX11::AddObserver(
216 views::DesktopWindowTreeHostObserverX11* observer) {
217 observer_list_.AddObserver(observer);
218 }
219
RemoveObserver(views::DesktopWindowTreeHostObserverX11 * observer)220 void DesktopWindowTreeHostX11::RemoveObserver(
221 views::DesktopWindowTreeHostObserverX11* observer) {
222 observer_list_.RemoveObserver(observer);
223 }
224
SwapNonClientEventHandler(scoped_ptr<ui::EventHandler> handler)225 void DesktopWindowTreeHostX11::SwapNonClientEventHandler(
226 scoped_ptr<ui::EventHandler> handler) {
227 wm::CompoundEventFilter* compound_event_filter =
228 desktop_native_widget_aura_->root_window_event_filter();
229 if (x11_non_client_event_filter_)
230 compound_event_filter->RemoveHandler(x11_non_client_event_filter_.get());
231 compound_event_filter->AddHandler(handler.get());
232 x11_non_client_event_filter_ = handler.Pass();
233 }
234
CleanUpWindowList()235 void DesktopWindowTreeHostX11::CleanUpWindowList() {
236 delete open_windows_;
237 open_windows_ = NULL;
238 }
239
240 ////////////////////////////////////////////////////////////////////////////////
241 // DesktopWindowTreeHostX11, DesktopWindowTreeHost implementation:
242
Init(aura::Window * content_window,const Widget::InitParams & params)243 void DesktopWindowTreeHostX11::Init(aura::Window* content_window,
244 const Widget::InitParams& params) {
245 content_window_ = content_window;
246
247 // TODO(erg): Check whether we *should* be building a WindowTreeHost here, or
248 // whether we should be proxying requests to another DRWHL.
249
250 // In some situations, views tries to make a zero sized window, and that
251 // makes us crash. Make sure we have valid sizes.
252 Widget::InitParams sanitized_params = params;
253 if (sanitized_params.bounds.width() == 0)
254 sanitized_params.bounds.set_width(100);
255 if (sanitized_params.bounds.height() == 0)
256 sanitized_params.bounds.set_height(100);
257
258 InitX11Window(sanitized_params);
259 }
260
OnNativeWidgetCreated(const Widget::InitParams & params)261 void DesktopWindowTreeHostX11::OnNativeWidgetCreated(
262 const Widget::InitParams& params) {
263 window()->SetProperty(kViewsWindowForRootWindow, content_window_);
264 window()->SetProperty(kHostForRootWindow, this);
265
266 // Ensure that the X11DesktopHandler exists so that it dispatches activation
267 // messages to us.
268 X11DesktopHandler::get();
269
270 // TODO(erg): Unify this code once the other consumer goes away.
271 SwapNonClientEventHandler(
272 scoped_ptr<ui::EventHandler>(new X11WindowEventFilter(this)).Pass());
273 SetUseNativeFrame(params.type == Widget::InitParams::TYPE_WINDOW &&
274 !params.remove_standard_frame);
275
276 x11_window_move_client_.reset(new X11DesktopWindowMoveClient);
277 aura::client::SetWindowMoveClient(window(), x11_window_move_client_.get());
278
279 SetWindowTransparency();
280
281 native_widget_delegate_->OnNativeWidgetCreated(true);
282 }
283
CreateTooltip()284 scoped_ptr<corewm::Tooltip> DesktopWindowTreeHostX11::CreateTooltip() {
285 return scoped_ptr<corewm::Tooltip>(
286 new corewm::TooltipAura(gfx::SCREEN_TYPE_NATIVE));
287 }
288
289 scoped_ptr<aura::client::DragDropClient>
CreateDragDropClient(DesktopNativeCursorManager * cursor_manager)290 DesktopWindowTreeHostX11::CreateDragDropClient(
291 DesktopNativeCursorManager* cursor_manager) {
292 drag_drop_client_ = new DesktopDragDropClientAuraX11(
293 window(), cursor_manager, xdisplay_, xwindow_);
294 return scoped_ptr<aura::client::DragDropClient>(drag_drop_client_).Pass();
295 }
296
Close()297 void DesktopWindowTreeHostX11::Close() {
298 // TODO(erg): Might need to do additional hiding tasks here.
299 delayed_resize_task_.Cancel();
300
301 if (!close_widget_factory_.HasWeakPtrs()) {
302 // And we delay the close so that if we are called from an ATL callback,
303 // we don't destroy the window before the callback returned (as the caller
304 // may delete ourselves on destroy and the ATL callback would still
305 // dereference us when the callback returns).
306 base::MessageLoop::current()->PostTask(
307 FROM_HERE,
308 base::Bind(&DesktopWindowTreeHostX11::CloseNow,
309 close_widget_factory_.GetWeakPtr()));
310 }
311 }
312
CloseNow()313 void DesktopWindowTreeHostX11::CloseNow() {
314 if (xwindow_ == None)
315 return;
316
317 x11_capture_.reset();
318 native_widget_delegate_->OnNativeWidgetDestroying();
319
320 // If we have children, close them. Use a copy for iteration because they'll
321 // remove themselves.
322 std::set<DesktopWindowTreeHostX11*> window_children_copy = window_children_;
323 for (std::set<DesktopWindowTreeHostX11*>::iterator it =
324 window_children_copy.begin(); it != window_children_copy.end();
325 ++it) {
326 (*it)->CloseNow();
327 }
328 DCHECK(window_children_.empty());
329
330 // If we have a parent, remove ourselves from its children list.
331 if (window_parent_) {
332 window_parent_->window_children_.erase(this);
333 window_parent_ = NULL;
334 }
335
336 // Remove the event listeners we've installed. We need to remove these
337 // because otherwise we get assert during ~WindowEventDispatcher().
338 desktop_native_widget_aura_->root_window_event_filter()->RemoveHandler(
339 x11_non_client_event_filter_.get());
340 x11_non_client_event_filter_.reset();
341
342 // Destroy the compositor before destroying the |xwindow_| since shutdown
343 // may try to swap, and the swap without a window causes an X error, which
344 // causes a crash with in-process renderer.
345 DestroyCompositor();
346
347 open_windows().remove(xwindow_);
348 // Actually free our native resources.
349 if (ui::PlatformEventSource::GetInstance())
350 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
351 XDestroyWindow(xdisplay_, xwindow_);
352 xwindow_ = None;
353
354 desktop_native_widget_aura_->OnHostClosed();
355 }
356
AsWindowTreeHost()357 aura::WindowTreeHost* DesktopWindowTreeHostX11::AsWindowTreeHost() {
358 return this;
359 }
360
ShowWindowWithState(ui::WindowShowState show_state)361 void DesktopWindowTreeHostX11::ShowWindowWithState(
362 ui::WindowShowState show_state) {
363 if (!window_mapped_)
364 MapWindow(show_state);
365
366 if (show_state == ui::SHOW_STATE_NORMAL ||
367 show_state == ui::SHOW_STATE_MAXIMIZED) {
368 // Note: XFCE ignores a maximize hint given before mapping the window.
369 if (show_state == ui::SHOW_STATE_MAXIMIZED)
370 Maximize();
371 Activate();
372 }
373
374 native_widget_delegate_->AsWidget()->SetInitialFocus(show_state);
375 }
376
ShowMaximizedWithBounds(const gfx::Rect & restored_bounds)377 void DesktopWindowTreeHostX11::ShowMaximizedWithBounds(
378 const gfx::Rect& restored_bounds) {
379 ShowWindowWithState(ui::SHOW_STATE_MAXIMIZED);
380 // Enforce |restored_bounds_| since calling Maximize() could have reset it.
381 restored_bounds_ = restored_bounds;
382 }
383
IsVisible() const384 bool DesktopWindowTreeHostX11::IsVisible() const {
385 return window_mapped_;
386 }
387
SetSize(const gfx::Size & size)388 void DesktopWindowTreeHostX11::SetSize(const gfx::Size& size) {
389 bool size_changed = bounds_.size() != size;
390 XResizeWindow(xdisplay_, xwindow_, size.width(), size.height());
391 bounds_.set_size(size);
392 if (size_changed) {
393 OnHostResized(size);
394 ResetWindowRegion();
395 }
396 }
397
StackAtTop()398 void DesktopWindowTreeHostX11::StackAtTop() {
399 XRaiseWindow(xdisplay_, xwindow_);
400 }
401
CenterWindow(const gfx::Size & size)402 void DesktopWindowTreeHostX11::CenterWindow(const gfx::Size& size) {
403 gfx::Rect parent_bounds = GetWorkAreaBoundsInScreen();
404
405 // If |window_|'s transient parent bounds are big enough to contain |size|,
406 // use them instead.
407 if (wm::GetTransientParent(content_window_)) {
408 gfx::Rect transient_parent_rect =
409 wm::GetTransientParent(content_window_)->GetBoundsInScreen();
410 if (transient_parent_rect.height() >= size.height() &&
411 transient_parent_rect.width() >= size.width()) {
412 parent_bounds = transient_parent_rect;
413 }
414 }
415
416 gfx::Rect window_bounds(
417 parent_bounds.x() + (parent_bounds.width() - size.width()) / 2,
418 parent_bounds.y() + (parent_bounds.height() - size.height()) / 2,
419 size.width(),
420 size.height());
421 // Don't size the window bigger than the parent, otherwise the user may not be
422 // able to close or move it.
423 window_bounds.AdjustToFit(parent_bounds);
424
425 SetBounds(window_bounds);
426 }
427
GetWindowPlacement(gfx::Rect * bounds,ui::WindowShowState * show_state) const428 void DesktopWindowTreeHostX11::GetWindowPlacement(
429 gfx::Rect* bounds,
430 ui::WindowShowState* show_state) const {
431 *bounds = GetRestoredBounds();
432
433 if (IsFullscreen()) {
434 *show_state = ui::SHOW_STATE_FULLSCREEN;
435 } else if (IsMinimized()) {
436 *show_state = ui::SHOW_STATE_MINIMIZED;
437 } else if (IsMaximized()) {
438 *show_state = ui::SHOW_STATE_MAXIMIZED;
439 } else if (!IsActive()) {
440 *show_state = ui::SHOW_STATE_INACTIVE;
441 } else {
442 *show_state = ui::SHOW_STATE_NORMAL;
443 }
444 }
445
GetWindowBoundsInScreen() const446 gfx::Rect DesktopWindowTreeHostX11::GetWindowBoundsInScreen() const {
447 return bounds_;
448 }
449
GetClientAreaBoundsInScreen() const450 gfx::Rect DesktopWindowTreeHostX11::GetClientAreaBoundsInScreen() const {
451 // TODO(erg): The NativeWidgetAura version returns |bounds_|, claiming its
452 // needed for View::ConvertPointToScreen() to work
453 // correctly. DesktopWindowTreeHostWin::GetClientAreaBoundsInScreen() just
454 // asks windows what it thinks the client rect is.
455 //
456 // Attempts to calculate the rect by asking the NonClientFrameView what it
457 // thought its GetBoundsForClientView() were broke combobox drop down
458 // placement.
459 return bounds_;
460 }
461
GetRestoredBounds() const462 gfx::Rect DesktopWindowTreeHostX11::GetRestoredBounds() const {
463 // We can't reliably track the restored bounds of a window, but we can get
464 // the 90% case down. When *chrome* is the process that requests maximizing
465 // or restoring bounds, we can record the current bounds before we request
466 // maximization, and clear it when we detect a state change.
467 if (!restored_bounds_.IsEmpty())
468 return restored_bounds_;
469
470 return GetWindowBoundsInScreen();
471 }
472
GetWorkAreaBoundsInScreen() const473 gfx::Rect DesktopWindowTreeHostX11::GetWorkAreaBoundsInScreen() const {
474 std::vector<int> value;
475 if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) &&
476 value.size() >= 4) {
477 return gfx::Rect(value[0], value[1], value[2], value[3]);
478 }
479
480 // Fetch the geometry of the root window.
481 Window root;
482 int x, y;
483 unsigned int width, height;
484 unsigned int border_width, depth;
485 if (!XGetGeometry(xdisplay_, x_root_window_, &root, &x, &y,
486 &width, &height, &border_width, &depth)) {
487 NOTIMPLEMENTED();
488 return gfx::Rect(0, 0, 10, 10);
489 }
490
491 return gfx::Rect(x, y, width, height);
492 }
493
SetShape(gfx::NativeRegion native_region)494 void DesktopWindowTreeHostX11::SetShape(gfx::NativeRegion native_region) {
495 if (window_shape_)
496 XDestroyRegion(window_shape_);
497 custom_window_shape_ = true;
498 window_shape_ = gfx::CreateRegionFromSkRegion(*native_region);
499 ResetWindowRegion();
500 delete native_region;
501 }
502
Activate()503 void DesktopWindowTreeHostX11::Activate() {
504 if (!window_mapped_)
505 return;
506
507 X11DesktopHandler::get()->ActivateWindow(xwindow_);
508 }
509
Deactivate()510 void DesktopWindowTreeHostX11::Deactivate() {
511 if (!IsActive())
512 return;
513
514 x11_capture_.reset();
515 XLowerWindow(xdisplay_, xwindow_);
516 }
517
IsActive() const518 bool DesktopWindowTreeHostX11::IsActive() const {
519 return X11DesktopHandler::get()->IsActiveWindow(xwindow_);
520 }
521
Maximize()522 void DesktopWindowTreeHostX11::Maximize() {
523 // When we are in the process of requesting to maximize a window, we can
524 // accurately keep track of our restored bounds instead of relying on the
525 // heuristics that are in the PropertyNotify and ConfigureNotify handlers.
526 restored_bounds_ = bounds_;
527
528 SetWMSpecState(true,
529 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"),
530 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ"));
531 if (IsMinimized())
532 ShowWindowWithState(ui::SHOW_STATE_NORMAL);
533 }
534
Minimize()535 void DesktopWindowTreeHostX11::Minimize() {
536 x11_capture_.reset();
537 XIconifyWindow(xdisplay_, xwindow_, 0);
538 }
539
Restore()540 void DesktopWindowTreeHostX11::Restore() {
541 SetWMSpecState(false,
542 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"),
543 atom_cache_.GetAtom("_NET_WM_STATE_MAXIMIZED_HORZ"));
544 if (IsMinimized())
545 ShowWindowWithState(ui::SHOW_STATE_NORMAL);
546 }
547
IsMaximized() const548 bool DesktopWindowTreeHostX11::IsMaximized() const {
549 return (HasWMSpecProperty("_NET_WM_STATE_MAXIMIZED_VERT") &&
550 HasWMSpecProperty("_NET_WM_STATE_MAXIMIZED_HORZ"));
551 }
552
IsMinimized() const553 bool DesktopWindowTreeHostX11::IsMinimized() const {
554 return HasWMSpecProperty("_NET_WM_STATE_HIDDEN");
555 }
556
HasCapture() const557 bool DesktopWindowTreeHostX11::HasCapture() const {
558 return g_current_capture == this;
559 }
560
SetAlwaysOnTop(bool always_on_top)561 void DesktopWindowTreeHostX11::SetAlwaysOnTop(bool always_on_top) {
562 is_always_on_top_ = always_on_top;
563 SetWMSpecState(always_on_top,
564 atom_cache_.GetAtom("_NET_WM_STATE_ABOVE"),
565 None);
566 }
567
IsAlwaysOnTop() const568 bool DesktopWindowTreeHostX11::IsAlwaysOnTop() const {
569 return is_always_on_top_;
570 }
571
SetVisibleOnAllWorkspaces(bool always_visible)572 void DesktopWindowTreeHostX11::SetVisibleOnAllWorkspaces(bool always_visible) {
573 SetWMSpecState(always_visible,
574 atom_cache_.GetAtom("_NET_WM_STATE_STICKY"),
575 None);
576
577 int new_desktop = 0;
578 if (always_visible) {
579 new_desktop = kAllDesktops;
580 } else {
581 if (!ui::GetCurrentDesktop(&new_desktop))
582 return;
583 }
584
585 XEvent xevent;
586 memset (&xevent, 0, sizeof (xevent));
587 xevent.type = ClientMessage;
588 xevent.xclient.window = xwindow_;
589 xevent.xclient.message_type = atom_cache_.GetAtom("_NET_WM_DESKTOP");
590 xevent.xclient.format = 32;
591 xevent.xclient.data.l[0] = new_desktop;
592 xevent.xclient.data.l[1] = 0;
593 xevent.xclient.data.l[2] = 0;
594 xevent.xclient.data.l[3] = 0;
595 xevent.xclient.data.l[4] = 0;
596 XSendEvent(xdisplay_, x_root_window_, False,
597 SubstructureRedirectMask | SubstructureNotifyMask,
598 &xevent);
599 }
600
SetWindowTitle(const base::string16 & title)601 bool DesktopWindowTreeHostX11::SetWindowTitle(const base::string16& title) {
602 if (window_title_ == title)
603 return false;
604 window_title_ = title;
605 std::string utf8str = base::UTF16ToUTF8(title);
606 XChangeProperty(xdisplay_,
607 xwindow_,
608 atom_cache_.GetAtom("_NET_WM_NAME"),
609 atom_cache_.GetAtom("UTF8_STRING"),
610 8,
611 PropModeReplace,
612 reinterpret_cast<const unsigned char*>(utf8str.c_str()),
613 utf8str.size());
614 // TODO(erg): This is technically wrong. So XStoreName and friends expect
615 // this in Host Portable Character Encoding instead of UTF-8, which I believe
616 // is Compound Text. This shouldn't matter 90% of the time since this is the
617 // fallback to the UTF8 property above.
618 XStoreName(xdisplay_, xwindow_, utf8str.c_str());
619 return true;
620 }
621
ClearNativeFocus()622 void DesktopWindowTreeHostX11::ClearNativeFocus() {
623 // This method is weird and misnamed. Instead of clearing the native focus,
624 // it sets the focus to our |content_window_|, which will trigger a cascade
625 // of focus changes into views.
626 if (content_window_ && aura::client::GetFocusClient(content_window_) &&
627 content_window_->Contains(
628 aura::client::GetFocusClient(content_window_)->GetFocusedWindow())) {
629 aura::client::GetFocusClient(content_window_)->FocusWindow(content_window_);
630 }
631 }
632
RunMoveLoop(const gfx::Vector2d & drag_offset,Widget::MoveLoopSource source,Widget::MoveLoopEscapeBehavior escape_behavior)633 Widget::MoveLoopResult DesktopWindowTreeHostX11::RunMoveLoop(
634 const gfx::Vector2d& drag_offset,
635 Widget::MoveLoopSource source,
636 Widget::MoveLoopEscapeBehavior escape_behavior) {
637 aura::client::WindowMoveSource window_move_source =
638 source == Widget::MOVE_LOOP_SOURCE_MOUSE ?
639 aura::client::WINDOW_MOVE_SOURCE_MOUSE :
640 aura::client::WINDOW_MOVE_SOURCE_TOUCH;
641 if (x11_window_move_client_->RunMoveLoop(content_window_, drag_offset,
642 window_move_source) == aura::client::MOVE_SUCCESSFUL)
643 return Widget::MOVE_LOOP_SUCCESSFUL;
644
645 return Widget::MOVE_LOOP_CANCELED;
646 }
647
EndMoveLoop()648 void DesktopWindowTreeHostX11::EndMoveLoop() {
649 x11_window_move_client_->EndMoveLoop();
650 }
651
SetVisibilityChangedAnimationsEnabled(bool value)652 void DesktopWindowTreeHostX11::SetVisibilityChangedAnimationsEnabled(
653 bool value) {
654 // Much like the previous NativeWidgetGtk, we don't have anything to do here.
655 }
656
ShouldUseNativeFrame() const657 bool DesktopWindowTreeHostX11::ShouldUseNativeFrame() const {
658 return use_native_frame_;
659 }
660
ShouldWindowContentsBeTransparent() const661 bool DesktopWindowTreeHostX11::ShouldWindowContentsBeTransparent() const {
662 return false;
663 }
664
FrameTypeChanged()665 void DesktopWindowTreeHostX11::FrameTypeChanged() {
666 Widget::FrameType new_type =
667 native_widget_delegate_->AsWidget()->frame_type();
668 if (new_type == Widget::FRAME_TYPE_DEFAULT) {
669 // The default is determined by Widget::InitParams::remove_standard_frame
670 // and does not change.
671 return;
672 }
673
674 SetUseNativeFrame(new_type == Widget::FRAME_TYPE_FORCE_NATIVE);
675 // Replace the frame and layout the contents. Even though we don't have a
676 // swapable glass frame like on Windows, we still replace the frame because
677 // the button assets don't update otherwise.
678 native_widget_delegate_->AsWidget()->non_client_view()->UpdateFrame();
679 }
680
SetFullscreen(bool fullscreen)681 void DesktopWindowTreeHostX11::SetFullscreen(bool fullscreen) {
682 if (is_fullscreen_ == fullscreen)
683 return;
684 is_fullscreen_ = fullscreen;
685 SetWMSpecState(fullscreen,
686 atom_cache_.GetAtom("_NET_WM_STATE_FULLSCREEN"),
687 None);
688 // Try to guess the size we will have after the switch to/from fullscreen:
689 // - (may) avoid transient states
690 // - works around Flash content which expects to have the size updated
691 // synchronously.
692 // See https://crbug.com/361408
693 if (fullscreen) {
694 restored_bounds_ = bounds_;
695 const gfx::Display display =
696 gfx::Screen::GetScreenFor(NULL)->GetDisplayNearestWindow(window());
697 bounds_ = display.bounds();
698 } else {
699 bounds_ = restored_bounds_;
700 }
701 OnHostMoved(bounds_.origin());
702 OnHostResized(bounds_.size());
703
704 if (HasWMSpecProperty("_NET_WM_STATE_FULLSCREEN") == fullscreen) {
705 Relayout();
706 ResetWindowRegion();
707 }
708 // Else: the widget will be relaid out either when the window bounds change or
709 // when |xwindow_|'s fullscreen state changes.
710 }
711
IsFullscreen() const712 bool DesktopWindowTreeHostX11::IsFullscreen() const {
713 return is_fullscreen_;
714 }
715
SetOpacity(unsigned char opacity)716 void DesktopWindowTreeHostX11::SetOpacity(unsigned char opacity) {
717 // X server opacity is in terms of 32 bit unsigned int space, and counts from
718 // the opposite direction.
719 // XChangeProperty() expects "cardinality" to be long.
720 unsigned long cardinality = opacity * 0x1010101;
721
722 if (cardinality == 0xffffffff) {
723 XDeleteProperty(xdisplay_, xwindow_,
724 atom_cache_.GetAtom("_NET_WM_WINDOW_OPACITY"));
725 } else {
726 XChangeProperty(xdisplay_, xwindow_,
727 atom_cache_.GetAtom("_NET_WM_WINDOW_OPACITY"),
728 XA_CARDINAL, 32,
729 PropModeReplace,
730 reinterpret_cast<unsigned char*>(&cardinality), 1);
731 }
732 }
733
SetWindowIcons(const gfx::ImageSkia & window_icon,const gfx::ImageSkia & app_icon)734 void DesktopWindowTreeHostX11::SetWindowIcons(
735 const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon) {
736 // TODO(erg): The way we handle icons across different versions of chrome
737 // could be substantially improved. The Windows version does its own thing
738 // and only sometimes comes down this code path. The icon stuff in
739 // ChromeViewsDelegate is hard coded to use HICONs. Likewise, we're hard
740 // coded to be given two images instead of an arbitrary collection of images
741 // so that we can pass to the WM.
742 //
743 // All of this could be made much, much better.
744 std::vector<unsigned long> data;
745
746 if (window_icon.HasRepresentation(1.0f))
747 SerializeImageRepresentation(window_icon.GetRepresentation(1.0f), &data);
748
749 if (app_icon.HasRepresentation(1.0f))
750 SerializeImageRepresentation(app_icon.GetRepresentation(1.0f), &data);
751
752 if (data.empty())
753 XDeleteProperty(xdisplay_, xwindow_, atom_cache_.GetAtom("_NET_WM_ICON"));
754 else
755 ui::SetAtomArrayProperty(xwindow_, "_NET_WM_ICON", "CARDINAL", data);
756 }
757
InitModalType(ui::ModalType modal_type)758 void DesktopWindowTreeHostX11::InitModalType(ui::ModalType modal_type) {
759 switch (modal_type) {
760 case ui::MODAL_TYPE_NONE:
761 break;
762 default:
763 // TODO(erg): Figure out under what situations |modal_type| isn't
764 // none. The comment in desktop_native_widget_aura.cc suggests that this
765 // is rare.
766 NOTIMPLEMENTED();
767 }
768 }
769
FlashFrame(bool flash_frame)770 void DesktopWindowTreeHostX11::FlashFrame(bool flash_frame) {
771 if (urgency_hint_set_ == flash_frame)
772 return;
773
774 XWMHints* hints = XGetWMHints(xdisplay_, xwindow_);
775 if (!hints) {
776 // The window hasn't had its hints set yet.
777 hints = XAllocWMHints();
778 }
779
780 if (flash_frame)
781 hints->flags |= XUrgencyHint;
782 else
783 hints->flags &= ~XUrgencyHint;
784
785 XSetWMHints(xdisplay_, xwindow_, hints);
786 XFree(hints);
787
788 urgency_hint_set_ = flash_frame;
789 }
790
OnRootViewLayout() const791 void DesktopWindowTreeHostX11::OnRootViewLayout() const {
792 if (!window_mapped_)
793 return;
794
795 XSizeHints hints;
796 long supplied_return;
797 XGetWMNormalHints(xdisplay_, xwindow_, &hints, &supplied_return);
798
799 gfx::Size minimum = native_widget_delegate_->GetMinimumSize();
800 if (minimum.IsEmpty()) {
801 hints.flags &= ~PMinSize;
802 } else {
803 hints.flags |= PMinSize;
804 hints.min_width = minimum.width();
805 hints.min_height = minimum.height();
806 }
807
808 gfx::Size maximum = native_widget_delegate_->GetMaximumSize();
809 if (maximum.IsEmpty()) {
810 hints.flags &= ~PMaxSize;
811 } else {
812 hints.flags |= PMaxSize;
813 hints.max_width = maximum.width();
814 hints.max_height = maximum.height();
815 }
816
817 XSetWMNormalHints(xdisplay_, xwindow_, &hints);
818 }
819
OnNativeWidgetFocus()820 void DesktopWindowTreeHostX11::OnNativeWidgetFocus() {
821 native_widget_delegate_->AsWidget()->GetInputMethod()->OnFocus();
822 }
823
OnNativeWidgetBlur()824 void DesktopWindowTreeHostX11::OnNativeWidgetBlur() {
825 if (xwindow_) {
826 x11_capture_.reset();
827 native_widget_delegate_->AsWidget()->GetInputMethod()->OnBlur();
828 }
829 }
830
IsAnimatingClosed() const831 bool DesktopWindowTreeHostX11::IsAnimatingClosed() const {
832 return false;
833 }
834
IsTranslucentWindowOpacitySupported() const835 bool DesktopWindowTreeHostX11::IsTranslucentWindowOpacitySupported() const {
836 return false;
837 }
838
839 ////////////////////////////////////////////////////////////////////////////////
840 // DesktopWindowTreeHostX11, aura::WindowTreeHost implementation:
841
GetEventSource()842 ui::EventSource* DesktopWindowTreeHostX11::GetEventSource() {
843 return this;
844 }
845
GetAcceleratedWidget()846 gfx::AcceleratedWidget DesktopWindowTreeHostX11::GetAcceleratedWidget() {
847 return xwindow_;
848 }
849
Show()850 void DesktopWindowTreeHostX11::Show() {
851 ShowWindowWithState(ui::SHOW_STATE_NORMAL);
852 native_widget_delegate_->OnNativeWidgetVisibilityChanged(true);
853 }
854
Hide()855 void DesktopWindowTreeHostX11::Hide() {
856 if (window_mapped_) {
857 XWithdrawWindow(xdisplay_, xwindow_, 0);
858 window_mapped_ = false;
859 }
860 native_widget_delegate_->OnNativeWidgetVisibilityChanged(false);
861 }
862
GetBounds() const863 gfx::Rect DesktopWindowTreeHostX11::GetBounds() const {
864 return bounds_;
865 }
866
SetBounds(const gfx::Rect & bounds)867 void DesktopWindowTreeHostX11::SetBounds(const gfx::Rect& bounds) {
868 bool origin_changed = bounds_.origin() != bounds.origin();
869 bool size_changed = bounds_.size() != bounds.size();
870 XWindowChanges changes = {0};
871 unsigned value_mask = 0;
872
873 if (size_changed) {
874 // X11 will send an XError at our process if have a 0 sized window.
875 DCHECK_GT(bounds.width(), 0);
876 DCHECK_GT(bounds.height(), 0);
877
878 changes.width = bounds.width();
879 changes.height = bounds.height();
880 value_mask |= CWHeight | CWWidth;
881 }
882
883 if (origin_changed) {
884 changes.x = bounds.x();
885 changes.y = bounds.y();
886 value_mask |= CWX | CWY;
887 }
888 if (value_mask)
889 XConfigureWindow(xdisplay_, xwindow_, value_mask, &changes);
890
891 // Assume that the resize will go through as requested, which should be the
892 // case if we're running without a window manager. If there's a window
893 // manager, it can modify or ignore the request, but (per ICCCM) we'll get a
894 // (possibly synthetic) ConfigureNotify about the actual size and correct
895 // |bounds_| later.
896 bounds_ = bounds;
897
898 if (origin_changed)
899 native_widget_delegate_->AsWidget()->OnNativeWidgetMove();
900 if (size_changed) {
901 OnHostResized(bounds.size());
902 ResetWindowRegion();
903 }
904 }
905
GetLocationOnNativeScreen() const906 gfx::Point DesktopWindowTreeHostX11::GetLocationOnNativeScreen() const {
907 return bounds_.origin();
908 }
909
SetCapture()910 void DesktopWindowTreeHostX11::SetCapture() {
911 // This is vaguely based on the old NativeWidgetGtk implementation.
912 //
913 // X11's XPointerGrab() shouldn't be used for everything; it doesn't map
914 // cleanly to Windows' SetCapture(). GTK only provides a separate concept of
915 // a grab that wasn't the X11 pointer grab, but was instead a manual
916 // redirection of the event. (You need to drop into GDK if you want to
917 // perform a raw X11 grab).
918
919 if (g_current_capture)
920 g_current_capture->OnCaptureReleased();
921
922 g_current_capture = this;
923 x11_capture_.reset(new X11ScopedCapture(xwindow_));
924 }
925
ReleaseCapture()926 void DesktopWindowTreeHostX11::ReleaseCapture() {
927 if (g_current_capture == this)
928 g_current_capture->OnCaptureReleased();
929 }
930
SetCursorNative(gfx::NativeCursor cursor)931 void DesktopWindowTreeHostX11::SetCursorNative(gfx::NativeCursor cursor) {
932 XDefineCursor(xdisplay_, xwindow_, cursor.platform());
933 }
934
MoveCursorToNative(const gfx::Point & location)935 void DesktopWindowTreeHostX11::MoveCursorToNative(const gfx::Point& location) {
936 XWarpPointer(xdisplay_, None, x_root_window_, 0, 0, 0, 0,
937 bounds_.x() + location.x(), bounds_.y() + location.y());
938 }
939
OnCursorVisibilityChangedNative(bool show)940 void DesktopWindowTreeHostX11::OnCursorVisibilityChangedNative(bool show) {
941 // TODO(erg): Conditional on us enabling touch on desktop linux builds, do
942 // the same tap-to-click disabling here that chromeos does.
943 }
944
PostNativeEvent(const base::NativeEvent & native_event)945 void DesktopWindowTreeHostX11::PostNativeEvent(
946 const base::NativeEvent& native_event) {
947 DCHECK(xwindow_);
948 DCHECK(xdisplay_);
949 XEvent xevent = *native_event;
950 xevent.xany.display = xdisplay_;
951 xevent.xany.window = xwindow_;
952
953 switch (xevent.type) {
954 case EnterNotify:
955 case LeaveNotify:
956 case MotionNotify:
957 case KeyPress:
958 case KeyRelease:
959 case ButtonPress:
960 case ButtonRelease: {
961 // The fields used below are in the same place for all of events
962 // above. Using xmotion from XEvent's unions to avoid repeating
963 // the code.
964 xevent.xmotion.root = x_root_window_;
965 xevent.xmotion.time = CurrentTime;
966
967 gfx::Point point(xevent.xmotion.x, xevent.xmotion.y);
968 ConvertPointToNativeScreen(&point);
969 xevent.xmotion.x_root = point.x();
970 xevent.xmotion.y_root = point.y();
971 }
972 default:
973 break;
974 }
975 XSendEvent(xdisplay_, xwindow_, False, 0, &xevent);
976 }
977
OnDeviceScaleFactorChanged(float device_scale_factor)978 void DesktopWindowTreeHostX11::OnDeviceScaleFactorChanged(
979 float device_scale_factor) {
980 }
981
982 ////////////////////////////////////////////////////////////////////////////////
983 // DesktopWindowTreeHostX11, ui::EventSource implementation:
984
GetEventProcessor()985 ui::EventProcessor* DesktopWindowTreeHostX11::GetEventProcessor() {
986 return dispatcher();
987 }
988
989 ////////////////////////////////////////////////////////////////////////////////
990 // DesktopWindowTreeHostX11, private:
991
InitX11Window(const Widget::InitParams & params)992 void DesktopWindowTreeHostX11::InitX11Window(
993 const Widget::InitParams& params) {
994 unsigned long attribute_mask = CWBackPixmap;
995 XSetWindowAttributes swa;
996 memset(&swa, 0, sizeof(swa));
997 swa.background_pixmap = None;
998
999 ::Atom window_type;
1000 switch (params.type) {
1001 case Widget::InitParams::TYPE_MENU:
1002 swa.override_redirect = True;
1003 window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_MENU");
1004 break;
1005 case Widget::InitParams::TYPE_TOOLTIP:
1006 swa.override_redirect = True;
1007 window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_TOOLTIP");
1008 break;
1009 case Widget::InitParams::TYPE_POPUP:
1010 swa.override_redirect = True;
1011 window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_NOTIFICATION");
1012 break;
1013 case Widget::InitParams::TYPE_DRAG:
1014 swa.override_redirect = True;
1015 window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_DND");
1016 break;
1017 default:
1018 window_type = atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE_NORMAL");
1019 break;
1020 }
1021 if (swa.override_redirect)
1022 attribute_mask |= CWOverrideRedirect;
1023
1024 // Detect whether we're running inside a compositing manager. If so, try to
1025 // use the ARGB visual. Otherwise, just use our parent's visual.
1026 Visual* visual = CopyFromParent;
1027 int depth = CopyFromParent;
1028 if (CommandLine::ForCurrentProcess()->HasSwitch(
1029 switches::kEnableTransparentVisuals) &&
1030 XGetSelectionOwner(xdisplay_,
1031 atom_cache_.GetAtom("_NET_WM_CM_S0")) != None) {
1032 Visual* rgba_visual = GetARGBVisual();
1033 if (rgba_visual) {
1034 visual = rgba_visual;
1035 depth = 32;
1036
1037 attribute_mask |= CWColormap;
1038 swa.colormap = XCreateColormap(xdisplay_, x_root_window_, visual,
1039 AllocNone);
1040
1041 // x.org will BadMatch if we don't set a border when the depth isn't the
1042 // same as the parent depth.
1043 attribute_mask |= CWBorderPixel;
1044 swa.border_pixel = 0;
1045
1046 use_argb_visual_ = true;
1047 }
1048 }
1049
1050 bounds_ = params.bounds;
1051 xwindow_ = XCreateWindow(
1052 xdisplay_, x_root_window_,
1053 bounds_.x(), bounds_.y(),
1054 bounds_.width(), bounds_.height(),
1055 0, // border width
1056 depth,
1057 InputOutput,
1058 visual,
1059 attribute_mask,
1060 &swa);
1061 if (ui::PlatformEventSource::GetInstance())
1062 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
1063 open_windows().push_back(xwindow_);
1064
1065 // TODO(erg): Maybe need to set a ViewProp here like in RWHL::RWHL().
1066
1067 long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
1068 KeyPressMask | KeyReleaseMask |
1069 EnterWindowMask | LeaveWindowMask |
1070 ExposureMask | VisibilityChangeMask |
1071 StructureNotifyMask | PropertyChangeMask |
1072 PointerMotionMask;
1073 XSelectInput(xdisplay_, xwindow_, event_mask);
1074 XFlush(xdisplay_);
1075
1076 if (ui::IsXInput2Available())
1077 ui::TouchFactory::GetInstance()->SetupXI2ForXWindow(xwindow_);
1078
1079 // TODO(erg): We currently only request window deletion events. We also
1080 // should listen for activation events and anything else that GTK+ listens
1081 // for, and do something useful.
1082 ::Atom protocols[2];
1083 protocols[0] = atom_cache_.GetAtom("WM_DELETE_WINDOW");
1084 protocols[1] = atom_cache_.GetAtom("_NET_WM_PING");
1085 XSetWMProtocols(xdisplay_, xwindow_, protocols, 2);
1086
1087 // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with
1088 // the desktop environment.
1089 XSetWMProperties(xdisplay_, xwindow_, NULL, NULL, NULL, 0, NULL, NULL, NULL);
1090
1091 // Likewise, the X server needs to know this window's pid so it knows which
1092 // program to kill if the window hangs.
1093 // XChangeProperty() expects "pid" to be long.
1094 COMPILE_ASSERT(sizeof(long) >= sizeof(pid_t), pid_t_bigger_than_long);
1095 long pid = getpid();
1096 XChangeProperty(xdisplay_,
1097 xwindow_,
1098 atom_cache_.GetAtom("_NET_WM_PID"),
1099 XA_CARDINAL,
1100 32,
1101 PropModeReplace,
1102 reinterpret_cast<unsigned char*>(&pid), 1);
1103
1104 XChangeProperty(xdisplay_,
1105 xwindow_,
1106 atom_cache_.GetAtom("_NET_WM_WINDOW_TYPE"),
1107 XA_ATOM,
1108 32,
1109 PropModeReplace,
1110 reinterpret_cast<unsigned char*>(&window_type), 1);
1111
1112 // List of window state properties (_NET_WM_STATE) to set, if any.
1113 std::vector< ::Atom> state_atom_list;
1114
1115 // Remove popup windows from taskbar unless overridden.
1116 if ((params.type == Widget::InitParams::TYPE_POPUP ||
1117 params.type == Widget::InitParams::TYPE_BUBBLE) &&
1118 !params.force_show_in_taskbar) {
1119 state_atom_list.push_back(
1120 atom_cache_.GetAtom("_NET_WM_STATE_SKIP_TASKBAR"));
1121 }
1122
1123 // If the window should stay on top of other windows, add the
1124 // _NET_WM_STATE_ABOVE property.
1125 is_always_on_top_ = params.keep_on_top;
1126 if (is_always_on_top_)
1127 state_atom_list.push_back(atom_cache_.GetAtom("_NET_WM_STATE_ABOVE"));
1128
1129 if (params.visible_on_all_workspaces) {
1130 state_atom_list.push_back(atom_cache_.GetAtom("_NET_WM_STATE_STICKY"));
1131 ui::SetIntProperty(xwindow_, "_NET_WM_DESKTOP", "CARDINAL", kAllDesktops);
1132 }
1133
1134 // Setting _NET_WM_STATE by sending a message to the root_window (with
1135 // SetWMSpecState) has no effect here since the window has not yet been
1136 // mapped. So we manually change the state.
1137 if (!state_atom_list.empty()) {
1138 ui::SetAtomArrayProperty(xwindow_,
1139 "_NET_WM_STATE",
1140 "ATOM",
1141 state_atom_list);
1142 }
1143
1144 if (!params.wm_class_name.empty() || !params.wm_class_class.empty()) {
1145 ui::SetWindowClassHint(
1146 xdisplay_, xwindow_, params.wm_class_name, params.wm_class_class);
1147 }
1148 if (!params.wm_role_name.empty() ||
1149 params.type == Widget::InitParams::TYPE_POPUP) {
1150 const char kX11WindowRolePopup[] = "popup";
1151 ui::SetWindowRole(xdisplay_, xwindow_, params.wm_role_name.empty() ?
1152 std::string(kX11WindowRolePopup) : params.wm_role_name);
1153 }
1154
1155 if (params.remove_standard_frame) {
1156 // Setting _GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED tells gnome-shell to not force
1157 // fullscreen on the window when it matches the desktop size.
1158 ui::SetHideTitlebarWhenMaximizedProperty(xwindow_,
1159 ui::HIDE_TITLEBAR_WHEN_MAXIMIZED);
1160 }
1161
1162 // If we have a parent, record the parent/child relationship. We use this
1163 // data during destruction to make sure that when we try to close a parent
1164 // window, we also destroy all child windows.
1165 if (params.parent && params.parent->GetHost()) {
1166 XID parent_xid =
1167 params.parent->GetHost()->GetAcceleratedWidget();
1168 window_parent_ = GetHostForXID(parent_xid);
1169 DCHECK(window_parent_);
1170 window_parent_->window_children_.insert(this);
1171 }
1172
1173 // If we have a delegate which is providing a default window icon, use that
1174 // icon.
1175 gfx::ImageSkia* window_icon = ViewsDelegate::views_delegate ?
1176 ViewsDelegate::views_delegate->GetDefaultWindowIcon() : NULL;
1177 if (window_icon) {
1178 SetWindowIcons(gfx::ImageSkia(), *window_icon);
1179 }
1180 CreateCompositor(GetAcceleratedWidget());
1181 }
1182
OnWMStateUpdated()1183 void DesktopWindowTreeHostX11::OnWMStateUpdated() {
1184 std::vector< ::Atom> atom_list;
1185 if (!ui::GetAtomArrayProperty(xwindow_, "_NET_WM_STATE", &atom_list))
1186 return;
1187
1188 bool was_minimized = IsMinimized();
1189
1190 window_properties_.clear();
1191 std::copy(atom_list.begin(), atom_list.end(),
1192 inserter(window_properties_, window_properties_.begin()));
1193
1194 // Propagate the window minimization information to the content window, so
1195 // the render side can update its visibility properly. OnWMStateUpdated() is
1196 // called by PropertyNofify event from DispatchEvent() when the browser is
1197 // minimized or shown from minimized state. On Windows, this is realized by
1198 // calling OnHostResized() with an empty size. In particular,
1199 // HWNDMessageHandler::GetClientAreaBounds() returns an empty size when the
1200 // window is minimized. On Linux, returning empty size in GetBounds() or
1201 // SetBounds() does not work.
1202 bool is_minimized = IsMinimized();
1203 if (is_minimized != was_minimized) {
1204 if (is_minimized)
1205 content_window_->Hide();
1206 else
1207 content_window_->Show();
1208 }
1209
1210 if (restored_bounds_.IsEmpty()) {
1211 DCHECK(!IsFullscreen());
1212 if (IsMaximized()) {
1213 // The request that we become maximized originated from a different
1214 // process. |bounds_| already contains our maximized bounds. Do a best
1215 // effort attempt to get restored bounds by setting it to our previously
1216 // set bounds (and if we get this wrong, we aren't any worse off since
1217 // we'd otherwise be returning our maximized bounds).
1218 restored_bounds_ = previous_bounds_;
1219 }
1220 } else if (!IsMaximized() && !IsFullscreen()) {
1221 // If we have restored bounds, but WM_STATE no longer claims to be
1222 // maximized or fullscreen, we should clear our restored bounds.
1223 restored_bounds_ = gfx::Rect();
1224 }
1225
1226 // Ignore requests by the window manager to enter or exit fullscreen (e.g. as
1227 // a result of pressing a window manager accelerator key). Chrome does not
1228 // handle window manager initiated fullscreen. In particular, Chrome needs to
1229 // do preprocessing before the x window's fullscreen state is toggled.
1230
1231 is_always_on_top_ = HasWMSpecProperty("_NET_WM_STATE_ABOVE");
1232
1233 // Now that we have different window properties, we may need to relayout the
1234 // window. (The windows code doesn't need this because their window change is
1235 // synchronous.)
1236 Relayout();
1237 ResetWindowRegion();
1238 }
1239
OnFrameExtentsUpdated()1240 void DesktopWindowTreeHostX11::OnFrameExtentsUpdated() {
1241 std::vector<int> insets;
1242 if (ui::GetIntArrayProperty(xwindow_, "_NET_FRAME_EXTENTS", &insets) &&
1243 insets.size() == 4) {
1244 // |insets| are returned in the order: [left, right, top, bottom].
1245 native_window_frame_borders_ = gfx::Insets(
1246 insets[2],
1247 insets[0],
1248 insets[3],
1249 insets[1]);
1250 } else {
1251 native_window_frame_borders_ = gfx::Insets();
1252 }
1253 }
1254
UpdateWMUserTime(const ui::PlatformEvent & event)1255 void DesktopWindowTreeHostX11::UpdateWMUserTime(
1256 const ui::PlatformEvent& event) {
1257 if (!IsActive())
1258 return;
1259
1260 ui::EventType type = ui::EventTypeFromNative(event);
1261 if (type == ui::ET_MOUSE_PRESSED ||
1262 type == ui::ET_KEY_PRESSED ||
1263 type == ui::ET_TOUCH_PRESSED) {
1264 unsigned long wm_user_time_ms = static_cast<unsigned long>(
1265 ui::EventTimeFromNative(event).InMilliseconds());
1266 XChangeProperty(xdisplay_,
1267 xwindow_,
1268 atom_cache_.GetAtom("_NET_WM_USER_TIME"),
1269 XA_CARDINAL,
1270 32,
1271 PropModeReplace,
1272 reinterpret_cast<const unsigned char *>(&wm_user_time_ms),
1273 1);
1274 X11DesktopHandler::get()->set_wm_user_time_ms(wm_user_time_ms);
1275 }
1276 }
1277
SetWMSpecState(bool enabled,::Atom state1,::Atom state2)1278 void DesktopWindowTreeHostX11::SetWMSpecState(bool enabled,
1279 ::Atom state1,
1280 ::Atom state2) {
1281 XEvent xclient;
1282 memset(&xclient, 0, sizeof(xclient));
1283 xclient.type = ClientMessage;
1284 xclient.xclient.window = xwindow_;
1285 xclient.xclient.message_type = atom_cache_.GetAtom("_NET_WM_STATE");
1286 xclient.xclient.format = 32;
1287 xclient.xclient.data.l[0] =
1288 enabled ? k_NET_WM_STATE_ADD : k_NET_WM_STATE_REMOVE;
1289 xclient.xclient.data.l[1] = state1;
1290 xclient.xclient.data.l[2] = state2;
1291 xclient.xclient.data.l[3] = 1;
1292 xclient.xclient.data.l[4] = 0;
1293
1294 XSendEvent(xdisplay_, x_root_window_, False,
1295 SubstructureRedirectMask | SubstructureNotifyMask,
1296 &xclient);
1297 }
1298
HasWMSpecProperty(const char * property) const1299 bool DesktopWindowTreeHostX11::HasWMSpecProperty(const char* property) const {
1300 return window_properties_.find(atom_cache_.GetAtom(property)) !=
1301 window_properties_.end();
1302 }
1303
SetUseNativeFrame(bool use_native_frame)1304 void DesktopWindowTreeHostX11::SetUseNativeFrame(bool use_native_frame) {
1305 use_native_frame_ = use_native_frame;
1306 ui::SetUseOSWindowFrame(xwindow_, use_native_frame);
1307 ResetWindowRegion();
1308 }
1309
OnCaptureReleased()1310 void DesktopWindowTreeHostX11::OnCaptureReleased() {
1311 x11_capture_.reset();
1312 g_current_capture = NULL;
1313 OnHostLostWindowCapture();
1314 native_widget_delegate_->OnMouseCaptureLost();
1315 }
1316
DispatchMouseEvent(ui::MouseEvent * event)1317 void DesktopWindowTreeHostX11::DispatchMouseEvent(ui::MouseEvent* event) {
1318 // In Windows, the native events sent to chrome are separated into client
1319 // and non-client versions of events, which we record on our LocatedEvent
1320 // structures. On X11, we emulate the concept of non-client. Before we pass
1321 // this event to the cross platform event handling framework, we need to
1322 // make sure it is appropriately marked as non-client if it's in the non
1323 // client area, or otherwise, we can get into a state where the a window is
1324 // set as the |mouse_pressed_handler_| in window_event_dispatcher.cc
1325 // despite the mouse button being released.
1326 //
1327 // We can't do this later in the dispatch process because we share that
1328 // with ash, and ash gets confused about event IS_NON_CLIENT-ness on
1329 // events, since ash doesn't expect this bit to be set, because it's never
1330 // been set before. (This works on ash on Windows because none of the mouse
1331 // events on the ash desktop are clicking in what Windows considers to be a
1332 // non client area.) Likewise, we won't want to do the following in any
1333 // WindowTreeHost that hosts ash.
1334 if (content_window_ && content_window_->delegate()) {
1335 int flags = event->flags();
1336 int hit_test_code =
1337 content_window_->delegate()->GetNonClientComponent(event->location());
1338 if (hit_test_code != HTCLIENT && hit_test_code != HTNOWHERE)
1339 flags |= ui::EF_IS_NON_CLIENT;
1340 event->set_flags(flags);
1341 }
1342
1343 // While we unset the urgency hint when we gain focus, we also must remove it
1344 // on mouse clicks because we can call FlashFrame() on an active window.
1345 if (event->IsAnyButton() || event->IsMouseWheelEvent())
1346 FlashFrame(false);
1347
1348 if (!g_current_capture || g_current_capture == this) {
1349 SendEventToProcessor(event);
1350 } else {
1351 // Another DesktopWindowTreeHostX11 has installed itself as
1352 // capture. Translate the event's location and dispatch to the other.
1353 event->ConvertLocationToTarget(window(), g_current_capture->window());
1354 g_current_capture->SendEventToProcessor(event);
1355 }
1356 }
1357
DispatchTouchEvent(ui::TouchEvent * event)1358 void DesktopWindowTreeHostX11::DispatchTouchEvent(ui::TouchEvent* event) {
1359 if (g_current_capture && g_current_capture != this &&
1360 event->type() == ui::ET_TOUCH_PRESSED) {
1361 event->ConvertLocationToTarget(window(), g_current_capture->window());
1362 g_current_capture->SendEventToProcessor(event);
1363 } else {
1364 SendEventToProcessor(event);
1365 }
1366 }
1367
ResetWindowRegion()1368 void DesktopWindowTreeHostX11::ResetWindowRegion() {
1369 // If a custom window shape was supplied then apply it.
1370 if (custom_window_shape_) {
1371 XShapeCombineRegion(
1372 xdisplay_, xwindow_, ShapeBounding, 0, 0, window_shape_, false);
1373 return;
1374 }
1375
1376 if (window_shape_)
1377 XDestroyRegion(window_shape_);
1378 window_shape_ = NULL;
1379
1380 if (!IsMaximized() && !IsFullscreen()) {
1381 gfx::Path window_mask;
1382 views::Widget* widget = native_widget_delegate_->AsWidget();
1383 if (widget->non_client_view()) {
1384 // Some frame views define a custom (non-rectangular) window mask. If
1385 // so, use it to define the window shape. If not, fall through.
1386 widget->non_client_view()->GetWindowMask(bounds_.size(), &window_mask);
1387 if (window_mask.countPoints() > 0) {
1388 window_shape_ = gfx::CreateRegionFromSkPath(window_mask);
1389 XShapeCombineRegion(xdisplay_, xwindow_, ShapeBounding,
1390 0, 0, window_shape_, false);
1391 return;
1392 }
1393 }
1394 }
1395
1396 // If we didn't set the shape for any reason, reset the shaping information.
1397 // How this is done depends on the border style, due to quirks and bugs in
1398 // various window managers.
1399 if (ShouldUseNativeFrame()) {
1400 // If the window has system borders, the mask must be set to null (not a
1401 // rectangle), because several window managers (eg, KDE, XFCE, XMonad) will
1402 // not put borders on a window with a custom shape.
1403 XShapeCombineMask(xdisplay_, xwindow_, ShapeBounding, 0, 0, None, ShapeSet);
1404 } else {
1405 // Conversely, if the window does not have system borders, the mask must be
1406 // manually set to a rectangle that covers the whole window (not null). This
1407 // is due to a bug in KWin <= 4.11.5 (KDE bug #330573) where setting a null
1408 // shape causes the hint to disable system borders to be ignored (resulting
1409 // in a double border).
1410 XRectangle r = {0, 0, static_cast<unsigned short>(bounds_.width()),
1411 static_cast<unsigned short>(bounds_.height())};
1412 XShapeCombineRectangles(
1413 xdisplay_, xwindow_, ShapeBounding, 0, 0, &r, 1, ShapeSet, YXBanded);
1414 }
1415 }
1416
SerializeImageRepresentation(const gfx::ImageSkiaRep & rep,std::vector<unsigned long> * data)1417 void DesktopWindowTreeHostX11::SerializeImageRepresentation(
1418 const gfx::ImageSkiaRep& rep,
1419 std::vector<unsigned long>* data) {
1420 int width = rep.GetWidth();
1421 data->push_back(width);
1422
1423 int height = rep.GetHeight();
1424 data->push_back(height);
1425
1426 const SkBitmap& bitmap = rep.sk_bitmap();
1427 SkAutoLockPixels locker(bitmap);
1428
1429 for (int y = 0; y < height; ++y)
1430 for (int x = 0; x < width; ++x)
1431 data->push_back(bitmap.getColor(x, y));
1432 }
1433
GetARGBVisual()1434 Visual* DesktopWindowTreeHostX11::GetARGBVisual() {
1435 XVisualInfo visual_template;
1436 visual_template.screen = 0;
1437 Visual* to_return = NULL;
1438
1439 int visuals_len;
1440 XVisualInfo* visual_list = XGetVisualInfo(xdisplay_,
1441 VisualScreenMask,
1442 &visual_template, &visuals_len);
1443 for (int i = 0; i < visuals_len; ++i) {
1444 // Why support only 8888 ARGB? Because it's all that GTK+ supports. In
1445 // gdkvisual-x11.cc, they look for this specific visual and use it for all
1446 // their alpha channel using needs.
1447 //
1448 // TODO(erg): While the following does find a valid visual, some GL drivers
1449 // don't believe that this has an alpha channel. According to marcheu@,
1450 // this should work on open source driver though. (It doesn't work with
1451 // NVidia's binaries currently.) http://crbug.com/369209
1452 if (visual_list[i].depth == 32 &&
1453 visual_list[i].visual->red_mask == 0xff0000 &&
1454 visual_list[i].visual->green_mask == 0x00ff00 &&
1455 visual_list[i].visual->blue_mask == 0x0000ff) {
1456 to_return = visual_list[i].visual;
1457 break;
1458 }
1459 }
1460
1461 if (visual_list)
1462 XFree(visual_list);
1463
1464 return to_return;
1465 }
1466
open_windows()1467 std::list<XID>& DesktopWindowTreeHostX11::open_windows() {
1468 if (!open_windows_)
1469 open_windows_ = new std::list<XID>();
1470 return *open_windows_;
1471 }
1472
MapWindow(ui::WindowShowState show_state)1473 void DesktopWindowTreeHostX11::MapWindow(ui::WindowShowState show_state) {
1474 if (show_state != ui::SHOW_STATE_DEFAULT &&
1475 show_state != ui::SHOW_STATE_NORMAL &&
1476 show_state != ui::SHOW_STATE_INACTIVE) {
1477 // It will behave like SHOW_STATE_NORMAL.
1478 NOTIMPLEMENTED();
1479 }
1480
1481 // Before we map the window, set size hints. Otherwise, some window managers
1482 // will ignore toplevel XMoveWindow commands.
1483 XSizeHints size_hints;
1484 size_hints.flags = PPosition;
1485 size_hints.x = bounds_.x();
1486 size_hints.y = bounds_.y();
1487 XSetWMNormalHints(xdisplay_, xwindow_, &size_hints);
1488
1489 // If SHOW_STATE_INACTIVE, tell the window manager not to focus the window
1490 // when mapping. This is done by setting the _NET_WM_USER_TIME to 0. See e.g.
1491 // http://standards.freedesktop.org/wm-spec/latest/ar01s05.html
1492 unsigned long wm_user_time_ms = (show_state == ui::SHOW_STATE_INACTIVE) ?
1493 0 : X11DesktopHandler::get()->wm_user_time_ms();
1494 if (show_state == ui::SHOW_STATE_INACTIVE || wm_user_time_ms != 0) {
1495 XChangeProperty(xdisplay_,
1496 xwindow_,
1497 atom_cache_.GetAtom("_NET_WM_USER_TIME"),
1498 XA_CARDINAL,
1499 32,
1500 PropModeReplace,
1501 reinterpret_cast<const unsigned char *>(&wm_user_time_ms),
1502 1);
1503 }
1504
1505 XMapWindow(xdisplay_, xwindow_);
1506
1507 // We now block until our window is mapped. Some X11 APIs will crash and
1508 // burn if passed |xwindow_| before the window is mapped, and XMapWindow is
1509 // asynchronous.
1510 if (ui::X11EventSource::GetInstance())
1511 ui::X11EventSource::GetInstance()->BlockUntilWindowMapped(xwindow_);
1512 window_mapped_ = true;
1513 }
1514
SetWindowTransparency()1515 void DesktopWindowTreeHostX11::SetWindowTransparency() {
1516 compositor()->SetHostHasTransparentBackground(use_argb_visual_);
1517 window()->SetTransparent(use_argb_visual_);
1518 content_window_->SetTransparent(use_argb_visual_);
1519 }
1520
Relayout()1521 void DesktopWindowTreeHostX11::Relayout() {
1522 Widget* widget = native_widget_delegate_->AsWidget();
1523 NonClientView* non_client_view = widget->non_client_view();
1524 // non_client_view may be NULL, especially during creation.
1525 if (non_client_view) {
1526 non_client_view->client_view()->InvalidateLayout();
1527 non_client_view->InvalidateLayout();
1528 }
1529 widget->GetRootView()->Layout();
1530 }
1531
1532 ////////////////////////////////////////////////////////////////////////////////
1533 // DesktopWindowTreeHostX11, ui::PlatformEventDispatcher implementation:
1534
CanDispatchEvent(const ui::PlatformEvent & event)1535 bool DesktopWindowTreeHostX11::CanDispatchEvent(
1536 const ui::PlatformEvent& event) {
1537 return event->xany.window == xwindow_ ||
1538 (event->type == GenericEvent &&
1539 static_cast<XIDeviceEvent*>(event->xcookie.data)->event == xwindow_);
1540 }
1541
DispatchEvent(const ui::PlatformEvent & event)1542 uint32_t DesktopWindowTreeHostX11::DispatchEvent(
1543 const ui::PlatformEvent& event) {
1544 XEvent* xev = event;
1545
1546 TRACE_EVENT1("views", "DesktopWindowTreeHostX11::Dispatch",
1547 "event->type", event->type);
1548
1549 UpdateWMUserTime(event);
1550
1551 // May want to factor CheckXEventForConsistency(xev); into a common location
1552 // since it is called here.
1553 switch (xev->type) {
1554 case EnterNotify:
1555 case LeaveNotify: {
1556 // Ignore EventNotify and LeaveNotify events from children of |xwindow_|.
1557 // NativeViewGLSurfaceGLX adds a child to |xwindow_|.
1558 // TODO(pkotwicz|tdanderson): Figure out whether the suppression is
1559 // necessary. crbug.com/385716
1560 if (xev->xcrossing.detail == NotifyInferior)
1561 break;
1562
1563 ui::MouseEvent mouse_event(xev);
1564 DispatchMouseEvent(&mouse_event);
1565 break;
1566 }
1567 case Expose: {
1568 gfx::Rect damage_rect(xev->xexpose.x, xev->xexpose.y,
1569 xev->xexpose.width, xev->xexpose.height);
1570 compositor()->ScheduleRedrawRect(damage_rect);
1571 break;
1572 }
1573 case KeyPress: {
1574 ui::KeyEvent keydown_event(xev, false);
1575 SendEventToProcessor(&keydown_event);
1576 break;
1577 }
1578 case KeyRelease: {
1579 ui::KeyEvent keyup_event(xev, false);
1580 SendEventToProcessor(&keyup_event);
1581 break;
1582 }
1583 case ButtonPress:
1584 case ButtonRelease: {
1585 ui::EventType event_type = ui::EventTypeFromNative(xev);
1586 switch (event_type) {
1587 case ui::ET_MOUSEWHEEL: {
1588 ui::MouseWheelEvent mouseev(xev);
1589 DispatchMouseEvent(&mouseev);
1590 break;
1591 }
1592 case ui::ET_MOUSE_PRESSED:
1593 case ui::ET_MOUSE_RELEASED: {
1594 ui::MouseEvent mouseev(xev);
1595 DispatchMouseEvent(&mouseev);
1596 break;
1597 }
1598 case ui::ET_UNKNOWN:
1599 // No event is created for X11-release events for mouse-wheel buttons.
1600 break;
1601 default:
1602 NOTREACHED() << event_type;
1603 }
1604 break;
1605 }
1606 case FocusOut:
1607 if (xev->xfocus.mode != NotifyGrab) {
1608 ReleaseCapture();
1609 OnHostLostWindowCapture();
1610 X11DesktopHandler::get()->ProcessXEvent(xev);
1611 } else {
1612 dispatcher()->OnHostLostMouseGrab();
1613 }
1614 break;
1615 case FocusIn:
1616 X11DesktopHandler::get()->ProcessXEvent(xev);
1617 break;
1618 case ConfigureNotify: {
1619 DCHECK_EQ(xwindow_, xev->xconfigure.window);
1620 DCHECK_EQ(xwindow_, xev->xconfigure.event);
1621 // It's possible that the X window may be resized by some other means than
1622 // from within aura (e.g. the X window manager can change the size). Make
1623 // sure the root window size is maintained properly.
1624 int translated_x = xev->xconfigure.x;
1625 int translated_y = xev->xconfigure.y;
1626 if (!xev->xconfigure.send_event && !xev->xconfigure.override_redirect) {
1627 Window unused;
1628 XTranslateCoordinates(xdisplay_, xwindow_, x_root_window_,
1629 0, 0, &translated_x, &translated_y, &unused);
1630 }
1631 gfx::Rect bounds(translated_x, translated_y,
1632 xev->xconfigure.width, xev->xconfigure.height);
1633 bool size_changed = bounds_.size() != bounds.size();
1634 bool origin_changed = bounds_.origin() != bounds.origin();
1635 previous_bounds_ = bounds_;
1636 bounds_ = bounds;
1637
1638 if (origin_changed)
1639 OnHostMoved(bounds_.origin());
1640
1641 if (size_changed) {
1642 delayed_resize_task_.Reset(base::Bind(
1643 &DesktopWindowTreeHostX11::DelayedResize,
1644 close_widget_factory_.GetWeakPtr(),
1645 bounds.size()));
1646 base::MessageLoop::current()->PostTask(
1647 FROM_HERE, delayed_resize_task_.callback());
1648 }
1649 break;
1650 }
1651 case GenericEvent: {
1652 ui::TouchFactory* factory = ui::TouchFactory::GetInstance();
1653 if (!factory->ShouldProcessXI2Event(xev))
1654 break;
1655
1656 ui::EventType type = ui::EventTypeFromNative(xev);
1657 XEvent last_event;
1658 int num_coalesced = 0;
1659
1660 switch (type) {
1661 case ui::ET_TOUCH_MOVED:
1662 num_coalesced = ui::CoalescePendingMotionEvents(xev, &last_event);
1663 if (num_coalesced > 0)
1664 xev = &last_event;
1665 // fallthrough
1666 case ui::ET_TOUCH_PRESSED:
1667 case ui::ET_TOUCH_RELEASED: {
1668 ui::TouchEvent touchev(xev);
1669 DispatchTouchEvent(&touchev);
1670 break;
1671 }
1672 case ui::ET_MOUSE_MOVED:
1673 case ui::ET_MOUSE_DRAGGED:
1674 case ui::ET_MOUSE_PRESSED:
1675 case ui::ET_MOUSE_RELEASED:
1676 case ui::ET_MOUSE_ENTERED:
1677 case ui::ET_MOUSE_EXITED: {
1678 if (type == ui::ET_MOUSE_MOVED || type == ui::ET_MOUSE_DRAGGED) {
1679 // If this is a motion event, we want to coalesce all pending motion
1680 // events that are at the top of the queue.
1681 num_coalesced = ui::CoalescePendingMotionEvents(xev, &last_event);
1682 if (num_coalesced > 0)
1683 xev = &last_event;
1684 }
1685 ui::MouseEvent mouseev(xev);
1686 DispatchMouseEvent(&mouseev);
1687 break;
1688 }
1689 case ui::ET_MOUSEWHEEL: {
1690 ui::MouseWheelEvent mouseev(xev);
1691 DispatchMouseEvent(&mouseev);
1692 break;
1693 }
1694 case ui::ET_SCROLL_FLING_START:
1695 case ui::ET_SCROLL_FLING_CANCEL:
1696 case ui::ET_SCROLL: {
1697 ui::ScrollEvent scrollev(xev);
1698 SendEventToProcessor(&scrollev);
1699 break;
1700 }
1701 case ui::ET_UNKNOWN:
1702 break;
1703 default:
1704 NOTREACHED();
1705 }
1706
1707 // If we coalesced an event we need to free its cookie.
1708 if (num_coalesced > 0)
1709 XFreeEventData(xev->xgeneric.display, &last_event.xcookie);
1710 break;
1711 }
1712 case MapNotify: {
1713 FOR_EACH_OBSERVER(DesktopWindowTreeHostObserverX11,
1714 observer_list_,
1715 OnWindowMapped(xwindow_));
1716 break;
1717 }
1718 case UnmapNotify: {
1719 FOR_EACH_OBSERVER(DesktopWindowTreeHostObserverX11,
1720 observer_list_,
1721 OnWindowUnmapped(xwindow_));
1722 break;
1723 }
1724 case ClientMessage: {
1725 Atom message_type = xev->xclient.message_type;
1726 if (message_type == atom_cache_.GetAtom("WM_PROTOCOLS")) {
1727 Atom protocol = static_cast<Atom>(xev->xclient.data.l[0]);
1728 if (protocol == atom_cache_.GetAtom("WM_DELETE_WINDOW")) {
1729 // We have received a close message from the window manager.
1730 OnHostCloseRequested();
1731 } else if (protocol == atom_cache_.GetAtom("_NET_WM_PING")) {
1732 XEvent reply_event = *xev;
1733 reply_event.xclient.window = x_root_window_;
1734
1735 XSendEvent(xdisplay_,
1736 reply_event.xclient.window,
1737 False,
1738 SubstructureRedirectMask | SubstructureNotifyMask,
1739 &reply_event);
1740 }
1741 } else if (message_type == atom_cache_.GetAtom("XdndEnter")) {
1742 drag_drop_client_->OnXdndEnter(xev->xclient);
1743 } else if (message_type == atom_cache_.GetAtom("XdndLeave")) {
1744 drag_drop_client_->OnXdndLeave(xev->xclient);
1745 } else if (message_type == atom_cache_.GetAtom("XdndPosition")) {
1746 drag_drop_client_->OnXdndPosition(xev->xclient);
1747 } else if (message_type == atom_cache_.GetAtom("XdndStatus")) {
1748 drag_drop_client_->OnXdndStatus(xev->xclient);
1749 } else if (message_type == atom_cache_.GetAtom("XdndFinished")) {
1750 drag_drop_client_->OnXdndFinished(xev->xclient);
1751 } else if (message_type == atom_cache_.GetAtom("XdndDrop")) {
1752 drag_drop_client_->OnXdndDrop(xev->xclient);
1753 }
1754 break;
1755 }
1756 case MappingNotify: {
1757 switch (xev->xmapping.request) {
1758 case MappingModifier:
1759 case MappingKeyboard:
1760 XRefreshKeyboardMapping(&xev->xmapping);
1761 break;
1762 case MappingPointer:
1763 ui::DeviceDataManager::GetInstance()->UpdateButtonMap();
1764 break;
1765 default:
1766 NOTIMPLEMENTED() << " Unknown request: " << xev->xmapping.request;
1767 break;
1768 }
1769 break;
1770 }
1771 case MotionNotify: {
1772 // Discard all but the most recent motion event that targets the same
1773 // window with unchanged state.
1774 XEvent last_event;
1775 while (XPending(xev->xany.display)) {
1776 XEvent next_event;
1777 XPeekEvent(xev->xany.display, &next_event);
1778 if (next_event.type == MotionNotify &&
1779 next_event.xmotion.window == xev->xmotion.window &&
1780 next_event.xmotion.subwindow == xev->xmotion.subwindow &&
1781 next_event.xmotion.state == xev->xmotion.state) {
1782 XNextEvent(xev->xany.display, &last_event);
1783 xev = &last_event;
1784 } else {
1785 break;
1786 }
1787 }
1788
1789 ui::MouseEvent mouseev(xev);
1790 DispatchMouseEvent(&mouseev);
1791 break;
1792 }
1793 case PropertyNotify: {
1794 ::Atom changed_atom = xev->xproperty.atom;
1795 if (changed_atom == atom_cache_.GetAtom("_NET_WM_STATE"))
1796 OnWMStateUpdated();
1797 else if (changed_atom == atom_cache_.GetAtom("_NET_FRAME_EXTENTS"))
1798 OnFrameExtentsUpdated();
1799 break;
1800 }
1801 case SelectionNotify: {
1802 drag_drop_client_->OnSelectionNotify(xev->xselection);
1803 break;
1804 }
1805 }
1806 return ui::POST_DISPATCH_STOP_PROPAGATION;
1807 }
1808
DelayedResize(const gfx::Size & size)1809 void DesktopWindowTreeHostX11::DelayedResize(const gfx::Size& size) {
1810 OnHostResized(size);
1811 ResetWindowRegion();
1812 delayed_resize_task_.Cancel();
1813 }
1814
1815 ////////////////////////////////////////////////////////////////////////////////
1816 // DesktopWindowTreeHost, public:
1817
1818 // static
Create(internal::NativeWidgetDelegate * native_widget_delegate,DesktopNativeWidgetAura * desktop_native_widget_aura)1819 DesktopWindowTreeHost* DesktopWindowTreeHost::Create(
1820 internal::NativeWidgetDelegate* native_widget_delegate,
1821 DesktopNativeWidgetAura* desktop_native_widget_aura) {
1822 return new DesktopWindowTreeHostX11(native_widget_delegate,
1823 desktop_native_widget_aura);
1824 }
1825
1826 // static
GetNativeTheme(aura::Window * window)1827 ui::NativeTheme* DesktopWindowTreeHost::GetNativeTheme(aura::Window* window) {
1828 const views::LinuxUI* linux_ui = views::LinuxUI::instance();
1829 if (linux_ui) {
1830 ui::NativeTheme* native_theme = linux_ui->GetNativeTheme(window);
1831 if (native_theme)
1832 return native_theme;
1833 }
1834
1835 return ui::NativeTheme::instance();
1836 }
1837
1838 } // namespace views
1839