1 // Take a look at the license at the top of the repository in the LICENSE file. 2 3 // TODO: support marshaller. 4 5 use std::mem; 6 use std::ptr; 7 use std::slice; 8 9 use libc::{c_uint, c_void}; 10 11 use crate::translate::{from_glib_none, mut_override, ToGlibPtr, ToGlibPtrMut, Uninitialized}; 12 use crate::ToValue; 13 use crate::Value; 14 15 wrapper! { 16 #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] 17 #[doc(alias = "GClosure")] 18 pub struct Closure(Shared<gobject_ffi::GClosure>); 19 20 match fn { 21 ref => |ptr| { 22 gobject_ffi::g_closure_ref(ptr); 23 gobject_ffi::g_closure_sink(ptr); 24 }, 25 unref => |ptr| gobject_ffi::g_closure_unref(ptr), 26 type_ => || gobject_ffi::g_closure_get_type(), 27 } 28 } 29 30 impl Closure { new<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(callback: F) -> Self31 pub fn new<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(callback: F) -> Self { 32 unsafe { Self::new_unsafe(callback) } 33 } 34 new_local<F: Fn(&[Value]) -> Option<Value> + 'static>(callback: F) -> Self35 pub fn new_local<F: Fn(&[Value]) -> Option<Value> + 'static>(callback: F) -> Self { 36 let callback = crate::ThreadGuard::new(callback); 37 38 unsafe { Self::new_unsafe(move |values| (callback.get_ref())(values)) } 39 } 40 new_unsafe<F: Fn(&[Value]) -> Option<Value>>(callback: F) -> Self41 pub unsafe fn new_unsafe<F: Fn(&[Value]) -> Option<Value>>(callback: F) -> Self { 42 unsafe extern "C" fn marshal<F>( 43 _closure: *mut gobject_ffi::GClosure, 44 return_value: *mut gobject_ffi::GValue, 45 n_param_values: c_uint, 46 param_values: *const gobject_ffi::GValue, 47 _invocation_hint: *mut c_void, 48 marshal_data: *mut c_void, 49 ) where 50 F: Fn(&[Value]) -> Option<Value>, 51 { 52 let values = slice::from_raw_parts(param_values as *const _, n_param_values as usize); 53 let callback: &F = &*(marshal_data as *mut _); 54 let result = callback(values); 55 if !return_value.is_null() { 56 match result { 57 Some(result) => *return_value = result.into_raw(), 58 None => { 59 let result = Value::uninitialized(); 60 *return_value = result.into_raw(); 61 } 62 } 63 } 64 } 65 66 unsafe extern "C" fn finalize<F>( 67 notify_data: *mut c_void, 68 _closure: *mut gobject_ffi::GClosure, 69 ) where 70 F: Fn(&[Value]) -> Option<Value>, 71 { 72 let _callback: Box<F> = Box::from_raw(notify_data as *mut _); 73 // callback is dropped here. 74 } 75 76 // Due to bitfields we have to do our own calculations here for the size of the GClosure: 77 // - 4: 32 bits in guint bitfields at the beginning 78 // - padding due to alignment needed for the following pointer 79 // - 3 * size_of<*mut c_void>: 3 pointers 80 // We don't store any custom data ourselves in the GClosure 81 let size = u32::max(4, mem::align_of::<*mut c_void>() as u32) 82 + 3 * mem::size_of::<*mut c_void>() as u32; 83 let closure = gobject_ffi::g_closure_new_simple(size, ptr::null_mut()); 84 assert_ne!(closure, ptr::null_mut()); 85 let callback = Box::new(callback); 86 let ptr: *mut F = Box::into_raw(callback); 87 let ptr: *mut c_void = ptr as *mut _; 88 gobject_ffi::g_closure_set_meta_marshal(closure, ptr, Some(marshal::<F>)); 89 gobject_ffi::g_closure_add_finalize_notifier(closure, ptr, Some(finalize::<F>)); 90 from_glib_none(closure) 91 } 92 invoke(&self, values: &[&dyn ToValue]) -> Option<Value>93 pub fn invoke(&self, values: &[&dyn ToValue]) -> Option<Value> { 94 let values = values 95 .iter() 96 .copied() 97 .map(ToValue::to_value) 98 .collect::<smallvec::SmallVec<[_; 10]>>(); 99 100 self.invoke_with_values(&values) 101 } 102 invoke_with_values(&self, values: &[Value]) -> Option<Value>103 pub fn invoke_with_values(&self, values: &[Value]) -> Option<Value> { 104 let result = unsafe { 105 let mut result = Value::uninitialized(); 106 gobject_ffi::g_closure_invoke( 107 self.to_glib_none().0 as *mut _, 108 result.to_glib_none_mut().0, 109 values.len() as u32, 110 mut_override(values.as_ptr()) as *mut gobject_ffi::GValue, 111 ptr::null_mut(), 112 ); 113 114 result 115 }; 116 117 Some(result).filter(|r| r.type_().is_valid()) 118 } 119 } 120 121 unsafe impl Send for Closure {} 122 unsafe impl Sync for Closure {} 123 124 #[cfg(test)] 125 mod tests { 126 use std::sync::atomic::{AtomicUsize, Ordering}; 127 use std::sync::Arc; 128 129 use super::Closure; 130 use crate::{ToValue, Value}; 131 132 #[allow(clippy::unnecessary_wraps)] closure_fn(values: &[Value]) -> Option<Value>133 fn closure_fn(values: &[Value]) -> Option<Value> { 134 assert_eq!(values.len(), 2); 135 let string_arg = values[0].get::<&str>(); 136 assert_eq!(string_arg, Ok("test")); 137 let int_arg = values[1].get::<i32>(); 138 assert_eq!(int_arg, Ok(42)); 139 Some(24.to_value()) 140 } 141 142 #[test] test_closure()143 fn test_closure() { 144 let call_count = Arc::new(AtomicUsize::new(0)); 145 146 let count = call_count.clone(); 147 let closure = Closure::new(move |values| { 148 count.fetch_add(1, Ordering::Relaxed); 149 assert_eq!(values.len(), 2); 150 let string_arg = values[0].get::<&str>(); 151 assert_eq!(string_arg, Ok("test")); 152 let int_arg = values[1].get::<i32>(); 153 assert_eq!(int_arg, Ok(42)); 154 None 155 }); 156 let result = closure.invoke(&[&"test", &42]); 157 assert!(result.is_none()); 158 assert_eq!(call_count.load(Ordering::Relaxed), 1); 159 160 let result = closure.invoke(&[&"test", &42]); 161 assert!(result.is_none()); 162 assert_eq!(call_count.load(Ordering::Relaxed), 2); 163 164 let closure = Closure::new(closure_fn); 165 let result = closure.invoke(&[&"test", &42]); 166 let int_res = result.map(|result| result.get::<i32>()); 167 assert_eq!(int_res, Some(Ok(24))); 168 } 169 } 170