diff --git a/deploy-notes.md b/deploy-notes.md index 7ca63887..5a287532 100644 --- a/deploy-notes.md +++ b/deploy-notes.md @@ -1,5 +1,7 @@ # Unreleased +- Needs jay-compositor release. + # 1.1.0 - Needs jay-config release. diff --git a/docs/features.md b/docs/features.md index dc0cb816..7942b959 100644 --- a/docs/features.md +++ b/docs/features.md @@ -118,6 +118,10 @@ You can explicitly opt into giving applications access to privileged protocols v Jay's shortcut system allows you to execute an action when a key is pressed and to execute a different action when the key is released. +## VR + +Jay's supports leasing VR headsets to applications. + ## Protocol Support Jay supports the following wayland protocols: @@ -139,6 +143,7 @@ Jay supports the following wayland protocols: | wp_alpha_modifier_v1 | 1 | | | wp_content_type_manager_v1 | 1 | | | wp_cursor_shape_manager_v1 | 1 | | +| wp_drm_lease_device_v1 | 1 | | | wp_fractional_scale_manager_v1 | 1 | | | wp_linux_drm_syncobj_manager_v1 | 1 | | | wp_presentation | 1 | | diff --git a/release-notes.md b/release-notes.md index 81957005..7e377eee 100644 --- a/release-notes.md +++ b/release-notes.md @@ -3,6 +3,7 @@ - Add support for wp-security-manager-v1. - Add support for xdg-dialog-v1. - Add support for ext-transient-seat-v1. +- Add support for wp-drm-lease-v1. # 1.1.0 (2024-04-22) diff --git a/src/acceptor.rs b/src/acceptor.rs index b8d584d0..5f5ce8ff 100644 --- a/src/acceptor.rs +++ b/src/acceptor.rs @@ -1,7 +1,7 @@ use { crate::{ async_engine::SpawnedFuture, - client::{ClientCaps, CAP_LAYER_SHELL}, + client::{ClientCaps, CAPS_DEFAULT}, state::State, utils::{errorfmt::ErrorFmt, oserror::OsError, xrd::xrd}, }, @@ -154,7 +154,7 @@ impl Acceptor { state.eng.spawn(accept( acc.socket.insecure.clone(), state.clone(), - CAP_LAYER_SHELL, + CAPS_DEFAULT, )), ]; state.acceptor.set(Some(acc.clone())); diff --git a/src/backend.rs b/src/backend.rs index fbe976d6..1bee5779 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -6,7 +6,7 @@ use { gfx_api::{GfxFramebuffer, SyncFile}, ifs::wl_seat::wl_pointer::{CONTINUOUS, FINGER, HORIZONTAL_SCROLL, VERTICAL_SCROLL, WHEEL}, libinput::consts::DeviceCapability, - video::drm::{ConnectorType, DrmError, DrmVersion}, + video::drm::{ConnectorType, DrmConnector, DrmError, DrmVersion}, }, jay_config::video::GfxApi, std::{ @@ -94,6 +94,9 @@ pub trait Connector { fn set_non_desktop_override(&self, non_desktop: Option) { let _ = non_desktop; } + fn drm_object_id(&self) -> Option { + None + } } #[derive(Debug)] diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index 1f156dbd..edf4a611 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -1246,6 +1246,10 @@ impl Connector for MetalConnector { } } } + + fn drm_object_id(&self) -> Option { + Some(self.id) + } } pub struct MetalCrtc { diff --git a/src/client.rs b/src/client.rs index 4b170cc8..ed64086d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -54,8 +54,12 @@ bitflags! { CAP_LAYER_SHELL = 1 << 6, CAP_SCREENCOPY_MANAGER = 1 << 7, CAP_SEAT_MANAGER = 1 << 8, + CAP_DRM_LEASE = 1 << 9, } +pub const CAPS_DEFAULT: ClientCaps = ClientCaps(CAP_LAYER_SHELL.0 | CAP_DRM_LEASE.0); +pub const CAPS_DEFAULT_SANDBOXED: ClientCaps = ClientCaps(CAP_DRM_LEASE.0); + #[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)] pub struct ClientId(u64); diff --git a/src/client/objects.rs b/src/client/objects.rs index def03614..7309ee1f 100644 --- a/src/client/objects.rs +++ b/src/client/objects.rs @@ -20,6 +20,7 @@ use { xdg_surface::{xdg_toplevel::XdgToplevel, XdgSurface}, WlSurface, }, + wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1, xdg_positioner::XdgPositioner, xdg_wm_base::XdgWmBase, @@ -32,8 +33,9 @@ use { wire::{ JayOutputId, JayScreencastId, JayToplevelId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId, WlPointerId, WlRegionId, WlRegistryId, WlSeatId, - WlSurfaceId, WpLinuxDrmSyncobjTimelineV1Id, XdgPositionerId, XdgSurfaceId, - XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id, ZwpPrimarySelectionSourceV1Id, + WlSurfaceId, WpDrmLeaseConnectorV1Id, WpLinuxDrmSyncobjTimelineV1Id, XdgPositionerId, + XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id, + ZwpPrimarySelectionSourceV1Id, }, }, std::{cell::RefCell, mem, rc::Rc}, @@ -62,6 +64,7 @@ pub struct Objects { pub timelines: CopyHashMap>, pub zwlr_data_sources: CopyHashMap>, pub jay_toplevels: CopyHashMap>, + pub drm_lease_outputs: CopyHashMap>, ids: RefCell>, } @@ -92,6 +95,7 @@ impl Objects { timelines: Default::default(), zwlr_data_sources: Default::default(), jay_toplevels: Default::default(), + drm_lease_outputs: Default::default(), ids: RefCell::new(vec![]), } } @@ -126,6 +130,7 @@ impl Objects { self.timelines.clear(); self.zwlr_data_sources.clear(); self.jay_toplevels.clear(); + self.drm_lease_outputs.clear(); } pub fn id(&self, client_data: &Client) -> Result diff --git a/src/ifs.rs b/src/ifs.rs index bb9c496b..9efdf7b0 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -41,6 +41,10 @@ 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_drm_lease_connector_v1; +pub mod wp_drm_lease_device_v1; +pub mod wp_drm_lease_request_v1; +pub mod wp_drm_lease_v1; pub mod wp_fractional_scale_manager_v1; pub mod wp_linux_drm_syncobj_manager_v1; pub mod wp_linux_drm_syncobj_timeline_v1; diff --git a/src/ifs/wp_drm_lease_connector_v1.rs b/src/ifs/wp_drm_lease_connector_v1.rs new file mode 100644 index 00000000..868a8412 --- /dev/null +++ b/src/ifs/wp_drm_lease_connector_v1.rs @@ -0,0 +1,93 @@ +use { + crate::{ + backend::ConnectorId as BackendConnectorId, + client::{Client, ClientError}, + ifs::wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, + leaks::Tracker, + object::{Object, Version}, + utils::bindings::Bindings, + wire::{wp_drm_lease_connector_v1::*, WpDrmLeaseConnectorV1Id}, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct WpDrmLeaseConnectorV1 { + pub id: WpDrmLeaseConnectorV1Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, + pub device: Rc, + pub connector_id: BackendConnectorId, + pub bindings: Rc>, +} + +impl WpDrmLeaseConnectorV1 { + fn detach(&self) { + self.bindings.remove(&self.client, self); + } + + pub fn send_name(&self, name: &str) { + self.client.event(Name { + self_id: self.id, + name, + }); + } + + #[allow(dead_code)] + pub fn send_description(&self, description: &str) { + self.client.event(Description { + self_id: self.id, + description, + }); + } + + pub fn send_connector_id(&self, connector_id: u32) { + self.client.event(ConnectorId { + self_id: self.id, + connector_id, + }); + } + + pub fn send_done(&self) { + self.client.event(Done { self_id: self.id }); + } + + pub fn send_withdrawn(&self) { + self.client.event(Withdrawn { self_id: self.id }); + } +} + +impl WpDrmLeaseConnectorV1RequestHandler for WpDrmLeaseConnectorV1 { + type Error = WpDrmLeaseConnectorV1Error; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.detach(); + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + self = WpDrmLeaseConnectorV1; + version = self.version; +} + +impl Object for WpDrmLeaseConnectorV1 { + fn break_loops(&self) { + self.detach(); + } +} + +dedicated_add_obj!( + WpDrmLeaseConnectorV1, + WpDrmLeaseConnectorV1Id, + drm_lease_outputs +); + +#[derive(Debug, Error)] +pub enum WpDrmLeaseConnectorV1Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(WpDrmLeaseConnectorV1Error, ClientError); diff --git a/src/ifs/wp_drm_lease_device_v1.rs b/src/ifs/wp_drm_lease_device_v1.rs new file mode 100644 index 00000000..c3bf68a5 --- /dev/null +++ b/src/ifs/wp_drm_lease_device_v1.rs @@ -0,0 +1,228 @@ +use { + crate::{ + backend::DrmDeviceId, + client::{Client, ClientCaps, ClientError, CAP_DRM_LEASE}, + globals::{Global, GlobalName}, + ifs::{ + wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, + wp_drm_lease_request_v1::WpDrmLeaseRequestV1, + }, + leaks::Tracker, + object::{Object, Version}, + state::OutputData, + utils::{bindings::Bindings, errorfmt::ErrorFmt, oserror::OsError}, + video::drm::{Drm, DrmError}, + wire::{wp_drm_lease_device_v1::*, WpDrmLeaseDeviceV1Id}, + }, + std::{cell::Cell, rc::Rc}, + thiserror::Error, + uapi::{c, OwnedFd}, +}; + +mod removed_device; + +pub struct WpDrmLeaseDeviceV1Global { + pub name: GlobalName, + pub device: DrmDeviceId, + pub bindings: Rc>, +} + +impl WpDrmLeaseDeviceV1Global { + fn bind_( + self: Rc, + id: WpDrmLeaseDeviceV1Id, + client: &Rc, + version: Version, + ) -> Result<(), WpDrmLeaseDeviceV1Error> { + let obj = Rc::new(WpDrmLeaseDeviceV1 { + id, + client: client.clone(), + tracker: Default::default(), + version, + bindings: self.bindings.clone(), + device: self.device, + destroyed: Cell::new(false), + }); + track!(client, obj); + client.add_client_obj(&obj)?; + if let Some(dev) = client.state.drm_devs.get(&self.device) { + if let Some(node) = &dev.devnode { + match reopen_card(node) { + Ok(f) => obj.send_drm_fd(&f), + Err(e) => { + log::error!("Could not open master device: {}", ErrorFmt(e)); + } + } + } + for c in dev.connectors.lock().keys() { + if let Some(o) = client.state.outputs.get(c) { + if o.monitor_info.non_desktop { + obj.create_connector(&o); + } + } + } + } + obj.send_done(); + self.bindings.add(client, &obj); + Ok(()) + } +} + +global_base!( + WpDrmLeaseDeviceV1Global, + WpDrmLeaseDeviceV1, + WpDrmLeaseDeviceV1Error +); + +simple_add_global!(WpDrmLeaseDeviceV1Global); + +impl Global for WpDrmLeaseDeviceV1Global { + fn singleton(&self) -> bool { + false + } + + fn version(&self) -> u32 { + 1 + } + + fn break_loops(&self) { + self.bindings.clear(); + } + + fn required_caps(&self) -> ClientCaps { + CAP_DRM_LEASE + } +} + +pub struct WpDrmLeaseDeviceV1 { + pub id: WpDrmLeaseDeviceV1Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, + pub bindings: Rc>, + pub device: DrmDeviceId, + pub destroyed: Cell, +} + +impl WpDrmLeaseDeviceV1 { + fn detach(&self) { + self.destroyed.set(true); + self.bindings.remove(&self.client, self); + } + + pub fn create_connector(self: &Rc, output: &Rc) { + let id = match self.client.new_id() { + Ok(i) => i, + Err(e) => { + self.client.error(e); + return; + } + }; + let obj = Rc::new(WpDrmLeaseConnectorV1 { + id, + client: self.client.clone(), + tracker: Default::default(), + version: self.version, + device: self.clone(), + connector_id: output.connector.connector.id(), + bindings: output.lease_connectors.clone(), + }); + self.client.add_server_obj(&obj); + self.send_connector(&obj); + obj.send_name(&output.connector.name); + if let Some(id) = output.connector.connector.drm_object_id() { + obj.send_connector_id(id.0); + } + obj.send_done(); + output.lease_connectors.add(&self.client, &obj); + } + + fn send_drm_fd(&self, fd: &Rc) { + self.client.event(DrmFd { + self_id: self.id, + fd: fd.clone(), + }); + } + + fn send_connector(&self, c: &Rc) { + self.client.event(Connector { + self_id: self.id, + id: c.id, + }); + } + + pub fn send_done(&self) { + self.client.event(Done { self_id: self.id }); + } + + fn send_released(&self) { + self.client.event(Released { self_id: self.id }); + } +} + +impl WpDrmLeaseDeviceV1RequestHandler for WpDrmLeaseDeviceV1 { + type Error = WpDrmLeaseDeviceV1Error; + + fn create_lease_request( + &self, + req: CreateLeaseRequest, + _slf: &Rc, + ) -> Result<(), Self::Error> { + let obj = Rc::new(WpDrmLeaseRequestV1 { + id: req.id, + client: self.client.clone(), + tracker: Default::default(), + version: self.version, + device: self.device, + connectors: Default::default(), + }); + self.client.add_client_obj(&obj)?; + Ok(()) + } + + fn release(&self, _req: Release, _slf: &Rc) -> Result<(), Self::Error> { + self.detach(); + self.send_released(); + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + self = WpDrmLeaseDeviceV1; + version = self.version; +} + +impl Object for WpDrmLeaseDeviceV1 { + fn break_loops(&self) { + self.detach(); + } +} + +simple_add_obj!(WpDrmLeaseDeviceV1); + +#[derive(Debug, Error)] +pub enum WpDrmLeaseDeviceV1Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(WpDrmLeaseDeviceV1Error, ClientError); + +#[derive(Debug, Error)] +enum ReopenError { + #[error("Could not open the dev node")] + OpenNode(#[source] OsError), + #[error("Could not drop DRM master")] + DropMaster(#[source] DrmError), +} + +fn reopen_card(devnode: &str) -> Result, ReopenError> { + let fd = uapi::open(devnode, c::O_RDWR | c::O_CLOEXEC, 0) + .map_err(|e| ReopenError::OpenNode(e.into()))?; + let fd = Rc::new(fd); + let drm = Drm::open_existing(fd.clone()); + if drm.is_master() { + drm.drop_master().map_err(ReopenError::DropMaster)?; + } + Ok(fd) +} diff --git a/src/ifs/wp_drm_lease_device_v1/removed_device.rs b/src/ifs/wp_drm_lease_device_v1/removed_device.rs new file mode 100644 index 00000000..ff4f4425 --- /dev/null +++ b/src/ifs/wp_drm_lease_device_v1/removed_device.rs @@ -0,0 +1,80 @@ +use { + crate::{ + backend::DrmDeviceId, + client::{Client, ClientCaps, ClientError, CAP_DRM_LEASE}, + globals::{Global, GlobalName, RemovableWaylandGlobal}, + ifs::wp_drm_lease_device_v1::{WpDrmLeaseDeviceV1, WpDrmLeaseDeviceV1Global}, + object::Version, + utils::bindings::Bindings, + wire::WpDrmLeaseDeviceV1Id, + }, + std::{cell::Cell, rc::Rc}, + thiserror::Error, +}; + +struct RemovedWpDrmLeaseDeviceV1Global { + name: GlobalName, + bindings: Rc>, +} + +impl RemovedWpDrmLeaseDeviceV1Global { + fn bind_( + self: Rc, + id: WpDrmLeaseDeviceV1Id, + client: &Rc, + version: Version, + ) -> Result<(), RemovedWpDrmLeaseDeviceV1Error> { + let dev = Rc::new(WpDrmLeaseDeviceV1 { + id, + client: client.clone(), + tracker: Default::default(), + version, + bindings: self.bindings.clone(), + device: DrmDeviceId::from_raw(0), + destroyed: Cell::new(false), + }); + track!(client, dev); + client.add_client_obj(&dev)?; + dev.send_done(); + dev.bindings.add(client, &dev); + Ok(()) + } +} + +global_base!( + RemovedWpDrmLeaseDeviceV1Global, + WpDrmLeaseDeviceV1, + RemovedWpDrmLeaseDeviceV1Error +); + +simple_add_global!(RemovedWpDrmLeaseDeviceV1Global); + +impl Global for RemovedWpDrmLeaseDeviceV1Global { + fn singleton(&self) -> bool { + false + } + + fn version(&self) -> u32 { + 1 + } + + fn required_caps(&self) -> ClientCaps { + CAP_DRM_LEASE + } +} + +impl RemovableWaylandGlobal for WpDrmLeaseDeviceV1Global { + fn create_replacement(&self) -> Rc { + Rc::new(RemovedWpDrmLeaseDeviceV1Global { + name: self.name, + bindings: Default::default(), + }) + } +} + +#[derive(Debug, Error)] +pub enum RemovedWpDrmLeaseDeviceV1Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(RemovedWpDrmLeaseDeviceV1Error, ClientError); diff --git a/src/ifs/wp_drm_lease_request_v1.rs b/src/ifs/wp_drm_lease_request_v1.rs new file mode 100644 index 00000000..70c95c18 --- /dev/null +++ b/src/ifs/wp_drm_lease_request_v1.rs @@ -0,0 +1,84 @@ +use { + crate::{ + backend::{ConnectorId, DrmDeviceId}, + client::{Client, ClientError}, + ifs::wp_drm_lease_v1::{WpDrmLeaseV1, WpDrmLeaseV1Lessee}, + leaks::Tracker, + object::{Object, Version}, + utils::copyhashmap::CopyHashMap, + wire::{wp_drm_lease_request_v1::*, WpDrmLeaseConnectorV1Id, WpDrmLeaseRequestV1Id}, + }, + std::{cell::Cell, rc::Rc}, + thiserror::Error, +}; + +pub struct WpDrmLeaseRequestV1 { + pub id: WpDrmLeaseRequestV1Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, + pub device: DrmDeviceId, + pub connectors: CopyHashMap, +} + +impl WpDrmLeaseRequestV1RequestHandler for WpDrmLeaseRequestV1 { + type Error = WpDrmLeaseRequestV1Error; + + fn request_connector(&self, req: RequestConnector, _slf: &Rc) -> Result<(), Self::Error> { + let c = self.client.lookup(req.connector)?; + if self.device != c.device.device { + return Err(WpDrmLeaseRequestV1Error::MismatchedDevice(c.id)); + } + if self.connectors.contains(&c.id) { + return Err(WpDrmLeaseRequestV1Error::RepeatedDevice(c.id)); + } + self.connectors.set(c.id, c.connector_id); + Ok(()) + } + + fn submit(&self, req: Submit, _slf: &Rc) -> Result<(), Self::Error> { + self.client.remove_obj(self)?; + let obj = Rc::new(WpDrmLeaseV1 { + id: req.id, + client: self.client.clone(), + tracker: Default::default(), + version: self.version, + finished: Cell::new(false), + lease: Default::default(), + }); + self.client.add_client_obj(&obj)?; + if self.connectors.is_empty() { + return Err(WpDrmLeaseRequestV1Error::EmptyLease); + } + let Some(dev) = self.client.state.drm_devs.get(&self.device) else { + obj.send_finished(); + return Ok(()); + }; + let lessee = Rc::new(WpDrmLeaseV1Lessee { obj }); + let connectors: Vec<_> = self.connectors.lock().values().copied().collect(); + dev.dev.clone().create_lease(lessee, &connectors); + Ok(()) + } +} + +object_base! { + self = WpDrmLeaseRequestV1; + version = self.version; +} + +impl Object for WpDrmLeaseRequestV1 {} + +simple_add_obj!(WpDrmLeaseRequestV1); + +#[derive(Debug, Error)] +pub enum WpDrmLeaseRequestV1Error { + #[error(transparent)] + ClientError(Box), + #[error("Connector {0} does not belong to this device")] + MismatchedDevice(WpDrmLeaseConnectorV1Id), + #[error("Connector {0} is already part of this request")] + RepeatedDevice(WpDrmLeaseConnectorV1Id), + #[error("Lease request is empty")] + EmptyLease, +} +efrom!(WpDrmLeaseRequestV1Error, ClientError); diff --git a/src/ifs/wp_drm_lease_v1.rs b/src/ifs/wp_drm_lease_v1.rs new file mode 100644 index 00000000..3613670e --- /dev/null +++ b/src/ifs/wp_drm_lease_v1.rs @@ -0,0 +1,92 @@ +use { + crate::{ + backend::{BackendDrmLease, BackendDrmLessee}, + client::{Client, ClientError}, + leaks::Tracker, + object::{Object, Version}, + utils::clonecell::CloneCell, + wire::{wp_drm_lease_v1::*, WpDrmLeaseV1Id}, + }, + std::{cell::Cell, rc::Rc}, + thiserror::Error, + uapi::OwnedFd, +}; + +pub struct WpDrmLeaseV1Lessee { + pub obj: Rc, +} + +impl BackendDrmLessee for WpDrmLeaseV1Lessee { + fn created(&self, lease: Rc) { + if !self.obj.finished.get() { + self.obj.send_lease_fd(lease.fd()); + self.obj.lease.set(Some(lease)); + } + } +} + +impl Drop for WpDrmLeaseV1Lessee { + fn drop(&mut self) { + if !self.obj.finished.get() { + self.obj.detach(); + self.obj.send_finished(); + } + } +} + +pub struct WpDrmLeaseV1 { + pub id: WpDrmLeaseV1Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, + pub finished: Cell, + pub lease: CloneCell>>, +} + +impl WpDrmLeaseV1 { + fn detach(&self) { + self.finished.set(true); + self.lease.take(); + } + + fn send_lease_fd(&self, fd: &Rc) { + self.client.event(LeaseFd { + self_id: self.id, + leased_fd: fd.clone(), + }); + } + + pub fn send_finished(&self) { + self.client.event(Finished { self_id: self.id }); + } +} + +impl WpDrmLeaseV1RequestHandler for WpDrmLeaseV1 { + type Error = WpDrmLeaseV1Error; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.detach(); + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + self = WpDrmLeaseV1; + version = self.version; +} + +impl Object for WpDrmLeaseV1 { + fn break_loops(&self) { + self.detach(); + } +} + +simple_add_obj!(WpDrmLeaseV1); + +#[derive(Debug, Error)] +pub enum WpDrmLeaseV1Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(WpDrmLeaseV1Error, ClientError); diff --git a/src/ifs/wp_security_context_v1.rs b/src/ifs/wp_security_context_v1.rs index 670a70d3..84ce3041 100644 --- a/src/ifs/wp_security_context_v1.rs +++ b/src/ifs/wp_security_context_v1.rs @@ -1,6 +1,6 @@ use { crate::{ - client::{Client, ClientCaps, ClientError}, + client::{Client, ClientError, CAPS_DEFAULT_SANDBOXED}, leaks::Tracker, object::{Object, Version}, wire::{wp_security_context_v1::*, WpSecurityContextV1Id}, @@ -80,7 +80,7 @@ impl WpSecurityContextV1RequestHandler for WpSecurityContextV1 { fn commit(&self, _req: Commit, _slf: &Rc) -> Result<(), Self::Error> { self.check_committed()?; self.committed.set(true); - let caps = ClientCaps::none() & self.client.bounding_caps; + let caps = CAPS_DEFAULT_SANDBOXED & self.client.bounding_caps; self.client.state.security_context_acceptors.spawn( &self.client.state, self.sandbox_engine.take(), diff --git a/src/state.rs b/src/state.rs index a051a165..48cc9e16 100644 --- a/src/state.rs +++ b/src/state.rs @@ -40,6 +40,8 @@ use { zwp_input_popup_surface_v2::ZwpInputPopupSurfaceV2, NoneSurfaceExt, WlSurface, }, + wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, + wp_drm_lease_device_v1::WpDrmLeaseDeviceV1Global, wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1Global, zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1, zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, @@ -59,8 +61,8 @@ use { WorkspaceNode, }, utils::{ - activation_token::ActivationToken, asyncevent::AsyncEvent, clonecell::CloneCell, - copyhashmap::CopyHashMap, errorfmt::ErrorFmt, fdcloser::FdCloser, + activation_token::ActivationToken, asyncevent::AsyncEvent, bindings::Bindings, + clonecell::CloneCell, copyhashmap::CopyHashMap, errorfmt::ErrorFmt, fdcloser::FdCloser, linkedlist::LinkedList, numcell::NumCell, queue::AsyncQueue, refcounted::RefCounted, run_toplevel::RunToplevel, }, @@ -269,6 +271,7 @@ pub struct OutputData { pub connector: Rc, pub monitor_info: MonitorInfo, pub node: Option>, + pub lease_connectors: Rc>, } pub struct DrmDevData { @@ -280,6 +283,7 @@ pub struct DrmDevData { pub vendor: Option, pub model: Option, pub pci_id: Option, + pub lease_global: Rc, } impl DrmDevData { diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index c5fb2382..bd61ea59 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -175,6 +175,7 @@ impl ConnectorHandler { connector: self.data.clone(), monitor_info: info, node: Some(on.clone()), + lease_connectors: Default::default(), }); self.state.outputs.set(self.id, output_data); on.schedule_update_render_data(); @@ -293,9 +294,6 @@ impl ConnectorHandler { seat.set_position((tpos.x1() + tpos.x2()) / 2, (tpos.y1() + tpos.y2()) / 2); } } - if let Some(dev) = &self.data.drm_dev { - dev.connectors.remove(&self.id); - } self.state .remove_output_scale(on.global.persistent.scale.get()); let _ = self.state.remove_global(&*global); @@ -308,8 +306,26 @@ impl ConnectorHandler { connector: self.data.clone(), monitor_info, node: None, + lease_connectors: Default::default(), }); - self.state.outputs.set(self.id, output_data); + self.state.outputs.set(self.id, output_data.clone()); + let advertise = || { + if let Some(dev) = &self.data.drm_dev { + for binding in dev.lease_global.bindings.lock().values() { + binding.create_connector(&output_data); + binding.send_done(); + } + } + }; + let withdraw = || { + for (_, con) in output_data.lease_connectors.lock().drain() { + con.send_withdrawn(); + if !con.device.destroyed.get() { + con.device.send_done(); + } + } + }; + advertise(); if let Some(config) = self.state.config.get() { config.connector_connected(self.id); } @@ -317,13 +333,14 @@ impl ConnectorHandler { while let Some(event) = self.data.connector.event() { match event { ConnectorEvent::Disconnected => break 'outer, - ConnectorEvent::Available => {} - ConnectorEvent::Unavailable => {} + ConnectorEvent::Available => advertise(), + ConnectorEvent::Unavailable => withdraw(), ev => unreachable!("received unexpected event {:?}", ev), } } self.data.async_event.triggered().await; } + withdraw(); self.state.outputs.remove(&self.id); if let Some(config) = self.state.config.get() { config.connector_disconnected(self.id); diff --git a/src/tasks/drmdev.rs b/src/tasks/drmdev.rs index 993c4aee..38d6c95b 100644 --- a/src/tasks/drmdev.rs +++ b/src/tasks/drmdev.rs @@ -1,6 +1,7 @@ use { crate::{ backend::{BackendDrmDevice, DrmDeviceId, DrmEvent}, + ifs::wp_drm_lease_device_v1::WpDrmLeaseDeviceV1Global, state::{DrmDevData, State}, tasks::udev_utils::udev_props, utils::asyncevent::AsyncEvent, @@ -11,6 +12,12 @@ use { pub fn handle(state: &Rc, dev: Rc) { let id = dev.id(); let props = udev_props(dev.dev_t(), 1); + let lease_global = Rc::new(WpDrmLeaseDeviceV1Global { + name: state.globals.name(), + device: id, + bindings: Default::default(), + }); + state.add_global(&lease_global); let data = Rc::new(DrmDevData { dev: dev.clone(), handler: Cell::new(None), @@ -20,6 +27,7 @@ pub fn handle(state: &Rc, dev: Rc) { vendor: props.vendor, model: props.model, pci_id: props.pci_id, + lease_global, }); let oh = DrvDevHandler { id, @@ -66,6 +74,8 @@ impl DrvDevHandler { if let Some(config) = self.state.config.get() { config.del_drm_dev(self.id); } + self.data.lease_global.bindings.clear(); + let _ = self.state.remove_global(&*self.data.lease_global); self.data.handler.set(None); self.state.drm_devs.remove(&self.id); } diff --git a/src/utils.rs b/src/utils.rs index 0ab8c1d3..2d198bb7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,7 @@ pub mod activation_token; pub mod array; pub mod asyncevent; +pub mod bindings; pub mod bitfield; pub mod bitflags; pub mod buf; diff --git a/src/utils/bindings.rs b/src/utils/bindings.rs new file mode 100644 index 00000000..60134b01 --- /dev/null +++ b/src/utils/bindings.rs @@ -0,0 +1,39 @@ +use { + crate::{ + client::{Client, ClientId}, + object::{Object, ObjectId}, + utils::copyhashmap::{CopyHashMap, Locked}, + }, + std::rc::Rc, +}; + +pub struct Bindings

