glib/
closure.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// TODO: support marshaller.
4
5use 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    // rustdoc-stripper-ignore-next
31    /// Creates a new closure around a Rust closure.
32    ///
33    /// See [`glib::closure!`](macro@crate::closure) for a way to create a closure with concrete
34    /// types.
35    ///
36    /// # Panics
37    ///
38    /// Invoking the closure with wrong argument types or returning the wrong return value type
39    /// will panic.
40    ///
41    /// # Example
42    ///
43    /// ```
44    /// use glib::prelude::*;
45    ///
46    /// let closure = glib::RustClosure::new(|values| {
47    ///     let x = values[0].get::<i32>().unwrap();
48    ///     Some((x + 1).to_value())
49    /// });
50    ///
51    /// assert_eq!(
52    ///     closure.invoke::<i32>(&[&1i32]),
53    ///     2,
54    /// );
55    /// ```
56    #[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    // rustdoc-stripper-ignore-next
62    /// Creates a new closure around a Rust closure.
63    ///
64    /// See [`glib::closure_local!`](crate::closure_local) for a way to create a closure with
65    /// concrete types.
66    ///
67    /// # Panics
68    ///
69    /// Invoking the closure with wrong argument types or returning the wrong return value type
70    /// will panic.
71    ///
72    /// Invoking the closure from a different thread than this one will panic.
73    #[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    // rustdoc-stripper-ignore-next
79    /// Invokes the closure with the given arguments.
80    ///
81    /// For invalidated closures this returns the "default" value of the return type. For nullable
82    /// types this is `None`, which means that e.g. requesting `R = String` will panic will `R =
83    /// Option<String>` will return `None`.
84    ///
85    /// # Panics
86    ///
87    /// The argument types and return value type must match the ones expected by the closure or
88    /// otherwise this function panics.
89    #[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    // rustdoc-stripper-ignore-next
102    /// Invokes the closure with the given arguments.
103    ///
104    /// For invalidated closures this returns the "default" value of the return type.
105    ///
106    /// # Panics
107    ///
108    /// The argument types and return value type must match the ones expected by the closure or
109    /// otherwise this function panics.
110    #[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    // rustdoc-stripper-ignore-next
116    /// Invalidates the closure.
117    ///
118    /// Invoking an invalidated closure has no effect.
119    #[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    // rustdoc-stripper-ignore-next
148    /// Creates a new closure around a Rust closure.
149    ///
150    /// Note that [`RustClosure`] provides more convenient and non-unsafe API for invoking
151    /// closures. This type mostly exists for FFI interop.
152    ///
153    /// # Panics
154    ///
155    /// Invoking the closure with wrong argument types or returning the wrong return value type
156    /// will panic.
157    ///
158    ///
159    /// # Example
160    ///
161    /// ```
162    /// use glib::prelude::*;
163    ///
164    /// let closure = glib::Closure::new(|values| {
165    ///     let x = values[0].get::<i32>().unwrap();
166    ///     Some((x + 1).to_value())
167    /// });
168    ///
169    /// // Invoking non-Rust closures is unsafe because of possibly missing
170    /// // argument and return value type checks.
171    /// let res = unsafe {
172    ///     closure
173    ///         .invoke_with_values(glib::Type::I32, &[1i32.to_value()])
174    ///         .and_then(|v| v.get::<i32>().ok())
175    ///         .expect("Invalid return value")
176    /// };
177    ///
178    /// assert_eq!(res, 2);
179    /// ```
180    #[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    // rustdoc-stripper-ignore-next
186    /// Creates a new closure around a Rust closure.
187    ///
188    /// Note that [`RustClosure`] provides more convenient and non-unsafe API for invoking
189    /// closures. This type mostly exists for FFI interop.
190    ///
191    /// # Panics
192    ///
193    /// Invoking the closure with wrong argument types or returning the wrong return value type
194    /// will panic.
195    ///
196    /// Invoking the closure from a different thread than this one will panic.
197    #[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    // rustdoc-stripper-ignore-next
205    /// Creates a new closure around a Rust closure.
206    ///
207    /// # Safety
208    ///
209    /// The captured variables of the closure must stay valid as long as the return value of this
210    /// constructor does, and it must be valid to call the closure from any thread that is used by
211    /// callers.
212    #[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                    // callback is dropped here.
272                }
273            }
274
275            // Due to bitfields we have to do our own calculations here for the size of the GClosure:
276            // - 4: 32 bits in guint bitfields at the beginning
277            // - padding due to alignment needed for the following pointer
278            // - 3 * size_of<*mut c_void>: 3 pointers
279            // We don't store any custom data ourselves in the GClosure
280            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    // rustdoc-stripper-ignore-next
293    /// Invokes the closure with the given arguments.
294    ///
295    /// For invalidated closures this returns the "default" value of the return type.
296    ///
297    /// # Safety
298    ///
299    /// The argument types and return value type must match the ones expected by the closure or
300    /// otherwise the behaviour is undefined.
301    ///
302    /// Closures created from Rust via e.g. [`Closure::new`] will panic on type mismatches but
303    /// this is not guaranteed for closures created from other languages.
304    #[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    // rustdoc-stripper-ignore-next
335    /// Invalidates the closure.
336    ///
337    /// Invoking an invalidated closure has no effect.
338    #[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}