// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef UI_BASE_X_X11_WINDOW_H_ #define UI_BASE_X_X11_WINDOW_H_ #include #include #include #include #include "base/cancelable_callback.h" #include "base/component_export.h" #include "base/containers/flat_set.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/strings/string16.h" #include "ui/base/x/x11_cursor.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size_f.h" #include "ui/gfx/x/event.h" #include "ui/gfx/x/sync.h" #include "ui/gfx/x/xfixes.h" #include "ui/gfx/x/xproto.h" class SkPath; namespace gfx { class ImageSkia; class Transform; } // namespace gfx namespace ui { class Event; class XScopedEventSelector; class X11Cursor; //////////////////////////////////////////////////////////////////////////////// // XWindow class // // Base class that encapsulates a full featured Xlib-based X11 Window, meant // to be used mainly in Linux desktop. Abstracts away most of X11 API // interaction and assumes event handling and some required getters are // implemented in subclasses. // // |XWindow::Configuration| holds parameters used in window initialization. // Fields are equivalent and a sub-set of Widget::InitParams. // // All bounds and size values are assumed to be expressed in pixels. class COMPONENT_EXPORT(UI_BASE_X) XWindow { public: using NativeShapeRects = std::vector; enum class WindowType { kWindow, kPopup, kMenu, kTooltip, kDrag, kBubble, }; enum class WindowOpacity { kInferOpacity, kOpaqueWindow, kTranslucentWindow, }; struct Configuration final { Configuration(); Configuration(const Configuration& config); ~Configuration(); WindowType type; WindowOpacity opacity; gfx::Rect bounds; gfx::ImageSkia* icon; base::Optional background_color; bool activatable; bool force_show_in_taskbar; bool keep_on_top; bool visible_on_all_workspaces; bool remove_standard_frame; bool prefer_dark_theme; bool override_redirect; std::string workspace; std::string wm_class_name; std::string wm_class_class; std::string wm_role_name; }; XWindow(); XWindow(const XWindow&) = delete; XWindow& operator=(const XWindow&) = delete; virtual ~XWindow(); void Init(const Configuration& config); void Map(bool inactive = false); void Close(); void Maximize(); void Minimize(); void Unmaximize(); bool Hide(); void Unhide(); void SetFullscreen(bool fullscreen); void Activate(); void Deactivate(); bool IsActive() const; void GrabPointer(); void ReleasePointerGrab(); void StackXWindowAbove(x11::Window window); void StackXWindowAtTop(); bool IsTargetedBy(const x11::Event& xev) const; bool IsTransientWindowTargetedBy(const x11::Event& x11_event) const; void SetTransientWindow(x11::Window window); void WmMoveResize(int hittest, const gfx::Point& location) const; void ProcessEvent(x11::Event* xev); void SetSize(const gfx::Size& size_in_pixels); void SetBounds(const gfx::Rect& requested_bounds); bool IsXWindowVisible() const; bool IsMinimized() const; bool IsMaximized() const; bool IsFullscreen() const; gfx::Rect GetOuterBounds() const; void SetCursor(scoped_refptr cursor); bool SetTitle(base::string16 title); void SetXWindowOpacity(float opacity); void SetXWindowAspectRatio(const gfx::SizeF& aspect_ratio); void SetXWindowIcons(const gfx::ImageSkia& window_icon, const gfx::ImageSkia& app_icon); void SetXWindowVisibleOnAllWorkspaces(bool visible); bool IsXWindowVisibleOnAllWorkspaces() const; void MoveCursorTo(const gfx::Point& location); void SetAlwaysOnTop(bool always_on_top); void SetFlashFrameHint(bool flash_frame); void UpdateMinAndMaxSize(); void SetUseNativeFrame(bool use_native_frame); void DispatchResize(); void CancelResize(); void NotifySwapAfterResize(); void ConfineCursorTo(const gfx::Rect& bounds); void LowerWindow(); void SetOverrideRedirect(bool override_redirect); // Returns if the point is within XWindow shape. If shape is not set, always // returns true. bool ContainsPointInRegion(const gfx::Point& point) const; void SetXWindowShape(std::unique_ptr native_shape, const gfx::Transform& transform); // Resets the window region for the current window bounds if necessary. void ResetWindowRegion(); gfx::Rect bounds() const { return bounds_in_pixels_; } gfx::Rect previous_bounds() const { return previous_bounds_in_pixels_; } void set_bounds(gfx::Rect new_bounds) { bounds_in_pixels_ = new_bounds; } bool mapped_in_client() const { return window_mapped_in_client_; } bool is_always_on_top() const { return is_always_on_top_; } bool use_native_frame() const { return use_native_frame_; } bool use_custom_shape() const { return custom_window_shape_; } bool was_minimized() const { return was_minimized_; } bool has_alpha() const { return visual_has_alpha_; } base::Optional workspace() const { return workspace_; } x11::Connection* connection() const { return connection_; } x11::Window window() const { return xwindow_; } x11::Window root_window() const { return x_root_window_; } std::vector* shape() const { return window_shape_.get(); } x11::Sync::Counter update_counter() const { return update_counter_; } x11::Sync::Counter extended_update_counter() const { return extended_update_counter_; } scoped_refptr last_cursor() const { return last_cursor_; } protected: // Updates |xwindow_|'s _NET_WM_USER_TIME if |xwindow_| is active. void UpdateWMUserTime(ui::Event* event); private: // Called on an XFocusInEvent, XFocusOutEvent, XIFocusInEvent, or an // XIFocusOutEvent. void OnFocusEvent(bool focus_in, x11::NotifyMode mode, x11::NotifyDetail detail); // Called on an XEnterWindowEvent, XLeaveWindowEvent, XIEnterEvent, or an // XILeaveEvent. void OnCrossingEvent(bool enter, bool focus_in_window_or_ancestor, x11::NotifyMode mode, x11::NotifyDetail detail); // Called when |xwindow_|'s _NET_WM_STATE property is updated. void OnWMStateUpdated(); // Called when |xwindow_|'s _NET_FRAME_EXTENTS property is updated. void OnFrameExtentsUpdated(); void OnConfigureEvent(const x11::ConfigureNotifyEvent& event); void OnWorkspaceUpdated(); void OnWindowMapped(); // Record the activation state. void BeforeActivationStateChanged(); // Handle the state change since BeforeActivationStateChanged(). void AfterActivationStateChanged(); void DelayedResize(const gfx::Rect& bounds_in_pixels); // If mapped, sends a message to the window manager to enable or disable the // states |state1| and |state2|. Otherwise, the states will be enabled or // disabled on the next map. It's the caller's responsibility to make sure // atoms are set and unset in the appropriate pairs. For example, if a caller // sets (_NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_MAXIMIZED_HORZ), it would // be invalid to unset the maximized state by making two calls like // (_NET_WM_STATE_MAXIMIZED_VERT, x11::None), (_NET_WM_STATE_MAXIMIZED_HORZ, // x11::None). void SetWMSpecState(bool enabled, x11::Atom state1, x11::Atom state2); // Updates |window_properties_| with |new_window_properties|. void UpdateWindowProperties( const base::flat_set& new_window_properties); void UnconfineCursor(); void UpdateWindowRegion(std::unique_ptr> region); void NotifyBoundsChanged(const gfx::Rect& new_bounds_in_px); // Initializes as a status icon window. bool InitializeAsStatusIcon(); // Interface that must be used by a class that inherits the XWindow to receive // different messages from X Server. virtual void OnXWindowCreated() = 0; virtual void OnXWindowStateChanged() = 0; virtual void OnXWindowDamageEvent(const gfx::Rect& damage_rect) = 0; virtual void OnXWindowBoundsChanged(const gfx::Rect& size) = 0; virtual void OnXWindowCloseRequested() = 0; virtual void OnXWindowIsActiveChanged(bool active) = 0; virtual void OnXWindowWorkspaceChanged() = 0; virtual void OnXWindowLostPointerGrab() = 0; virtual void OnXWindowLostCapture() = 0; virtual void OnXWindowSelectionEvent(x11::Event* xev) = 0; virtual void OnXWindowDragDropEvent(x11::Event* xev) = 0; virtual base::Optional GetMinimumSizeForXWindow() = 0; virtual base::Optional GetMaximumSizeForXWindow() = 0; virtual void GetWindowMaskForXWindow(const gfx::Size& size, SkPath* window_mask) = 0; // The display and the native X window hosting the root window. x11::Connection* const connection_; x11::Window xwindow_ = x11::Window::None; x11::Window x_root_window_ = x11::Window::None; // Any native, modal dialog hanging from this window. x11::Window transient_window_ = x11::Window::None; // Events selected on |xwindow_|. std::unique_ptr xwindow_events_; // The window manager state bits. base::flat_set window_properties_; // Is this window able to receive focus? bool activatable_ = true; // Was this window initialized with the override_redirect window attribute? bool override_redirect_ = false; base::string16 window_title_; // Whether the window is visible with respect to Aura. bool window_mapped_in_client_ = false; // Whether the window is mapped with respect to the X server. bool window_mapped_in_server_ = false; // The bounds of |xwindow_|. gfx::Rect bounds_in_pixels_; x11::VisualId visual_id_{}; // Whether we used an ARGB visual for our window. bool visual_has_alpha_ = false; // The workspace containing |xwindow_|. This will be base::nullopt when // _NET_WM_DESKTOP is unset. base::Optional workspace_; // True if the window should stay on top of most other windows. bool is_always_on_top_ = false; // Does |xwindow_| have the pointer grab (XI2 or normal)? bool has_pointer_grab_ = false; // The focus-tracking state variables are as described in // gtk/docs/focus_tracking.txt // // |xwindow_| is active iff: // (|has_window_focus_| || |has_pointer_focus_|) && // !|ignore_keyboard_input_| // Is the pointer in |xwindow_| or one of its children? bool has_pointer_ = false; // Is |xwindow_| or one of its children focused? bool has_window_focus_ = false; // (An ancestor window or the PointerRoot is focused) && |has_pointer_|. // |has_pointer_focus_| == true is the odd case where we will receive keyboard // input when |has_window_focus_| == false. |has_window_focus_| and // |has_pointer_focus_| are mutually exclusive. bool has_pointer_focus_ = false; // X11 does not support defocusing windows; you can only focus a different // window. If we would like to be defocused, we just ignore keyboard input we // no longer care about. bool ignore_keyboard_input_ = false; // Used for tracking activation state in {Before|After}ActivationStateChanged. bool was_active_ = false; bool had_pointer_ = false; bool had_pointer_grab_ = false; bool had_window_focus_ = false; bool was_minimized_ = false; // Used for synchronizing between |xwindow_| and desktop compositor during // resizing. x11::Sync::Counter update_counter_{}; x11::Sync::Counter extended_update_counter_{}; // Whenever the bounds are set, we keep the previous set of bounds around so // we can have a better chance of getting the real // |restored_bounds_in_pixels_|. Window managers tend to send a Configure // message with the maximized bounds, and then set the window maximized // property. (We don't rely on this for when we request that the window be // maximized, only when we detect that some other process has requested that // we become the maximized window.) gfx::Rect previous_bounds_in_pixels_; // True if a Maximize() call should be done after mapping the window. bool should_maximize_after_map_ = false; // Whether we currently are flashing our frame. This feature is implemented // by setting the urgency hint with the window manager, which can draw // attention to the window or completely ignore the hint. We stop flashing // the frame when |xwindow_| gains focus or handles a mouse button event. bool urgency_hint_set_ = false; // |xwindow_|'s minimum size. gfx::Size min_size_in_pixels_; // |xwindow_|'s maximum size. gfx::Size max_size_in_pixels_; // The window shape if the window is non-rectangular. std::unique_ptr> window_shape_; // Whether |window_shape_| was set via SetShape(). bool custom_window_shape_ = false; // True if the window has title-bar / borders provided by the window manager. bool use_native_frame_ = false; // The size of the window manager provided borders (if any). gfx::Insets native_window_frame_borders_in_pixels_; // Used for synchronizing between |xwindow_| between desktop compositor during // resizing. int64_t pending_counter_value_ = 0; int64_t configure_counter_value_ = 0; int64_t current_counter_value_ = 0; bool pending_counter_value_is_extended_ = false; bool configure_counter_value_is_extended_ = false; base::CancelableOnceClosure delayed_resize_task_; // Keep track of barriers to confine cursor. bool has_pointer_barriers_ = false; std::array pointer_barriers_; scoped_refptr last_cursor_; base::CancelableOnceCallback on_cursor_loaded_; }; } // namespace ui #endif // UI_BASE_X_X11_WINDOW_H_