1#![deny(rust_2018_idioms)]
2#![deny(rustdoc::broken_intra_doc_links)]
3#![deny(unsafe_op_in_unsafe_fn)]
4#![deny(improper_ctypes, improper_ctypes_definitions)]
5#![deny(clippy::all)]
6#![deny(missing_debug_implementations)]
7#![deny(missing_docs)]
8#![forbid(unsafe_code)]
9#![cfg_attr(feature = "cargo-clippy", deny(warnings))]
10#![cfg_attr(docsrs, feature(doc_auto_cfg))]
11
12//! The interface for wayland client side decorations (CSD).
13//!
14//! The crate is intended to be used by libraries providing client
15//! side decorations for the xdg-shell protocol.
16//!
17//! Examples could be found in [`client toolkit`] and [`sctk-adwaita`].
18//!
19//! [`client toolkit`]: https://github.com/smithay/client-toolkit
20//! [`sctk-adwaita`]: https://github.com/PolyMeilex/sctk-adwaita
21
22use std::num::NonZeroU32;
23use std::time::Duration;
24
25use bitflags::bitflags;
26use wayland_backend::client::ObjectId;
27
28#[doc(inline)]
29pub use cursor_icon::{CursorIcon, ParseError as CursorIconParseError};
30
31/// The interface for the client side decorations.
32pub trait DecorationsFrame: Sized {
33 /// Emulate click on the decorations.
34 ///
35 /// The `click` is a variant of click to use, see [`FrameClick`] for more
36 /// information. `timestamp` is the time when event happened.
37 ///
38 /// The return value is a [`FrameAction`] you should apply, this action
39 /// could be ignored.
40 ///
41 /// The location of the click is the one passed to
42 /// [`Self::click_point_moved`].
43 fn on_click(
44 &mut self,
45 timestamp: Duration,
46 click: FrameClick,
47 pressed: bool,
48 ) -> Option<FrameAction>;
49
50 /// Emulate pointer moved event on the decorations frame.
51 ///
52 /// The `x` and `y` are location in the surface local coordinates relative
53 /// to the `surface`. `timestamp` is the time when event happened.
54 ///
55 /// The return value is the new cursor icon you should apply to provide
56 /// better visual feedback for the user. However, you might want to
57 /// ignore it, if you're using touch events to drive the movements.
58 fn click_point_moved(
59 &mut self,
60 timestamp: Duration,
61 surface_id: &ObjectId,
62 x: f64,
63 y: f64,
64 ) -> Option<CursorIcon>;
65
66 /// All clicks left the decorations.
67 ///
68 /// This function should be called when input leaves the decorations.
69 fn click_point_left(&mut self);
70
71 /// Update the state of the frame.
72 ///
73 /// The state is usually obtained from the `xdg_toplevel::configure` event.
74 fn update_state(&mut self, state: WindowState);
75
76 /// Update the window manager capabilites.
77 ///
78 /// The capabilites are usually obtained from the
79 /// `xdg_toplevel::wm_capabilities` event.
80 fn update_wm_capabilities(&mut self, wm_capabilities: WindowManagerCapabilities);
81
82 /// Resize the window to the new size.
83 ///
84 /// The size must be without the borders, as in [`Self::subtract_borders]`
85 /// were used on it.
86 ///
87 /// **Note:** The [`Self::update_state`] and
88 /// [`Self::update_wm_capabilities`] **must be** applied before calling
89 /// this function.
90 ///
91 /// # Panics
92 ///
93 /// Panics when resizing the hidden frame.
94 fn resize(&mut self, width: NonZeroU32, height: NonZeroU32);
95
96 /// Set the scaling of the decorations frame.
97 ///
98 /// If the decorations frame is not supporting fractional scaling it'll
99 /// `ceil` the scaling factor.
100 fn set_scaling_factor(&mut self, scale_factor: f64);
101
102 /// Return the coordinates of the top-left corner of the borders relative to
103 /// the content.
104 ///
105 /// Values **must** thus be non-positive.
106 fn location(&self) -> (i32, i32);
107
108 /// Subtract the borders from the given `width` and `height`.
109 ///
110 /// `None` will be returned for the particular dimension when the given
111 /// value for it was too small.
112 fn subtract_borders(
113 &self,
114 width: NonZeroU32,
115 height: NonZeroU32,
116 ) -> (Option<NonZeroU32>, Option<NonZeroU32>);
117
118 /// Add the borders to the given `width` and `height`.
119 ///
120 /// Passing zero for both width and height could be used to get the size
121 /// of the decorations frame.
122 fn add_borders(&self, width: u32, height: u32) -> (u32, u32);
123
124 /// Whether the given frame is dirty and should be redrawn.
125 fn is_dirty(&self) -> bool;
126
127 /// Set the frame as hidden.
128 ///
129 /// The frame **must be** visible by default.
130 fn set_hidden(&mut self, hidden: bool);
131
132 /// Get the frame hidden state.
133 ///
134 /// Get the state of the last [`DecorationsFrame::set_hidden`].
135 fn is_hidden(&self) -> bool;
136
137 /// Mark the frame as resizable.
138 ///
139 /// By default the frame is resizable.
140 fn set_resizable(&mut self, resizable: bool);
141
142 /// Draw the decorations frame.
143 ///
144 /// Return `true` when the main surface must be redrawn as well. This
145 /// usually happens when `sync` is being set on the internal subsurfaces and
146 /// they've changed their size.
147 ///
148 /// The user of the frame **must** commit the base surface afterwards.
149 fn draw(&mut self) -> bool;
150
151 /// Set the frames title.
152 fn set_title(&mut self, title: impl Into<String>);
153}
154
155/// The Frame action user should perform in responce to mouse click events.
156#[non_exhaustive]
157#[derive(Debug, Clone, Copy)]
158pub enum FrameAction {
159 /// The window should be minimized.
160 Minimize,
161 /// The window should be maximized.
162 Maximize,
163 /// The window should be unmaximized.
164 UnMaximize,
165 /// The window should be closed.
166 Close,
167 /// An interactive move should be started.
168 Move,
169 /// An interactive resize should be started with the provided edge.
170 Resize(ResizeEdge),
171 /// Show window menu.
172 ///
173 /// The coordinates are relative to the base surface, as in should be
174 /// directly passed to the `xdg_toplevel::show_window_menu`.
175 ShowMenu(i32, i32),
176}
177
178/// The user clicked or touched the decoractions frame.
179#[non_exhaustive]
180#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
181pub enum FrameClick {
182 /// The user done normal click, likely with left mouse button or single
183 /// finger touch.
184 Normal,
185
186 /// The user done right mouse click or some touch sequence that was treated
187 /// as alternate click.
188 ///
189 /// The alternate click exists solely to provide alternative action, like
190 /// show window menu when doing right mouse button cilck on the header
191 /// decorations, nothing more.
192 Alternate,
193}
194
195bitflags! {
196 /// The configured state of the window.
197 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
198 pub struct WindowState: u16 {
199 /// The surface is maximized. The window geometry specified in the
200 /// configure event must be obeyed by the client. The client should
201 /// draw without shadow or other decoration outside of the window
202 /// geometry.
203 const MAXIMIZED = 0b0000_0000_0000_0001;
204 /// The surface is fullscreen. The window geometry specified in the
205 /// configure event is a maximum; the client cannot resize beyond it.
206 /// For a surface to cover the whole fullscreened area, the geometry
207 /// dimensions must be obeyed by the client. For more details, see
208 /// xdg_toplevel.set_fullscreen.
209 const FULLSCREEN = 0b0000_0000_0000_0010;
210 /// The surface is being resized. The window geometry specified in the
211 /// configure event is a maximum; the client cannot resize beyond it.
212 /// Clients that have aspect ratio or cell sizing configuration can use
213 /// a smaller size, however.
214 const RESIZING = 0b0000_0000_0000_0100;
215 /// Client window decorations should be painted as if the window is
216 /// active. Do not assume this means that the window actually has
217 /// keyboard or pointer focus.
218 const ACTIVATED = 0b0000_0000_0000_1000;
219 /// The window is currently in a tiled layout and the left edge is
220 /// considered to be adjacent to another part of the tiling grid.
221 const TILED_LEFT = 0b0000_0000_0001_0000;
222 /// The window is currently in a tiled layout and the right edge is
223 /// considered to be adjacent to another part of the tiling grid.
224 const TILED_RIGHT = 0b0000_0000_0010_0000;
225 /// The window is currently in a tiled layout and the top edge is
226 /// considered to be adjacent to another part of the tiling grid.
227 const TILED_TOP = 0b0000_0000_0100_0000;
228 /// The window is currently in a tiled layout and the bottom edge is
229 /// considered to be adjacent to another part of the tiling grid.
230 const TILED_BOTTOM = 0b0000_0000_1000_0000;
231 /// An alias for all tiled bits set.
232 const TILED = Self::TILED_TOP.bits() | Self::TILED_LEFT.bits() | Self::TILED_RIGHT.bits() | Self::TILED_BOTTOM.bits();
233 /// The surface is currently not ordinarily being repainted; for example
234 /// because its content is occluded by another window, or its outputs are
235 /// switched off due to screen locking.
236 const SUSPENDED = 0b0000_0001_0000_0000;
237 }
238}
239
240bitflags! {
241 /// The capabilities of the window manager.
242 ///
243 /// This is a hint to hide UI elements which provide functionality
244 /// not supported by compositor.
245 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
246 pub struct WindowManagerCapabilities : u16 {
247 /// `show_window_menu` is available.
248 const WINDOW_MENU = 0b0000_0000_0000_0001;
249 /// Window can be maximized and unmaximized.
250 const MAXIMIZE = 0b0000_0000_0000_0010;
251 /// Window can be fullscreened and unfullscreened.
252 const FULLSCREEN = 0b0000_0000_0000_0100;
253 /// Window could be minimized.
254 const MINIMIZE = 0b0000_0000_0000_1000;
255 }
256}
257
258/// Which edge or corner is being dragged.
259#[non_exhaustive]
260#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
261pub enum ResizeEdge {
262 /// Nothing is being dragged.
263 None,
264 /// The top edge is being dragged.
265 Top,
266 /// The bottom edge is being dragged.
267 Bottom,
268 /// The left edge is being dragged.
269 Left,
270 /// The top left corner is being dragged.
271 TopLeft,
272 /// The bottom left corner is being dragged.
273 BottomLeft,
274 /// The right edge is being dragged.
275 Right,
276 /// The top right corner is being dragged.
277 TopRight,
278 /// The bottom right corner is being dragged.
279 BottomRight,
280}
281