1 | use std::{ |
2 | convert::{TryFrom, TryInto}, |
3 | num::NonZeroU32, |
4 | sync::Mutex, |
5 | }; |
6 | |
7 | use wayland_client::{Connection, Dispatch, QueueHandle}; |
8 | use 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 | |
19 | use crate::{ |
20 | error::GlobalError, |
21 | globals::{GlobalData, ProvidesBoundGlobal}, |
22 | shell::xdg::{XdgShell, XdgShellSurface}, |
23 | }; |
24 | |
25 | use super::{ |
26 | DecorationMode, Window, WindowConfigure, WindowData, WindowHandler, WindowManagerCapabilities, |
27 | WindowState, |
28 | }; |
29 | |
30 | impl 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)] |
45 | pub 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 | |
52 | impl 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 | |
60 | impl<D> Dispatch<xdg_surface::XdgSurface, WindowData, D> for XdgShell |
61 | where |
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 | |
88 | impl<D> Dispatch<xdg_toplevel::XdgToplevel, WindowData, D> for XdgShell |
89 | where |
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 | |
182 | impl<D> Dispatch<zxdg_decoration_manager_v1::ZxdgDecorationManagerV1, GlobalData, D> for XdgShell |
183 | where |
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 | |
198 | impl<D> Dispatch<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1, WindowData, D> for XdgShell |
199 | where |
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 | |