From 1fdff156ecae9ab0fd169ce29af227c98a10236a Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 6 Feb 2022 16:34:20 +0100 Subject: [PATCH] autocommit 2022-02-06 16:34:20 CET --- src/ifs/wl_surface/xdg_surface.rs | 482 ++++++++++++++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 src/ifs/wl_surface/xdg_surface.rs diff --git a/src/ifs/wl_surface/xdg_surface.rs b/src/ifs/wl_surface/xdg_surface.rs new file mode 100644 index 00000000..0b948709 --- /dev/null +++ b/src/ifs/wl_surface/xdg_surface.rs @@ -0,0 +1,482 @@ +pub mod xdg_popup; +pub mod xdg_toplevel; + +use crate::backend::SeatId; +use crate::client::{ClientError, DynEventFormatter}; +use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal}; +use crate::ifs::wl_surface::xdg_surface::xdg_popup::{XdgPopup, XdgPopupError}; +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; +use thiserror::Error; +use crate::wire::xdg_surface::*; +use crate::utils::buffd::MsgParserError; +use crate::wire::{WlSurfaceId, XdgPopupId, XdgSurfaceId}; + +#[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", + } + } +} + +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 { + self_id: self.id, + 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(); + } +} + +#[derive(Debug, Error)] +pub enum XdgSurfaceError { + #[error("Could not process `destroy` request")] + DestroyError(#[from] DestroyError), + #[error("Could not process `get_toplevel` request")] + GetToplevelError(#[from] GetToplevelError), + #[error("Could not process `get_popup` request")] + GetPopupError(#[from] GetPopupError), + #[error("Could not process `set_window_geometry` request")] + SetWindowGeometryError(#[from] SetWindowGeometryError), + #[error("Could not process `ack_configure` request")] + AckConfigureError(#[from] AckConfigureError), + #[error("Surface {0} cannot be turned into a xdg_surface because it already has an attached xdg_surface")] + AlreadyAttached(WlSurfaceId), + #[error(transparent)] + WlSurfaceError(Box), + #[error(transparent)] + XdgPopupError(#[from] XdgPopupError), + #[error("Surface {} cannot be assigned the role {} because it already has the role {}", .id, .new.name(), .old.name())] + IncompatibleRole { + id: XdgSurfaceId, + old: XdgSurfaceRole, + new: XdgSurfaceRole, + }, +} +efrom!(XdgSurfaceError, WlSurfaceError); + +#[derive(Debug, Error)] +pub enum DestroyError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), + #[error("Cannot destroy xdg_surface {0} because it's associated xdg_toplevel/popup is not yet destroyed")] + RoleNotYetDestroyed(XdgSurfaceId), + #[error("The surface still has popups attached")] + PopupsNotYetDestroyed, +} +efrom!(DestroyError, ParseFailed, MsgParserError); +efrom!(DestroyError, ClientError); + +#[derive(Debug, Error)] +pub enum GetToplevelError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), + #[error("The surface already has an assigned xdg_toplevel")] + AlreadyConstructed, + #[error(transparent)] + WlSurfaceError(Box), + #[error(transparent)] + XdgSurfaceError(Box), +} +efrom!(GetToplevelError, ParseFailed, MsgParserError); +efrom!(GetToplevelError, ClientError); +efrom!(GetToplevelError, WlSurfaceError); +efrom!(GetToplevelError, XdgSurfaceError); + +#[derive(Debug, Error)] +pub enum GetPopupError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), + #[error("The surface already has an assigned xdg_popup")] + AlreadyConstructed, + #[error(transparent)] + WlSurfaceError(Box), + #[error(transparent)] + XdgPopupError(Box), + #[error(transparent)] + XdgSurfaceError(Box), +} +efrom!(GetPopupError, ParseFailed, MsgParserError); +efrom!(GetPopupError, ClientError); +efrom!(GetPopupError, XdgPopupError); +efrom!(GetPopupError, WlSurfaceError); +efrom!(GetPopupError, XdgSurfaceError); + +#[derive(Debug, Error)] +pub enum SetWindowGeometryError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), + #[error("Tried no set a non-positive width/height")] + NonPositiveWidthHeight, +} +efrom!(SetWindowGeometryError, ParseFailed, MsgParserError); +efrom!(SetWindowGeometryError, ClientError); + +#[derive(Debug, Error)] +pub enum AckConfigureError { + #[error("Parsing failed")] + ParseFailed(#[source] Box), + #[error(transparent)] + ClientError(Box), +} +efrom!(AckConfigureError, ParseFailed, MsgParserError); +efrom!(AckConfigureError, ClientError);