From a5fd78e05c90d4aceefa0c6d641c331464310e8e Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Mon, 16 Mar 2026 17:14:47 +0100 Subject: [PATCH] wayland: implement wl_display_upgrade --- src/client.rs | 2 + src/client/objects.rs | 4 ++ src/globals.rs | 2 + src/ifs.rs | 1 + src/ifs/wl_display.rs | 15 ++++- src/ifs/wl_display_upgrade.rs | 105 ++++++++++++++++++++++++++++++++++ src/ifs/wl_registry.rs | 16 +++++- wire/wl_display_upgrade.txt | 7 +++ wire/wl_registry.txt | 3 + 9 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 src/ifs/wl_display_upgrade.rs create mode 100644 wire/wl_display_upgrade.txt diff --git a/src/client.rs b/src/client.rs index a1fa33cb1..9ef60819d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -218,6 +218,7 @@ impl Clients { changed_properties: Default::default(), destroyed: Default::default(), acceptor: acceptor.clone(), + has_display_upgrade: Default::default(), }); track!(data, data); global.update_capabilities(&data, bounding_caps, set_bounding_caps_for_children); @@ -345,6 +346,7 @@ pub struct Client { pub changed_properties: Cell, pub destroyed: CopyHashMap>>>, pub acceptor: Rc, + pub has_display_upgrade: Cell, } pub const NUM_CACHED_SERIAL_RANGES: usize = 64; diff --git a/src/client/objects.rs b/src/client/objects.rs index d4449f849..15ac3abc9 100644 --- a/src/client/objects.rs +++ b/src/client/objects.rs @@ -148,6 +148,10 @@ impl Objects { } } + pub fn len(&self) -> usize { + self.registry.len() + } + pub fn destroy(&self) { for surface in self.surfaces.lock().values() { if let Some(tl) = surface.get_toplevel() { diff --git a/src/globals.rs b/src/globals.rs index ad4b64bec..477939cd6 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -23,6 +23,7 @@ use { jay_popup_ext_manager_v1::JayPopupExtManagerV1Global, org_kde_kwin_server_decoration_manager::OrgKdeKwinServerDecorationManagerGlobal, wl_compositor::WlCompositorGlobal, + wl_display_upgrade::WlDisplayUpgradeGlobal, wl_drm::WlDrmGlobal, wl_fixes::WlFixesGlobal, wl_output::WlOutputGlobal, @@ -248,6 +249,7 @@ singletons! { ZwpLinuxDmabufV1, WpLinuxDrmSyncobjManagerV1, WpPresentation, + WlDisplayUpgrade, } pub struct Globals { diff --git a/src/ifs.rs b/src/ifs.rs index f11689083..bf416ca60 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -45,6 +45,7 @@ pub mod wl_buffer; pub mod wl_callback; pub mod wl_compositor; pub mod wl_display; +pub mod wl_display_upgrade; pub mod wl_drm; pub mod wl_fixes; pub mod wl_output; diff --git a/src/ifs/wl_display.rs b/src/ifs/wl_display.rs index f87189559..449de9b47 100644 --- a/src/ifs/wl_display.rs +++ b/src/ifs/wl_display.rs @@ -6,7 +6,7 @@ use { object::{Object, ObjectId, Version, WL_DISPLAY_ID}, wire::{WlDisplayId, wl_display::*}, }, - std::rc::Rc, + std::{cell::Cell, rc::Rc}, thiserror::Error, }; @@ -16,10 +16,14 @@ const INVALID_METHOD: u32 = 1; const NO_MEMORY: u32 = 2; const IMPLEMENTATION: u32 = 3; +pub const MIN_DISPLAY_VERSION: Version = Version(1); +pub const MAX_DISPLAY_VERSION: Version = Version(2); + pub struct WlDisplay { pub id: WlDisplayId, pub client: Rc, pub tracker: Tracker, + pub version: Cell, } impl WlDisplay { @@ -28,6 +32,7 @@ impl WlDisplay { id: WL_DISPLAY_ID, client: client.clone(), tracker: Default::default(), + version: Cell::new(MIN_DISPLAY_VERSION), } } } @@ -45,7 +50,11 @@ impl WlDisplayRequestHandler for WlDisplay { } fn get_registry(&self, req: GetRegistry, _slf: &Rc) -> Result<(), Self::Error> { - let registry = Rc::new(WlRegistry::new(req.registry, &self.client)); + let registry = Rc::new(WlRegistry::new( + req.registry, + self.version.get(), + &self.client, + )); track!(self.client, registry); self.client.add_client_obj(®istry)?; self.client.state.globals.notify_all(®istry); @@ -93,7 +102,7 @@ impl WlDisplay { object_base! { self = WlDisplay; - version = Version(1); + version = self.version.get(); } impl Object for WlDisplay {} diff --git a/src/ifs/wl_display_upgrade.rs b/src/ifs/wl_display_upgrade.rs new file mode 100644 index 000000000..dcef11113 --- /dev/null +++ b/src/ifs/wl_display_upgrade.rs @@ -0,0 +1,105 @@ +use { + crate::{ + client::{Client, ClientError}, + globals::{Global, GlobalName}, + ifs::wl_display::{MAX_DISPLAY_VERSION, MIN_DISPLAY_VERSION}, + leaks::Tracker, + object::{Object, Version}, + wire::{WlDisplayUpgradeId, wl_display_upgrade::*}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct WlDisplayUpgradeGlobal { + pub name: GlobalName, +} + +impl WlDisplayUpgradeGlobal { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + fn bind_( + self: Rc, + id: WlDisplayUpgradeId, + client: &Rc, + version: Version, + ) -> Result<(), WlDisplayUpgradeError> { + if client.has_display_upgrade.replace(true) { + return Err(WlDisplayUpgradeError::HasDisplayUpgrade); + } + let mgr = Rc::new(WlDisplayUpgrade { + id, + client: client.clone(), + tracker: Default::default(), + version, + }); + track!(client, mgr); + client.add_client_obj(&mgr)?; + client.event(MaxVersion { + self_id: id, + version: MAX_DISPLAY_VERSION.0, + }); + Ok(()) + } +} + +global_base!( + WlDisplayUpgradeGlobal, + WlDisplayUpgrade, + WlDisplayUpgradeError +); + +simple_add_global!(WlDisplayUpgradeGlobal); + +impl Global for WlDisplayUpgradeGlobal { + fn version(&self) -> u32 { + 1 + } +} + +pub struct WlDisplayUpgrade { + pub id: WlDisplayUpgradeId, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, +} + +impl WlDisplayUpgradeRequestHandler for WlDisplayUpgrade { + type Error = WlDisplayUpgradeError; + + fn upgrade(&self, req: Upgrade, _slf: &Rc) -> Result<(), Self::Error> { + self.client.remove_obj(self)?; + if self.client.objects.len() > 1 { + return Err(WlDisplayUpgradeError::HasObjects); + } + if req.version < MIN_DISPLAY_VERSION.0 || req.version > MAX_DISPLAY_VERSION.0 { + return Err(WlDisplayUpgradeError::OutOfBounds); + } + self.client.display()?.version.set(Version(req.version)); + Ok(()) + } +} + +object_base! { + self = WlDisplayUpgrade; + version = self.version; +} + +impl Object for WlDisplayUpgrade {} + +simple_add_obj!(WlDisplayUpgrade); + +#[derive(Debug, Error)] +pub enum WlDisplayUpgradeError { + #[error("Tried to bind wl_display_upgrade more than once")] + HasDisplayUpgrade, + #[error("Tried to upgrade with existing objects")] + HasObjects, + #[error("The requested version is out of bounds")] + OutOfBounds, + #[error(transparent)] + ClientError(Box), +} +efrom!(WlDisplayUpgradeError, ClientError); diff --git a/src/ifs/wl_registry.rs b/src/ifs/wl_registry.rs index f0dfda676..185ebd879 100644 --- a/src/ifs/wl_registry.rs +++ b/src/ifs/wl_registry.rs @@ -1,6 +1,6 @@ use { crate::{ - client::Client, + client::{Client, ClientError}, globals::{Global, GlobalName, GlobalsError, Singleton}, leaks::Tracker, object::{Interface, Object, Version}, @@ -15,15 +15,17 @@ pub struct WlRegistry { id: WlRegistryId, pub client: Rc, pub tracker: Tracker, + pub version: Version, advertised: StaticMap>, } impl WlRegistry { - pub fn new(id: WlRegistryId, client: &Rc) -> Self { + pub fn new(id: WlRegistryId, version: Version, client: &Rc) -> Self { Self { id, client: client.clone(), tracker: Default::default(), + version, advertised: Default::default(), } } @@ -84,11 +86,16 @@ impl WlRegistryRequestHandler for WlRegistry { global.bind(&self.client, bind.id, Version(bind.version))?; Ok(()) } + + fn release(&self, _req: Release, _slf: &Rc) -> Result<(), Self::Error> { + self.client.remove_obj(self)?; + Ok(()) + } } object_base! { self = WlRegistry; - version = Version(1); + version = self.version; } impl Object for WlRegistry {} @@ -97,6 +104,8 @@ dedicated_add_obj!(WlRegistry, WlRegistryId, registries); #[derive(Debug, Error)] pub enum WlRegistryError { + #[error(transparent)] + ClientError(Box), #[error(transparent)] GlobalsError(Box), #[error("Tried to bind to global {} of type {} using interface {}", .0.name, .0.interface.name(), .0.actual)] @@ -105,6 +114,7 @@ pub enum WlRegistryError { InvalidVersion(VersionError), } efrom!(WlRegistryError, GlobalsError); +efrom!(WlRegistryError, ClientError); #[derive(Debug)] pub struct InterfaceError { diff --git a/wire/wl_display_upgrade.txt b/wire/wl_display_upgrade.txt new file mode 100644 index 000000000..0a220e27a --- /dev/null +++ b/wire/wl_display_upgrade.txt @@ -0,0 +1,7 @@ +event max_version { + version: u32, +} + +request upgrade (destructor) { + version: u32, +} diff --git a/wire/wl_registry.txt b/wire/wl_registry.txt index 64433d32c..429a8c861 100644 --- a/wire/wl_registry.txt +++ b/wire/wl_registry.txt @@ -14,3 +14,6 @@ event global { event global_remove { name: u32, } + +request release (destructor, since = 2) { +}