From 84d3d6e6098d9fb0468706ebafc714cd1f90cd96 Mon Sep 17 00:00:00 2001 From: Poly Date: Sat, 31 Jul 2021 03:51:35 +0200 Subject: [PATCH] wayland.shell: implement wlr_layer_shell --- src/wayland/shell/mod.rs | 2 + src/wayland/shell/wlr_layer/handlers.rs | 298 ++++++++++++++++ src/wayland/shell/wlr_layer/mod.rs | 454 ++++++++++++++++++++++++ src/wayland/shell/wlr_layer/types.rs | 231 ++++++++++++ src/wayland/shell/xdg/mod.rs | 2 +- 5 files changed, 986 insertions(+), 1 deletion(-) create mode 100644 src/wayland/shell/wlr_layer/handlers.rs create mode 100644 src/wayland/shell/wlr_layer/mod.rs create mode 100644 src/wayland/shell/wlr_layer/types.rs diff --git a/src/wayland/shell/mod.rs b/src/wayland/shell/mod.rs index 470bf67..03a9dd5 100644 --- a/src/wayland/shell/mod.rs +++ b/src/wayland/shell/mod.rs @@ -22,6 +22,8 @@ use wayland_server::protocol::wl_surface::WlSurface; pub mod legacy; pub mod xdg; +pub mod wlr_layer; + /// Represents the possible errors returned from /// a surface ping #[derive(Debug, Error)] diff --git a/src/wayland/shell/wlr_layer/handlers.rs b/src/wayland/shell/wlr_layer/handlers.rs new file mode 100644 index 0000000..3e97e78 --- /dev/null +++ b/src/wayland/shell/wlr_layer/handlers.rs @@ -0,0 +1,298 @@ +use std::{convert::TryFrom, ops::Deref, sync::Mutex}; + +use wayland_protocols::wlr::unstable::layer_shell::v1::server::{zwlr_layer_shell_v1, zwlr_layer_surface_v1}; +use wayland_server::{protocol::wl_surface, DispatchData, Filter, Main}; + +use crate::wayland::{compositor, shell::wlr_layer::Layer, Serial}; + +use super::{ + Anchor, KeyboardInteractivity, LayerShellRequest, LayerSurfaceAttributes, LayerSurfaceCachedState, + Margins, ShellUserData, +}; + +use super::LAYER_SURFACE_ROLE; + +pub(super) fn layer_shell_implementation( + shell: Main, + request: zwlr_layer_shell_v1::Request, + dispatch_data: DispatchData<'_>, +) { + let data = shell.as_ref().user_data().get::().unwrap(); + match request { + zwlr_layer_shell_v1::Request::GetLayerSurface { + id, + surface, + output, + layer, + namespace, + } => { + let layer = match Layer::try_from(layer) { + Ok(layer) => layer, + Err((err, msg)) => { + id.as_ref().post_error(err as u32, msg); + return; + } + }; + + if compositor::give_role(&surface, LAYER_SURFACE_ROLE).is_err() { + shell.as_ref().post_error( + zwlr_layer_shell_v1::Error::Role as u32, + "Surface already has a role.".into(), + ); + return; + } + + compositor::with_states(&surface, |states| { + states.data_map.insert_if_missing_threadsafe(|| { + Mutex::new(LayerSurfaceAttributes::new(id.deref().clone())) + }); + + states.cached_state.pending::().layer = layer; + }) + .unwrap(); + + compositor::add_commit_hook(&surface, |surface| { + compositor::with_states(surface, |states| { + let mut guard = states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap(); + + let pending = states.cached_state.pending::(); + + if pending.size.w == 0 && !pending.anchor.anchored_horizontally() { + guard.surface.as_ref().post_error( + zwlr_layer_surface_v1::Error::InvalidSize as u32, + "width 0 requested without setting left and right anchors".into(), + ); + return; + } + + if pending.size.h == 0 && !pending.anchor.anchored_vertically() { + guard.surface.as_ref().post_error( + zwlr_layer_surface_v1::Error::InvalidSize as u32, + "height 0 requested without setting top and bottom anchors".into(), + ); + return; + } + + if let Some(state) = guard.last_acked.clone() { + guard.current = state; + } + }) + .unwrap(); + }); + + id.quick_assign(|surface, req, dispatch_data| { + layer_surface_implementation(surface.deref().clone(), req, dispatch_data) + }); + + id.assign_destructor(Filter::new( + |layer_surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, _, _| { + let data = layer_surface + .as_ref() + .user_data() + .get::() + .unwrap(); + + // remove this surface from the known ones (as well as any leftover dead surface) + data.shell_data + .shell_state + .lock() + .unwrap() + .known_layers + .retain(|other| other.alive()); + }, + )); + + id.as_ref().user_data().set(|| LayerSurfaceUserData { + shell_data: data.clone(), + wl_surface: surface.clone(), + }); + + let handle = super::LayerSurface { + wl_surface: surface, + shell_surface: id.deref().clone(), + }; + + data.shell_state.lock().unwrap().known_layers.push(handle.clone()); + + let mut user_impl = data.user_impl.borrow_mut(); + (&mut *user_impl)( + LayerShellRequest::NewLayerSurface { + surface: handle, + output, + layer, + namespace, + }, + dispatch_data, + ); + } + zwlr_layer_shell_v1::Request::Destroy => { + // Handled by destructor + } + _ => {} + } +} + +fn layer_surface_implementation( + layer_surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, + request: zwlr_layer_surface_v1::Request, + dispatch_data: DispatchData<'_>, +) { + match request { + zwlr_layer_surface_v1::Request::SetSize { width, height } => { + with_surface_pending_state(&layer_surface, |data| { + data.size = (width as i32, height as i32).into(); + }); + } + zwlr_layer_surface_v1::Request::SetAnchor { anchor } => { + match Anchor::try_from(anchor) { + Ok(anchor) => { + with_surface_pending_state(&layer_surface, |data| { + data.anchor = Anchor::from_bits(anchor.bits()).unwrap_or_default(); + }); + } + Err((err, msg)) => { + layer_surface.as_ref().post_error(err as u32, msg); + } + }; + } + zwlr_layer_surface_v1::Request::SetExclusiveZone { zone } => { + with_surface_pending_state(&layer_surface, |data| { + data.exclusive_zone = zone.into(); + }); + } + zwlr_layer_surface_v1::Request::SetMargin { + top, + right, + bottom, + left, + } => { + with_surface_pending_state(&layer_surface, |data| { + data.margin = Margins { + top, + right, + bottom, + left, + }; + }); + } + zwlr_layer_surface_v1::Request::SetKeyboardInteractivity { + keyboard_interactivity, + } => { + match KeyboardInteractivity::try_from(keyboard_interactivity) { + Ok(keyboard_interactivity) => { + with_surface_pending_state(&layer_surface, |data| { + data.keyboard_interactivity = keyboard_interactivity; + }); + } + Err((err, msg)) => { + layer_surface.as_ref().post_error(err as u32, msg); + } + }; + } + zwlr_layer_surface_v1::Request::SetLayer { layer } => { + match Layer::try_from(layer) { + Ok(layer) => { + with_surface_pending_state(&layer_surface, |data| { + data.layer = layer; + }); + } + Err((err, msg)) => { + layer_surface.as_ref().post_error(err as u32, msg); + } + }; + } + zwlr_layer_surface_v1::Request::GetPopup { popup } => { + let data = layer_surface + .as_ref() + .user_data() + .get::() + .unwrap(); + + let parent_surface = data.wl_surface.clone(); + + let data = popup + .as_ref() + .user_data() + .get::() + .unwrap(); + + compositor::with_states(&data.wl_surface, move |states| { + states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap() + .parent = Some(parent_surface); + }) + .unwrap(); + } + zwlr_layer_surface_v1::Request::AckConfigure { serial } => { + let data = layer_surface + .as_ref() + .user_data() + .get::() + .unwrap(); + + let serial = Serial::from(serial); + let surface = &data.wl_surface; + + let found_configure = compositor::with_states(surface, |states| { + states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap() + .ack_configure(serial) + }) + .unwrap(); + + let configure = match found_configure { + Some(configure) => configure, + None => { + layer_surface.as_ref().post_error( + zwlr_layer_surface_v1::Error::InvalidSurfaceState as u32, + format!("wrong configure serial: {}", ::from(serial)), + ); + return; + } + }; + + let mut user_impl = data.shell_data.user_impl.borrow_mut(); + (&mut *user_impl)( + LayerShellRequest::AckConfigure { + surface: data.wl_surface.clone(), + configure, + }, + dispatch_data, + ); + } + _ => {} + } +} + +struct LayerSurfaceUserData { + shell_data: ShellUserData, + wl_surface: wl_surface::WlSurface, +} + +fn with_surface_pending_state(layer_surface: &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, f: F) -> T +where + F: FnOnce(&mut LayerSurfaceCachedState) -> T, +{ + let data = layer_surface + .as_ref() + .user_data() + .get::() + .unwrap(); + compositor::with_states(&data.wl_surface, |states| { + f(&mut *states.cached_state.pending::()) + }) + .unwrap() +} diff --git a/src/wayland/shell/wlr_layer/mod.rs b/src/wayland/shell/wlr_layer/mod.rs new file mode 100644 index 0000000..f012dbd --- /dev/null +++ b/src/wayland/shell/wlr_layer/mod.rs @@ -0,0 +1,454 @@ +//! Utilities for handling shell surfaces with the `wlr_layer_shell` protocol +//! +//! This interface should be suitable for the implementation of many desktop shell components, +//! and a broad number of other applications that interact with the desktop. +//! +//! ### Initialization +//! +//! To initialize this handler, simple use the [`wlr_layer_shell_init`] function provided in this module. +//! You need to provide a closure that will be invoked whenever some action is required from you, +//! are represented by the [`LayerShellRequest`] enum. +//! +//! ```no_run +//! # extern crate wayland_server; +//! # +//! use smithay::wayland::shell::wlr_layer::{wlr_layer_shell_init, LayerShellRequest}; +//! +//! # let mut display = wayland_server::Display::new(); +//! let (shell_state, _) = wlr_layer_shell_init( +//! &mut display, +//! // your implementation +//! |event: LayerShellRequest, dispatch_data| { /* handle the shell requests here */ }, +//! None // put a logger if you want +//! ); +//! +//! // You're now ready to go! +//! ``` + +use std::{ + cell::RefCell, + rc::Rc, + sync::{Arc, Mutex}, +}; + +use wayland_protocols::wlr::unstable::layer_shell::v1::server::{zwlr_layer_shell_v1, zwlr_layer_surface_v1}; +use wayland_server::{ + protocol::{wl_output::WlOutput, wl_surface}, + DispatchData, Display, Filter, Global, Main, +}; + +use crate::{ + utils::{DeadResource, Logical, Size}, + wayland::{ + compositor::{self, Cacheable}, + Serial, SERIAL_COUNTER, + }, +}; + +mod handlers; +mod types; + +pub use types::{Anchor, ExclusiveZone, KeyboardInteractivity, Layer, Margins}; + +/// The role of a wlr_layer_shell_surface +pub static LAYER_SURFACE_ROLE: &str = "zwlr_layer_surface_v1"; + +/// Attributes for layer surface +#[derive(Debug)] +pub struct LayerSurfaceAttributes { + surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, + /// Defines if the surface has received at least one + /// layer_surface.ack_configure from the client + pub configured: bool, + /// The serial of the last acked configure + pub configure_serial: Option, + /// Holds the state if the surface has sent the initial + /// configure event to the client. It is expected that + /// during the first commit a initial + /// configure event is sent to the client + pub initial_configure_sent: bool, + /// Holds the configures the server has sent out + /// to the client waiting to be acknowledged by + /// the client. All pending configures that are older + /// than the acknowledged one will be discarded during + /// processing layer_surface.ack_configure. + pending_configures: Vec, + /// Holds the pending state as set by the server. + pub server_pending: Option, + /// Holds the last server_pending state that has been acknowledged + /// by the client. This state should be cloned to the current + /// during a commit. + pub last_acked: Option, + /// Holds the current state of the layer after a successful + /// commit. + pub current: LayerSurfaceState, +} + +impl LayerSurfaceAttributes { + fn new(surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1) -> Self { + Self { + surface, + configured: false, + configure_serial: None, + initial_configure_sent: false, + pending_configures: Vec::new(), + server_pending: None, + last_acked: None, + current: Default::default(), + } + } + + fn ack_configure(&mut self, serial: Serial) -> Option { + let configure = self + .pending_configures + .iter() + .find(|configure| configure.serial == serial) + .cloned()?; + + self.last_acked = Some(configure.state.clone()); + + self.configured = true; + self.configure_serial = Some(serial); + self.pending_configures.retain(|c| c.serial > serial); + Some(configure) + } +} + +/// State of a layer surface +#[derive(Debug, Default, Clone, PartialEq)] +pub struct LayerSurfaceState { + /// The suggested size of the surface + pub size: Option>, +} + +/// Represents the client pending state +#[derive(Debug, Default, Clone, Copy)] +pub struct LayerSurfaceCachedState { + /// The size requested by the client + pub size: Size, + /// Anchor bitflags, describing how the layers surface should be positioned and sized + pub anchor: Anchor, + /// Descripton of exclusive zone + pub exclusive_zone: ExclusiveZone, + /// Describes distance from the anchor point of the output + pub margin: Margins, + /// Describes how keyboard events are delivered to this surface + pub keyboard_interactivity: KeyboardInteractivity, + /// The layer that the surface is rendered on + pub layer: Layer, +} + +impl Cacheable for LayerSurfaceCachedState { + fn commit(&mut self) -> Self { + *self + } + fn merge_into(self, into: &mut Self) { + *into = self; + } +} + +/// Shell global state +/// +/// This state allows you to retrieve a list of surfaces +/// currently known to the shell global. +#[derive(Debug)] +pub struct LayerShellState { + known_layers: Vec, +} + +impl LayerShellState { + /// Access all the shell surfaces known by this handler + pub fn layer_surfaces(&self) -> &[LayerSurface] { + &self.known_layers[..] + } +} + +#[derive(Clone)] +struct ShellUserData { + log: ::slog::Logger, + user_impl: Rc)>>, + shell_state: Arc>, +} + +/// Create a new `wlr_layer_shell` globals +pub fn wlr_layer_shell_init( + display: &mut Display, + implementation: Impl, + logger: L, +) -> ( + Arc>, + Global, +) +where + L: Into>, + Impl: FnMut(LayerShellRequest, DispatchData<'_>) + 'static, +{ + let log = crate::slog_or_fallback(logger); + let shell_state = Arc::new(Mutex::new(LayerShellState { + known_layers: Vec::new(), + })); + + let shell_data = ShellUserData { + log: log.new(slog::o!("smithay_module" => "layer_shell_handler")), + user_impl: Rc::new(RefCell::new(implementation)), + shell_state: shell_state.clone(), + }; + + let layer_shell_global = display.create_global( + 4, + Filter::new( + move |(shell, _version): (Main, _), _, _ddata| { + shell.quick_assign(self::handlers::layer_shell_implementation); + shell.as_ref().user_data().set({ + let shell_data = shell_data.clone(); + move || shell_data + }); + }, + ), + ); + + (shell_state, layer_shell_global) +} + +/// A handle to a layer surface +#[derive(Debug, Clone)] +pub struct LayerSurface { + wl_surface: wl_surface::WlSurface, + shell_surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, +} + +impl std::cmp::PartialEq for LayerSurface { + fn eq(&self, other: &Self) -> bool { + self.alive() && other.alive() && self.wl_surface == other.wl_surface + } +} + +impl LayerSurface { + /// Is the layer surface referred by this handle still alive? + pub fn alive(&self) -> bool { + self.shell_surface.as_ref().is_alive() && self.wl_surface.as_ref().is_alive() + } + + /// Gets the current pending state for a configure + /// + /// Returns `Some` if either no initial configure has been sent or + /// the `server_pending` is `Some` and different from the last pending + /// configure or `last_acked` if there is no pending + /// + /// Returns `None` if either no `server_pending` or the pending + /// has already been sent to the client or the pending is equal + /// to the `last_acked` + fn get_pending_state(&self, attributes: &mut LayerSurfaceAttributes) -> Option { + if !attributes.initial_configure_sent { + return Some(attributes.server_pending.take().unwrap_or_default()); + } + + let server_pending = match attributes.server_pending.take() { + Some(state) => state, + None => { + return None; + } + }; + + let last_state = attributes + .pending_configures + .last() + .map(|c| &c.state) + .or_else(|| attributes.last_acked.as_ref()); + + if let Some(state) = last_state { + if state == &server_pending { + return None; + } + } + + Some(server_pending) + } + + /// Send a configure event to this layer surface to suggest it a new configuration + /// + /// The serial of this configure will be tracked waiting for the client to ACK it. + /// + /// You can manipulate the state that will be sent to the client with the [`with_pending_state`](#method.with_pending_state) + /// method. + pub fn send_configure(&self) { + if let Some(surface) = self.get_surface() { + let configure = compositor::with_states(surface, |states| { + let mut attributes = states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap(); + if let Some(pending) = self.get_pending_state(&mut *attributes) { + let configure = LayerSurfaceConfigure { + serial: SERIAL_COUNTER.next_serial(), + state: pending, + }; + + attributes.pending_configures.push(configure.clone()); + attributes.initial_configure_sent = true; + + Some(configure) + } else { + None + } + }) + .unwrap_or(None); + + // send surface configure + if let Some(configure) = configure { + let (width, height) = configure.state.size.unwrap_or_default().into(); + let serial = configure.serial; + self.shell_surface + .configure(serial.into(), width as u32, height as u32); + } + } + } + + /// Make sure this surface was configured + /// + /// Returns `true` if it was, if not, returns `false` and raise + /// a protocol error to the associated layer surface. Also returns `false` + /// if the surface is already destroyed. + pub fn ensure_configured(&self) -> bool { + if !self.alive() { + return false; + } + let configured = compositor::with_states(&self.wl_surface, |states| { + states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap() + .configured + }) + .unwrap(); + if !configured { + self.shell_surface.as_ref().post_error( + zwlr_layer_shell_v1::Error::AlreadyConstructed as u32, + "layer_surface has never been configured".into(), + ); + } + configured + } + + /// Send a "close" event to the client + pub fn send_close(&self) { + self.shell_surface.closed() + } + + /// Access the underlying `wl_surface` of this layer surface + /// + /// Returns `None` if the layer surface actually no longer exists. + pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> { + if self.alive() { + Some(&self.wl_surface) + } else { + None + } + } + + /// Allows the pending state of this layer to + /// be manipulated. + /// + /// This should be used to inform the client about size and state changes, + /// for example after a resize request from the client. + /// + /// The state will be sent to the client when calling [`send_configure`](#method.send_configure). + pub fn with_pending_state(&self, f: F) -> Result + where + F: FnOnce(&mut LayerSurfaceState) -> T, + { + if !self.alive() { + return Err(DeadResource); + } + + Ok(compositor::with_states(&self.wl_surface, |states| { + let mut attributes = states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap(); + if attributes.server_pending.is_none() { + attributes.server_pending = Some(attributes.current.clone()); + } + + let server_pending = attributes.server_pending.as_mut().unwrap(); + f(server_pending) + }) + .unwrap()) + } + + /// Gets a copy of the current state of this layer + /// + /// Returns `None` if the underlying surface has been + /// destroyed + pub fn current_state(&self) -> Option { + if !self.alive() { + return None; + } + + Some( + compositor::with_states(&self.wl_surface, |states| { + let attributes = states + .data_map + .get::>() + .unwrap() + .lock() + .unwrap(); + + attributes.current.clone() + }) + .unwrap(), + ) + } +} + +/// A configure message for layer surfaces +#[derive(Debug, Clone)] +pub struct LayerSurfaceConfigure { + /// The state associated with this configure + pub state: LayerSurfaceState, + + /// A serial number to track ACK from the client + /// + /// This should be an ever increasing number, as the ACK-ing + /// from a client for a serial will validate all pending lower + /// serials. + pub serial: Serial, +} + +/// Events generated by layer shell surfaces +/// +/// Depending on what you want to do, you might ignore some of them +#[derive(Debug)] +pub enum LayerShellRequest { + /// A new layer surface was created + /// + /// You likely need to send a [`LayerSurfaceConfigure`] to the surface, to hint the + /// client as to how its layer surface should be sized. + NewLayerSurface { + /// the surface + surface: LayerSurface, + /// The output that the layer will be displayed on + /// + /// None means that the compositor should decide which output to use, + /// Generally this will be the one that the user most recently interacted with + output: Option, + /// This values indicate on which layer a surface should be rendered on + layer: Layer, + /// namespace that defines the purpose of the layer surface + namespace: String, + }, + + /// A surface has acknowledged a configure serial. + AckConfigure { + /// The surface. + surface: wl_surface::WlSurface, + /// The configure serial. + configure: LayerSurfaceConfigure, + }, +} diff --git a/src/wayland/shell/wlr_layer/types.rs b/src/wayland/shell/wlr_layer/types.rs new file mode 100644 index 0000000..2fe383c --- /dev/null +++ b/src/wayland/shell/wlr_layer/types.rs @@ -0,0 +1,231 @@ +use std::{cmp::Ordering, convert::TryFrom}; + +use wayland_protocols::wlr::unstable::layer_shell::v1::server::{zwlr_layer_shell_v1, zwlr_layer_surface_v1}; + +/// Available layers for surfaces +/// +/// These values indicate which layers a surface can be rendered in. +/// They are ordered by z depth, bottom-most first. +/// Traditional shell surfaces will typically be rendered between the bottom and top layers. +/// Fullscreen shell surfaces are typically rendered at the top layer. +/// Multiple surfaces can share a single layer, and ordering within a single layer is undefined. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Layer { + /// The lowest layer, used usualy for wallpapers + Background, + /// The layer bellow the windows and above the wallpaper + Bottom, + /// The layer above the windows and bellow overlay + Top, + /// The top layer above all other layers + Overlay, +} + +impl TryFrom for Layer { + type Error = (zwlr_layer_shell_v1::Error, String); + + fn try_from(layer: zwlr_layer_shell_v1::Layer) -> Result { + match layer { + zwlr_layer_shell_v1::Layer::Background => Ok(Self::Background), + zwlr_layer_shell_v1::Layer::Bottom => Ok(Self::Bottom), + zwlr_layer_shell_v1::Layer::Top => Ok(Self::Top), + zwlr_layer_shell_v1::Layer::Overlay => Ok(Self::Overlay), + _ => Err(( + zwlr_layer_shell_v1::Error::InvalidLayer, + format!("invalid layer: {:?}", layer), + )), + } + } +} + +impl Default for Layer { + fn default() -> Self { + Self::Background + } +} + +/// Types of keyboard interaction possible for a layer shell surface +/// +/// The rationale for this is twofold: +/// - some applications are not interested in keyboard events +/// and not allowing them to be focused can improve the desktop experience +/// - some applications will want to take exclusive keyboard focus. +#[derive(Debug, Clone, Copy)] +pub enum KeyboardInteractivity { + /// This value indicates that this surface is not interested in keyboard events + /// and the compositor should never assign it the keyboard focus. + /// + /// This is the default value, set for newly created layer shell surfaces. + /// + /// This is useful for e.g. desktop widgets that display information + /// or only have interaction with non-keyboard input devices. + None, + /// Request exclusive keyboard focus if this surface is above the shell surface layer. + /// + /// For the top and overlay layers, the seat will always give exclusive keyboard focus + /// to the top-most layer which has keyboard interactivity set to exclusive. + /// If this layer contains multiple surfaces with keyboard interactivity set to exclusive, + /// the compositor determines the one receiving keyboard events in an implementation- defined manner. + /// In this case, no guarantee is made when this surface will receive keyboard focus (if ever). + /// + /// For the bottom and background layers, the compositor is allowed to use normal focus semantics. + /// + /// This setting is mainly intended for applications that need to + /// ensure they receive all keyboard events, such as a lock screen or a password prompt. + Exclusive, + /// This requests the compositor to allow this surface + /// to be focused and unfocused by the user in an implementation-defined manner. + /// The user should be able to unfocus this surface even regardless of the layer it is on. + /// + /// Typically, the compositor will want to use its normal mechanism + /// to manage keyboard focus between layer shell surfaces + /// with this setting and regular toplevels on the desktop layer (e.g. click to focus). + /// Nevertheless, it is possible for a compositor to require a special interaction + /// to focus or unfocus layer shell surfaces + /// + /// This setting is mainly intended for desktop shell components that allow keyboard interaction. + /// Using this option can allow implementing a desktop shell that can be fully usable without the mouse. + OnDemand, +} + +impl Default for KeyboardInteractivity { + fn default() -> Self { + Self::None + } +} + +impl TryFrom for KeyboardInteractivity { + type Error = (zwlr_layer_surface_v1::Error, String); + + fn try_from(ki: zwlr_layer_surface_v1::KeyboardInteractivity) -> Result { + match ki { + zwlr_layer_surface_v1::KeyboardInteractivity::None => Ok(Self::None), + zwlr_layer_surface_v1::KeyboardInteractivity::Exclusive => Ok(Self::Exclusive), + zwlr_layer_surface_v1::KeyboardInteractivity::OnDemand => Ok(Self::OnDemand), + _ => Err(( + zwlr_layer_surface_v1::Error::InvalidKeyboardInteractivity, + format!("wrong keyboard interactivity value: {:?}", ki), + )), + } + } +} + +bitflags::bitflags! { + /// Anchor bitflags, describing how the layers surface should be positioned and sized + pub struct Anchor: u32 { + /// The top edge of the anchor rectangle + const TOP = 1; + /// The bottom edge of the anchor rectangle + const BOTTOM = 2; + /// The left edge of the anchor rectangle + const LEFT = 4; + /// The right edge of the anchor rectangle + const RIGHT = 8; + } +} + +impl Anchor { + /// Check if anchored horizontally + /// + /// If it is anchored to `left` and `right` anchor at the same time + /// it returns `true` + pub fn anchored_horizontally(&self) -> bool { + self.contains(Self::LEFT) && self.contains(Self::RIGHT) + } + /// Check if anchored vertically + /// + /// If it is anchored to `top` and `bottom` anchor at the same time + /// it returns `true` + pub fn anchored_vertically(&self) -> bool { + self.contains(Self::TOP) && self.contains(Self::BOTTOM) + } +} + +impl Default for Anchor { + fn default() -> Self { + Self::empty() + } +} + +impl TryFrom for Anchor { + type Error = (zwlr_layer_surface_v1::Error, String); + + fn try_from(anchor: zwlr_layer_surface_v1::Anchor) -> Result { + match Anchor::from_bits(anchor.bits()) { + Some(a) => Ok(a), + _ => Err(( + zwlr_layer_surface_v1::Error::InvalidAnchor, + format!("invalid anchor {:?}", anchor), + )), + } + } +} + +/// Exclusive zone descriptor +#[derive(Debug, Clone, Copy)] +pub enum ExclusiveZone { + /// Requests that the compositor avoids occluding an area with other surfaces. + /// + /// A exclusive zone value is the distance from the edge in surface-local coordinates to consider exclusive. + /// + /// A exclusive value is only meaningful if the surface is + /// anchored to one edge or an edge and both perpendicular edges. + /// + /// If the surface is: + /// - not anchored + /// - anchored to only two perpendicular edges (a corner), + /// - anchored to only two parallel edges or anchored to all edges, + /// + /// The exclusive value should be treated the same as [`ExclusiveZone::Neutral`]. + Exclusive(u32), + /// If set to Neutral, + /// the surface indicates that it would like to be moved to avoid occluding surfaces with a exclusive zone. + Neutral, + /// If set to DontCare, + /// the surface indicates that it would not like to be moved to accommodate for other surfaces, + /// and the compositor should extend it all the way to the edges it is anchored to. + DontCare, +} + +impl Default for ExclusiveZone { + fn default() -> Self { + Self::Neutral + } +} + +impl From for ExclusiveZone { + fn from(v: i32) -> Self { + match v.cmp(&0) { + Ordering::Greater => Self::Exclusive(v as u32), + Ordering::Equal => Self::Neutral, + Ordering::Less => Self::DontCare, + } + } +} + +impl From for i32 { + fn from(z: ExclusiveZone) -> i32 { + match z { + ExclusiveZone::Exclusive(v) => v as i32, + ExclusiveZone::Neutral => 0, + ExclusiveZone::DontCare => -1, + } + } +} + +/// Describes distance from the anchor point of the output, in surface-local coordinates. +/// +/// If surface did not anchor curtain edge, margin for that edge should be ignored. +/// +/// The exclusive zone should includes the margins. +#[derive(Debug, Default, Clone, Copy)] +pub struct Margins { + /// Distance from [`Anchor::TOP`] + pub top: i32, + /// Distance from [`Anchor::TOP`] + pub right: i32, + /// Distance from [`Anchor::BOTTOM`] + pub bottom: i32, + /// Distance from [`Anchor::LEFT`] + pub left: i32, +} diff --git a/src/wayland/shell/xdg/mod.rs b/src/wayland/shell/xdg/mod.rs index b5e2bd0..6ddc174 100644 --- a/src/wayland/shell/xdg/mod.rs +++ b/src/wayland/shell/xdg/mod.rs @@ -89,7 +89,7 @@ use wayland_server::{ use super::PingError; // handlers for the xdg_shell protocol -mod xdg_handlers; +pub(super) mod xdg_handlers; // compatibility handlers for the zxdg_shell_v6 protocol, its earlier version mod zxdgv6_handlers;