| 1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
| 2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 |
| 3 | |
| 4 | /*! |
| 5 | Callback that can be connected to one single handler. |
| 6 | |
| 7 | TODO: reconsider if we should rename that to `Event` |
| 8 | but then it should also be renamed everywhere, including in the language grammar |
| 9 | */ |
| 10 | |
| 11 | #![warn (missing_docs)] |
| 12 | |
| 13 | use alloc::boxed::Box; |
| 14 | use core::cell::Cell; |
| 15 | |
| 16 | /// A Callback that can be connected to a handler. |
| 17 | /// |
| 18 | /// The Arg represents the argument. It should always be a tuple |
| 19 | /// |
| 20 | #[repr (C)] |
| 21 | pub struct Callback<Arg: ?Sized, Ret = ()> { |
| 22 | /// FIXME: `Box<dyn>` is a fat object and we probably want to put an erased type in there |
| 23 | handler: Cell<Option<Box<dyn FnMut(&Arg, &mut Ret)>>>, |
| 24 | } |
| 25 | |
| 26 | impl<Arg: ?Sized, Ret> Default for Callback<Arg, Ret> { |
| 27 | fn default() -> Self { |
| 28 | Self { handler: Default::default() } |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | impl<Arg: ?Sized, Ret: Default> Callback<Arg, Ret> { |
| 33 | /// Call the callback with the given argument. |
| 34 | pub fn call(&self, a: &Arg) -> Ret { |
| 35 | let mut r = Ret::default(); |
| 36 | if let Some(mut h) = self.handler.take() { |
| 37 | h(a, &mut r); |
| 38 | assert!(self.handler.take().is_none(), "Callback Handler set while called" ); |
| 39 | self.handler.set(Some(h)); |
| 40 | } |
| 41 | r |
| 42 | } |
| 43 | |
| 44 | /// Return whether a callback is registered or not. |
| 45 | pub fn has_handler(&self) -> bool { |
| 46 | let handler = self.handler.take(); |
| 47 | let result = handler.is_some(); |
| 48 | self.handler.set(handler); |
| 49 | result |
| 50 | } |
| 51 | |
| 52 | /// Set an handler to be called when the callback is called |
| 53 | /// |
| 54 | /// There can only be one single handler per callback. |
| 55 | pub fn set_handler(&self, mut f: impl FnMut(&Arg) -> Ret + 'static) { |
| 56 | self.handler.set(Some(Box::new(move |a: &Arg, r: &mut Ret| *r = f(a)))); |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | #[test ] |
| 61 | fn callback_simple_test() { |
| 62 | use std::rc::Rc; |
| 63 | #[derive (Default)] |
| 64 | struct Component { |
| 65 | pressed: core::cell::Cell<bool>, |
| 66 | clicked: Callback<()>, |
| 67 | } |
| 68 | let c = Rc::new(Component::default()); |
| 69 | let weak = Rc::downgrade(&c); |
| 70 | c.clicked.set_handler(move |()| weak.upgrade().unwrap().pressed.set(true)); |
| 71 | c.clicked.call(&()); |
| 72 | assert!(c.pressed.get()); |
| 73 | } |
| 74 | |
| 75 | #[cfg (feature = "ffi" )] |
| 76 | pub(crate) mod ffi { |
| 77 | #![allow (unsafe_code)] |
| 78 | |
| 79 | use super::*; |
| 80 | |
| 81 | #[allow (non_camel_case_types)] |
| 82 | type c_void = (); |
| 83 | #[repr (C)] |
| 84 | /// Has the same layout as Callback<_> |
| 85 | pub struct CallbackOpaque(*const c_void, *const c_void); |
| 86 | |
| 87 | static_assertions::assert_eq_align!(CallbackOpaque, Callback<()>); |
| 88 | static_assertions::assert_eq_size!(CallbackOpaque, Callback<()>); |
| 89 | static_assertions::assert_eq_align!(CallbackOpaque, Callback<(alloc::string::String,)>); |
| 90 | static_assertions::assert_eq_size!(CallbackOpaque, Callback<(alloc::string::String,)>); |
| 91 | |
| 92 | /// Initialize the callback. |
| 93 | /// slint_callback_drop must be called. |
| 94 | #[no_mangle ] |
| 95 | pub unsafe extern "C" fn slint_callback_init(out: *mut CallbackOpaque) { |
| 96 | assert_eq!(core::mem::size_of::<CallbackOpaque>(), core::mem::size_of::<Callback<()>>()); |
| 97 | core::ptr::write(out as *mut Callback<()>, Default::default()); |
| 98 | } |
| 99 | |
| 100 | /// Emit the callback |
| 101 | #[no_mangle ] |
| 102 | pub unsafe extern "C" fn slint_callback_call( |
| 103 | sig: *const CallbackOpaque, |
| 104 | arg: *const c_void, |
| 105 | ret: *mut c_void, |
| 106 | ) { |
| 107 | let sig = &*(sig as *const Callback<c_void>); |
| 108 | if let Some(mut h) = sig.handler.take() { |
| 109 | h(&*arg, &mut *ret); |
| 110 | assert!(sig.handler.take().is_none(), "Callback Handler set while called" ); |
| 111 | sig.handler.set(Some(h)); |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | /// Set callback handler. |
| 116 | /// |
| 117 | /// The binding has signature fn(user_data) |
| 118 | #[no_mangle ] |
| 119 | pub unsafe extern "C" fn slint_callback_set_handler( |
| 120 | sig: *const CallbackOpaque, |
| 121 | binding: extern "C" fn(user_data: *mut c_void, arg: *const c_void, ret: *mut c_void), |
| 122 | user_data: *mut c_void, |
| 123 | drop_user_data: Option<extern "C" fn(*mut c_void)>, |
| 124 | ) { |
| 125 | let sig = &mut *(sig as *mut Callback<c_void>); |
| 126 | |
| 127 | struct UserData { |
| 128 | user_data: *mut c_void, |
| 129 | drop_user_data: Option<extern "C" fn(*mut c_void)>, |
| 130 | binding: extern "C" fn(user_data: *mut c_void, arg: *const c_void, ret: *mut c_void), |
| 131 | } |
| 132 | |
| 133 | impl Drop for UserData { |
| 134 | fn drop(&mut self) { |
| 135 | if let Some(x) = self.drop_user_data { |
| 136 | x(self.user_data) |
| 137 | } |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | impl UserData { |
| 142 | /// Safety: the arguments must be valid pointers |
| 143 | unsafe fn call(&self, arg: *const c_void, ret: *mut c_void) { |
| 144 | (self.binding)(self.user_data, arg, ret) |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | let ud = UserData { user_data, drop_user_data, binding }; |
| 149 | sig.handler.set(Some(Box::new(move |a: &(), r: &mut ()| { |
| 150 | ud.call(a as *const c_void, r as *mut c_void) |
| 151 | }))); |
| 152 | } |
| 153 | |
| 154 | /// Destroy callback |
| 155 | #[no_mangle ] |
| 156 | pub unsafe extern "C" fn slint_callback_drop(handle: *mut CallbackOpaque) { |
| 157 | core::ptr::drop_in_place(handle as *mut Callback<()>); |
| 158 | } |
| 159 | } |
| 160 | |