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 ProvidesBoundGlobal<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, 1> for XdgShell {
54    fn bound_global(
55        &self,
56    ) -> Result<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, GlobalError> {
57        self.xdg_decoration_manager.get().cloned()
58    }
59}
60
61impl<D> Dispatch2<xdg_surface::XdgSurface, D> for WindowData
62where
63    D: WindowHandler,
64{
65    fn event(
66        &self,
67        data: &mut D,
68        xdg_surface: &xdg_surface::XdgSurface,
69        event: xdg_surface::Event,
70        conn: &Connection,
71        qh: &QueueHandle<D>,
72    ) {
73        if let Some(window) = Window::from_xdg_surface(xdg_surface) {
74            match event {
75                xdg_surface::Event::Configure { serial } => {
76                    // Acknowledge the configure per protocol requirements.
77                    xdg_surface.ack_configure(serial);
78
79                    let configure = { window.0.pending_configure.lock().unwrap().clone() };
80                    WindowHandler::configure(data, conn, qh, &window, configure, serial);
81                }
82
83                _ => unreachable!(),
84            }
85        }
86    }
87}
88
89pub(crate) fn determine_window_state(states: &[u8]) -> WindowState {
90    // The states are encoded as a bunch of u32 of native endian, but are encoded in an array of
91    // bytes.
92    states
93        .chunks_exact(4)
94        .flat_map(TryInto::<[u8; 4]>::try_into)
95        .map(u32::from_ne_bytes)
96        .flat_map(State::try_from)
97        .fold(WindowState::empty(), |mut acc, state| {
98            match state {
99                State::Maximized => acc.set(WindowState::MAXIMIZED, true),
100                State::Fullscreen => acc.set(WindowState::FULLSCREEN, true),
101                State::Resizing => acc.set(WindowState::RESIZING, true),
102                State::Activated => acc.set(WindowState::ACTIVATED, true),
103                State::TiledLeft => acc.set(WindowState::TILED_LEFT, true),
104                State::TiledRight => acc.set(WindowState::TILED_RIGHT, true),
105                State::TiledTop => acc.set(WindowState::TILED_TOP, true),
106                State::TiledBottom => acc.set(WindowState::TILED_BOTTOM, true),
107                State::Suspended => acc.set(WindowState::SUSPENDED, true),
108                _ => (),
109            }
110            acc
111        })
112}
113
114pub(crate) fn determine_wm_capabilities(capabilities: &[u8]) -> WindowManagerCapabilities {
115    capabilities
116        .chunks_exact(4)
117        .flat_map(TryInto::<[u8; 4]>::try_into)
118        .map(u32::from_ne_bytes)
119        .flat_map(WmCapabilities::try_from)
120        .fold(WindowManagerCapabilities::empty(), |mut acc, capability| {
121            match capability {
122                WmCapabilities::WindowMenu => acc.set(WindowManagerCapabilities::WINDOW_MENU, true),
123                WmCapabilities::Maximize => acc.set(WindowManagerCapabilities::MAXIMIZE, true),
124                WmCapabilities::Fullscreen => acc.set(WindowManagerCapabilities::FULLSCREEN, true),
125                WmCapabilities::Minimize => acc.set(WindowManagerCapabilities::MINIMIZE, true),
126                _ => (),
127            }
128            acc
129        })
130}
131
132pub(crate) fn determine_decoration_mode(mode: Mode) -> DecorationMode {
133    match mode {
134        Mode::ClientSide => DecorationMode::Client,
135        Mode::ServerSide => DecorationMode::Server,
136
137        _ => unreachable!(),
138    }
139}
140
141impl<D> Dispatch2<xdg_toplevel::XdgToplevel, D> for WindowData
142where
143    D: WindowHandler,
144{
145    fn event(
146        &self,
147        data: &mut D,
148        toplevel: &xdg_toplevel::XdgToplevel,
149        event: xdg_toplevel::Event,
150        conn: &Connection,
151        qh: &QueueHandle<D>,
152    ) {
153        if let Some(window) = Window::from_xdg_toplevel(toplevel) {
154            match event {
155                xdg_toplevel::Event::Configure { width, height, states } => {
156                    let new_state = determine_window_state(&states);
157
158                    // XXX we do explicit convertion and sanity checking because compositor
159                    // could pass negative values which we should ignore all together.
160                    let width = u32::try_from(width).ok().and_then(NonZeroU32::new);
161                    let height = u32::try_from(height).ok().and_then(NonZeroU32::new);
162
163                    let pending_configure = &mut window.0.pending_configure.lock().unwrap();
164                    pending_configure.new_size = (width, height);
165                    pending_configure.state = new_state;
166                }
167
168                xdg_toplevel::Event::Close => {
169                    data.request_close(conn, qh, &window);
170                }
171
172                xdg_toplevel::Event::ConfigureBounds { width, height } => {
173                    let pending_configure = &mut window.0.pending_configure.lock().unwrap();
174                    if width == 0 && height == 0 {
175                        pending_configure.suggested_bounds = None;
176                    } else {
177                        pending_configure.suggested_bounds = Some((width as u32, height as u32));
178                    }
179                }
180                xdg_toplevel::Event::WmCapabilities { capabilities } => {
181                    let pending_configure = &mut window.0.pending_configure.lock().unwrap();
182                    pending_configure.capabilities = determine_wm_capabilities(&capabilities)
183                }
184                _ => unreachable!(),
185            }
186        }
187    }
188}
189
190// XDG decoration
191
192impl<D> Dispatch2<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, D> for GlobalData
193where
194    D: WindowHandler,
195{
196    fn event(
197        &self,
198        _: &mut D,
199        _: &zxdg_decoration_manager_v1::ZxdgDecorationManagerV1,
200        _: zxdg_decoration_manager_v1::Event,
201        _: &Connection,
202        _: &QueueHandle<D>,
203    ) {
204        unreachable!("zxdg_decoration_manager_v1 has no events")
205    }
206}
207
208impl<D> Dispatch2<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, D> for WindowData
209where
210    D: WindowHandler,
211{
212    fn event(
213        &self,
214        _: &mut D,
215        decoration: &zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1,
216        event: zxdg_toplevel_decoration_v1::Event,
217        _: &Connection,
218        _: &QueueHandle<D>,
219    ) {
220        if let Some(window) = Window::from_toplevel_decoration(decoration) {
221            match event {
222                zxdg_toplevel_decoration_v1::Event::Configure { mode } => match mode {
223                    wayland_client::WEnum::Value(mode) => {
224                        let mode = determine_decoration_mode(mode);
225                        window.0.pending_configure.lock().unwrap().decoration_mode = mode;
226                    }
227
228                    wayland_client::WEnum::Unknown(unknown) => {
229                        log::error!(target: "sctk", "unknown decoration mode 0x{:x}", unknown);
230                    }
231                },
232
233                _ => unreachable!(),
234            }
235        }
236    }
237}