blob: 8466d83dedb5c884c99bd185419302cdab88793a [file] [log] [blame] [edit]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use log::debug;
use std::boxed::Box;
use std::marker::PhantomData;
use std::os::unix::io::RawFd;
use std::rc::Rc;
use wayland_commons::MessageGroup;
use crate::ffi;
#[derive(Eq, PartialEq, Debug)]
pub struct Resource<I>
where
I: wayland_commons::Interface,
{
native: *mut ffi::wl_resource,
phantom: PhantomData<I>,
}
impl<I> Clone for Resource<I>
where
I: wayland_commons::Interface,
{
fn clone(&self) -> Self {
Self {
native: self.native,
phantom: PhantomData,
}
}
}
impl<I> From<Main<I>> for Resource<I>
where
I: wayland_commons::Interface,
{
fn from(m: Main<I>) -> Self {
Self {
native: m.resource.native,
phantom: PhantomData,
}
}
}
impl<I> Resource<I>
where
I: wayland_commons::Interface,
{
pub fn new<J>(&self, id: u32) -> Resource<J>
where
J: wayland_commons::Interface + From<Resource<J>> + AsRef<Resource<J>>,
{
unsafe {
let client = ffi::wl_resource_get_client(self.native);
let version = ffi::wl_resource_get_version(self.native);
Resource::from_c_ptr(ffi::wl_resource_create(
client,
J::c_interface() as *mut ffi::wl_interface,
version,
id,
))
}
}
pub fn from_c_ptr(native: *mut ffi::wl_resource) -> Self {
Resource::<I> { native, phantom: PhantomData }
}
pub fn drop_native(&mut self) {
unsafe { ffi::wl_resource_destroy(self.native) }
}
pub unsafe fn make_child_for<J>(&self, id: u32) -> Option<Main<J>>
where
J: wayland_commons::Interface + From<Resource<J>> + AsRef<Resource<J>>,
{
Some(Main::<J> { resource: self.new(id) })
}
pub fn c_ptr(&self) -> *mut ffi::wl_resource {
self.native
}
pub fn client(&self) -> *mut ffi::wl_client {
unsafe { ffi::wl_resource_get_client(self.native) }
}
pub fn id(&self) -> u32 {
unsafe { ffi::wl_resource_get_id(self.native) }
}
pub fn set_user_data<J>(&self, data: *const J) {
debug!(
"{}: set_user_data: native={:?} from_raw={:?}",
std::any::type_name::<J>(),
self.native,
data
);
unsafe { ffi::wl_resource_set_user_data(self.native, data as *mut _) }
}
pub fn user_data<J>(&self) -> &mut J {
unsafe {
let data = ffi::wl_resource_get_user_data(self.native);
debug!(
"{}: user_data: native={:?} from_raw={:?}",
std::any::type_name::<J>(),
self.native,
data
);
let data = ffi::wl_resource_get_user_data(self.native) as *mut J;
&mut *data
}
}
pub fn set_user_data_rc<J>(&self, rc: Rc<J>) {
unsafe extern "C" fn call_drop<J>(resource: *mut ffi::wl_resource) {
let data = ffi::wl_resource_get_user_data(resource);
let rc = Rc::from_raw(data as *mut J);
debug!(
"rc resource destroyed: resource={:?}, strong count={}, type={}",
resource,
Rc::strong_count(&rc),
std::any::type_name::<J>()
);
}
debug!(
"set_user_data_rc: resource={:?}, strong count={}, type={}",
self.native,
Rc::strong_count(&rc),
std::any::type_name::<J>(),
);
unsafe {
ffi::wl_resource_set_user_data(self.native, Rc::into_raw(rc) as *mut _);
ffi::wl_resource_set_destructor(self.native, Some(call_drop::<J>));
}
}
pub fn user_data_rc<J>(&self) -> Rc<J> {
unsafe {
let data = ffi::wl_resource_get_user_data(self.native);
debug!(
"{}: user_data_rc: native={:?} from_raw={:?}",
std::any::type_name::<J>(),
self.native,
data
);
Rc::increment_strong_count(data);
let rc = Rc::from_raw(data as *mut J);
rc
}
}
pub fn send(&self, message: I::Event) {
message.as_raw_c_in(|opcode, args| unsafe {
ffi::wl_resource_post_event_array(
self.native,
opcode,
args.as_mut_ptr() as *mut ffi::wl_argument,
);
})
}
pub fn post_error(&self, err: u32, msg: &str) {
unsafe {
let msg = std::ffi::CString::new(msg).expect("invalid error message");
ffi::wl_resource_post_error(self.native, err, msg.as_ptr())
}
}
pub fn post_no_memory(&self) {
unsafe { ffi::wl_resource_post_no_memory(self.native) }
}
pub fn on_request<F>(&self, f: F) -> &Self
where
F: 'static + Fn(&mut Resource<I>, &I::Request),
{
unsafe extern "C" fn call_dispatcher<I, F>(
data: *const ::std::ffi::c_void,
object: *mut ::std::ffi::c_void,
opcode: u32,
_message: *const ffi::wl_message,
args: *mut ffi::wl_argument,
) -> ::std::os::raw::c_int
where
I: wayland_commons::Interface,
F: 'static + Fn(&mut Resource<I>, &I::Request),
{
let request = <I::Request as wayland_commons::MessageGroup>::from_raw_c(
object,
opcode,
args as *const wayland_sys::common::wl_argument,
);
let mut resource = Resource::<I>::from_c_ptr(object as *mut _);
let f = &mut *(data as *mut F);
if let Ok(request) = request {
(*f)(&mut resource, &request)
}
0
}
debug!(
"set dispatcher for resource {}: {:?}",
I::NAME,
&f as *const _
);
let dispatcher = Box::new(f);
unsafe {
ffi::wl_resource_set_dispatcher(
self.native,
Some(call_dispatcher::<I, F>),
Box::into_raw(dispatcher) as *mut _,
std::ptr::null_mut(),
None,
)
}
self
}
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct Main<I>
where
I: wayland_commons::Interface,
{
resource: Resource<I>,
}
impl<I> Main<I>
where
I: wayland_commons::Interface,
{
pub fn on_request<F>(&self, f: F) -> &Self
where
F: 'static + Fn(&mut Resource<I>, &I::Request),
{
self.resource.on_request(f);
self
}
pub fn set_user_data<J>(&self, data: *const J) {
self.resource.set_user_data(data);
}
pub fn set_user_data_rc<J>(&self, data: Rc<J>) {
self.resource.set_user_data_rc(data);
}
}
pub struct ResourceMap {}
impl ResourceMap {
pub fn get_new<I>(&mut self, _id: u32) -> Option<Main<I>>
where
I: wayland_commons::Interface,
{
panic!("ResourceMap::get_new called");
}
pub fn get<I: wayland_commons::Interface>(&mut self, _id: u32) -> Option<Resource<I>> {
panic!("ResourceMap::get called");
}
}
struct GlobalHandlers<I>
where
I: wayland_commons::Interface,
{
handle_bind: Option<Box<dyn Fn(&mut Resource<I>) -> bool>>,
dispatcher: Option<Box<dyn Fn(&mut Resource<I>, &I::Request)>>,
handle_destroy: Option<Box<dyn Fn(&mut Resource<I>)>>,
}
pub struct Global<I>
where
I: wayland_commons::Interface,
{
native: *mut ffi::wl_global,
handlers: Box<GlobalHandlers<I>>,
}
impl<I> Drop for Global<I>
where
I: wayland_commons::Interface,
{
fn drop(&mut self) {
debug!("drop global");
}
}
impl<I: wayland_commons::Interface> Global<I> {
pub fn on_bind<F>(mut self, f: F) -> Self
where
F: 'static + Fn(&mut Resource<I>) -> bool,
{
// hook for handling bind time checks or allocations
self.handlers.handle_bind = Some(Box::new(f));
self
}
pub fn on_request<F>(mut self, f: F) -> Self
where
F: 'static + Fn(&mut Resource<I>, &I::Request),
{
debug!(
"set dispatcher for global {}: {:?}",
I::NAME,
&f as *const _
);
self.handlers.dispatcher = Some(Box::new(f));
self
}
pub fn on_destroy<F>(mut self, f: F) -> Self
where
F: 'static + Fn(&mut Resource<I>),
{
self.handlers.handle_destroy = Some(Box::new(f));
self
}
}
#[derive(Debug)]
pub struct Server {
pub native: *mut ffi::wl_display,
}
impl Server {
pub fn new() -> Self {
let native_display = unsafe { ffi::wl_display_create() };
Server { native: native_display }
}
pub fn create_client(&mut self, fd: RawFd) -> *mut ffi::wl_client {
unsafe { ffi::wl_client_create(self.native, fd) }
}
pub fn terminate(&mut self) {
todo!("terminate")
}
pub fn create_global<I>(&mut self, version: u32) -> Global<I>
where
I: wayland_commons::Interface,
{
unsafe extern "C" fn call_dispatcher<I>(
data: *const ::std::ffi::c_void,
object: *mut ::std::ffi::c_void,
opcode: u32,
_message: *const ffi::wl_message,
args: *mut ffi::wl_argument,
) -> ::std::os::raw::c_int
where
I: wayland_commons::Interface,
{
let request = <I::Request as wayland_commons::MessageGroup>::from_raw_c(
object,
opcode,
args as *const wayland_sys::common::wl_argument,
);
let mut resource = Resource::<I> {
native: object as *mut ffi::wl_resource,
phantom: PhantomData,
};
let handlers = &mut *(data as *mut GlobalHandlers<I>);
if let (Ok(request), Some(dispatcher)) = (request, handlers.dispatcher.as_ref()) {
(*dispatcher)(&mut resource, &request)
}
0
}
unsafe extern "C" fn call_destroy_closure<I>(native: *mut ffi::wl_resource)
where
I: wayland_commons::Interface,
{
let b =
&*(ffi::wl_resource_get_user_data(native) as *mut Box<dyn Fn(&mut Resource<I>)>);
let mut resource = Resource::<I> { native, phantom: PhantomData };
b(&mut resource)
}
unsafe extern "C" fn call_bind_closure<I>(
client: *mut ffi::wl_client,
data: *mut ::std::os::raw::c_void,
version: u32,
id: u32,
) where
I: wayland_commons::Interface,
{
let client = &mut *client;
let native = client.create_resource(&mut *(I::c_interface() as *mut _), version, id);
let mut resource = Resource::<I> { native, phantom: PhantomData };
let handlers = &mut *(data as *mut GlobalHandlers<I>);
if let Some(ref dispatcher) = handlers.dispatcher {
debug!(
"setting dispatcher for {}: {:?}",
I::NAME,
&(*dispatcher) as *const _
);
ffi::wl_resource_set_dispatcher(
resource.native,
Some(call_dispatcher::<I>),
handlers as *mut _ as *mut std::ffi::c_void,
std::ptr::null_mut(),
None,
)
}
let success = if let Some(ref handle_bind) = handlers.handle_bind {
debug!(
"calling handle_bind for {}: {:?}",
I::NAME,
&(*handle_bind) as *const _
);
(*handle_bind)(&mut resource)
} else {
true
};
if !success {
debug!("on_bind returns failure");
resource.drop_native();
return;
}
if let Some(ref mut handle_destroy) = handlers.handle_destroy {
ffi::wl_resource_set_user_data(
resource.native,
handle_destroy as *mut _ as *mut std::ffi::c_void,
);
ffi::wl_resource_set_destructor(resource.native, Some(call_destroy_closure::<I>));
}
}
let mut global = Global {
native: std::ptr::null_mut(),
handlers: Box::new(GlobalHandlers {
handle_bind: None,
dispatcher: None,
handle_destroy: None,
}),
};
global.native = unsafe {
ffi::wl_global_create(
self.native,
I::c_interface() as *const _,
version as i32,
&mut *global.handlers as *mut _ as *mut std::ffi::c_void,
Some(call_bind_closure::<I>),
)
};
global
}
}
impl Drop for Server {
fn drop(&mut self) {
debug!("drop display");
unsafe { ffi::wl_display_destroy(self.native) }
}
}