🔒 Repository is read-only – file editing is disabled.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005
//! DRM Lease protocol
//!
//! This module provides helpers to handle the wp_drm_lease_v1 protocol,
//! which allows clients to lease DRM resources from the compositor.
//!
//! This is in particular useful for VR applications, that would like to take over
//! a directly attached VR display from the DRM device otherwise controlled by the compositor.
//! In theory however any sorts of DRM resources can be lend out to less privileged clients via this protocol.
//!
//! ## How to use
//!
//! To setup the drm_lease global, you will need to first provide the `DrmNode` you want to lease resources from.
//! You can usually get that from your [`DrmDevice`]. Once the global is up,
//! you can advertise connectors as available through [`DrmLeaseState::add_connector`].
//! Should a connector become unavailable or is used by the compositor, you may remove it again using [`DrmLeaseState::withdraw_connector`].
//!
//! Any client requests will be issued through [`DrmLeaseHandler::lease_request`], which
//! allows you to add additional needed DRM resources to the lease and accept or decline the request.
//!
//! ```no_run
//! # use smithay::delegate_drm_lease;
//! # use smithay::wayland::drm_lease::*;
//! # use smithay::backend::drm::{DrmDevice, DrmNode};
//!
//! pub struct State {
//! drm_lease_state: DrmLeaseState,
//! active_leases: Vec<DrmLease>,
//! }
//!
//! # impl State {
//! # fn get_device_for_node(&self, node: &DrmNode) -> &DrmDevice { todo!() }
//! # }
//!
//! impl DrmLeaseHandler for State {
//! fn drm_lease_state(&mut self, node: DrmNode) -> &mut DrmLeaseState { &mut self.drm_lease_state }
//! fn lease_request(&mut self, node: DrmNode, request: DrmLeaseRequest) -> Result<DrmLeaseBuilder, LeaseRejected> {
//! let device = self.get_device_for_node(&node);
//! let mut builder = DrmLeaseBuilder::new(device);
//! for connector in request.connectors {
//! builder.add_connector(connector);
//! # let some_free_crtc = todo!();
//! # let primary_plane_for_connector = todo!();
//! # let primary_plane_claim_for_connector = todo!();
//! builder.add_crtc(some_free_crtc);
//! builder.add_plane(primary_plane_for_connector, primary_plane_claim_for_connector);
//! }
//! Ok(builder)
//! }
//! fn new_active_lease(&mut self, node: DrmNode, lease: DrmLease) { self.active_leases.push(lease); }
//! fn lease_destroyed(&mut self, node: DrmNode, lease_id: u32) { self.active_leases.retain(|l| l.id() != lease_id); }
//! }
//!
//! delegate_drm_lease!(State);
//!
//! # let mut display = wayland_server::Display::<State>::new().unwrap();
//! # let display_handle = display.handle();
//! # let drm_device: DrmDevice = todo!();
//! let mut drm_lease_state = DrmLeaseState::new::<State>(
//! &display.handle(),
//! &DrmNode::from_file(
//! drm_device.device_fd()
//! ).unwrap()
//! ).unwrap();
//!
//! // Add some connectors
//! # let some_connector = todo!();
//! drm_lease_state.add_connector::<State>(some_connector, "Unknown".into(), "Unknown".into());
//!
//! let state = State {
//! drm_lease_state,
//! active_leases: Vec::new(),
//! };
//!
//! // Rest of the compositor goes here
//! ```
use std::{
collections::{HashMap, HashSet},
fmt, io,
num::NonZeroU32,
os::unix::{io::OwnedFd, prelude::AsFd},
path::{Path, PathBuf},
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex, Weak,
},
};
use drm::control::{connector, crtc, plane, Device, RawResourceHandle};
use rustix::fs::OFlags;
use wayland_protocols::wp::drm_lease::v1::server::*;
use wayland_server::backend::GlobalId;
use wayland_server::{Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource};
use crate::backend::drm::{DrmDevice, DrmDeviceFd, DrmNode, NodeType, PlaneClaim};
/// Delegate type for a drm_lease global
#[derive(Debug)]
pub struct DrmLeaseState {
node: DrmNode,
dh: DisplayHandle,
global: Option<GlobalId>,
connectors: Vec<DrmLeaseConnector>,
known_lease_devices: Vec<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1>,
active_leases: Vec<DrmLeaseRef>,
}
#[derive(Debug)]
struct DrmLeaseConnector {
name: String,
description: String,
node: DrmNode,
handle: connector::Handle,
enabled: bool,
known_instances: Vec<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1>,
}
/// Data attached to wp_drm_lease_request_v1 objects
#[derive(Debug)]
pub struct DrmLeaseRequestData {
node: DrmNode,
connectors: Mutex<Vec<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1>>,
}
/// DRM lease request containing a set of requested connectors
#[derive(Debug)]
pub struct DrmLeaseRequest {
/// requested connectors
pub connectors: Vec<connector::Handle>,
}
/// Builder struct to collect DRM resources to be leased
#[derive(Debug)]
pub struct DrmLeaseBuilder {
drm: DrmDeviceFd,
planes: HashMap<plane::Handle, PlaneClaim>,
connectors: HashSet<connector::Handle>,
crtcs: HashSet<crtc::Handle>,
}
impl DrmLeaseBuilder {
/// Create a new builder from a DRM Device
pub fn new(drm: &DrmDevice) -> DrmLeaseBuilder {
DrmLeaseBuilder {
drm: drm.device_fd().clone(),
planes: HashMap::new(),
connectors: HashSet::new(),
crtcs: HashSet::new(),
}
}
/// Add a CRTC to the to be leased resources
pub fn add_crtc(&mut self, crtc: crtc::Handle) {
self.crtcs.insert(crtc);
}
/// Add a connector to the to be leased resources
pub fn add_connector(&mut self, conn: connector::Handle) {
self.connectors.insert(conn);
}
/// Add a plane to the to be leased resources
pub fn add_plane(&mut self, plane: plane::Handle, claim: PlaneClaim) {
self.planes.insert(plane, claim);
}
fn build(self) -> io::Result<DrmLease> {
let objects: Vec<RawResourceHandle> = self
.planes
.keys()
.cloned()
.map(Into::into)
.chain(self.connectors.iter().cloned().map(Into::into))
.chain(self.crtcs.iter().cloned().map(Into::into))
.collect();
let (id, fd) = self.drm.create_lease(&objects, OFlags::CLOEXEC.bits())?;
Ok(DrmLease {
drm: self.drm.clone(),
planes: self.planes,
connectors: self.connectors,
crtcs: self.crtcs,
lease_id: id,
obj: Arc::new(Mutex::new(None)),
fd: Arc::new(Mutex::new(Some(fd))),
revoked: Arc::new(AtomicBool::new(false)),
})
}
}
/// Active DRM Lease
///
/// Dropping will revoke the lease
#[derive(Debug, Clone)]
pub struct DrmLease {
drm: DrmDeviceFd,
planes: HashMap<plane::Handle, PlaneClaim>,
connectors: HashSet<connector::Handle>,
crtcs: HashSet<crtc::Handle>,
lease_id: NonZeroU32,
obj: Arc<Mutex<Option<wp_drm_lease_v1::WpDrmLeaseV1>>>,
fd: Arc<Mutex<Option<OwnedFd>>>,
revoked: Arc<AtomicBool>,
}
impl DrmLease {
/// CRTCs being leased
pub fn crtcs(&self) -> impl Iterator<Item = &crtc::Handle> {
self.crtcs.iter()
}
/// Connectors being leased
pub fn connectors(&self) -> impl Iterator<Item = &connector::Handle> {
self.connectors.iter()
}
/// Planes being leased
pub fn planes(&self) -> impl Iterator<Item = &plane::Handle> {
self.planes.keys()
}
/// Lease Id
pub fn id(&self) -> u32 {
self.lease_id.get()
}
fn set_obj(&self, obj: wp_drm_lease_v1::WpDrmLeaseV1) {
*self.obj.lock().unwrap() = Some(obj);
}
fn take_fd(&mut self) -> Option<OwnedFd> {
self.fd.lock().unwrap().take()
}
}
#[derive(Debug)]
struct DrmLeaseRef {
drm: DrmDeviceFd,
obj: Weak<Mutex<Option<wp_drm_lease_v1::WpDrmLeaseV1>>>,
lease_id: NonZeroU32,
revoked: Arc<AtomicBool>,
connectors: HashSet<connector::Handle>,
}
impl Drop for DrmLease {
fn drop(&mut self) {
if let Some(obj) = &self.obj.lock().unwrap().take() {
obj.finished();
}
if !self.revoked.swap(true, Ordering::SeqCst) {
tracing::info!(?self.lease_id, "Revoking lease");
if let Err(err) = self.drm.revoke_lease(self.lease_id) {
tracing::warn!(?err, "Error revoking lease");
};
}
}
}
impl DrmLeaseRef {
fn force_close(&self) {
if let Some(obj) = Weak::upgrade(&self.obj).and_then(|obj| obj.lock().unwrap().take()) {
obj.finished();
}
if !self.revoked.swap(true, Ordering::SeqCst) {
tracing::info!(?self.lease_id, "Revoking lease");
if let Err(err) = self.drm.revoke_lease(self.lease_id) {
tracing::warn!(?err, "Error revoking lease");
};
}
}
}
/// Data attached to wp_drm_lease_v1 objects
#[derive(Debug)]
pub struct DrmLeaseData {
id: u32,
node: DrmNode,
}
/// DRM lease was rejected by the compositor.
#[derive(Debug, Default)]
pub struct LeaseRejected(Option<Box<dyn std::error::Error + 'static>>);
impl fmt::Display for LeaseRejected {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
Some(reason) => f.write_fmt(format_args!("Lease rejected, reason: {}", &**reason)),
None => f.write_str("Lease rejected"),
}
}
}
impl std::error::Error for LeaseRejected {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.0.as_deref()
}
}
impl LeaseRejected {
/// Wrap any error type in a `LeaseRejected` error to be returned from [`DrmLeaseHandler::lease_request`].
pub fn with_cause<T: std::error::Error + 'static>(err: T) -> Self {
LeaseRejected(Some(Box::new(err)))
}
}
/// Handler trait for drm leasing from the compositor.
pub trait DrmLeaseHandler:
GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData>
+ Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, Self>
+ Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, Self>
+ Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, Self>
+ Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, Self>
+ 'static
{
/// Returns a mutable reference to the [`DrmLeaseState`] delegate type
fn drm_lease_state(&mut self, node: DrmNode) -> &mut DrmLeaseState;
/// A client has issued a new request.
///
/// The request only contains connectors and the compositor is lease any resources it may seem fit.
/// It is recommended however to lease the requested connectors and any resources necessary to drive them
/// (free CRTCs per connector and at least their primary plane) or reject the request.
///
/// To reject the request return `Err(LeaseRejected::default())`, otherwise return a [`DrmLeaseBuilder`] with all the resources added.
fn lease_request(
&mut self,
node: DrmNode,
request: DrmLeaseRequest,
) -> Result<DrmLeaseBuilder, LeaseRejected>;
/// A new DRM lease is active. Dropping the provided [`DrmLease`] will revoke the lease.
fn new_active_lease(&mut self, node: DrmNode, lease: DrmLease);
/// A DRM lease was destroyed, the previously given [`DrmLease`] should be cleaned up, if not revoked already.
fn lease_destroyed(&mut self, node: DrmNode, lease_id: u32);
}
/// Data attached to a wp_drm_lease_device_v1 global
pub struct DrmLeaseDeviceGlobalData {
filter: Box<dyn for<'c> Fn(&'c Client) -> bool + Send + Sync>,
path: PathBuf,
node: DrmNode,
}
impl fmt::Debug for DrmLeaseDeviceGlobalData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DrmLeaseDeviceGlobalData")
.field("path", &self.path)
.field("node", &self.node)
.finish()
}
}
/// Errors thrown by the DRM lease global
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Unable to figure out the path of the drm device used
#[error("Unable to get modesetting node for drm device")]
UnableToGetPath,
/// Unable to get a new file descriptor for the drm device used
#[error("Unable to get file descriptor for drm device")]
UnableToGetFd,
/// Unable to open a new file descriptor for the drm device used
#[error("Unable to open new file descriptor for drm device")]
UnableToOpenNode(#[source] rustix::io::Errno),
/// Unable to drop DRM Master on a new file descriptor
#[error("Unable to drop master status on file descriptor for drm device")]
UnableToDropMaster(#[source] io::Error),
}
fn get_non_master_fd<P: AsRef<Path>>(path: P) -> Result<OwnedFd, Error> {
let fd = rustix::fs::open(
path.as_ref(),
OFlags::RDWR | OFlags::CLOEXEC,
rustix::fs::Mode::empty(),
)
.map_err(Error::UnableToOpenNode)?;
// check if the fd has master
if drm_ffi::get_client(fd.as_fd(), 0)
.map(|client| client.auth == 1)
.unwrap_or(false)
{
drm_ffi::auth::release_master(fd.as_fd()).map_err(Error::UnableToDropMaster)?;
}
Ok(fd)
}
impl DrmLeaseState {
/// Create a new DRM lease global for a given [`DrmNode`].
pub fn new<D>(display: &DisplayHandle, drm_node: &DrmNode) -> Result<DrmLeaseState, Error>
where
D: DrmLeaseHandler
+ GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData>
+ Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, D>
+ Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, D>
+ Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, D>
+ Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, D>
+ 'static,
{
Self::new_with_filter::<D, _>(display, drm_node, |_| true)
}
/// Create a new DRM lease global for a given [`DrmNode`] and a filter.
///
/// Filters can be used to limit visibility of a global to certain clients.
pub fn new_with_filter<D, F>(
display: &DisplayHandle,
drm_node: &DrmNode,
filter: F,
) -> Result<DrmLeaseState, Error>
where
D: DrmLeaseHandler
+ GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData>
+ Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, D>
+ Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, D>
+ Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, D>
+ Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, D>
+ 'static,
F: for<'c> Fn(&'c Client) -> bool + Send + Sync + 'static,
{
let path = drm_node
.dev_path_with_type(NodeType::Primary)
.ok_or(Error::UnableToGetPath)?;
// test once if we can get a non-master fd before initializing the global
let _ = get_non_master_fd(&path)?;
let data = DrmLeaseDeviceGlobalData {
filter: Box::new(filter),
path,
node: *drm_node,
};
let global = display.create_global::<D, wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, _>(1, data);
Ok(DrmLeaseState {
node: *drm_node,
dh: display.clone(),
global: Some(global),
connectors: Vec::new(),
known_lease_devices: Vec::new(),
active_leases: Vec::new(),
})
}
/// Add a connector, that is free to be leased to clients.
pub fn add_connector<D>(&mut self, connector: connector::Handle, name: String, description: String)
where
D: DrmLeaseHandler
+ GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData>
+ Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, D>
+ Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, D>
+ Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, D>
+ Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, D>
+ 'static,
{
if self.connectors.iter().any(|conn| conn.handle == connector) {
return;
}
let mut instances = Vec::new();
for instance in &self.known_lease_devices {
if let Ok(client) = self.dh.get_client(instance.id()) {
if let Ok(lease_connector) = client
.create_resource::<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, _, D>(
&self.dh, 1, self.node,
)
{
instance.connector(&lease_connector);
lease_connector.name(name.clone());
lease_connector.description(description.clone());
lease_connector.connector_id(connector.into());
instances.push(lease_connector);
instance.done();
}
}
}
self.connectors.push(DrmLeaseConnector {
name,
description,
node: self.node,
handle: connector,
enabled: true,
known_instances: instances,
});
}
/// Withdraw a connector from the set of connectors available for leasing
pub fn withdraw_connector(&mut self, connector: connector::Handle) {
#[allow(clippy::mutable_key_type)]
let mut clients = HashSet::new();
if let Some(pos) = self.connectors.iter().position(|conn| conn.handle == connector) {
let lease_connector = self.connectors.remove(pos);
for instance in &lease_connector.known_instances {
instance.withdrawn();
if let Some(client) = instance.client() {
clients.insert(client.id());
}
}
}
for instance in &self.known_lease_devices {
if let Some(client) = instance.client() {
if clients.contains(&client.id()) {
instance.done();
}
}
}
self.active_leases
.retain(|lease| !lease.connectors.contains(&connector));
}
/// Suspend all connectors temporarily (e.g. upon loosing DRM master as the session becomes inactive)
pub fn suspend(&mut self) {
self.suspend_internal(None);
}
fn suspend_internal(&mut self, connectors: Option<&HashSet<connector::Handle>>) {
#[allow(clippy::mutable_key_type)]
let mut clients = HashSet::new();
for connector in self.connectors.iter_mut().filter(|c| {
connectors
.map(|filter| filter.contains(&c.handle))
.unwrap_or(true)
}) {
for instance in connector.known_instances.drain(..) {
instance.withdrawn();
if let Some(client) = instance.client() {
clients.insert(client.id());
}
}
connector.enabled = false;
}
for (instance, client) in self
.known_lease_devices
.iter()
.flat_map(|instance| instance.client().map(|client| (instance, client)))
{
if clients.contains(&client.id()) {
instance.done();
}
}
}
/// Resume all connectors temporarily (e.g. upon gaining DRM master as the session becomes active)
pub fn resume<D>(&mut self)
where
D: DrmLeaseHandler
+ GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData>
+ Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, D>
+ Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, D>
+ Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, D>
+ Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, D>
+ 'static,
{
self.resume_internal::<D>(None);
}
fn resume_internal<D>(&mut self, connectors: Option<&HashSet<connector::Handle>>)
where
D: DrmLeaseHandler
+ GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData>
+ Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, D>
+ Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, D>
+ Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, D>
+ Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, D>
+ 'static,
{
for (instance, client) in self
.known_lease_devices
.iter()
.flat_map(|instance| instance.client().map(|client| (instance, client)))
{
let mut once = false;
for connector in self.connectors.iter_mut().filter(|c| {
connectors
.map(|filter| filter.contains(&c.handle))
.unwrap_or(true)
}) {
if let Some(lease_connector) = connector.new_instance::<D>(&self.dh, &client) {
instance.connector(&lease_connector);
connector.send_info(&lease_connector);
once = true;
}
}
if once {
instance.done();
}
}
for connector in self.connectors.iter_mut().filter(|c| {
connectors
.map(|filter| filter.contains(&c.handle))
.unwrap_or(true)
}) {
connector.enabled = true;
}
}
fn remove_lease<D>(&mut self, id: u32) -> Option<DrmLeaseRef>
where
D: DrmLeaseHandler
+ GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData>
+ Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, D>
+ Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, D>
+ Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, D>
+ Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, D>
+ 'static,
{
let lease_ref = {
if let Some(pos) = self
.active_leases
.iter()
.position(|lease| lease.lease_id.get() == id)
{
let lease = self.active_leases.remove(pos);
self.resume_internal::<D>(Some(&lease.connectors));
lease
} else {
return None;
}
};
lease_ref.force_close();
Some(lease_ref)
}
/// [`DrmNode`] belonging to this DRM lease global
pub fn node(&self) -> DrmNode {
self.node
}
/// Disable the global, it will no longer be advertised to new clients
pub fn disable_global<D>(&mut self)
where
D: DrmLeaseHandler
+ GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData>
+ Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, D>
+ Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, D>
+ Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, D>
+ Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, D>
+ 'static,
{
if let Some(global) = self.global.take() {
self.dh.disable_global::<D>(global);
}
}
}
impl Drop for DrmLeaseState {
fn drop(&mut self) {
// End all leases
self.active_leases.clear();
// withdraw all connectors
for connector in &self.connectors {
for instance in &connector.known_instances {
instance.withdrawn();
}
}
// end all devices
for device in &*self.known_lease_devices {
device.released();
}
}
}
impl DrmLeaseConnector {
fn new_instance<D>(
&mut self,
dh: &DisplayHandle,
client: &Client,
) -> Option<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1>
where
D: DrmLeaseHandler
+ GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData>
+ Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, D>
+ Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, D>
+ Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, D>
+ Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, D>
+ 'static,
{
if let Ok(lease_connector) =
client.create_resource::<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, _, D>(dh, 1, self.node)
{
self.known_instances.push(lease_connector.clone());
Some(lease_connector)
} else {
None
}
}
fn send_info(&self, connector: &wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1) {
connector.name(self.name.clone());
connector.description(self.description.clone());
connector.connector_id(self.handle.into());
connector.done();
}
}
impl<D> GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData, D>
for DrmLeaseState
where
D: DrmLeaseHandler
+ GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData>
+ Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, D>
+ Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, D>
+ Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, D>
+ Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, D>
+ 'static,
{
fn bind(
state: &mut D,
dh: &DisplayHandle,
client: &Client,
resource: New<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1>,
global_data: &DrmLeaseDeviceGlobalData,
data_init: &mut DataInit<'_, D>,
) {
let drm_lease_state = state.drm_lease_state(global_data.node);
let wp_drm_lease_device = data_init.init(resource, global_data.node);
let Ok(fd) = get_non_master_fd(&global_data.path) else {
// nothing we can do
wp_drm_lease_device.released();
return;
};
wp_drm_lease_device.drm_fd(fd.as_fd());
for connector in drm_lease_state.connectors.iter_mut().filter(|c| c.enabled) {
if let Some(id) = connector.new_instance::<D>(dh, client) {
wp_drm_lease_device.connector(&id);
connector.send_info(&id);
}
}
wp_drm_lease_device.done();
drm_lease_state.known_lease_devices.push(wp_drm_lease_device);
}
fn can_view(client: Client, global_data: &DrmLeaseDeviceGlobalData) -> bool {
(global_data.filter)(&client)
}
}
impl<D> Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, D> for DrmLeaseState
where
D: DrmLeaseHandler
+ GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData>
+ Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, D>
+ Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, D>
+ Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, D>
+ Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, D>
+ 'static,
{
fn request(
state: &mut D,
_client: &Client,
resource: &wp_drm_lease_device_v1::WpDrmLeaseDeviceV1,
request: <wp_drm_lease_device_v1::WpDrmLeaseDeviceV1 as Resource>::Request,
data: &DrmNode,
_dhandle: &DisplayHandle,
data_init: &mut DataInit<'_, D>,
) {
match request {
wp_drm_lease_device_v1::Request::CreateLeaseRequest { id } => {
data_init.init(
id,
DrmLeaseRequestData {
node: *data,
connectors: Mutex::new(Vec::new()),
},
);
}
wp_drm_lease_device_v1::Request::Release => {
let drm_lease_state = state.drm_lease_state(*data);
drm_lease_state
.known_lease_devices
.retain(|device| device != resource);
resource.released();
}
_ => {}
}
}
}
impl<D> Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, D> for DrmLeaseState
where
D: DrmLeaseHandler
+ GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData>
+ Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, D>
+ Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, D>
+ Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, D>
+ Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, D>
+ 'static,
{
fn request(
_state: &mut D,
_client: &Client,
_resource: &wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1,
_request: <wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1 as Resource>::Request,
_data: &DrmNode,
_dhandle: &DisplayHandle,
_data_init: &mut DataInit<'_, D>,
) {
}
fn destroyed(
state: &mut D,
_client: wayland_server::backend::ClientId,
resource: &wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1,
data: &DrmNode,
) {
let drm_lease_state = state.drm_lease_state(*data);
drm_lease_state.connectors.iter_mut().for_each(|connector| {
connector.known_instances.retain(|obj| obj != resource);
});
}
}
impl<D> Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, D> for DrmLeaseState
where
D: DrmLeaseHandler
+ GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData>
+ Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, D>
+ Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, D>
+ Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, D>
+ Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, D>
+ 'static,
{
fn request(
state: &mut D,
_client: &Client,
resource: &wp_drm_lease_request_v1::WpDrmLeaseRequestV1,
request: <wp_drm_lease_request_v1::WpDrmLeaseRequestV1 as Resource>::Request,
data: &DrmLeaseRequestData,
_dhandle: &DisplayHandle,
data_init: &mut DataInit<'_, D>,
) {
match request {
wp_drm_lease_request_v1::Request::RequestConnector { connector } => {
data.connectors.lock().unwrap().push(connector);
}
wp_drm_lease_request_v1::Request::Submit { id } => {
let drm_lease_state = state.drm_lease_state(data.node);
let mut connector_handles = Vec::new();
for connector in &*data.connectors.lock().unwrap() {
if let Some(conn) = drm_lease_state
.connectors
.iter()
.find(|conn| conn.known_instances.iter().any(|obj| obj == connector))
{
if connector_handles.iter().any(|handle| handle == &conn.handle) {
resource.post_error(
wp_drm_lease_request_v1::Error::DuplicateConnector,
format!("Duplicate connector: {}", conn.name),
);
return;
} else {
connector_handles.push(conn.handle);
}
} else {
resource.post_error(
wp_drm_lease_request_v1::Error::WrongDevice,
"Requested lease for wrong device",
);
return;
}
}
if connector_handles.is_empty() {
resource.post_error(
wp_drm_lease_request_v1::Error::EmptyLease,
"Lease doesn't contain any connectors",
);
}
match state.lease_request(
data.node,
DrmLeaseRequest {
connectors: connector_handles,
},
) {
Ok(builder) => match builder.build() {
Ok(mut lease) => {
let lease_obj = data_init.init(
id,
DrmLeaseData {
id: lease.lease_id.get(),
node: data.node,
},
);
lease.set_obj(lease_obj.clone());
let fd = lease.take_fd().unwrap();
lease_obj.lease_fd(fd.as_fd());
let lease_ref = DrmLeaseRef {
drm: lease.drm.clone(),
obj: Arc::downgrade(&lease.obj),
lease_id: lease.lease_id,
connectors: lease.connectors.clone(),
revoked: lease.revoked.clone(),
};
let drm_lease_state = state.drm_lease_state(data.node);
drm_lease_state.suspend_internal(Some(&lease.connectors));
drm_lease_state.active_leases.push(lease_ref);
state.new_active_lease(data.node, lease);
}
Err(err) => {
tracing::error!(?err, "Failed to create lease");
let lease_obj = data_init.init(
id,
DrmLeaseData {
id: 0,
node: data.node,
},
);
lease_obj.finished();
}
},
Err(err) => {
tracing::debug!(?err, "Compositor denied lease request");
let lease_obj = data_init.init(
id,
DrmLeaseData {
id: 0,
node: data.node,
},
);
lease_obj.finished();
}
}
}
_ => {}
}
}
}
impl<D> Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, D> for DrmLeaseState
where
D: DrmLeaseHandler
+ GlobalDispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmLeaseDeviceGlobalData>
+ Dispatch<wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1, DrmNode, D>
+ Dispatch<wp_drm_lease_device_v1::WpDrmLeaseDeviceV1, DrmNode, D>
+ Dispatch<wp_drm_lease_request_v1::WpDrmLeaseRequestV1, DrmLeaseRequestData, D>
+ Dispatch<wp_drm_lease_v1::WpDrmLeaseV1, DrmLeaseData, D>
+ 'static,
{
fn request(
_state: &mut D,
_client: &Client,
_resource: &wp_drm_lease_v1::WpDrmLeaseV1,
_request: <wp_drm_lease_v1::WpDrmLeaseV1 as Resource>::Request,
_data: &DrmLeaseData,
_dhandle: &DisplayHandle,
_data_init: &mut DataInit<'_, D>,
) {
}
fn destroyed(
state: &mut D,
_client: wayland_server::backend::ClientId,
_resource: &wp_drm_lease_v1::WpDrmLeaseV1,
data: &DrmLeaseData,
) {
let drm_lease_state = state.drm_lease_state(data.node);
if let Some(lease_ref) = drm_lease_state.remove_lease::<D>(data.id) {
state.lease_destroyed(data.node, lease_ref.lease_id.get());
}
}
}
/// Macro to delegate implementation of the drm-lease protocol to [`DrmLeaseState`].
///
/// You must also implement [`DrmLeaseHandler`] to use this.
#[macro_export]
macro_rules! delegate_drm_lease {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
type __WpDrmLeaseDeviceV1 =
$crate::reexports::wayland_protocols::wp::drm_lease::v1::server::wp_drm_lease_device_v1::WpDrmLeaseDeviceV1;
type __WpDrmLeaseConnectorV1 =
$crate::reexports::wayland_protocols::wp::drm_lease::v1::server::wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1;
type __WpDrmLeaseRequestV1 =
$crate::reexports::wayland_protocols::wp::drm_lease::v1::server::wp_drm_lease_request_v1::WpDrmLeaseRequestV1;
type __WpDrmLeaseV1 =
$crate::reexports::wayland_protocols::wp::drm_lease::v1::server::wp_drm_lease_v1::WpDrmLeaseV1;
$crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
__WpDrmLeaseDeviceV1: $crate::wayland::drm_lease::DrmLeaseDeviceGlobalData
] => $crate::wayland::drm_lease::DrmLeaseState);
$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
__WpDrmLeaseConnectorV1: $crate::backend::drm::DrmNode
] => $crate::wayland::drm_lease::DrmLeaseState);
$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
__WpDrmLeaseDeviceV1: $crate::backend::drm::DrmNode
] => $crate::wayland::drm_lease::DrmLeaseState);
$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
__WpDrmLeaseRequestV1: $crate::wayland::drm_lease::DrmLeaseRequestData
] => $crate::wayland::drm_lease::DrmLeaseState);
$crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
__WpDrmLeaseV1: $crate::wayland::drm_lease::DrmLeaseData
] => $crate::wayland::drm_lease::DrmLeaseState);
};
}