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: &{unknown}) = 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) = Window::from_xdg_surface(xdg_surface) {
73 match event {
74 xdg_surface::Event::Configure { serial: u32 } => {
75 // Acknowledge the configure per protocol requirements.
76 xdg_surface.ack_configure(serial);
77
78 let configure: WindowConfigure = { window.0.pending_configure.lock().unwrap().clone() };
79 WindowHandler::configure(self: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}
234