use std::os::unix::io::OwnedFd;
use std::sync::Arc;
use wayland_backend::{
protocol::ProtocolError,
server::{ClientId, DisconnectReason, ObjectData, ObjectId},
};
use crate::{Client, DisplayHandle, Resource};
pub trait Dispatch<I: Resource, UserData, State = Self>: Sized {
fn request(
state: &mut State,
client: &Client,
resource: &I,
request: I::Request,
data: &UserData,
dhandle: &DisplayHandle,
data_init: &mut DataInit<'_, State>,
);
fn destroyed(
_state: &mut State,
_client: wayland_backend::server::ClientId,
_resource: &I,
_data: &UserData,
) {
}
}
#[derive(Debug)]
pub struct ResourceData<I, U> {
marker: std::marker::PhantomData<fn(I)>,
pub udata: U,
}
#[derive(Debug)]
#[must_use = "The protocol object must be initialized using DataInit"]
pub struct New<I> {
id: I,
}
impl<I> New<I> {
#[doc(hidden)]
pub fn wrap(id: I) -> New<I> {
New { id }
}
}
#[derive(Debug)]
pub struct DataInit<'a, D: 'static> {
pub(crate) store: &'a mut Option<Arc<dyn ObjectData<D>>>,
pub(crate) error: &'a mut Option<(u32, String)>,
}
impl<D> DataInit<'_, D> {
pub fn init<I: Resource + 'static, U: Send + Sync + 'static>(
&mut self,
resource: New<I>,
data: U,
) -> I
where
D: Dispatch<I, U> + 'static,
{
let arc = Arc::new(ResourceData::<I, _>::new(data));
*self.store = Some(arc.clone() as Arc<_>);
let mut obj = resource.id;
obj.__set_object_data(arc);
obj
}
pub fn custom_init<I: Resource + 'static>(
&mut self,
resource: New<I>,
data: Arc<dyn ObjectData<D>>,
) -> I {
*self.store = Some(data.clone());
let mut obj = resource.id;
obj.__set_object_data(data.into_any_arc());
obj
}
pub fn post_error<I: Resource + 'static>(
&mut self,
_resource: New<I>,
code: impl Into<u32>,
error: impl Into<String>,
) {
*self.error = Some((code.into(), error.into()));
}
}
impl<I, U> ResourceData<I, U> {
pub(crate) fn new(udata: U) -> Self {
ResourceData { marker: std::marker::PhantomData, udata }
}
}
impl<I: Resource + 'static, U: Send + Sync + 'static, D: Dispatch<I, U> + 'static> ObjectData<D>
for ResourceData<I, U>
{
fn request(
self: Arc<Self>,
handle: &wayland_backend::server::Handle,
data: &mut D,
client_id: wayland_backend::server::ClientId,
msg: wayland_backend::protocol::Message<wayland_backend::server::ObjectId, OwnedFd>,
) -> Option<Arc<dyn ObjectData<D>>> {
let dhandle = DisplayHandle::from(handle.clone());
let client = match Client::from_id(&dhandle, client_id) {
Ok(v) => v,
Err(_) => {
crate::log_error!("Receiving a request from a dead client ?!");
return None;
}
};
let (sender_id, opcode) = (msg.sender_id.protocol_id(), msg.opcode);
let (resource, request) = match I::parse_request(&dhandle, msg) {
Ok(v) => v,
Err(e) => {
crate::log_warn!("Dispatching error encountered: {e:?}, killing client.");
handle.kill_client(
client.id(),
DisconnectReason::ProtocolError(ProtocolError {
code: 1,
object_id: 0,
object_interface: "wl_display".into(),
message: format!(
"Malformed request received for id {sender_id} and opcode {opcode}."
),
}),
);
return None;
}
};
let udata = resource.data::<U>().expect("Wrong user_data value for object");
let mut new_data = None;
<D as Dispatch<I, U>>::request(
data,
&client,
&resource,
request,
udata,
&dhandle,
&mut DataInit { store: &mut new_data, error: &mut None },
);
new_data
}
fn destroyed(
self: Arc<Self>,
handle: &wayland_backend::server::Handle,
data: &mut D,
client_id: ClientId,
object_id: ObjectId,
) {
let dhandle = DisplayHandle::from(handle.clone());
let mut resource = I::from_id(&dhandle, object_id).unwrap();
resource.__set_object_data(self.clone());
<D as Dispatch<I, U>>::destroyed(data, client_id, &resource, &self.udata)
}
}
#[macro_export]
macro_rules! delegate_dispatch {
($(@< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? $dispatch_from:ty : [$interface: ty: $udata: ty] => $dispatch_to: ty) => {
impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::Dispatch<$interface, $udata> for $dispatch_from {
fn request(
state: &mut Self,
client: &$crate::Client,
resource: &$interface,
request: <$interface as $crate::Resource>::Request,
data: &$udata,
dhandle: &$crate::DisplayHandle,
data_init: &mut $crate::DataInit<'_, Self>,
) {
<$dispatch_to as $crate::Dispatch<$interface, $udata, Self>>::request(state, client, resource, request, data, dhandle, data_init)
}
fn destroyed(state: &mut Self, client: $crate::backend::ClientId, resource: &$interface, data: &$udata) {
<$dispatch_to as $crate::Dispatch<$interface, $udata, Self>>::destroyed(state, client, resource, data)
}
}
};
}