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