// SPDX-License-Identifier: GPL-2.0-only use std::ffi::CStr; use std::{ffi::{c_char,c_void}, ptr::NonNull}; use crate::registry::ffi::{wl_proxy, wl_proxy_add_listener, wl_registry, wl_registry_listener}; mod ffi { use std::ffi::{c_char, c_void}; #[repr(C)] #[allow(non_camel_case_types)] pub(super) struct wl_registry; #[repr(C)] #[allow(non_camel_case_types)] pub(super) struct wl_proxy; // function types #[allow(non_camel_case_types)] pub(super) type registry_global_callback = unsafe extern "C" fn(*mut c_void, *mut wl_registry, u32, *const c_char, u32); #[allow(non_camel_case_types)] pub(super) type registry_global_remove_callback = unsafe extern "C" fn(*mut c_void, *mut wl_registry, u32); #[allow(non_camel_case_types)] pub(super) type cb_implementation = unsafe extern "C" fn(); #[repr(C)] pub(super) struct wl_registry_listener { pub global: Option, pub global_remove: Option } #[link(name = "wayland-client")] unsafe extern "C" { pub(super) unsafe fn wl_proxy_add_listener(proxy: *mut wl_proxy, implementation: *const cb_implementation, data: *mut c_void); } } // C->Rust trampoline functions unsafe extern "C" fn registry_handle_global( data: *mut c_void, _registry: *mut wl_registry, name: u32, interface: *const c_char, version: u32, ) { let registry = unsafe { &mut *(data as *mut RegistryListener) }; let interface = unsafe { CStr::from_ptr(interface) .to_string_lossy() .into_owned() }; if let Some(callback) = registry.global.as_mut() { callback(interface, name, version) } } unsafe extern "C" fn registry_handle_global_remove( data: *mut c_void, _registry: *mut wl_registry, name: u32, ) { let registry = unsafe { &mut *(data as *mut RegistryListener) }; if let Some(callback) = registry.global_remove.as_mut() { callback(name) } } // callback holder cell pub struct RegistryListener { global: Option>, global_remove: Option>, } // the main registry cell pub struct Registry { raw: NonNull, callbacks: Box, listener: Box, } impl Registry { pub fn on_global(&mut self, global: F) where F: FnMut(String, u32, u32) + 'static { self.callbacks.global = Some(Box::new(global)); } pub fn on_global_remove(&mut self, global_remove: F) where F: FnMut(u32) + 'static { self.callbacks.global_remove = Some(Box::new(global_remove)); } pub fn register_listener(&mut self) { unsafe { wl_proxy_add_listener( self.raw.as_ptr() as *mut wl_proxy, &*self.listener as *const _ as *const ffi::cb_implementation, self.callbacks.as_mut() as *mut _ as *mut c_void) }; } } impl From<*mut c_void> for Registry { fn from(value: *mut c_void) -> Self { let raw = NonNull::new(value as *mut wl_registry) .expect("unable to get display registry"); Self { raw, callbacks: Box::new( RegistryListener { global: None, global_remove: None } ), listener: Box::new(ffi::wl_registry_listener { global: Some(registry_handle_global), global_remove: Some(registry_handle_global_remove), }), } } }