Skip to main content

smithay_client_toolkit/shell/xdg/window/
mod.rs

1//! XDG shell windows.
2
3use std::{
4    num::NonZeroU32,
5    sync::{Arc, Weak},
6};
7
8use crate::reexports::client::{
9    protocol::{wl_output, wl_seat, wl_surface},
10    Connection, Proxy, QueueHandle,
11};
12use crate::reexports::csd_frame::{WindowManagerCapabilities, WindowState};
13use crate::reexports::protocols::{
14    xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1::{self, Mode},
15    xdg::shell::client::{xdg_surface, xdg_toplevel},
16};
17
18use crate::shell::WaylandSurface;
19
20use self::inner::WindowInner;
21
22use super::XdgSurface;
23
24pub(super) mod inner;
25
26/// Handler for toplevel operations on a [`Window`].
27pub trait WindowHandler: Sized {
28    /// Request to close a window.
29    ///
30    /// This request does not destroy the window. You must drop all [`Window`] handles to destroy the window.
31    /// This request may be sent either by the compositor or by some other mechanism (such as client side decorations).
32    fn request_close(&mut self, conn: &Connection, qh: &QueueHandle<Self>, window: &Window);
33
34    /// Apply a suggested surface change.
35    ///
36    /// When this function is called, the compositor is requesting the window's size or state to change.
37    ///
38    /// Internally this function is called when the underlying `xdg_surface` is configured. Any extension
39    /// protocols that interface with xdg-shell are able to be notified that the surface's configure sequence
40    /// is complete by using this function.
41    ///
42    /// # Double buffering
43    ///
44    /// Configure events in Wayland are considered to be double buffered and the state of the window does not
45    /// change until committed.
46    fn configure(
47        &mut self,
48        conn: &Connection,
49        qh: &QueueHandle<Self>,
50        window: &Window,
51        configure: WindowConfigure,
52        serial: u32,
53    );
54}
55
56/// Decoration mode of a window.
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub enum DecorationMode {
59    /// The window should draw client side decorations.
60    Client,
61
62    /// The server will draw window decorations.
63    Server,
64}
65
66/// A window configure.
67///
68/// A configure describes a compositor request to resize the window or change it's state.
69#[non_exhaustive]
70#[derive(Debug, Clone)]
71pub struct WindowConfigure {
72    /// The compositor suggested new size of the window in window geometry coordinates.
73    ///
74    /// If this value is [`None`], you may set the size of the window as you wish.
75    pub new_size: (Option<NonZeroU32>, Option<NonZeroU32>),
76
77    /// Compositor suggested maximum bounds for a window.
78    ///
79    /// This may be used to ensure a window is not created in a way where it will not fit.
80    ///
81    /// If xdg-shell is version 3 or lower, this will always be [`None`].
82    pub suggested_bounds: Option<(u32, u32)>,
83
84    /// The compositor set decoration mode of the window.
85    ///
86    /// This will always be [`DecorationMode::Client`] if server side decorations are not enabled or
87    /// supported.
88    pub decoration_mode: DecorationMode,
89
90    /// The current state of the window.
91    ///
92    /// For more see [`WindowState`] documentation on the flag values.
93    pub state: WindowState,
94
95    /// The capabilities supported by the compositor.
96    ///
97    /// For more see [`WindowManagerCapabilities`] documentation on the flag values.
98    pub capabilities: WindowManagerCapabilities,
99}
100
101impl Default for WindowConfigure {
102    fn default() -> Self {
103        Self {
104            new_size: (None, None),
105            suggested_bounds: None,
106            // Initial configure will indicate whether there are server side decorations.
107            decoration_mode: DecorationMode::Client,
108            state: WindowState::empty(),
109            // XXX by default we assume that everything is supported.
110            capabilities: WindowManagerCapabilities::all(),
111        }
112    }
113}
114
115impl WindowConfigure {
116    /// Is [`WindowState::MAXIMIZED`] state is set.
117    #[inline]
118    pub fn is_maximized(&self) -> bool {
119        self.state.contains(WindowState::MAXIMIZED)
120    }
121
122    /// Is [`WindowState::FULLSCREEN`] state is set.
123    #[inline]
124    pub fn is_fullscreen(&self) -> bool {
125        self.state.contains(WindowState::FULLSCREEN)
126    }
127
128    /// Is [`WindowState::RESIZING`] state is set.
129    #[inline]
130    pub fn is_resizing(&self) -> bool {
131        self.state.contains(WindowState::RESIZING)
132    }
133
134    /// Is [`WindowState::TILED`] state is set.
135    #[inline]
136    pub fn is_tiled(&self) -> bool {
137        self.state.contains(WindowState::TILED)
138    }
139
140    /// Is [`WindowState::ACTIVATED`] state is set.
141    #[inline]
142    pub fn is_activated(&self) -> bool {
143        self.state.contains(WindowState::ACTIVATED)
144    }
145
146    /// Is [`WindowState::TILED_LEFT`] state is set.
147    #[inline]
148    pub fn is_tiled_left(&self) -> bool {
149        self.state.contains(WindowState::TILED_LEFT)
150    }
151
152    /// Is [`WindowState::TILED_RIGHT`] state is set.
153    #[inline]
154    pub fn is_tiled_right(&self) -> bool {
155        self.state.contains(WindowState::TILED_RIGHT)
156    }
157
158    /// Is [`WindowState::TILED_TOP`] state is set.
159    #[inline]
160    pub fn is_tiled_top(&self) -> bool {
161        self.state.contains(WindowState::TILED_TOP)
162    }
163
164    /// Is [`WindowState::TILED_BOTTOM`] state is set.
165    #[inline]
166    pub fn is_tiled_bottom(&self) -> bool {
167        self.state.contains(WindowState::TILED_BOTTOM)
168    }
169}
170
171/// Decorations a window is created with.
172#[derive(Debug, Clone, Copy, PartialEq, Eq)]
173pub enum WindowDecorations {
174    /// The window should use the decoration mode the server asks for.
175    ///
176    /// The server may ask the client to render with or without client side decorations. If server side
177    /// decorations are not available, client side decorations are drawn instead.
178    ServerDefault,
179
180    /// The window should request server side decorations.
181    ///
182    /// The server may ignore this request and ask the client to render with client side decorations. If
183    /// server side decorations are not available, client side decorations are drawn instead.
184    RequestServer,
185
186    /// The window should request client side decorations.
187    ///
188    /// The server may ignore this request and render server side decorations. If server side decorations are
189    /// not available, client side decorations are drawn.
190    RequestClient,
191
192    /// The window should always draw it's own client side decorations.
193    ClientOnly,
194
195    /// The window should use server side decorations or draw any client side decorations.
196    None,
197}
198
199#[derive(Debug, Clone)]
200pub struct Window(pub(super) Arc<WindowInner>);
201
202impl Window {
203    pub fn from_xdg_toplevel(toplevel: &xdg_toplevel::XdgToplevel) -> Option<Window> {
204        toplevel.data::<WindowData>().and_then(|data| data.0.upgrade()).map(Window)
205    }
206
207    pub fn from_xdg_surface(surface: &xdg_surface::XdgSurface) -> Option<Window> {
208        surface.data::<WindowData>().and_then(|data| data.0.upgrade()).map(Window)
209    }
210
211    pub fn from_toplevel_decoration(
212        decoration: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
213    ) -> Option<Window> {
214        decoration.data::<WindowData>().and_then(|data| data.0.upgrade()).map(Window)
215    }
216
217    pub fn show_window_menu(&self, seat: &wl_seat::WlSeat, serial: u32, position: (i32, i32)) {
218        self.xdg_toplevel().show_window_menu(seat, serial, position.0, position.1);
219    }
220
221    pub fn set_title(&self, title: impl Into<String>) {
222        self.xdg_toplevel().set_title(title.into());
223    }
224
225    pub fn set_app_id(&self, app_id: impl Into<String>) {
226        self.xdg_toplevel().set_app_id(app_id.into());
227    }
228
229    pub fn set_parent(&self, parent: Option<&Window>) {
230        self.xdg_toplevel().set_parent(parent.map(Window::xdg_toplevel));
231    }
232
233    pub fn set_maximized(&self) {
234        self.xdg_toplevel().set_maximized()
235    }
236
237    pub fn unset_maximized(&self) {
238        self.xdg_toplevel().unset_maximized()
239    }
240
241    pub fn set_minimized(&self) {
242        self.xdg_toplevel().set_minimized()
243    }
244
245    pub fn set_fullscreen(&self, output: Option<&wl_output::WlOutput>) {
246        self.xdg_toplevel().set_fullscreen(output)
247    }
248
249    pub fn unset_fullscreen(&self) {
250        self.xdg_toplevel().unset_fullscreen()
251    }
252
253    /// Requests the window should use the specified decoration mode.
254    ///
255    /// A mode of [`None`] indicates that the window does not care what type of decorations are used.
256    ///
257    /// The compositor will respond with a [`configure`](WindowHandler::configure). The configure will
258    /// indicate whether the window's decoration mode has changed.
259    ///
260    /// # Configure loops
261    ///
262    /// You should avoid sending multiple decoration mode requests to ensure you do not enter a configure loop.
263    pub fn request_decoration_mode(&self, mode: Option<DecorationMode>) {
264        if let Some(toplevel_decoration) = &self.0.toplevel_decoration {
265            match mode {
266                Some(DecorationMode::Client) => toplevel_decoration.set_mode(Mode::ClientSide),
267                Some(DecorationMode::Server) => toplevel_decoration.set_mode(Mode::ServerSide),
268                None => toplevel_decoration.unset_mode(),
269            }
270        }
271    }
272
273    pub fn move_(&self, seat: &wl_seat::WlSeat, serial: u32) {
274        self.xdg_toplevel()._move(seat, serial)
275    }
276
277    pub fn resize(&self, seat: &wl_seat::WlSeat, serial: u32, edges: xdg_toplevel::ResizeEdge) {
278        self.xdg_toplevel().resize(seat, serial, edges)
279    }
280
281    // Double buffered window state
282
283    pub fn set_min_size(&self, min_size: Option<(u32, u32)>) {
284        let min_size = min_size.unwrap_or_default();
285        self.xdg_toplevel().set_min_size(min_size.0 as i32, min_size.1 as i32);
286    }
287
288    /// # Protocol errors
289    ///
290    /// The maximum size of the window may not be smaller than the minimum size.
291    pub fn set_max_size(&self, max_size: Option<(u32, u32)>) {
292        let max_size = max_size.unwrap_or_default();
293        self.xdg_toplevel().set_max_size(max_size.0 as i32, max_size.1 as i32);
294    }
295
296    // Other
297
298    /// Returns the underlying xdg toplevel wrapped by this window.
299    pub fn xdg_toplevel(&self) -> &xdg_toplevel::XdgToplevel {
300        &self.0.xdg_toplevel
301    }
302}
303
304impl WaylandSurface for Window {
305    fn wl_surface(&self) -> &wl_surface::WlSurface {
306        self.0.xdg_surface.wl_surface()
307    }
308}
309
310impl XdgSurface for Window {
311    fn xdg_surface(&self) -> &xdg_surface::XdgSurface {
312        self.0.xdg_surface.xdg_surface()
313    }
314}
315
316impl PartialEq for Window {
317    fn eq(&self, other: &Self) -> bool {
318        Arc::ptr_eq(&self.0, &other.0)
319    }
320}
321
322#[derive(Debug, Clone)]
323pub struct WindowData(pub(crate) Weak<WindowInner>);