mod types; pub mod xdg_popup; pub mod xdg_toplevel; use crate::backend::SeatId; use crate::client::DynEventFormatter; use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal}; use crate::ifs::wl_surface::xdg_surface::xdg_popup::{XdgPopup, XdgPopupId}; use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel; use crate::ifs::wl_surface::{ CommitAction, CommitContext, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError, }; use crate::ifs::xdg_wm_base::XdgWmBase; use crate::object::Object; use crate::rect::Rect; use crate::tree::{FindTreeResult, FoundNode, Node, WorkspaceNode}; use crate::utils::buffd::MsgParser; use crate::utils::clonecell::CloneCell; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::smallmap::SmallMap; use crate::NumCell; use std::cell::Cell; use std::rc::Rc; pub use types::*; const DESTROY: u32 = 0; const GET_TOPLEVEL: u32 = 1; const GET_POPUP: u32 = 2; const SET_WINDOW_GEOMETRY: u32 = 3; const ACK_CONFIGURE: u32 = 4; const CONFIGURE: u32 = 0; #[allow(dead_code)] const NOT_CONSTRUCTED: u32 = 1; const ALREADY_CONSTRUCTED: u32 = 2; #[allow(dead_code)] const UNCONFIGURED_BUFFER: u32 = 3; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum XdgSurfaceRole { None, XdgPopup, XdgToplevel, } impl XdgSurfaceRole { fn name(self) -> &'static str { match self { XdgSurfaceRole::None => "none", XdgSurfaceRole::XdgPopup => "xdg_popup", XdgSurfaceRole::XdgToplevel => "xdg_toplevel", } } } id!(XdgSurfaceId); pub struct XdgSurface { id: XdgSurfaceId, base: Rc, role: Cell, pub surface: Rc, requested_serial: NumCell, acked_serial: Cell>, geometry: Cell>, extents: Cell, pub absolute_desired_extents: Cell, ext: CloneCell>>, popups: CopyHashMap>, pending: PendingXdgSurfaceData, pub(super) focus_surface: SmallMap, 1>, seat_state: NodeSeatState, pub workspace: CloneCell>>, } #[derive(Default)] struct PendingXdgSurfaceData { geometry: Cell>, } trait XdgSurfaceExt { fn initial_configure(self: Rc) -> Result<(), XdgSurfaceError> { Ok(()) } fn post_commit(self: Rc) { // nothing } fn into_node(self: Rc) -> Option> { None } fn extents_changed(&self) { // nothing } fn surface_active_changed(self: Rc, active: bool) { let _ = active; } } impl XdgSurface { pub fn new(wm_base: &Rc, id: XdgSurfaceId, surface: &Rc) -> Self { Self { id, base: wm_base.clone(), role: Cell::new(XdgSurfaceRole::None), surface: surface.clone(), requested_serial: NumCell::new(0), acked_serial: Cell::new(None), geometry: Cell::new(None), extents: Cell::new(Default::default()), absolute_desired_extents: Cell::new(Default::default()), ext: Default::default(), popups: Default::default(), pending: Default::default(), focus_surface: Default::default(), seat_state: Default::default(), workspace: Default::default(), } } fn set_absolute_desired_extents(&self, ext: &Rect) { let prev = self.absolute_desired_extents.replace(*ext); if ext.x1() != prev.x1() || ext.y1() != prev.y1() { let (mut x1, mut y1) = (ext.x1(), ext.y1()); if let Some(geo) = self.geometry.get() { x1 -= geo.x1(); y1 -= geo.y1(); } self.surface.set_absolute_position(x1, y1); self.update_popup_positions(); } } pub fn surface_active_changed(&self, active: bool) { if let Some(ext) = self.ext.get() { ext.surface_active_changed(active); } } pub fn role(&self) -> XdgSurfaceRole { self.role.get() } fn set_workspace(&self, ws: &Rc) { self.workspace.set(Some(ws.clone())); let pu = self.popups.lock(); for pu in pu.values() { pu.xdg.set_workspace(ws); } } fn set_role(&self, role: XdgSurfaceRole) -> Result<(), XdgSurfaceError> { use XdgSurfaceRole::*; match (self.role.get(), role) { (None, _) => {} (old, new) if old == new => {} (old, new) => { return Err(XdgSurfaceError::IncompatibleRole { id: self.id, old, new, }) } } self.role.set(role); Ok(()) } pub fn focus_surface(&self, seat: &WlSeatGlobal) -> Rc { self.focus_surface .get(&seat.id()) .unwrap_or_else(|| self.surface.clone()) } fn destroy_node(&self) { self.workspace.set(None); self.surface.destroy_node(false); let popups = self.popups.lock(); for popup in popups.values() { popup.destroy_node(true); } } pub fn geometry(&self) -> Option { self.geometry.get() } pub fn send_configure(self: &Rc) { let serial = self.requested_serial.fetch_add(1) + 1; self.surface.client.event(self.configure(serial)); } pub fn configure(self: &Rc, serial: u32) -> DynEventFormatter { Box::new(Configure { obj: self.clone(), serial, }) } pub fn install(self: &Rc) -> Result<(), XdgSurfaceError> { self.surface.set_role(SurfaceRole::XdgSurface)?; if self.surface.ext.get().is_some() { return Err(XdgSurfaceError::AlreadyAttached(self.surface.id)); } self.surface.ext.set(self.clone()); self.surface.set_xdg_surface(Some(self.clone())); Ok(()) } fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> { let _req: Destroy = self.surface.client.parse(self, parser)?; if self.ext.get().is_some() { return Err(DestroyError::RoleNotYetDestroyed(self.id)); } { let children = self.popups.lock(); if !children.is_empty() { return Err(DestroyError::PopupsNotYetDestroyed); } } self.surface.set_xdg_surface(None); self.surface.unset_ext(); self.base.surfaces.remove(&self.id); self.surface.client.remove_obj(self)?; Ok(()) } fn get_toplevel(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), GetToplevelError> { let req: GetToplevel = self.surface.client.parse(&**self, parser)?; self.set_role(XdgSurfaceRole::XdgToplevel)?; if self.ext.get().is_some() { self.surface.client.protocol_error( &**self, ALREADY_CONSTRUCTED, format!( "wl_surface {} already has an assigned xdg_toplevel", self.surface.id ), ); return Err(GetToplevelError::AlreadyConstructed); } let toplevel = Rc::new(XdgToplevel::new(req.id, self)); self.surface.client.add_client_obj(&toplevel)?; self.ext.set(Some(toplevel)); Ok(()) } fn get_popup(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), GetPopupError> { let req: GetPopup = self.surface.client.parse(&**self, parser)?; self.set_role(XdgSurfaceRole::XdgPopup)?; let mut parent = None; if req.parent.is_some() { parent = Some(self.surface.client.lookup(req.parent)?); } let positioner = self.surface.client.lookup(req.positioner)?; if self.ext.get().is_some() { self.surface.client.protocol_error( &**self, ALREADY_CONSTRUCTED, format!( "wl_surface {} already has an assigned xdg_popup", self.surface.id ), ); return Err(GetPopupError::AlreadyConstructed); } let popup = Rc::new(XdgPopup::new(req.id, self, parent.as_ref(), &positioner)?); self.surface.client.add_client_obj(&popup)?; if let Some(parent) = &parent { parent.popups.set(req.id, popup.clone()); } self.ext.set(Some(popup)); Ok(()) } fn set_window_geometry(&self, parser: MsgParser<'_, '_>) -> Result<(), SetWindowGeometryError> { let req: SetWindowGeometry = self.surface.client.parse(self, parser)?; if req.height <= 0 || req.width <= 0 { return Err(SetWindowGeometryError::NonPositiveWidthHeight); } let extents = Rect::new_sized(req.x, req.y, req.width, req.height).unwrap(); self.pending.geometry.set(Some(extents)); Ok(()) } fn ack_configure(&self, parser: MsgParser<'_, '_>) -> Result<(), AckConfigureError> { let req: AckConfigure = self.surface.client.parse(self, parser)?; if self.requested_serial.get() == req.serial { self.acked_serial.set(Some(req.serial)); } Ok(()) } fn update_extents(&self) { let old_extents = self.extents.get(); let mut new_extents = self.surface.extents.get(); if let Some(geometry) = self.geometry.get() { new_extents = new_extents.intersect(geometry); } self.extents.set(new_extents); if old_extents != new_extents { if let Some(ext) = self.ext.get() { ext.extents_changed(); } } } fn find_tree_at(&self, mut x: i32, mut y: i32, tree: &mut Vec) -> FindTreeResult { if let Some(geo) = self.geometry.get() { let (xt, yt) = geo.translate_inv(x, y); x = xt; y = yt; } match self.surface.find_surface_at(x, y) { Some((node, x, y)) => { tree.push(FoundNode { node, x, y }); FindTreeResult::AcceptsInput } _ => FindTreeResult::Other, } } fn update_popup_positions(&self) { let popups = self.popups.lock(); for popup in popups.values() { popup.update_absolute_position(); } } } object_base! { XdgSurface, XdgSurfaceError; DESTROY => destroy, GET_TOPLEVEL => get_toplevel, GET_POPUP => get_popup, SET_WINDOW_GEOMETRY => set_window_geometry, ACK_CONFIGURE => ack_configure, } impl Object for XdgSurface { fn num_requests(&self) -> u32 { ACK_CONFIGURE + 1 } fn break_loops(&self) { self.focus_surface.take(); } } dedicated_add_obj!(XdgSurface, XdgSurfaceId, xdg_surfaces); impl SurfaceExt for XdgSurface { fn pre_commit(self: Rc, _ctx: CommitContext) -> Result { { let ase = self.acked_serial.get(); let rse = self.requested_serial.get(); if ase != Some(rse) { if ase.is_none() { if let Some(ext) = self.ext.get() { ext.initial_configure()?; } self.surface.client.event(self.configure(rse)); } // return CommitAction::AbortCommit; } } if let Some(geometry) = self.pending.geometry.take() { self.geometry.set(Some(geometry)); self.update_extents(); } Ok(CommitAction::ContinueCommit) } fn post_commit(&self) { if let Some(ext) = self.ext.get() { ext.post_commit(); } } fn extents_changed(&self) { self.update_extents(); } }