| 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 | |