1use std::mem;
8use std::ptr;
9use std::slice;
10
11use libc::{c_uint, c_void};
12
13use gobject_sys;
14use translate::{from_glib_none, mut_override, ToGlibPtr, ToGlibPtrMut, Uninitialized};
15use types::Type;
16use ToValue;
17use Value;
18
19glib_wrapper! {
20 #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
21 pub struct Closure(Shared<gobject_sys::GClosure>);
22
23 match fn {
24 ref => |ptr| {
25 gobject_sys::g_closure_ref(ptr);
26 gobject_sys::g_closure_sink(ptr);
27 },
28 unref => |ptr| gobject_sys::g_closure_unref(ptr),
29 get_type => || gobject_sys::g_closure_get_type(),
30 }
31}
32
33impl Closure {
34 pub fn new<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(callback: F) -> Self {
35 unsafe { Closure::new_unsafe(callback) }
36 }
37
38 pub unsafe fn new_unsafe<F: Fn(&[Value]) -> Option<Value>>(callback: F) -> Self {
39 unsafe extern "C" fn marshal<F>(
40 _closure: *mut gobject_sys::GClosure,
41 return_value: *mut gobject_sys::GValue,
42 n_param_values: c_uint,
43 param_values: *const gobject_sys::GValue,
44 _invocation_hint: *mut c_void,
45 marshal_data: *mut c_void,
46 ) where
47 F: Fn(&[Value]) -> Option<Value>,
48 {
49 let values = slice::from_raw_parts(param_values as *const _, n_param_values as usize);
50 let callback: Box<F> = Box::from_raw(marshal_data as *mut _);
51 let result = callback(values);
52 if !return_value.is_null() {
53 match result {
54 Some(result) => *return_value = result.into_raw(),
55 None => {
56 let result = Value::uninitialized();
57 *return_value = result.into_raw();
58 }
59 }
60 }
61 mem::forget(callback);
62 }
63
64 unsafe extern "C" fn finalize<F>(
65 notify_data: *mut c_void,
66 _closure: *mut gobject_sys::GClosure,
67 ) where
68 F: Fn(&[Value]) -> Option<Value>,
69 {
70 let _callback: Box<F> = Box::from_raw(notify_data as *mut _);
71 }
73
74 let size = u32::max(4, mem::align_of::<*mut c_void>() as u32)
80 + 3 * mem::size_of::<*mut c_void>() as u32;
81 let closure = gobject_sys::g_closure_new_simple(size, ptr::null_mut());
82 assert_ne!(closure, ptr::null_mut());
83 let callback = Box::new(callback);
84 let ptr: *mut F = Box::into_raw(callback);
85 let ptr: *mut c_void = ptr as *mut _;
86 gobject_sys::g_closure_set_meta_marshal(closure, ptr, Some(marshal::<F>));
87 gobject_sys::g_closure_add_finalize_notifier(closure, ptr, Some(finalize::<F>));
88 from_glib_none(closure)
89 }
90
91 #[allow(clippy::redundant_closure)]
92 pub fn invoke(&self, values: &[&dyn ToValue]) -> Option<Value> {
93 let mut result = unsafe { Value::uninitialized() };
94
95 let v_args: Vec<Value>;
96 let mut s_args: [Value; 10] = unsafe { mem::zeroed() };
97 let values = if values.len() <= 10 {
98 for (i, arg) in values.iter().enumerate() {
99 s_args[i] = arg.to_value();
100 }
101 &s_args[0..values.len()]
102 } else {
103 v_args = values.iter().map(|v| v.to_value()).collect();
104 v_args.as_slice()
105 };
106
107 unsafe {
108 gobject_sys::g_closure_invoke(
109 self.to_glib_none().0 as *mut _,
110 result.to_glib_none_mut().0,
111 values.len() as u32,
112 mut_override(values.as_ptr()) as *mut gobject_sys::GValue,
113 ptr::null_mut(),
114 );
115 }
116 if result.type_() == Type::Invalid {
117 None
118 } else {
119 Some(result)
120 }
121 }
122}
123
124unsafe impl Send for Closure {}
125unsafe impl Sync for Closure {}
126
127#[cfg(test)]
128mod tests {
129 use std::sync::atomic::{AtomicUsize, Ordering};
130 use std::sync::Arc;
131
132 use super::Closure;
133 use ToValue;
134 use Value;
135
136 fn closure_fn(values: &[Value]) -> Option<Value> {
137 assert_eq!(values.len(), 2);
138 let string: Option<String> = values[0].get();
139 assert_eq!(string, Some("test".to_string()));
140 let int: Option<i32> = values[1].get();
141 assert_eq!(int, Some(42));
142 Some(24.to_value())
143 }
144
145 #[test]
146 fn test_closure() {
147 let call_count = Arc::new(AtomicUsize::new(0));
148
149 let count = call_count.clone();
150 let closure = Closure::new(move |values| {
151 count.fetch_add(1, Ordering::Relaxed);
152 assert_eq!(values.len(), 2);
153 let string: Option<String> = values[0].get();
154 assert_eq!(string, Some("test".to_string()));
155 let int: Option<i32> = values[1].get();
156 assert_eq!(int, Some(42));
157 None
158 });
159 let result = closure.invoke(&[&"test".to_string(), &42]);
160 assert!(result.is_none());
161 assert_eq!(call_count.load(Ordering::Relaxed), 1);
162
163 let result = closure.invoke(&[&"test".to_string(), &42]);
164 assert!(result.is_none());
165 assert_eq!(call_count.load(Ordering::Relaxed), 2);
166
167 let closure = Closure::new(closure_fn);
168 let result = closure.invoke(&[&"test".to_string(), &42]);
169 let int: Option<i32> = result.and_then(|result| result.get());
170 assert_eq!(int, Some(24));
171 }
172}