| // 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) } |
| } |
| } |