diff --git a/src/globals.rs b/src/globals.rs index c894c631..0c78870f 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -20,6 +20,7 @@ use { wl_shm::WlShmGlobal, wl_subcompositor::WlSubcompositorGlobal, wl_surface::xwayland_shell_v1::XwaylandShellV1Global, + wp_content_type_manager_v1::WpContentTypeManagerV1Global, wp_cursor_shape_manager_v1::WpCursorShapeManagerV1Global, wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1Global, wp_presentation::WpPresentationGlobal, @@ -160,6 +161,7 @@ impl Globals { add_singleton!(WpTearingControlManagerV1Global); add_singleton!(WpSinglePixelBufferManagerV1Global); add_singleton!(WpCursorShapeManagerV1Global); + add_singleton!(WpContentTypeManagerV1Global); } pub fn add_backend_singletons(&self, backend: &Rc) { diff --git a/src/ifs.rs b/src/ifs.rs index 7159237a..36da165e 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -27,6 +27,8 @@ pub mod wl_shm; pub mod wl_shm_pool; pub mod wl_subcompositor; pub mod wl_surface; +pub mod wp_content_type_manager_v1; +pub mod wp_content_type_v1; pub mod wp_cursor_shape_device_v1; pub mod wp_cursor_shape_manager_v1; pub mod wp_fractional_scale_manager_v1; diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index ee28db9f..3488d6ef 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -34,6 +34,7 @@ use { x_surface::XSurface, xdg_surface::XdgSurfaceError, zwlr_layer_surface_v1::ZwlrLayerSurfaceV1Error, }, + wp_content_type_v1::ContentType, wp_presentation_feedback::WpPresentationFeedback, }, leaks::Tracker, @@ -256,6 +257,8 @@ pub struct WlSurface { tearing_control: CloneCell>>, tearing: Cell, version: u32, + pub has_content_type_manager: Cell, + content_type: Cell>, } impl Debug for WlSurface { @@ -350,6 +353,7 @@ struct PendingState { transform: Cell>, xwayland_serial: Cell>, tearing: Cell>, + content_type: Cell>>, } #[derive(Default)] @@ -405,6 +409,8 @@ impl WlSurface { tearing_control: Default::default(), tearing: Cell::new(false), version, + has_content_type_manager: Default::default(), + content_type: Default::default(), } } @@ -867,6 +873,9 @@ impl WlSurface { if let Some(tearing) = self.pending.tearing.take() { self.tearing.set(tearing); } + if let Some(content_type) = self.pending.content_type.take() { + self.content_type.set(content_type); + } if let Some(xwayland_serial) = self.pending.xwayland_serial.take() { self.xwayland_serial.set(Some(xwayland_serial)); self.client @@ -1046,6 +1055,10 @@ impl WlSurface { self.send_seat_release_events(); self.seat_state.destroy_node(self); } + + pub fn set_content_type(&self, content_type: Option) { + self.pending.content_type.set(Some(content_type)); + } } object_base! { diff --git a/src/ifs/wp_content_type_manager_v1.rs b/src/ifs/wp_content_type_manager_v1.rs new file mode 100644 index 00000000..bca1ddf8 --- /dev/null +++ b/src/ifs/wp_content_type_manager_v1.rs @@ -0,0 +1,116 @@ +use { + crate::{ + client::{Client, ClientError}, + globals::{Global, GlobalName}, + ifs::wp_content_type_v1::WpContentTypeV1, + leaks::Tracker, + object::Object, + utils::buffd::{MsgParser, MsgParserError}, + wire::{wp_content_type_manager_v1::*, WpContentTypeManagerV1Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct WpContentTypeManagerV1Global { + pub name: GlobalName, +} + +impl WpContentTypeManagerV1Global { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + fn bind_( + self: Rc, + id: WpContentTypeManagerV1Id, + client: &Rc, + version: u32, + ) -> Result<(), WpContentTypeManagerV1Error> { + let mgr = Rc::new(WpContentTypeManagerV1 { + id, + client: client.clone(), + tracker: Default::default(), + version, + }); + track!(client, mgr); + client.add_client_obj(&mgr)?; + Ok(()) + } +} + +global_base!( + WpContentTypeManagerV1Global, + WpContentTypeManagerV1, + WpContentTypeManagerV1Error +); + +simple_add_global!(WpContentTypeManagerV1Global); + +impl Global for WpContentTypeManagerV1Global { + fn singleton(&self) -> bool { + true + } + + fn version(&self) -> u32 { + 1 + } +} + +pub struct WpContentTypeManagerV1 { + pub id: WpContentTypeManagerV1Id, + pub client: Rc, + pub tracker: Tracker, + pub version: u32, +} + +impl WpContentTypeManagerV1 { + fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WpContentTypeManagerV1Error> { + let _req: Destroy = self.client.parse(self, parser)?; + self.client.remove_obj(self)?; + Ok(()) + } + + fn get_surface_content_type( + &self, + parser: MsgParser<'_, '_>, + ) -> Result<(), WpContentTypeManagerV1Error> { + let req: GetSurfaceContentType = self.client.parse(self, parser)?; + let surface = self.client.lookup(req.surface)?; + if surface.has_content_type_manager.replace(true) { + return Err(WpContentTypeManagerV1Error::DuplicateContentType); + } + let device = Rc::new(WpContentTypeV1 { + id: req.id, + client: self.client.clone(), + surface, + tracker: Default::default(), + }); + track!(self.client, device); + self.client.add_client_obj(&device)?; + Ok(()) + } +} + +object_base! { + self = WpContentTypeManagerV1; + + DESTROY => destroy, + GET_SURFACE_CONTENT_TYPE => get_surface_content_type, +} + +impl Object for WpContentTypeManagerV1 {} + +simple_add_obj!(WpContentTypeManagerV1); + +#[derive(Debug, Error)] +pub enum WpContentTypeManagerV1Error { + #[error(transparent)] + ClientError(Box), + #[error("Parsing failed")] + MsgParserError(#[source] Box), + #[error("Surface already has a content type object")] + DuplicateContentType, +} +efrom!(WpContentTypeManagerV1Error, ClientError); +efrom!(WpContentTypeManagerV1Error, MsgParserError); diff --git a/src/ifs/wp_content_type_v1.rs b/src/ifs/wp_content_type_v1.rs new file mode 100644 index 00000000..2b2da660 --- /dev/null +++ b/src/ifs/wp_content_type_v1.rs @@ -0,0 +1,79 @@ +use { + crate::{ + client::{Client, ClientError}, + ifs::wl_surface::WlSurface, + leaks::Tracker, + object::Object, + utils::buffd::{MsgParser, MsgParserError}, + wire::{wp_content_type_v1::*, WpContentTypeV1Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +const NONE: u32 = 0; +const PHOTO: u32 = 1; +const VIDEO: u32 = 2; +const GAME: u32 = 3; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum ContentType { + Photo, + Video, + Game, +} + +pub struct WpContentTypeV1 { + pub id: WpContentTypeV1Id, + pub client: Rc, + pub surface: Rc, + pub tracker: Tracker, +} + +impl WpContentTypeV1 { + fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WpContentTypeV1Error> { + let _req: Destroy = self.client.parse(self, parser)?; + self.surface.has_content_type_manager.set(false); + self.client.remove_obj(self)?; + Ok(()) + } + + fn set_content_type(&self, parser: MsgParser<'_, '_>) -> Result<(), WpContentTypeV1Error> { + let req: SetContentType = self.client.parse(self, parser)?; + if req.content_type == NONE { + self.surface.set_content_type(None); + return Ok(()); + } + let ct = match req.content_type { + PHOTO => ContentType::Photo, + VIDEO => ContentType::Video, + GAME => ContentType::Game, + _ => return Err(WpContentTypeV1Error::UnknownContentType(req.content_type)), + }; + self.surface.set_content_type(Some(ct)); + Ok(()) + } +} + +object_base! { + self = WpContentTypeV1; + + DESTROY => destroy, + SET_CONTENT_TYPE => set_content_type, +} + +impl Object for WpContentTypeV1 {} + +simple_add_obj!(WpContentTypeV1); + +#[derive(Debug, Error)] +pub enum WpContentTypeV1Error { + #[error(transparent)] + ClientError(Box), + #[error("Parsing failed")] + MsgParserError(#[source] Box), + #[error("Content type {0} is unknown")] + UnknownContentType(u32), +} +efrom!(WpContentTypeV1Error, ClientError); +efrom!(WpContentTypeV1Error, MsgParserError); diff --git a/wire/wp_content_type_manager_v1.txt b/wire/wp_content_type_manager_v1.txt new file mode 100644 index 00000000..b70a0196 --- /dev/null +++ b/wire/wp_content_type_manager_v1.txt @@ -0,0 +1,9 @@ +# requests + +msg destroy = 0 { +} + +msg get_surface_content_type = 1 { + id: id(wp_content_type_v1), + surface: id(wl_surface), +} diff --git a/wire/wp_content_type_v1.txt b/wire/wp_content_type_v1.txt new file mode 100644 index 00000000..59a64906 --- /dev/null +++ b/wire/wp_content_type_v1.txt @@ -0,0 +1,8 @@ +# requests + +msg destroy = 0 { +} + +msg set_content_type = 1 { + content_type: u32, +}