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