blob: af282d4190dd24e2b3828fafbce6f18082cb67c4 [file] [log] [blame]
// 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 <array>
#include <memory>
#include <string>
#include <vector>
#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/x11.h"
#include "ui/gfx/x/x11_types.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<gfx::Rect>;
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<int> 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<X11Cursor> 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<NativeShapeRects> 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<int> 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<x11::Rectangle>* 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<X11Cursor> 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<x11::Atom>& new_window_properties);
void UnconfineCursor();
void UpdateWindowRegion(std::unique_ptr<std::vector<x11::Rectangle>> 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<gfx::Size> GetMinimumSizeForXWindow() = 0;
virtual base::Optional<gfx::Size> 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<ui::XScopedEventSelector> xwindow_events_;
// The window manager state bits.
base::flat_set<x11::Atom> 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<int> 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<std::vector<x11::Rectangle>> 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<x11::XFixes::Barrier, 4> pointer_barriers_;
scoped_refptr<X11Cursor> last_cursor_;
base::CancelableOnceCallback<void(x11::Cursor)> on_cursor_loaded_;
};
} // namespace ui
#endif // UI_BASE_X_X11_WINDOW_H_