// Take a look at the license at the top of the repository in the LICENSE file. // TODO: support marshaller. use std::{mem, ptr, slice}; use libc::{c_uint, c_void}; use crate::{prelude::*, translate::*, value::FromValue, Type, Value}; wrapper! { #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] #[doc(alias = "GClosure")] pub struct Closure(Shared); match fn { ref => |ptr| { gobject_ffi::g_closure_ref(ptr); gobject_ffi::g_closure_sink(ptr); }, unref => |ptr| gobject_ffi::g_closure_unref(ptr), type_ => || gobject_ffi::g_closure_get_type(), } } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct RustClosure(Closure); impl RustClosure { // rustdoc-stripper-ignore-next /// Creates a new closure around a Rust closure. /// /// See [`glib::closure!`](macro@crate::closure) for a way to create a closure with concrete /// types. /// /// # Panics /// /// Invoking the closure with wrong argument types or returning the wrong return value type /// will panic. /// /// # Example /// /// ``` /// use glib::prelude::*; /// /// let closure = glib::RustClosure::new(|values| { /// let x = values[0].get::().unwrap(); /// Some((x + 1).to_value()) /// }); /// /// assert_eq!( /// closure.invoke::(&[&1i32]), /// 2, /// ); /// ``` #[doc(alias = "g_closure_new")] pub fn new Option + Send + Sync + 'static>(callback: F) -> Self { Self(Closure::new(callback)) } // rustdoc-stripper-ignore-next /// Creates a new closure around a Rust closure. /// /// See [`glib::closure_local!`](crate::closure_local) for a way to create a closure with /// concrete types. /// /// # Panics /// /// Invoking the closure with wrong argument types or returning the wrong return value type /// will panic. /// /// Invoking the closure from a different thread than this one will panic. #[doc(alias = "g_closure_new")] pub fn new_local Option + 'static>(callback: F) -> Self { Self(Closure::new_local(callback)) } // rustdoc-stripper-ignore-next /// Invokes the closure with the given arguments. /// /// For invalidated closures this returns the "default" value of the return type. For nullable /// types this is `None`, which means that e.g. requesting `R = String` will panic will `R = /// Option` will return `None`. /// /// # Panics /// /// The argument types and return value type must match the ones expected by the closure or /// otherwise this function panics. #[doc(alias = "g_closure_invoke")] pub fn invoke(&self, values: &[&dyn ToValue]) -> R { let values = values .iter() .copied() .map(ToValue::to_value) .collect::>(); R::try_from_closure_return_value(self.invoke_with_values(R::static_type(), &values)) .expect("Invalid return value") } // rustdoc-stripper-ignore-next /// Invokes the closure with the given arguments. /// /// For invalidated closures this returns the "default" value of the return type. /// /// # Panics /// /// The argument types and return value type must match the ones expected by the closure or /// otherwise this function panics. #[doc(alias = "g_closure_invoke")] pub fn invoke_with_values(&self, return_type: Type, values: &[Value]) -> Option { unsafe { self.0.invoke_with_values(return_type, values) } } // rustdoc-stripper-ignore-next /// Invalidates the closure. /// /// Invoking an invalidated closure has no effect. #[doc(alias = "g_closure_invalidate")] pub fn invalidate(&self) { self.0.invalidate(); } } impl From for Closure { #[inline] fn from(c: RustClosure) -> Self { c.0 } } impl AsRef for RustClosure { #[inline] fn as_ref(&self) -> &Closure { &self.0 } } impl AsRef for Closure { #[inline] fn as_ref(&self) -> &Closure { self } } impl Closure { // rustdoc-stripper-ignore-next /// Creates a new closure around a Rust closure. /// /// Note that [`RustClosure`] provides more convenient and non-unsafe API for invoking /// closures. This type mostly exists for FFI interop. /// /// # Panics /// /// Invoking the closure with wrong argument types or returning the wrong return value type /// will panic. /// /// /// # Example /// /// ``` /// use glib::prelude::*; /// /// let closure = glib::Closure::new(|values| { /// let x = values[0].get::().unwrap(); /// Some((x + 1).to_value()) /// }); /// /// // Invoking non-Rust closures is unsafe because of possibly missing /// // argument and return value type checks. /// let res = unsafe { /// closure /// .invoke_with_values(glib::Type::I32, &[1i32.to_value()]) /// .and_then(|v| v.get::().ok()) /// .expect("Invalid return value") /// }; /// /// assert_eq!(res, 2); /// ``` #[doc(alias = "g_closure_new")] pub fn new Option + Send + Sync + 'static>(callback: F) -> Self { unsafe { Self::new_unsafe(callback) } } // rustdoc-stripper-ignore-next /// Creates a new closure around a Rust closure. /// /// Note that [`RustClosure`] provides more convenient and non-unsafe API for invoking /// closures. This type mostly exists for FFI interop. /// /// # Panics /// /// Invoking the closure with wrong argument types or returning the wrong return value type /// will panic. /// /// Invoking the closure from a different thread than this one will panic. #[doc(alias = "g_closure_new")] pub fn new_local Option + 'static>(callback: F) -> Self { let callback = crate::thread_guard::ThreadGuard::new(callback); unsafe { Self::new_unsafe(move |values| (callback.get_ref())(values)) } } // rustdoc-stripper-ignore-next /// Creates a new closure around a Rust closure. /// /// # Safety /// /// The captured variables of the closure must stay valid as long as the return value of this /// constructor does, and it must be valid to call the closure from any thread that is used by /// callers. #[doc(alias = "g_closure_new")] pub unsafe fn new_unsafe Option>(callback: F) -> Self { unsafe extern "C" fn marshal( _closure: *mut gobject_ffi::GClosure, return_value: *mut gobject_ffi::GValue, n_param_values: c_uint, param_values: *const gobject_ffi::GValue, _invocation_hint: *mut c_void, marshal_data: *mut c_void, ) where F: Fn(&[Value]) -> Option, { let values = if n_param_values == 0 { &[] } else { slice::from_raw_parts(param_values as *const _, n_param_values as usize) }; let callback: &F = &*(marshal_data as *mut _); let result = callback(values); if return_value.is_null() { assert!( result.is_none(), "Closure returned a return value but the caller did not expect one" ); } else { let return_value = &mut *(return_value as *mut Value); match result { Some(result) => { assert!( result.type_().is_a(return_value.type_()), "Closure returned a value of type {} but caller expected {}", result.type_(), return_value.type_() ); *return_value = result; } None if return_value.type_() == Type::INVALID => (), None => { panic!( "Closure returned no value but the caller expected a value of type {}", return_value.type_() ); } } } } unsafe extern "C" fn finalize( notify_data: *mut c_void, _closure: *mut gobject_ffi::GClosure, ) where F: Fn(&[Value]) -> Option, { let _callback: Box = Box::from_raw(notify_data as *mut _); // callback is dropped here. } // Due to bitfields we have to do our own calculations here for the size of the GClosure: // - 4: 32 bits in guint bitfields at the beginning // - padding due to alignment needed for the following pointer // - 3 * size_of<*mut c_void>: 3 pointers // We don't store any custom data ourselves in the GClosure let size = u32::max(4, mem::align_of::<*mut c_void>() as u32) + 3 * mem::size_of::<*mut c_void>() as u32; let closure = gobject_ffi::g_closure_new_simple(size, ptr::null_mut()); let callback = Box::new(callback); let ptr: *mut F = Box::into_raw(callback); let ptr: *mut c_void = ptr as *mut _; gobject_ffi::g_closure_set_meta_marshal(closure, ptr, Some(marshal::)); gobject_ffi::g_closure_add_finalize_notifier(closure, ptr, Some(finalize::)); from_glib_none(closure) } // rustdoc-stripper-ignore-next /// Invokes the closure with the given arguments. /// /// For invalidated closures this returns the "default" value of the return type. /// /// # Safety /// /// The argument types and return value type must match the ones expected by the closure or /// otherwise the behaviour is undefined. /// /// Closures created from Rust via e.g. [`Closure::new`] will panic on type mismatches but /// this is not guaranteed for closures created from other languages. #[doc(alias = "g_closure_invoke")] pub unsafe fn invoke_with_values(&self, return_type: Type, values: &[Value]) -> Option { let mut result = if return_type == Type::UNIT { Value::uninitialized() } else { Value::from_type(return_type) }; let result_ptr = if return_type == Type::UNIT { ptr::null_mut() } else { result.to_glib_none_mut().0 }; gobject_ffi::g_closure_invoke( self.to_glib_none().0, result_ptr, values.len() as u32, mut_override(values.as_ptr()) as *mut gobject_ffi::GValue, ptr::null_mut(), ); if return_type == Type::UNIT { None } else { Some(result) } } // rustdoc-stripper-ignore-next /// Invalidates the closure. /// /// Invoking an invalidated closure has no effect. #[doc(alias = "g_closure_invalidate")] pub fn invalidate(&self) { unsafe { gobject_ffi::g_closure_invalidate(self.to_glib_none().0); } } } pub trait IntoClosureReturnValue { fn into_closure_return_value(self) -> Option; } impl IntoClosureReturnValue for () { #[inline] fn into_closure_return_value(self) -> Option { None } } impl> IntoClosureReturnValue for T { #[inline] fn into_closure_return_value(self) -> Option { Some(self.into()) } } pub trait TryFromClosureReturnValue: StaticType + Sized + 'static { fn try_from_closure_return_value(v: Option) -> Result; } impl TryFromClosureReturnValue for () { #[inline] fn try_from_closure_return_value(v: Option) -> Result { match v { None => Ok(()), Some(v) => Err(bool_error!( "Invalid return value: expected (), got {}", v.type_() )), } } } impl FromValue<'a> + StaticType + 'static> TryFromClosureReturnValue for T { #[inline] fn try_from_closure_return_value(v: Option) -> Result { v.ok_or_else(|| { bool_error!( "Invalid return value: expected {}, got ()", T::static_type() ) }) .and_then(|v| { v.get_owned::().map_err(|_| { bool_error!( "Invalid return value: expected {}, got {}", T::static_type(), v.type_() ) }) }) } } unsafe impl Send for Closure {} unsafe impl Sync for Closure {} #[cfg(test)] mod tests { use std::sync::{ atomic::{AtomicUsize, Ordering}, Arc, }; use super::*; #[allow(clippy::unnecessary_wraps)] fn closure_fn(values: &[Value]) -> Option { assert_eq!(values.len(), 2); let string_arg = values[0].get::<&str>(); assert_eq!(string_arg, Ok("test")); let int_arg = values[1].get::(); assert_eq!(int_arg, Ok(42)); Some(24.to_value()) } #[test] fn test_closure() { let call_count = Arc::new(AtomicUsize::new(0)); let count = call_count.clone(); let closure = RustClosure::new(move |values| { count.fetch_add(1, Ordering::Relaxed); assert_eq!(values.len(), 2); let string_arg = values[0].get::<&str>(); assert_eq!(string_arg, Ok("test")); let int_arg = values[1].get::(); assert_eq!(int_arg, Ok(42)); None }); closure.invoke::<()>(&[&"test", &42]); assert_eq!(call_count.load(Ordering::Relaxed), 1); closure.invoke::<()>(&[&"test", &42]); assert_eq!(call_count.load(Ordering::Relaxed), 2); closure.invalidate(); closure.invoke::<()>(&[&"test", &42]); assert_eq!(call_count.load(Ordering::Relaxed), 2); let closure = RustClosure::new(closure_fn); let result = closure.invoke::(&[&"test", &42]); assert_eq!(result, 24); closure.invalidate(); let result = closure.invoke::(&[&"test", &42]); assert_eq!(result, 0); } }