1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
//! Unified shell surface handling //! //! This module provides an abstraction unifying the various iterations of //! the shell surface protocols (`wl_shell`, `zxdg_shell_v6` and `xdg_shell`, //! the current standard). //! //! This abstraction only manages the protocol part of shell surfaces. If you're //! looking for a more battery-included abstraction for creating windows, //! consider the `Window` type. use std::cell::RefCell; use wayland_client::{ protocol::{wl_output, wl_registry, wl_seat, wl_shell, wl_surface}, Attached, DispatchData, }; pub use wayland_protocols::xdg_shell::client::xdg_toplevel::State; use wayland_protocols::{ unstable::xdg_shell::v6::client::zxdg_shell_v6, xdg_shell::client::{xdg_toplevel, xdg_wm_base}, }; use crate::environment::{Environment, GlobalHandler}; mod wl; mod xdg; mod zxdg; use crate::lazy_global::LazyGlobal; /// Possible events generated by a shell surface that you need to handle #[derive(Clone, Debug)] pub enum Event { /// The state of your window has been changed Configure { /// Optional new size for your shell surface /// /// This is the new size of the contents of your shell surface /// as suggested by the server. You can ignore it and choose /// a new size if you want better control on the possible /// sizes of your shell surface. /// /// In all cases, these events can be generated in large batches /// during an interactive resize, and you should buffer them before /// processing them. You only need to handle the last one of a batch. new_size: Option<(u32, u32)>, /// New combination of states of your window /// /// Typically tells you if your surface is active/inactive, maximized, /// etc... states: Vec<State>, }, /// A close request has been received /// /// Most likely the user has clicked on the close button of the decorations /// or something equivalent Close, } /// Possible supported shell protocols pub enum Shell { /// The current standard `xdg_shell` protocol. Xdg(Attached<xdg_wm_base::XdgWmBase>), /// A previous iteration of the `xdg_shell` protocol. /// /// It has been replaced by the stable `xdg_shell`, and is only present here for /// compatibility purposes. Zxdg(Attached<zxdg_shell_v6::ZxdgShellV6>), /// The legacy `wl_shell`. /// /// It is deprecated and only present here for compatibility purposes. Wl(Attached<wl_shell::WlShell>), } impl Shell { /// Check if the shell in use needs you to wait for a `configure` event /// before you are allowed to draw. pub fn needs_configure(&self) -> bool { match self { Shell::Wl(_) => false, Shell::Xdg(_) => true, Shell::Zxdg(_) => true, } } } pub(crate) fn create_shell_surface<F>( shell: &Shell, surface: &wl_surface::WlSurface, callback: F, ) -> Box<dyn ShellSurface> where F: FnMut(Event, DispatchData) + 'static, { match *shell { Shell::Wl(ref shell) => Box::new(wl::Wl::create(surface, shell, callback)) as Box<_>, Shell::Xdg(ref shell) => Box::new(xdg::Xdg::create(surface, shell, callback)) as Box<_>, Shell::Zxdg(ref shell) => Box::new(zxdg::Zxdg::create(surface, shell, callback)) as Box<_>, } } /// Trait abstracting over shell surface protocols /// /// This trait's API is designed to reflect the behavior of the current standard /// shell surface protocol: `xdg_shell`. Compatibility implementations are /// provided for older protocols. pub trait ShellSurface: Send + Sync { /// Resizes the shell surface fn resize(&self, seat: &wl_seat::WlSeat, serial: u32, edges: xdg_toplevel::ResizeEdge); /// Moves the shell surface fn move_(&self, seat: &wl_seat::WlSeat, serial: u32); /// Set the title of the shell surface fn set_title(&self, title: String); /// Set the app id of the shell surface fn set_app_id(&self, app_id: String); /// Make fullscreen fn set_fullscreen(&self, output: Option<&wl_output::WlOutput>); /// Unset fullscreen fn unset_fullscreen(&self); /// Maximize surface fn set_maximized(&self); /// Unmaximize surface fn unset_maximized(&self); /// Minimize surface fn set_minimized(&self); /// Set geometry fn set_geometry(&self, x: i32, y: i32, width: i32, height: i32); /// Set minimum surface size fn set_min_size(&self, size: Option<(i32, i32)>); /// Set maximum surface size fn set_max_size(&self, size: Option<(i32, i32)>); /// Show window menu. fn show_window_menu(&self, seat: &wl_seat::WlSeat, serial: u32, x: i32, y: i32); /// Retrive the `XdgToplevel` proxy if the underlying shell surface /// uses the `xdg_shell` protocol. /// /// This allows interactions with other protocol extensions, like /// `xdg_decoratins` for example. fn get_xdg(&self) -> Option<&xdg_toplevel::XdgToplevel>; } struct ShellInner { registry: Option<Attached<wl_registry::WlRegistry>>, wl_shell: LazyGlobal<wl_shell::WlShell>, xdg_shell: LazyGlobal<xdg_wm_base::XdgWmBase>, zxdg_shell: LazyGlobal<zxdg_shell_v6::ZxdgShellV6>, } /// A handler for shells /// /// For use with the [`environment!`](../macro.environment.html) macro. It is already /// automatically included if you use the [`default_environment!`](../macro.default_environment.hmtl). /// /// To use it, you need to set it as a handler for the shells you want to support (`xdg_wm_base`, /// `zxdg_shell_v6` and/or `wl_shell`). You can then implement the /// [`ShellHandling`](trait.ShellHandling.html) by delegating it, to get the shell-related methods on /// [`Environment`](../environment/struct.environment.html) /// /// ```no_run /// # extern crate smithay_client_toolkit as sctk; /// # use sctk::environment; /// # use sctk::environment::Environment; /// # use sctk::shell::*; /// # use sctk::reexports::client::protocol::wl_shell; /// # use sctk::reexports::protocols::xdg_shell::client::xdg_wm_base; /// # use sctk::reexports::protocols::unstable::xdg_shell::v6::client::zxdg_shell_v6; /// # let display = sctk::reexports::client::Display::connect_to_env().unwrap(); /// # let mut queue = display.create_event_queue(); /// # let attached_display = display.attach(queue.token()); /// struct MyEnv { /// my_shell: ShellHandler /// } /// /// environment!(MyEnv, /// singles=[ /// wl_shell::WlShell => my_shell, /// xdg_wm_base::XdgWmBase => my_shell, /// zxdg_shell_v6::ZxdgShellV6 => my_shell /// ], /// multis=[], /// ); /// /// impl ShellHandling for MyEnv { /// fn get_shell(&self) -> Option<Shell> { /// // delegate the impl to the stored handler /// self.my_shell.get_shell() /// } /// } /// /// let env = Environment::new(&attached_display, &mut queue, MyEnv { /// my_shell: ShellHandler::new() /// }); /// ``` pub struct ShellHandler { inner: RefCell<ShellInner>, } impl ShellHandler { /// Create a new handler pub fn new() -> ShellHandler { ShellHandler { inner: RefCell::new(ShellInner { registry: None, wl_shell: LazyGlobal::Unknown, xdg_shell: LazyGlobal::Unknown, zxdg_shell: LazyGlobal::Unknown, }), } } } impl GlobalHandler<wl_shell::WlShell> for ShellHandler { fn created( &mut self, registry: Attached<wl_registry::WlRegistry>, id: u32, version: u32, _: DispatchData, ) { let mut inner = self.inner.borrow_mut(); if inner.registry.is_none() { inner.registry = Some(registry); } if let LazyGlobal::Unknown = inner.wl_shell { inner.wl_shell = LazyGlobal::Seen { id, version }; } else { log::warn!("Compositor advertised wl_shell multiple times, ignoring.") } } fn get(&self) -> Option<Attached<wl_shell::WlShell>> { let mut inner = self.inner.borrow_mut(); match inner.wl_shell { LazyGlobal::Bound(ref shell) => Some(shell.clone()), LazyGlobal::Unknown => None, LazyGlobal::Seen { id, .. } => { // registry cannot be None if we have seen the global let registry = inner.registry.as_ref().unwrap(); // only version 1 of wl_shell is supported let shell = registry.bind::<wl_shell::WlShell>(1, id); inner.wl_shell = LazyGlobal::Bound((*shell).clone()); Some((*shell).clone()) } } } } impl GlobalHandler<xdg_wm_base::XdgWmBase> for ShellHandler { fn created( &mut self, registry: Attached<wl_registry::WlRegistry>, id: u32, version: u32, _: DispatchData, ) { let mut inner = self.inner.borrow_mut(); if inner.registry.is_none() { inner.registry = Some(registry); } if let LazyGlobal::Unknown = inner.xdg_shell { inner.xdg_shell = LazyGlobal::Seen { id, version }; } else { log::warn!("Compositor advertised xdg_wm_base multiple times, ignoring.") } } fn get(&self) -> Option<Attached<xdg_wm_base::XdgWmBase>> { let mut inner = self.inner.borrow_mut(); match inner.xdg_shell { LazyGlobal::Bound(ref shell) => Some(shell.clone()), LazyGlobal::Unknown => None, LazyGlobal::Seen { version, id } => { // registry cannot be None if we have seen the global let registry = inner.registry.as_ref().unwrap(); // we currently support xdg_shell up to version 2 let version = std::cmp::min(2, version); let shell = registry.bind::<xdg_wm_base::XdgWmBase>(version, id); shell.quick_assign(|shell, event, _| { if let xdg_wm_base::Event::Ping { serial } = event { shell.pong(serial); } }); inner.xdg_shell = LazyGlobal::Bound((*shell).clone()); Some((*shell).clone()) } } } } impl GlobalHandler<zxdg_shell_v6::ZxdgShellV6> for ShellHandler { fn created( &mut self, registry: Attached<wl_registry::WlRegistry>, id: u32, version: u32, _: DispatchData, ) { let mut inner = self.inner.borrow_mut(); if inner.registry.is_none() { inner.registry = Some(registry); } if let LazyGlobal::Unknown = inner.zxdg_shell { inner.zxdg_shell = LazyGlobal::Seen { id, version }; } else { log::warn!("Compositor advertised zxdg_shell_v6 multiple times, ignoring.") } } fn get(&self) -> Option<Attached<zxdg_shell_v6::ZxdgShellV6>> { let mut inner = self.inner.borrow_mut(); match inner.zxdg_shell { LazyGlobal::Bound(ref shell) => Some(shell.clone()), LazyGlobal::Unknown => None, LazyGlobal::Seen { id, .. } => { // registry cannot be None if we have seen the global let registry = inner.registry.as_ref().unwrap(); // only version 1 of zxdg_shell_v6 is supported let shell = registry.bind::<zxdg_shell_v6::ZxdgShellV6>(1, id); shell.quick_assign(|shell, event, _| { if let zxdg_shell_v6::Event::Ping { serial } = event { shell.pong(serial); } }); inner.zxdg_shell = LazyGlobal::Bound((*shell).clone()); Some((*shell).clone()) } } } } impl ShellHandling for ShellHandler { fn get_shell(&self) -> Option<Shell> { if let Some(xdg) = GlobalHandler::<xdg_wm_base::XdgWmBase>::get(self) { Some(Shell::Xdg(xdg)) } else if let Some(zxdg) = GlobalHandler::<zxdg_shell_v6::ZxdgShellV6>::get(self) { Some(Shell::Zxdg(zxdg)) } else if let Some(wl) = GlobalHandler::<wl_shell::WlShell>::get(self) { Some(Shell::Wl(wl)) } else { None } } } /// A helper trait for delegating shell handling /// /// If you don't use [`declare_default_environment!`](../macro.declare_default_environment.html) but still /// want to use the shell helpers provided here, you need to implement this trait for your /// [`declare_environment!`](../macro.declare_environment.html)-generated type, by delegating it to one /// of the handlers you provided for the different shells. pub trait ShellHandling { /// Get the best available shell fn get_shell(&self) -> Option<Shell>; } impl<E: ShellHandling> Environment<E> { /// Get the best available shell protocol /// /// Returns `None` if no shell was advertised. pub fn get_shell(&self) -> Option<Shell> { self.with_inner(|extras| extras.get_shell()) } /// Create a new shell surface for this surface /// /// This helper abstracts over the `xdg_shell` protocol and its precursors (`zxdg_shell_v6` /// and `wl_shell`) for retro-compatibility. It'll attempt to use them in this order. /// /// You need to provide a closure that will process the events generated by the shell surface. /// /// *Panic* /// /// This function will panic if no supported shell was advertised by the compositor. pub fn create_shell_surface<F>( &self, surface: &wl_surface::WlSurface, f: F, ) -> Box<dyn ShellSurface> where F: FnMut(Event, DispatchData) + 'static, { let shell = self .get_shell() .expect("SCTK: trying to create a shell surface without any supported shell."); create_shell_surface(&shell, surface, f) } }