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::{prelude::*, translate::*, value::FromValue, Type, Value};
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 extern "C" fn marshal<F>(
215 _closure: *mut gobject_ffi::GClosure,
216 return_value: *mut gobject_ffi::GValue,
217 n_param_values: c_uint,
218 param_values: *const gobject_ffi::GValue,
219 _invocation_hint: *mut c_void,
220 marshal_data: *mut c_void,
221 ) where
222 F: Fn(&[Value]) -> Option<Value>,
223 {
224 let values = if n_param_values == 0 {
225 &[]
226 } else {
227 slice::from_raw_parts(param_values as *const _, n_param_values as usize)
228 };
229 let callback: &F = &*(marshal_data as *mut _);
230 let result = callback(values);
231
232 if return_value.is_null() {
233 assert!(
234 result.is_none(),
235 "Closure returned a return value but the caller did not expect one"
236 );
237 } else {
238 let return_value = &mut *(return_value as *mut Value);
239 match result {
240 Some(result) => {
241 assert!(
242 result.type_().is_a(return_value.type_()),
243 "Closure returned a value of type {} but caller expected {}",
244 result.type_(),
245 return_value.type_()
246 );
247 *return_value = result;
248 }
249 None if return_value.type_() == Type::INVALID => (),
250 None => {
251 panic!(
252 "Closure returned no value but the caller expected a value of type {}",
253 return_value.type_()
254 );
255 }
256 }
257 }
258 }
259
260 unsafe extern "C" fn finalize<F>(
261 notify_data: *mut c_void,
262 _closure: *mut gobject_ffi::GClosure,
263 ) where
264 F: Fn(&[Value]) -> Option<Value>,
265 {
266 let _callback: Box<F> = Box::from_raw(notify_data as *mut _);
267 // callback is dropped here.
268 }
269
270 // Due to bitfields we have to do our own calculations here for the size of the GClosure:
271 // - 4: 32 bits in guint bitfields at the beginning
272 // - padding due to alignment needed for the following pointer
273 // - 3 * size_of<*mut c_void>: 3 pointers
274 // We don't store any custom data ourselves in the GClosure
275 let size = u32::max(4, mem::align_of::<*mut c_void>() as u32)
276 + 3 * mem::size_of::<*mut c_void>() as u32;
277 let closure = gobject_ffi::g_closure_new_simple(size, ptr::null_mut());
278 let callback = Box::new(callback);
279 let ptr: *mut F = Box::into_raw(callback);
280 let ptr: *mut c_void = ptr as *mut _;
281 gobject_ffi::g_closure_set_meta_marshal(closure, ptr, Some(marshal::<F>));
282 gobject_ffi::g_closure_add_finalize_notifier(closure, ptr, Some(finalize::<F>));
283 from_glib_none(closure)
284 }
285
286 // rustdoc-stripper-ignore-next
287 /// Invokes the closure with the given arguments.
288 ///
289 /// For invalidated closures this returns the "default" value of the return type.
290 ///
291 /// # Safety
292 ///
293 /// The argument types and return value type must match the ones expected by the closure or
294 /// otherwise the behaviour is undefined.
295 ///
296 /// Closures created from Rust via e.g. [`Closure::new`] will panic on type mismatches but
297 /// this is not guaranteed for closures created from other languages.
298 #[doc(alias = "g_closure_invoke")]
299 pub unsafe fn invoke_with_values(&self, return_type: Type, values: &[Value]) -> Option<Value> {
300 let mut result = if return_type == Type::UNIT {
301 Value::uninitialized()
302 } else {
303 Value::from_type(return_type)
304 };
305 let result_ptr = if return_type == Type::UNIT {
306 ptr::null_mut()
307 } else {
308 result.to_glib_none_mut().0
309 };
310
311 gobject_ffi::g_closure_invoke(
312 self.to_glib_none().0,
313 result_ptr,
314 values.len() as u32,
315 mut_override(values.as_ptr()) as *mut gobject_ffi::GValue,
316 ptr::null_mut(),
317 );
318
319 if return_type == Type::UNIT {
320 None
321 } else {
322 Some(result)
323 }
324 }
325
326 // rustdoc-stripper-ignore-next
327 /// Invalidates the closure.
328 ///
329 /// Invoking an invalidated closure has no effect.
330 #[doc(alias = "g_closure_invalidate")]
331 pub fn invalidate(&self) {
332 unsafe {
333 gobject_ffi::g_closure_invalidate(self.to_glib_none().0);
334 }
335 }
336}
337
338pub trait IntoClosureReturnValue {
339 fn into_closure_return_value(self) -> Option<Value>;
340}
341
342impl IntoClosureReturnValue for () {
343 #[inline]
344 fn into_closure_return_value(self) -> Option<Value> {
345 None
346 }
347}
348
349impl<T: Into<Value>> IntoClosureReturnValue for T {
350 #[inline]
351 fn into_closure_return_value(self) -> Option<Value> {
352 Some(self.into())
353 }
354}
355
356pub trait TryFromClosureReturnValue: StaticType + Sized + 'static {
357 fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError>;
358}
359
360impl TryFromClosureReturnValue for () {
361 #[inline]
362 fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError> {
363 match v {
364 None => Ok(()),
365 Some(v: Value) => Err(bool_error!(
366 "Invalid return value: expected (), got {}",
367 v.type_()
368 )),
369 }
370 }
371}
372
373impl<T: for<'a> FromValue<'a> + StaticType + 'static> TryFromClosureReturnValue for T {
374 #[inline]
375 fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError> {
376 v.ok_or_else(|| {
377 bool_error!(
378 "Invalid return value: expected {}, got ()",
379 T::static_type()
380 )
381 })
382 .and_then(|v: Value| {
383 v.get_owned::<T>().map_err(|_| {
384 bool_error!(
385 "Invalid return value: expected {}, got {}",
386 T::static_type(),
387 v.type_()
388 )
389 })
390 })
391 }
392}
393
394unsafe impl Send for Closure {}
395unsafe impl Sync for Closure {}
396
397#[cfg(test)]
398mod tests {
399 use std::sync::{
400 atomic::{AtomicUsize, Ordering},
401 Arc,
402 };
403
404 use super::*;
405
406 #[allow(clippy::unnecessary_wraps)]
407 fn closure_fn(values: &[Value]) -> Option<Value> {
408 assert_eq!(values.len(), 2);
409 let string_arg = values[0].get::<&str>();
410 assert_eq!(string_arg, Ok("test"));
411 let int_arg = values[1].get::<i32>();
412 assert_eq!(int_arg, Ok(42));
413 Some(24.to_value())
414 }
415
416 #[test]
417 fn test_closure() {
418 let call_count = Arc::new(AtomicUsize::new(0));
419
420 let count = call_count.clone();
421 let closure = RustClosure::new(move |values| {
422 count.fetch_add(1, Ordering::Relaxed);
423 assert_eq!(values.len(), 2);
424 let string_arg = values[0].get::<&str>();
425 assert_eq!(string_arg, Ok("test"));
426 let int_arg = values[1].get::<i32>();
427 assert_eq!(int_arg, Ok(42));
428 None
429 });
430 closure.invoke::<()>(&[&"test", &42]);
431 assert_eq!(call_count.load(Ordering::Relaxed), 1);
432
433 closure.invoke::<()>(&[&"test", &42]);
434 assert_eq!(call_count.load(Ordering::Relaxed), 2);
435
436 closure.invalidate();
437 closure.invoke::<()>(&[&"test", &42]);
438 assert_eq!(call_count.load(Ordering::Relaxed), 2);
439
440 let closure = RustClosure::new(closure_fn);
441 let result = closure.invoke::<i32>(&[&"test", &42]);
442 assert_eq!(result, 24);
443 closure.invalidate();
444 let result = closure.invoke::<i32>(&[&"test", &42]);
445 assert_eq!(result, 0);
446 }
447}
448