wayland.shell: implement wlr_layer_shell

This commit is contained in:
Poly 2021-07-31 03:51:35 +02:00
parent 23a8af399d
commit 84d3d6e609
5 changed files with 986 additions and 1 deletions

View File

@ -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)]

View File

@ -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<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
request: zwlr_layer_shell_v1::Request,
dispatch_data: DispatchData<'_>,
) {
let data = shell.as_ref().user_data().get::<ShellUserData>().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::<LayerSurfaceCachedState>().layer = layer;
})
.unwrap();
compositor::add_commit_hook(&surface, |surface| {
compositor::with_states(surface, |states| {
let mut guard = states
.data_map
.get::<Mutex<LayerSurfaceAttributes>>()
.unwrap()
.lock()
.unwrap();
let pending = states.cached_state.pending::<LayerSurfaceCachedState>();
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::<LayerSurfaceUserData>()
.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::<LayerSurfaceUserData>()
.unwrap();
let parent_surface = data.wl_surface.clone();
let data = popup
.as_ref()
.user_data()
.get::<crate::wayland::shell::xdg::xdg_handlers::ShellSurfaceUserData>()
.unwrap();
compositor::with_states(&data.wl_surface, move |states| {
states
.data_map
.get::<Mutex<crate::wayland::shell::xdg::XdgPopupSurfaceRoleAttributes>>()
.unwrap()
.lock()
.unwrap()
.parent = Some(parent_surface);
})
.unwrap();
}
zwlr_layer_surface_v1::Request::AckConfigure { serial } => {
let data = layer_surface
.as_ref()
.user_data()
.get::<LayerSurfaceUserData>()
.unwrap();
let serial = Serial::from(serial);
let surface = &data.wl_surface;
let found_configure = compositor::with_states(surface, |states| {
states
.data_map
.get::<Mutex<LayerSurfaceAttributes>>()
.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: {}", <u32>::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<F, T>(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::<LayerSurfaceUserData>()
.unwrap();
compositor::with_states(&data.wl_surface, |states| {
f(&mut *states.cached_state.pending::<LayerSurfaceCachedState>())
})
.unwrap()
}

View File

@ -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<Serial>,
/// 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<LayerSurfaceConfigure>,
/// Holds the pending state as set by the server.
pub server_pending: Option<LayerSurfaceState>,
/// 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<LayerSurfaceState>,
/// 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<LayerSurfaceConfigure> {
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<Size<i32, Logical>>,
}
/// Represents the client pending state
#[derive(Debug, Default, Clone, Copy)]
pub struct LayerSurfaceCachedState {
/// The size requested by the client
pub size: Size<i32, Logical>,
/// 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<LayerSurface>,
}
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<RefCell<dyn FnMut(LayerShellRequest, DispatchData<'_>)>>,
shell_state: Arc<Mutex<LayerShellState>>,
}
/// Create a new `wlr_layer_shell` globals
pub fn wlr_layer_shell_init<L, Impl>(
display: &mut Display,
implementation: Impl,
logger: L,
) -> (
Arc<Mutex<LayerShellState>>,
Global<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
)
where
L: Into<Option<::slog::Logger>>,
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<zwlr_layer_shell_v1::ZwlrLayerShellV1>, _), _, _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<LayerSurfaceState> {
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::<Mutex<LayerSurfaceAttributes>>()
.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::<Mutex<LayerSurfaceAttributes>>()
.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<F, T>(&self, f: F) -> Result<T, DeadResource>
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::<Mutex<LayerSurfaceAttributes>>()
.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<LayerSurfaceState> {
if !self.alive() {
return None;
}
Some(
compositor::with_states(&self.wl_surface, |states| {
let attributes = states
.data_map
.get::<Mutex<LayerSurfaceAttributes>>()
.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<WlOutput>,
/// 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,
},
}

View File

@ -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<zwlr_layer_shell_v1::Layer> for Layer {
type Error = (zwlr_layer_shell_v1::Error, String);
fn try_from(layer: zwlr_layer_shell_v1::Layer) -> Result<Self, Self::Error> {
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<zwlr_layer_surface_v1::KeyboardInteractivity> for KeyboardInteractivity {
type Error = (zwlr_layer_surface_v1::Error, String);
fn try_from(ki: zwlr_layer_surface_v1::KeyboardInteractivity) -> Result<Self, Self::Error> {
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<zwlr_layer_surface_v1::Anchor> for Anchor {
type Error = (zwlr_layer_surface_v1::Error, String);
fn try_from(anchor: zwlr_layer_surface_v1::Anchor) -> Result<Self, Self::Error> {
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<i32> 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<ExclusiveZone> 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,
}

View File

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