smithay_client_toolkit/shell/xdg/window/
inner.rs1use 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 if let Some(toplevel_decoration) = self.toplevel_decoration.as_ref() {
35 toplevel_decoration.destroy();
36 }
37
38 self.xdg_toplevel.destroy();
40 }
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 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 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 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 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
214impl<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}