diff --git a/wayland-backend/CHANGELOG.md b/wayland-backend/CHANGELOG.md index 88b8a0c6006..f7d39bf04c9 100644 --- a/wayland-backend/CHANGELOG.md +++ b/wayland-backend/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +### Additions +- backend: Added a `destroy_object` method + ## 0.3.10 -- 2025-04-29 #### Bugfixes diff --git a/wayland-backend/src/client_api.rs b/wayland-backend/src/client_api.rs index f28f9eeaeb1..5c817595e04 100644 --- a/wayland-backend/src/client_api.rs +++ b/wayland-backend/src/client_api.rs @@ -214,6 +214,17 @@ impl Backend { self.backend.info(id) } + /// Destroy an object + /// + /// For most protocols, this is handled automatically when a destructor + /// message is sent or received. + /// + /// This corresponds to `wl_proxy_destroy` in the C API. Or a `_destroy` + /// method generated for an object without a destructor request. + pub fn destroy_object(&self, id: &ObjectId) -> Result<(), InvalidId> { + self.backend.destroy_object(id) + } + /// Sends a request to the server /// /// Returns an error if the sender ID of the provided message is no longer valid. diff --git a/wayland-backend/src/rs/client_impl/mod.rs b/wayland-backend/src/rs/client_impl/mod.rs index 24c8b1d1981..d507a5cf2e7 100644 --- a/wayland-backend/src/rs/client_impl/mod.rs +++ b/wayland-backend/src/rs/client_impl/mod.rs @@ -298,6 +298,19 @@ impl InnerBackend { ObjectId { id: InnerObjectId { serial: 0, id: 0, interface: &ANONYMOUS_INTERFACE } } } + pub fn destroy_object(&self, id: &ObjectId) -> Result<(), InvalidId> { + let mut guard = self.state.lock_protocol(); + let object = guard.get_object(id.id.clone())?; + guard + .map + .with(id.id.id, |obj| { + obj.data.client_destroyed = true; + }) + .unwrap(); + object.data.user_data.destroyed(id.clone()); + Ok(()) + } + pub fn send_request( &self, Message { sender_id: ObjectId { id }, opcode, args }: Message, diff --git a/wayland-backend/src/rs/server_impl/client.rs b/wayland-backend/src/rs/server_impl/client.rs index 39a37ec6b94..581b609b630 100644 --- a/wayland-backend/src/rs/server_impl/client.rs +++ b/wayland-backend/src/rs/server_impl/client.rs @@ -101,6 +101,17 @@ impl Client { InnerObjectId { id, serial, client_id: self.id.clone(), interface } } + pub(crate) fn destroy_object( + &mut self, + id: InnerObjectId, + pending_destructors: &mut Vec>, + ) -> Result<(), InvalidId> { + let object = self.get_object(id.clone())?; + pending_destructors.push((object.data.user_data.clone(), self.id.clone(), id.clone())); + self.send_delete_id(id.clone()); + Ok(()) + } + pub(crate) fn object_info(&self, id: InnerObjectId) -> Result { let object = self.get_object(id.clone())?; Ok(ObjectInfo { id: id.id, interface: object.interface, version: object.version }) @@ -201,7 +212,6 @@ impl Client { // Handle destruction if relevant if message_desc.is_destructor { - self.map.remove(object_id.id.id); if let Some(vec) = pending_destructors { vec.push((object.data.user_data.clone(), self.id.clone(), object_id.id.clone())); } @@ -378,7 +388,7 @@ impl Client { } } - fn get_object(&self, id: InnerObjectId) -> Result>, InvalidId> { + pub(crate) fn get_object(&self, id: InnerObjectId) -> Result>, InvalidId> { let object = self.map.find(id.id).ok_or(InvalidId)?; if object.data.serial != id.serial { return Err(InvalidId); diff --git a/wayland-backend/src/rs/server_impl/handle.rs b/wayland-backend/src/rs/server_impl/handle.rs index d48ed189a8e..5c966cb6d6c 100644 --- a/wayland-backend/src/rs/server_impl/handle.rs +++ b/wayland-backend/src/rs/server_impl/handle.rs @@ -165,6 +165,15 @@ impl InnerHandle { Ok(ObjectId { id: client.create_object(interface, version, data) }) } + pub fn destroy_object(&self, id: &ObjectId) -> Result<(), InvalidId> { + let mut state = self.state.lock().unwrap(); + let state = (&mut *state as &mut dyn ErasedState) + .downcast_mut::>() + .expect("Wrong type parameter passed to Handle::destroy_object()."); + let client = state.clients.get_client_mut(id.id.client_id.clone())?; + client.destroy_object(id.id.clone(), &mut state.pending_destructors) + } + pub fn null_id() -> ObjectId { ObjectId { id: InnerObjectId { diff --git a/wayland-backend/src/server_api.rs b/wayland-backend/src/server_api.rs index 64e155693f6..5357fa93ad0 100644 --- a/wayland-backend/src/server_api.rs +++ b/wayland-backend/src/server_api.rs @@ -359,6 +359,21 @@ impl Handle { self.handle.create_object(client_id.id, interface, version, data) } + /// Destroy an object + /// + /// For most protocols, this is handled automatically when a destructor + /// message is sent or received. + /// + /// This corresponds to `wl_resource_destroy` in the C API. + /// + /// # Panics + /// + /// This method will panic if the type parameter `D` is not same to the same type as the + /// one the backend was initialized with. + pub fn destroy_object(&self, id: &ObjectId) -> Result<(), InvalidId> { + self.handle.destroy_object::(id) + } + /// Send an event to the client /// /// Returns an error if the sender ID of the provided message is no longer valid. diff --git a/wayland-backend/src/sys/client_impl/mod.rs b/wayland-backend/src/sys/client_impl/mod.rs index 45e746515aa..08ea5c0b72a 100644 --- a/wayland-backend/src/sys/client_impl/mod.rs +++ b/wayland-backend/src/sys/client_impl/mod.rs @@ -499,6 +499,43 @@ impl InnerBackend { } } + fn destroy_object_inner(&self, guard: &mut MutexGuard, id: &ObjectId) { + if let Some(ref alive) = id.id.alive { + let udata = unsafe { + Box::from_raw(ffi_dispatch!( + wayland_client_handle(), + wl_proxy_get_user_data, + id.id.ptr + ) as *mut ProxyUserData) + }; + unsafe { + ffi_dispatch!( + wayland_client_handle(), + wl_proxy_set_user_data, + id.id.ptr, + std::ptr::null_mut() + ); + } + alive.store(false, Ordering::Release); + udata.data.destroyed(id.clone()); + } + + guard.known_proxies.remove(&id.id.ptr); + + unsafe { + ffi_dispatch!(wayland_client_handle(), wl_proxy_destroy, id.id.ptr); + } + } + + pub fn destroy_object(&self, id: &ObjectId) -> Result<(), InvalidId> { + if !id.id.alive.as_ref().map(|a| a.load(Ordering::Acquire)).unwrap_or(false) { + return Err(InvalidId); + } + + self.destroy_object_inner(&mut self.lock_state(), id); + Ok(()) + } + pub fn send_request( &self, Message { sender_id: ObjectId { id }, opcode, args }: Message, @@ -693,31 +730,7 @@ impl InnerBackend { }; if message_desc.is_destructor { - if let Some(ref alive) = id.alive { - let udata = unsafe { - Box::from_raw(ffi_dispatch!( - wayland_client_handle(), - wl_proxy_get_user_data, - id.ptr - ) as *mut ProxyUserData) - }; - unsafe { - ffi_dispatch!( - wayland_client_handle(), - wl_proxy_set_user_data, - id.ptr, - std::ptr::null_mut() - ); - } - alive.store(false, Ordering::Release); - udata.data.destroyed(ObjectId { id: id.clone() }); - } - - guard.known_proxies.remove(&id.ptr); - - unsafe { - ffi_dispatch!(wayland_client_handle(), wl_proxy_destroy, id.ptr); - } + self.destroy_object_inner(&mut guard, &ObjectId { id }) } Ok(child_id) diff --git a/wayland-backend/src/sys/server_impl/mod.rs b/wayland-backend/src/sys/server_impl/mod.rs index 4bba83cd823..46d0b3ed47b 100644 --- a/wayland-backend/src/sys/server_impl/mod.rs +++ b/wayland-backend/src/sys/server_impl/mod.rs @@ -564,6 +564,24 @@ impl InnerHandle { Ok(ObjectId { id: unsafe { init_resource(resource, interface, Some(data)).0 } }) } + pub fn destroy_object(&self, id: &ObjectId) -> Result<(), InvalidId> { + let mut state = self.state.lock().unwrap(); + // Keep this guard alive while the code is run to protect the C state + let state = (&mut *state as &mut dyn ErasedState) + .downcast_mut::>() + .expect("Wrong type parameter passed to Handle::destroy_object()."); + + if !id.id.alive.load(Ordering::Acquire) { + return Err(InvalidId); + } + + PENDING_DESTRUCTORS.set(&(&mut state.pending_destructors as *mut _ as *mut _), || unsafe { + ffi_dispatch!(wayland_server_handle(), wl_resource_destroy, id.id.ptr); + }); + + Ok(()) + } + pub fn null_id() -> ObjectId { ObjectId { id: InnerObjectId {