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