1 // Copyright 2019 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 #ifndef UI_BASE_X_X11_WINDOW_H_
6 #define UI_BASE_X_X11_WINDOW_H_
7 
8 #include <array>
9 #include <memory>
10 #include <string>
11 
12 #include "base/cancelable_callback.h"
13 #include "base/component_export.h"
14 #include "base/containers/flat_set.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/optional.h"
17 #include "base/strings/string16.h"
18 #include "ui/gfx/geometry/insets.h"
19 #include "ui/gfx/geometry/rect.h"
20 #include "ui/gfx/geometry/size.h"
21 #include "ui/gfx/geometry/size_f.h"
22 #include "ui/gfx/x/x11.h"
23 #include "ui/gfx/x/x11_types.h"
24 
25 class SkPath;
26 
27 namespace gfx {
28 class ImageSkia;
29 class Transform;
30 }  // namespace gfx
31 
32 namespace ui {
33 
34 class Event;
35 class XScopedEventSelector;
36 
37 ////////////////////////////////////////////////////////////////////////////////
38 // XWindow class
39 //
40 // Base class that encapsulates a full featured Xlib-based X11 Window, meant
41 // to be used mainly in Linux desktop. Abstracts away most of X11 API
42 // interaction and assumes event handling and some required getters are
43 // implemented in subclasses.
44 //
45 // |XWindow::Configuration| holds parameters used in window initialization.
46 // Fields are equivalent and a sub-set of Widget::InitParams.
47 //
48 // All bounds and size values are assumed to be expressed in pixels.
COMPONENT_EXPORT(UI_BASE_X)49 class COMPONENT_EXPORT(UI_BASE_X) XWindow {
50  public:
51   using NativeShapeRects = std::vector<gfx::Rect>;
52 
53   enum class WindowType {
54     kWindow,
55     kPopup,
56     kMenu,
57     kTooltip,
58     kDrag,
59     kBubble,
60   };
61 
62   enum class WindowOpacity {
63     kInferOpacity,
64     kOpaqueWindow,
65     kTranslucentWindow,
66   };
67 
68   struct Configuration final {
69     Configuration();
70     Configuration(const Configuration& config);
71     ~Configuration();
72 
73     WindowType type;
74     WindowOpacity opacity;
75     gfx::Rect bounds;
76     gfx::ImageSkia* icon;
77     base::Optional<int> background_color;
78     bool activatable;
79     bool force_show_in_taskbar;
80     bool keep_on_top;
81     bool visible_on_all_workspaces;
82     bool remove_standard_frame;
83     bool prefer_dark_theme;
84     bool override_redirect;
85     std::string workspace;
86     std::string wm_class_name;
87     std::string wm_class_class;
88     std::string wm_role_name;
89   };
90 
91   XWindow();
92   XWindow(const XWindow&) = delete;
93   XWindow& operator=(const XWindow&) = delete;
94   virtual ~XWindow();
95 
96   void Init(const Configuration& config);
97   void Map(bool inactive = false);
98   void Close();
99   void Maximize();
100   void Minimize();
101   void Unmaximize();
102   bool Hide();
103   void Unhide();
104   void SetFullscreen(bool fullscreen);
105   void Activate();
106   void Deactivate();
107   bool IsActive() const;
108   void GrabPointer();
109   void ReleasePointerGrab();
110   void StackXWindowAbove(::Window window);
111   void StackXWindowAtTop();
112   bool IsTargetedBy(const XEvent& xev) const;
113   void WmMoveResize(int hittest, const gfx::Point& location) const;
114   void ProcessEvent(XEvent* xev);
115 
116   void SetSize(const gfx::Size& size_in_pixels);
117   void SetBounds(const gfx::Rect& requested_bounds);
118   bool IsXWindowVisible() const;
119   bool IsMinimized() const;
120   bool IsMaximized() const;
121   bool IsFullscreen() const;
122   gfx::Rect GetOutterBounds() const;
123 
124   void SetCursor(::Cursor cursor);
125   bool SetTitle(base::string16 title);
126   void SetXWindowOpacity(float opacity);
127   void SetXWindowAspectRatio(const gfx::SizeF& aspect_ratio);
128   void SetXWindowIcons(const gfx::ImageSkia& window_icon,
129                        const gfx::ImageSkia& app_icon);
130   void SetXWindowVisibleOnAllWorkspaces(bool visible);
131   bool IsXWindowVisibleOnAllWorkspaces() const;
132   void MoveCursorTo(const gfx::Point& location);
133   void SetAlwaysOnTop(bool always_on_top);
134   void SetFlashFrameHint(bool flash_frame);
135   void UpdateMinAndMaxSize();
136   void SetUseNativeFrame(bool use_native_frame);
137   void DispatchResize();
138   void CancelResize();
139   void NotifySwapAfterResize();
140   void ConfineCursorTo(const gfx::Rect& bounds);
141   void LowerWindow();
142   void SetOverrideRedirect(bool override_redirect);
143 
144   // Returns if the point is within XWindow shape. If shape is not set, always
145   // returns true.
146   bool ContainsPointInRegion(const gfx::Point& point) const;
147 
148   void SetXWindowShape(std::unique_ptr<NativeShapeRects> native_shape,
149                        const gfx::Transform& transform);
150 
151   // Resets the window region for the current window bounds if necessary.
152   void ResetWindowRegion();
153 
154   gfx::Rect bounds() const { return bounds_in_pixels_; }
155   gfx::Rect previous_bounds() const { return previous_bounds_in_pixels_; }
156   void set_bounds(gfx::Rect new_bounds) { bounds_in_pixels_ = new_bounds; }
157 
158   bool mapped_in_client() const { return window_mapped_in_client_; }
159   bool is_always_on_top() const { return is_always_on_top_; }
160   bool use_native_frame() const { return use_native_frame_; }
161   bool use_custom_shape() const { return custom_window_shape_; }
162   bool was_minimized() const { return was_minimized_; }
163   bool has_alpha() const { return visual_has_alpha_; }
164   base::Optional<int> workspace() const { return workspace_; }
165 
166   XDisplay* display() const { return xdisplay_; }
167   ::Window window() const { return xwindow_; }
168   ::Window root_window() const { return x_root_window_; }
169   ::Region shape() const { return window_shape_.get(); }
170   XID update_counter() const { return update_counter_; }
171   XID extended_update_counter() const { return extended_update_counter_; }
172 
173  protected:
174   // Updates |xwindow_|'s _NET_WM_USER_TIME if |xwindow_| is active.
175   void UpdateWMUserTime(ui::Event* event);
176 
177  private:
178   // Called on an XFocusInEvent, XFocusOutEvent, XIFocusInEvent, or an
179   // XIFocusOutEvent.
180   void OnFocusEvent(bool focus_in, int mode, int detail);
181 
182   // Called on an XEnterWindowEvent, XLeaveWindowEvent, XIEnterEvent, or an
183   // XILeaveEvent.
184   void OnCrossingEvent(bool enter,
185                        bool focus_in_window_or_ancestor,
186                        int mode,
187                        int detail);
188 
189   // Called when |xwindow_|'s _NET_WM_STATE property is updated.
190   void OnWMStateUpdated();
191 
192   // Called when |xwindow_|'s _NET_FRAME_EXTENTS property is updated.
193   void OnFrameExtentsUpdated();
194 
195   void OnConfigureEvent(XEvent* xev);
196 
197   void OnWorkspaceUpdated();
198 
199   void OnWindowMapped();
200 
201   // Record the activation state.
202   void BeforeActivationStateChanged();
203 
204   // Handle the state change since BeforeActivationStateChanged().
205   void AfterActivationStateChanged();
206 
207   void DelayedResize(const gfx::Rect& bounds_in_pixels);
208 
209   // If mapped, sends a message to the window manager to enable or disable the
210   // states |state1| and |state2|.  Otherwise, the states will be enabled or
211   // disabled on the next map.  It's the caller's responsibility to make sure
212   // atoms are set and unset in the appropriate pairs.  For example, if a caller
213   // sets (_NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_MAXIMIZED_HORZ), it would
214   // be invalid to unset the maximized state by making two calls like
215   // (_NET_WM_STATE_MAXIMIZED_VERT, x11::None), (_NET_WM_STATE_MAXIMIZED_HORZ,
216   // x11::None).
217   void SetWMSpecState(bool enabled, XAtom state1, XAtom state2);
218 
219   // Updates |window_properties_| with |new_window_properties|.
220   void UpdateWindowProperties(
221       const base::flat_set<XAtom>& new_window_properties);
222 
223   void UnconfineCursor();
224 
225   void UpdateWindowRegion(XRegion* xregion);
226 
227   void NotifyBoundsChanged(const gfx::Rect& new_bounds_in_px);
228 
229   // Initializes as a status icon window.
230   bool InitializeAsStatusIcon();
231 
232   // Interface that must be used by a class that inherits the XWindow to receive
233   // different messages from X Server.
234   virtual void OnXWindowCreated() = 0;
235   virtual void OnXWindowStateChanged() = 0;
236   virtual void OnXWindowDamageEvent(const gfx::Rect& damage_rect) = 0;
237   virtual void OnXWindowBoundsChanged(const gfx::Rect& size) = 0;
238   virtual void OnXWindowCloseRequested() = 0;
239   virtual void OnXWindowIsActiveChanged(bool active) = 0;
240   virtual void OnXWindowWorkspaceChanged() = 0;
241   virtual void OnXWindowLostPointerGrab() = 0;
242   virtual void OnXWindowLostCapture() = 0;
243   virtual void OnXWindowSelectionEvent(XEvent* xev) = 0;
244   virtual void OnXWindowDragDropEvent(XEvent* xev) = 0;
245   virtual base::Optional<gfx::Size> GetMinimumSizeForXWindow() = 0;
246   virtual base::Optional<gfx::Size> GetMaximumSizeForXWindow() = 0;
247   virtual void GetWindowMaskForXWindow(const gfx::Size& size,
248                                        SkPath* window_mask) = 0;
249 
250   // The display and the native X window hosting the root window.
251   XDisplay* xdisplay_ = nullptr;
252   ::Window xwindow_ = x11::None;
253   ::Window x_root_window_ = x11::None;
254 
255   // Events selected on |xwindow_|.
256   std::unique_ptr<ui::XScopedEventSelector> xwindow_events_;
257 
258   // The window manager state bits.
259   base::flat_set<XAtom> window_properties_;
260 
261   // Is this window able to receive focus?
262   bool activatable_ = true;
263 
264   // Was this window initialized with the override_redirect window attribute?
265   bool override_redirect_ = false;
266 
267   base::string16 window_title_;
268 
269   // Whether the window is visible with respect to Aura.
270   bool window_mapped_in_client_ = false;
271 
272   // Whether the window is mapped with respect to the X server.
273   bool window_mapped_in_server_ = false;
274 
275   // The bounds of |xwindow_|.
276   gfx::Rect bounds_in_pixels_;
277 
278   VisualID visual_id_ = 0;
279 
280   // Whether we used an ARGB visual for our window.
281   bool visual_has_alpha_ = false;
282 
283   // The workspace containing |xwindow_|.  This will be base::nullopt when
284   // _NET_WM_DESKTOP is unset.
285   base::Optional<int> workspace_;
286 
287   // True if the window should stay on top of most other windows.
288   bool is_always_on_top_ = false;
289 
290   // Does |xwindow_| have the pointer grab (XI2 or normal)?
291   bool has_pointer_grab_ = false;
292 
293   // The focus-tracking state variables are as described in
294   // gtk/docs/focus_tracking.txt
295   //
296   // |xwindow_| is active iff:
297   //     (|has_window_focus_| || |has_pointer_focus_|) &&
298   //     !|ignore_keyboard_input_|
299 
300   // Is the pointer in |xwindow_| or one of its children?
301   bool has_pointer_ = false;
302 
303   // Is |xwindow_| or one of its children focused?
304   bool has_window_focus_ = false;
305 
306   // (An ancestor window or the PointerRoot is focused) && |has_pointer_|.
307   // |has_pointer_focus_| == true is the odd case where we will receive keyboard
308   // input when |has_window_focus_| == false.  |has_window_focus_| and
309   // |has_pointer_focus_| are mutually exclusive.
310   bool has_pointer_focus_ = false;
311 
312   // X11 does not support defocusing windows; you can only focus a different
313   // window.  If we would like to be defocused, we just ignore keyboard input we
314   // no longer care about.
315   bool ignore_keyboard_input_ = false;
316 
317   // Used for tracking activation state in {Before|After}ActivationStateChanged.
318   bool was_active_ = false;
319   bool had_pointer_ = false;
320   bool had_pointer_grab_ = false;
321   bool had_window_focus_ = false;
322 
323   bool was_minimized_ = false;
324 
325   // Used for synchronizing between |xwindow_| and desktop compositor during
326   // resizing.
327   XID update_counter_ = x11::None;
328   XID extended_update_counter_ = x11::None;
329 
330   // Whenever the bounds are set, we keep the previous set of bounds around so
331   // we can have a better chance of getting the real
332   // |restored_bounds_in_pixels_|. Window managers tend to send a Configure
333   // message with the maximized bounds, and then set the window maximized
334   // property. (We don't rely on this for when we request that the window be
335   // maximized, only when we detect that some other process has requested that
336   // we become the maximized window.)
337   gfx::Rect previous_bounds_in_pixels_;
338 
339   // True if a Maximize() call should be done after mapping the window.
340   bool should_maximize_after_map_ = false;
341 
342   // Whether we currently are flashing our frame. This feature is implemented
343   // by setting the urgency hint with the window manager, which can draw
344   // attention to the window or completely ignore the hint. We stop flashing
345   // the frame when |xwindow_| gains focus or handles a mouse button event.
346   bool urgency_hint_set_ = false;
347 
348   // |xwindow_|'s minimum size.
349   gfx::Size min_size_in_pixels_;
350 
351   // |xwindow_|'s maximum size.
352   gfx::Size max_size_in_pixels_;
353 
354   // The window shape if the window is non-rectangular.
355   gfx::XScopedPtr<XRegion, gfx::XObjectDeleter<XRegion, int, XDestroyRegion>>
356       window_shape_;
357 
358   // Whether |window_shape_| was set via SetShape().
359   bool custom_window_shape_ = false;
360 
361   // True if the window has title-bar / borders provided by the window manager.
362   bool use_native_frame_ = false;
363 
364   // The size of the window manager provided borders (if any).
365   gfx::Insets native_window_frame_borders_in_pixels_;
366 
367   // Used for synchronizing between |xwindow_| between desktop compositor during
368   // resizing.
369   int64_t pending_counter_value_ = 0;
370   int64_t configure_counter_value_ = 0;
371   int64_t current_counter_value_ = 0;
372   bool pending_counter_value_is_extended_ = false;
373   bool configure_counter_value_is_extended_ = false;
374 
375   base::CancelableOnceClosure delayed_resize_task_;
376 
377   // Keep track of barriers to confine cursor.
378   bool has_pointer_barriers_ = false;
379   std::array<XID, 4> pointer_barriers_;
380 
381   base::WeakPtrFactory<XWindow> resize_weak_factory_{this};
382 };
383 
384 }  // namespace ui
385 
386 #endif  // UI_BASE_X_X11_WINDOW_H_
387