{ + bindings: CopyHashMap<(ClientId, ObjectId), Rc

>, +} + +impl

Default for Bindings

{ + fn default() -> Self { + Self { + bindings: Default::default(), + } + } +} + +impl Bindings

{ + pub fn add(&self, client: &Client, obj: &Rc

) { + let prev = self.bindings.set((client.id, obj.id()), obj.clone()); + assert!(prev.is_none()); + } + + pub fn remove(&self, client: &Client, obj: &P) { + self.bindings.remove(&(client.id, obj.id())); + } + + pub fn clear(&self) { + self.bindings.clear(); + } + + pub fn lock(&self) -> Locked<(ClientId, ObjectId), Rc

> { + self.bindings.lock() + } +} diff --git a/src/utils/oserror.rs b/src/utils/oserror.rs index a0ea8f5f..3f6c73e0 100644 --- a/src/utils/oserror.rs +++ b/src/utils/oserror.rs @@ -163,7 +163,7 @@ static ERRORS: Lazy<&'static [Option<&'static str>]> = Lazy::new(|| { res.leak() }); -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq)] pub struct OsError(pub c::c_int); impl From for OsError { diff --git a/src/video/drm.rs b/src/video/drm.rs index 051181ba..39194a49 100644 --- a/src/video/drm.rs +++ b/src/video/drm.rs @@ -40,8 +40,8 @@ use crate::{ video::{ dmabuf::DmaBuf, drm::sys::{ - drm_format_modifier, drm_format_modifier_blob, get_version, revoke_lease, - DRM_CAP_CURSOR_HEIGHT, DRM_CAP_CURSOR_WIDTH, FORMAT_BLOB_CURRENT, + auth_magic, drm_format_modifier, drm_format_modifier_blob, drop_master, get_version, + revoke_lease, DRM_CAP_CURSOR_HEIGHT, DRM_CAP_CURSOR_WIDTH, FORMAT_BLOB_CURRENT, }, Modifier, INVALID_MODIFIER, }, @@ -139,6 +139,8 @@ pub enum DrmError { ImportSyncFile(#[source] OsError), #[error("Could not create a lease")] CreateLease(#[source] OsError), + #[error("Could not drop DRM master")] + DropMaster(#[source] OsError), } fn render_node_name(fd: c::c_int) -> Result { @@ -175,7 +177,6 @@ pub struct Drm { } impl Drm { - #[cfg_attr(not(feature = "it"), allow(dead_code))] pub fn open_existing(fd: Rc) -> Self { Self { fd } } @@ -213,6 +214,14 @@ impl Drm { pub fn version(&self) -> Result { get_version(self.fd.raw()).map_err(DrmError::Version) } + + pub fn drop_master(&self) -> Result<(), DrmError> { + drop_master(self.fd.raw()).map_err(DrmError::DropMaster) + } + + pub fn is_master(&self) -> bool { + auth_magic(self.fd.raw(), 0) != Err(OsError(c::EACCES)) + } } pub struct InFormat { diff --git a/src/video/drm/sys.rs b/src/video/drm/sys.rs index 8ad7f5e3..61579ce5 100644 --- a/src/video/drm/sys.rs +++ b/src/video/drm/sys.rs @@ -37,6 +37,10 @@ pub unsafe fn ioctl(fd: c::c_int, request: c::c_ulong, t: &mut T) -> Result u64 { + uapi::_IO(DRM_IOCTL_BASE, nr) +} + pub const fn drm_iow(nr: u64) -> u64 { uapi::_IOW::(DRM_IOCTL_BASE, nr) } @@ -1369,3 +1373,28 @@ pub fn sync_ioc_merge(left: c::c_int, right: c::c_int) -> Result Result<(), OsError> { + let mut res = 0u8; + unsafe { + ioctl(fd, DRM_IOCTL_DROP_MASTER, &mut res)?; + } + Ok(()) +} + +const DRM_IOCTL_AUTH_MAGIC: u64 = drm_iow::(0x11); + +#[repr(C)] +struct drm_auth { + magic: c::c_uint, +} + +pub fn auth_magic(fd: c::c_int, magic: c::c_uint) -> Result<(), OsError> { + let mut res = drm_auth { magic }; + unsafe { + ioctl(fd, DRM_IOCTL_AUTH_MAGIC, &mut res)?; + } + Ok(()) +} diff --git a/wire/wp_drm_lease_connector_v1.txt b/wire/wp_drm_lease_connector_v1.txt new file mode 100644 index 00000000..853296f7 --- /dev/null +++ b/wire/wp_drm_lease_connector_v1.txt @@ -0,0 +1,23 @@ +request destroy { + +} + +event name { + name: str, +} + +event description { + description: str, +} + +event connector_id { + connector_id: u32, +} + +event done { + +} + +event withdrawn { + +} diff --git a/wire/wp_drm_lease_device_v1.txt b/wire/wp_drm_lease_device_v1.txt new file mode 100644 index 00000000..c0a938b9 --- /dev/null +++ b/wire/wp_drm_lease_device_v1.txt @@ -0,0 +1,23 @@ +request create_lease_request { + id: id(wp_drm_lease_request_v1), +} + +request release { + +} + +event drm_fd { + fd: fd, +} + +event connector { + id: id(wp_drm_lease_connector_v1), +} + +event done { + +} + +event released { + +} diff --git a/wire/wp_drm_lease_request_v1.txt b/wire/wp_drm_lease_request_v1.txt new file mode 100644 index 00000000..527e4a14 --- /dev/null +++ b/wire/wp_drm_lease_request_v1.txt @@ -0,0 +1,7 @@ +request request_connector { + connector: id(wp_drm_lease_connector_v1), +} + +request submit { + id: id(wp_drm_lease_v1), +} diff --git a/wire/wp_drm_lease_v1.txt b/wire/wp_drm_lease_v1.txt new file mode 100644 index 00000000..83055426 --- /dev/null +++ b/wire/wp_drm_lease_v1.txt @@ -0,0 +1,11 @@ +request destroy { + +} + +event lease_fd { + leased_fd: fd, +} + +event finished { + +}