1use std::{mem, ptr, slice};
6
7use libc::{c_uint, c_void};
8
9use crate::{Type, Value, gobject_ffi, prelude::*, translate::*};
10
11wrapper! {
12 #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
13 #[doc(alias = "GClosure")]
14 pub struct Closure(Shared<gobject_ffi::GClosure>);
15
16 match fn {
17 ref => |ptr| {
18 gobject_ffi::g_closure_ref(ptr);
19 gobject_ffi::g_closure_sink(ptr);
20 },
21 unref => |ptr| gobject_ffi::g_closure_unref(ptr),
22 type_ => || gobject_ffi::g_closure_get_type(),
23 }
24}
25
26#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
27pub struct RustClosure(Closure);
28
29impl RustClosure {
30 #[doc(alias = "g_closure_new")]
57 pub fn new<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(callback: F) -> Self {
58 Self(Closure::new(callback))
59 }
60
61 #[doc(alias = "g_closure_new")]
74 pub fn new_local<F: Fn(&[Value]) -> Option<Value> + 'static>(callback: F) -> Self {
75 Self(Closure::new_local(callback))
76 }
77
78 #[doc(alias = "g_closure_invoke")]
90 pub fn invoke<R: TryFromClosureReturnValue>(&self, values: &[&dyn ToValue]) -> R {
91 let values = values
92 .iter()
93 .copied()
94 .map(ToValue::to_value)
95 .collect::<smallvec::SmallVec<[_; 10]>>();
96
97 R::try_from_closure_return_value(self.invoke_with_values(R::static_type(), &values))
98 .expect("Invalid return value")
99 }
100
101 #[doc(alias = "g_closure_invoke")]
111 pub fn invoke_with_values(&self, return_type: Type, values: &[Value]) -> Option<Value> {
112 unsafe { self.0.invoke_with_values(return_type, values) }
113 }
114
115 #[doc(alias = "g_closure_invalidate")]
120 pub fn invalidate(&self) {
121 self.0.invalidate();
122 }
123}
124
125impl From<RustClosure> for Closure {
126 #[inline]
127 fn from(c: RustClosure) -> Self {
128 c.0
129 }
130}
131
132impl AsRef<Closure> for RustClosure {
133 #[inline]
134 fn as_ref(&self) -> &Closure {
135 &self.0
136 }
137}
138
139impl AsRef<Closure> for Closure {
140 #[inline]
141 fn as_ref(&self) -> &Closure {
142 self
143 }
144}
145
146impl Closure {
147 #[doc(alias = "g_closure_new")]
181 pub fn new<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(callback: F) -> Self {
182 unsafe { Self::new_unsafe(callback) }
183 }
184
185 #[doc(alias = "g_closure_new")]
198 pub fn new_local<F: Fn(&[Value]) -> Option<Value> + 'static>(callback: F) -> Self {
199 let callback = crate::thread_guard::ThreadGuard::new(callback);
200
201 unsafe { Self::new_unsafe(move |values| (callback.get_ref())(values)) }
202 }
203
204 #[doc(alias = "g_closure_new")]
213 pub unsafe fn new_unsafe<F: Fn(&[Value]) -> Option<Value>>(callback: F) -> Self {
214 unsafe {
215 unsafe extern "C" fn marshal<F>(
216 _closure: *mut gobject_ffi::GClosure,
217 return_value: *mut gobject_ffi::GValue,
218 n_param_values: c_uint,
219 param_values: *const gobject_ffi::GValue,
220 _invocation_hint: *mut c_void,
221 marshal_data: *mut c_void,
222 ) where
223 F: Fn(&[Value]) -> Option<Value>,
224 {
225 unsafe {
226 let values = if n_param_values == 0 {
227 &[]
228 } else {
229 slice::from_raw_parts(param_values as *const _, n_param_values as usize)
230 };
231 let callback: &F = &*(marshal_data as *mut _);
232 let result = callback(values);
233
234 if return_value.is_null() {
235 assert!(
236 result.is_none(),
237 "Closure returned a return value but the caller did not expect one"
238 );
239 } else {
240 let return_value = &mut *(return_value as *mut Value);
241 match result {
242 Some(result) => {
243 assert!(
244 result.type_().is_a(return_value.type_()),
245 "Closure returned a value of type {} but caller expected {}",
246 result.type_(),
247 return_value.type_()
248 );
249 *return_value = result;
250 }
251 None if return_value.type_() == Type::INVALID => (),
252 None => {
253 panic!(
254 "Closure returned no value but the caller expected a value of type {}",
255 return_value.type_()
256 );
257 }
258 }
259 }
260 }
261 }
262
263 unsafe extern "C" fn finalize<F>(
264 notify_data: *mut c_void,
265 _closure: *mut gobject_ffi::GClosure,
266 ) where
267 F: Fn(&[Value]) -> Option<Value>,
268 {
269 unsafe {
270 let _callback: Box<F> = Box::from_raw(notify_data as *mut _);
271 }
273 }
274
275 let size = u32::max(4, mem::align_of::<*mut c_void>() as u32)
281 + 3 * mem::size_of::<*mut c_void>() as u32;
282 let closure = gobject_ffi::g_closure_new_simple(size, ptr::null_mut());
283 let callback = Box::new(callback);
284 let ptr: *mut F = Box::into_raw(callback);
285 let ptr: *mut c_void = ptr as *mut _;
286 gobject_ffi::g_closure_set_meta_marshal(closure, ptr, Some(marshal::<F>));
287 gobject_ffi::g_closure_add_finalize_notifier(closure, ptr, Some(finalize::<F>));
288 from_glib_none(closure)
289 }
290 }
291
292 #[doc(alias = "g_closure_invoke")]
305 pub unsafe fn invoke_with_values(&self, return_type: Type, values: &[Value]) -> Option<Value> {
306 unsafe {
307 let mut result = if return_type == Type::UNIT {
308 Value::uninitialized()
309 } else {
310 Value::from_type(return_type)
311 };
312 let result_ptr = if return_type == Type::UNIT {
313 ptr::null_mut()
314 } else {
315 result.to_glib_none_mut().0
316 };
317
318 gobject_ffi::g_closure_invoke(
319 self.to_glib_none().0,
320 result_ptr,
321 values.len() as u32,
322 mut_override(values.as_ptr()) as *mut gobject_ffi::GValue,
323 ptr::null_mut(),
324 );
325
326 if return_type == Type::UNIT {
327 None
328 } else {
329 Some(result)
330 }
331 }
332 }
333
334 #[doc(alias = "g_closure_invalidate")]
339 pub fn invalidate(&self) {
340 unsafe {
341 gobject_ffi::g_closure_invalidate(self.to_glib_none().0);
342 }
343 }
344}
345
346pub trait IntoClosureReturnValue {
347 fn into_closure_return_value(self) -> Option<Value>;
348}
349
350impl IntoClosureReturnValue for () {
351 #[inline]
352 fn into_closure_return_value(self) -> Option<Value> {
353 None
354 }
355}
356
357impl<T: Into<Value>> IntoClosureReturnValue for T {
358 #[inline]
359 fn into_closure_return_value(self) -> Option<Value> {
360 Some(self.into())
361 }
362}
363
364pub trait TryFromClosureReturnValue: StaticType + Sized + 'static {
365 fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError>;
366}
367
368impl TryFromClosureReturnValue for () {
369 #[inline]
370 fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError> {
371 match v {
372 None => Ok(()),
373 Some(v) => Err(bool_error!(
374 "Invalid return value: expected (), got {}",
375 v.type_()
376 )),
377 }
378 }
379}
380
381impl<T: for<'a> crate::value::FromValue<'a> + StaticType + 'static> TryFromClosureReturnValue
382 for T
383{
384 #[inline]
385 fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError> {
386 v.ok_or_else(|| {
387 bool_error!(
388 "Invalid return value: expected {}, got ()",
389 T::static_type()
390 )
391 })
392 .and_then(|v| {
393 v.get_owned::<T>().map_err(|_| {
394 bool_error!(
395 "Invalid return value: expected {}, got {}",
396 T::static_type(),
397 v.type_()
398 )
399 })
400 })
401 }
402}
403
404unsafe impl Send for Closure {}
405unsafe impl Sync for Closure {}
406
407#[cfg(test)]
408mod tests {
409 use std::sync::{
410 Arc,
411 atomic::{AtomicUsize, Ordering},
412 };
413
414 use super::*;
415
416 #[allow(clippy::unnecessary_wraps)]
417 fn closure_fn(values: &[Value]) -> Option<Value> {
418 assert_eq!(values.len(), 2);
419 let string_arg = values[0].get::<&str>();
420 assert_eq!(string_arg, Ok("test"));
421 let int_arg = values[1].get::<i32>();
422 assert_eq!(int_arg, Ok(42));
423 Some(24.to_value())
424 }
425
426 #[test]
427 fn test_closure() {
428 let call_count = Arc::new(AtomicUsize::new(0));
429
430 let count = call_count.clone();
431 let closure = RustClosure::new(move |values| {
432 count.fetch_add(1, Ordering::Relaxed);
433 assert_eq!(values.len(), 2);
434 let string_arg = values[0].get::<&str>();
435 assert_eq!(string_arg, Ok("test"));
436 let int_arg = values[1].get::<i32>();
437 assert_eq!(int_arg, Ok(42));
438 None
439 });
440 closure.invoke::<()>(&[&"test", &42]);
441 assert_eq!(call_count.load(Ordering::Relaxed), 1);
442
443 closure.invoke::<()>(&[&"test", &42]);
444 assert_eq!(call_count.load(Ordering::Relaxed), 2);
445
446 closure.invalidate();
447 closure.invoke::<()>(&[&"test", &42]);
448 assert_eq!(call_count.load(Ordering::Relaxed), 2);
449
450 let closure = RustClosure::new(closure_fn);
451 let result = closure.invoke::<i32>(&[&"test", &42]);
452 assert_eq!(result, 24);
453 closure.invalidate();
454 let result = closure.invoke::<i32>(&[&"test", &42]);
455 assert_eq!(result, 0);
456 }
457}