Skip to main content

smithay_client_toolkit/shell/xdg/window/
inner.rs

1use std::{
2    convert::{TryFrom, TryInto},
3    num::NonZeroU32,
4    sync::Mutex,
5};
6
7use wayland_client::{Connection, QueueHandle};
8use wayland_protocols::{
9    xdg::decoration::zv1::client::{
10        zxdg_decoration_manager_v1,
11        zxdg_toplevel_decoration_v1::{self, Mode},
12    },
13    xdg::shell::client::{
14        xdg_surface,
15        xdg_toplevel::{self, State, WmCapabilities},
16    },
17};
18
19use crate::{
20    dispatch2::Dispatch2,
21    error::GlobalError,
22    globals::{GlobalData, ProvidesBoundGlobal},
23    shell::xdg::{XdgShell, XdgShellSurface},
24};
25
26use super::{
27    DecorationMode, Window, WindowConfigure, WindowData, WindowHandler, WindowManagerCapabilities,
28    WindowState,
29};
30
31impl Drop for WindowInner {
32    fn drop(&mut self) {
33        // XDG decoration says we must destroy the decoration object before the toplevel
34        if let Some(toplevel_decoration) = self.toplevel_decoration.as_ref() {
35            toplevel_decoration.destroy();
36        }
37
38        // XDG Shell protocol dictates we must destroy the role object before the xdg surface.
39        self.xdg_toplevel.destroy();
40        // XdgShellSurface will do it's own drop
41        // self.xdg_surface.destroy();
42    }
43}
44
45#[derive(Debug)]
46pub struct WindowInner {
47    pub xdg_surface: XdgShellSurface,
48    pub xdg_toplevel: xdg_toplevel::XdgToplevel,
49    pub toplevel_decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
50    pub pending_configure: Mutex<WindowConfigure>,
51}
52
53impl WindowInner {
54    /// Requests the surface should use the specified decoration mode.
55    ///
56    /// A mode of [`None`] indicates that the surface does not care what type of decorations are
57    /// used.
58    ///
59    /// The compositor will respond with a configure indicating whether the decoration mode has
60    /// changed.
61    ///
62    /// # Configure loops
63    ///
64    /// You should avoid sending multiple decoration mode requests to ensure you do not enter a
65    /// configure loop.
66    pub fn request_decoration_mode(&self, mode: Option<DecorationMode>) {
67        if let Some(toplevel_decoration) = &self.toplevel_decoration {
68            match mode {
69                Some(DecorationMode::Client) => toplevel_decoration.set_mode(Mode::ClientSide),
70                Some(DecorationMode::Server) => toplevel_decoration.set_mode(Mode::ServerSide),
71                None => toplevel_decoration.unset_mode(),
72            }
73        }
74    }
75}
76
77impl ProvidesBoundGlobal<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, 1> for XdgShell {
78    fn bound_global(
79        &self,
80    ) -> Result<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, GlobalError> {
81        self.xdg_decoration_manager.get().cloned()
82    }
83}
84
85impl<D> Dispatch2<xdg_surface::XdgSurface, D> for WindowData
86where
87    D: WindowHandler,
88{
89    fn event(
90        &self,
91        data: &mut D,
92        xdg_surface: &xdg_surface::XdgSurface,
93        event: xdg_surface::Event,
94        conn: &Connection,
95        qh: &QueueHandle<D>,
96    ) {
97        if let Some(window) = Window::from_xdg_surface(xdg_surface) {
98            match event {
99                xdg_surface::Event::Configure { serial } => {
100                    // Acknowledge the configure per protocol requirements.
101                    xdg_surface.ack_configure(serial);
102
103                    let configure = { window.0.pending_configure.lock().unwrap().clone() };
104                    WindowHandler::configure(data, conn, qh, &window, configure, serial);
105                }
106
107                _ => unreachable!(),
108            }
109        }
110    }
111}
112
113pub(crate) fn determine_window_state(states: &[u8]) -> WindowState {
114    // The states are encoded as a bunch of u32 of native endian, but are encoded in an array of
115    // bytes.
116    states
117        .chunks_exact(4)
118        .flat_map(TryInto::<[u8; 4]>::try_into)
119        .map(u32::from_ne_bytes)
120        .flat_map(State::try_from)
121        .fold(WindowState::empty(), |mut acc, state| {
122            match state {
123                State::Maximized => acc.set(WindowState::MAXIMIZED, true),
124                State::Fullscreen => acc.set(WindowState::FULLSCREEN, true),
125                State::Resizing => acc.set(WindowState::RESIZING, true),
126                State::Activated => acc.set(WindowState::ACTIVATED, true),
127                State::TiledLeft => acc.set(WindowState::TILED_LEFT, true),
128                State::TiledRight => acc.set(WindowState::TILED_RIGHT, true),
129                State::TiledTop => acc.set(WindowState::TILED_TOP, true),
130                State::TiledBottom => acc.set(WindowState::TILED_BOTTOM, true),
131                State::Suspended => acc.set(WindowState::SUSPENDED, true),
132                _ => (),
133            }
134            acc
135        })
136}
137
138pub(crate) fn determine_wm_capabilities(capabilities: &[u8]) -> WindowManagerCapabilities {
139    capabilities
140        .chunks_exact(4)
141        .flat_map(TryInto::<[u8; 4]>::try_into)
142        .map(u32::from_ne_bytes)
143        .flat_map(WmCapabilities::try_from)
144        .fold(WindowManagerCapabilities::empty(), |mut acc, capability| {
145            match capability {
146                WmCapabilities::WindowMenu => acc.set(WindowManagerCapabilities::WINDOW_MENU, true),
147                WmCapabilities::Maximize => acc.set(WindowManagerCapabilities::MAXIMIZE, true),
148                WmCapabilities::Fullscreen => acc.set(WindowManagerCapabilities::FULLSCREEN, true),
149                WmCapabilities::Minimize => acc.set(WindowManagerCapabilities::MINIMIZE, true),
150                _ => (),
151            }
152            acc
153        })
154}
155
156pub(crate) fn determine_decoration_mode(mode: Mode) -> DecorationMode {
157    match mode {
158        Mode::ClientSide => DecorationMode::Client,
159        Mode::ServerSide => DecorationMode::Server,
160
161        _ => unreachable!(),
162    }
163}
164
165impl<D> Dispatch2<xdg_toplevel::XdgToplevel, D> for WindowData
166where
167    D: WindowHandler,
168{
169    fn event(
170        &self,
171        data: &mut D,
172        toplevel: &xdg_toplevel::XdgToplevel,
173        event: xdg_toplevel::Event,
174        conn: &Connection,
175        qh: &QueueHandle<D>,
176    ) {
177        if let Some(window) = Window::from_xdg_toplevel(toplevel) {
178            match event {
179                xdg_toplevel::Event::Configure { width, height, states } => {
180                    let new_state = determine_window_state(&states);
181
182                    // XXX we do explicit convertion and sanity checking because compositor
183                    // could pass negative values which we should ignore all together.
184                    let width = u32::try_from(width).ok().and_then(NonZeroU32::new);
185                    let height = u32::try_from(height).ok().and_then(NonZeroU32::new);
186
187                    let pending_configure = &mut window.0.pending_configure.lock().unwrap();
188                    pending_configure.new_size = (width, height);
189                    pending_configure.state = new_state;
190                }
191
192                xdg_toplevel::Event::Close => {
193                    data.request_close(conn, qh, &window);
194                }
195
196                xdg_toplevel::Event::ConfigureBounds { width, height } => {
197                    let pending_configure = &mut window.0.pending_configure.lock().unwrap();
198                    if width == 0 && height == 0 {
199                        pending_configure.suggested_bounds = None;
200                    } else {
201                        pending_configure.suggested_bounds = Some((width as u32, height as u32));
202                    }
203                }
204                xdg_toplevel::Event::WmCapabilities { capabilities } => {
205                    let pending_configure = &mut window.0.pending_configure.lock().unwrap();
206                    pending_configure.capabilities = determine_wm_capabilities(&capabilities)
207                }
208                _ => unreachable!(),
209            }
210        }
211    }
212}
213
214// XDG decoration
215
216impl<D> Dispatch2<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, D> for GlobalData
217where
218    D: WindowHandler,
219{
220    fn event(
221        &self,
222        _: &mut D,
223        _: &zxdg_decoration_manager_v1::ZxdgDecorationManagerV1,
224        _: zxdg_decoration_manager_v1::Event,
225        _: &Connection,
226        _: &QueueHandle<D>,
227    ) {
228        unreachable!("zxdg_decoration_manager_v1 has no events")
229    }
230}
231
232impl<D> Dispatch2<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, D> for WindowData
233where
234    D: WindowHandler,
235{
236    fn event(
237        &self,
238        _: &mut D,
239        decoration: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
240        event: zxdg_toplevel_decoration_v1::Event,
241        _: &Connection,
242        _: &QueueHandle<D>,
243    ) {
244        if let Some(window) = Window::from_toplevel_decoration(decoration) {
245            match event {
246                zxdg_toplevel_decoration_v1::Event::Configure { mode } => match mode {
247                    wayland_client::WEnum::Value(mode) => {
248                        let mode = determine_decoration_mode(mode);
249                        window.0.pending_configure.lock().unwrap().decoration_mode = mode;
250                    }
251
252                    wayland_client::WEnum::Unknown(unknown) => {
253                        log::error!(target: "sctk", "unknown decoration mode 0x{:x}", unknown);
254                    }
255                },
256
257                _ => unreachable!(),
258            }
259        }
260    }
261}