use std::sync::Mutex; use wayland_protocols_wlr::layer_shell::v1::server::zwlr_layer_shell_v1::{self, ZwlrLayerShellV1}; use wayland_protocols_wlr::layer_shell::v1::server::zwlr_layer_surface_v1; use wayland_protocols_wlr::layer_shell::v1::server::zwlr_layer_surface_v1::ZwlrLayerSurfaceV1; use wayland_server::protocol::wl_surface; use wayland_server::{Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, Resource, Weak}; use crate::utils::{ alive_tracker::{AliveTracker, IsAlive}, Serial, }; use crate::wayland::shell::xdg::XdgPopupSurfaceData; use crate::wayland::{compositor, shell::wlr_layer::Layer}; use super::{ Anchor, KeyboardInteractivity, LayerSurfaceAttributes, LayerSurfaceCachedState, LayerSurfaceData, Margins, WlrLayerShellGlobalData, WlrLayerShellHandler, WlrLayerShellState, }; use super::LAYER_SURFACE_ROLE; /* * layer_shell */ impl GlobalDispatch for WlrLayerShellState where D: GlobalDispatch, D: Dispatch, D: Dispatch, D: WlrLayerShellHandler, D: 'static, { fn bind( _state: &mut D, _handle: &DisplayHandle, _client: &Client, resource: wayland_server::New, _global_data: &WlrLayerShellGlobalData, data_init: &mut DataInit<'_, D>, ) { data_init.init(resource, ()); } fn can_view(client: Client, global_data: &WlrLayerShellGlobalData) -> bool { (global_data.filter)(&client) } } impl Dispatch for WlrLayerShellState where D: Dispatch, D: Dispatch, D: WlrLayerShellHandler, D: 'static, { fn request( state: &mut D, _client: &Client, shell: &ZwlrLayerShellV1, request: zwlr_layer_shell_v1::Request, _data: &(), _dh: &DisplayHandle, data_init: &mut DataInit<'_, D>, ) { match request { zwlr_layer_shell_v1::Request::GetLayerSurface { id, surface: wl_surface, output, layer, namespace, } => { let layer: Layer = match layer.try_into() { Ok(layer) => layer, Err(layer) => { shell.post_error( zwlr_layer_shell_v1::Error::InvalidLayer, format!("invalid layer: {:?}", layer), ); return; } }; if compositor::give_role(&wl_surface, LAYER_SURFACE_ROLE).is_err() { shell.post_error(zwlr_layer_shell_v1::Error::Role, "Surface already has a role."); return; } let id = data_init.init( id, WlrLayerSurfaceUserData { shell_data: state.shell_state().clone(), wl_surface: wl_surface.downgrade(), alive_tracker: Default::default(), }, ); let initial = compositor::with_states(&wl_surface, |states| { let inserted = states .data_map .insert_if_missing_threadsafe(|| Mutex::new(LayerSurfaceAttributes::new(id.clone()))); if !inserted { let mut attributes = states .data_map .get::>() .unwrap() .lock() .unwrap(); attributes.surface = id.clone(); } states .cached_state .get::() .pending() .layer = layer; inserted }); if initial { compositor::add_pre_commit_hook::(&wl_surface, |_state, _dh, surface| { compositor::with_states(surface, |states| { let guard = states .data_map .get::>() .unwrap() .lock() .unwrap(); let mut cached_guard = states.cached_state.get::(); let pending = cached_guard.pending(); if pending.size.w == 0 && !pending.anchor.anchored_horizontally() { guard.surface.post_error( zwlr_layer_surface_v1::Error::InvalidSize, "width 0 requested without setting left and right anchors", ); return; } if pending.size.h == 0 && !pending.anchor.anchored_vertically() { guard.surface.post_error( zwlr_layer_surface_v1::Error::InvalidSize, "height 0 requested without setting top and bottom anchors", ); } }); }); compositor::add_post_commit_hook::(&wl_surface, |_state, _dh, surface| { compositor::with_states(surface, |states| { let mut guard = states .data_map .get::>() .unwrap() .lock() .unwrap(); if let Some(state) = guard.last_acked.clone() { guard.current = state; } }); }); } let handle = super::LayerSurface { wl_surface, shell_surface: id, }; state .shell_state() .known_layers .lock() .unwrap() .push(handle.clone()); WlrLayerShellHandler::new_layer_surface(state, handle, output, layer, namespace); } zwlr_layer_shell_v1::Request::Destroy => { // Handled by destructor } _ => {} } } } /* * layer_surface */ /// User data for wlr layer surface #[derive(Debug)] pub struct WlrLayerSurfaceUserData { shell_data: WlrLayerShellState, // `LayerSurfaceAttributes` stored in the surface `data_map`, contains a // `ZwlrLayerSurfaceV1`. So this reference needs to be weak to avoid a // cycle. wl_surface: Weak, alive_tracker: AliveTracker, } impl IsAlive for ZwlrLayerSurfaceV1 { #[inline] fn alive(&self) -> bool { let data: &WlrLayerSurfaceUserData = self.data().unwrap(); data.alive_tracker.alive() } } impl Dispatch for WlrLayerShellState where D: Dispatch, D: WlrLayerShellHandler, { fn request( state: &mut D, _client: &Client, layer_surface: &ZwlrLayerSurfaceV1, request: zwlr_layer_surface_v1::Request, data: &WlrLayerSurfaceUserData, _dh: &DisplayHandle, _data_init: &mut DataInit<'_, D>, ) { match request { zwlr_layer_surface_v1::Request::SetSize { width, height } => { let _ = 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) => { let _ = with_surface_pending_state(layer_surface, |data| { data.anchor = Anchor::from_bits(anchor.bits()).unwrap_or_default(); }); } Err((err, msg)) => { layer_surface.post_error(err, msg); } }; } zwlr_layer_surface_v1::Request::SetExclusiveZone { zone } => { let _ = with_surface_pending_state(layer_surface, |data| { data.exclusive_zone = zone.into(); }); } zwlr_layer_surface_v1::Request::SetMargin { top, right, bottom, left, } => { let _ = 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) => { let _ = with_surface_pending_state(layer_surface, |data| { data.keyboard_interactivity = keyboard_interactivity; }); } Err((err, msg)) => { layer_surface.post_error(err, msg); } }; } zwlr_layer_surface_v1::Request::SetLayer { layer } => { match Layer::try_from(layer) { Ok(layer) => { let _ = with_surface_pending_state(layer_surface, |data| { data.layer = layer; }); } Err((err, msg)) => { layer_surface.post_error(err, msg); } }; } zwlr_layer_surface_v1::Request::GetPopup { popup } => { let Ok(parent_surface) = data.wl_surface.upgrade() else { return; }; let data = popup .data::() .unwrap(); compositor::with_states(&data.wl_surface, move |states| { states .data_map .get::() .unwrap() .lock() .unwrap() .parent = Some(parent_surface); }); WlrLayerShellHandler::new_popup( state, make_surface_handle(layer_surface).unwrap(), crate::wayland::shell::xdg::handlers::make_popup_handle(&popup), ); } zwlr_layer_surface_v1::Request::AckConfigure { serial } => { let Ok(surface) = data.wl_surface.upgrade() else { return; }; let serial = Serial::from(serial); let found_configure = compositor::with_states(&surface, |states| { states .data_map .get::() .unwrap() .lock() .unwrap() .ack_configure(serial) }); let configure = match found_configure { Some(configure) => configure, None => { layer_surface.post_error( zwlr_layer_surface_v1::Error::InvalidSurfaceState, format!("wrong configure serial: {}", ::from(serial)), ); return; } }; WlrLayerShellHandler::ack_configure(state, surface, configure); } _ => {} } } fn destroyed( state: &mut D, _client_id: wayland_server::backend::ClientId, layer_surface: &ZwlrLayerSurfaceV1, data: &WlrLayerSurfaceUserData, ) { data.alive_tracker.destroy_notify(); // remove this surface from the known ones (as well as any leftover dead surface) let mut layers = data.shell_data.known_layers.lock().unwrap(); if let Some(index) = layers .iter() .position(|layer| layer.shell_surface.id() == layer_surface.id()) { let layer = layers.remove(index); drop(layers); let surface = layer.wl_surface().clone(); WlrLayerShellHandler::layer_destroyed(state, layer); compositor::with_states(&surface, |states| { let mut attributes = states .data_map .get::>() .unwrap() .lock() .unwrap(); attributes.reset(); let mut guard = states.cached_state.get::(); *guard.pending() = Default::default(); *guard.current() = Default::default(); }); } } } fn with_surface_pending_state( layer_surface: &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, f: F, ) -> Result where F: FnOnce(&mut LayerSurfaceCachedState) -> T, { let data = layer_surface.data::().unwrap(); let surface = data.wl_surface.upgrade()?; Ok(compositor::with_states(&surface, |states| { f(states.cached_state.get::().pending()) })) } pub fn make_surface_handle( resource: &zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, ) -> Result { let data = resource.data::().unwrap(); let wl_surface = data.wl_surface.upgrade()?; Ok(crate::wayland::shell::wlr_layer::LayerSurface { wl_surface, shell_surface: resource.clone(), }) }