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