Skip to main content

i_slint_core/
callbacks.rs

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/*!
5Callback that can be connected to one single handler.
6
7TODO: reconsider if we should rename that to `Event`
8but then it should also be renamed everywhere, including in the language grammar
9*/
10
11#![warn(missing_docs)]
12
13use alloc::boxed::Box;
14use 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)]
21pub 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
26impl<Arg: ?Sized, Ret> Default for Callback<Arg, Ret> {
27    fn default() -> Self {
28        Self { handler: Default::default() }
29    }
30}
31
32impl<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]
61fn 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")]
76pub(crate) mod ffi {
77    #![allow(unsafe_code)]
78
79    use super::*;
80    use core::ffi::c_void;
81
82    #[repr(C)]
83    /// Has the same layout as Callback<_>
84    pub struct CallbackOpaque(*const c_void, *const c_void);
85
86    static_assertions::assert_eq_align!(CallbackOpaque, Callback<(), c_void>);
87    static_assertions::assert_eq_size!(CallbackOpaque, Callback<(), c_void>);
88    static_assertions::assert_eq_align!(CallbackOpaque, Callback<(alloc::string::String,), c_void>);
89    static_assertions::assert_eq_size!(CallbackOpaque, Callback<(alloc::string::String,), c_void>);
90
91    /// Initialize the callback.
92    /// slint_callback_drop must be called.
93    #[unsafe(no_mangle)]
94    pub unsafe extern "C" fn slint_callback_init(out: *mut CallbackOpaque) {
95        assert_eq!(core::mem::size_of::<CallbackOpaque>(), core::mem::size_of::<Callback<()>>());
96        unsafe { core::ptr::write(out as *mut Callback<()>, Default::default()) };
97    }
98
99    /// Emit the callback
100    #[unsafe(no_mangle)]
101    pub unsafe extern "C" fn slint_callback_call(
102        sig: *const CallbackOpaque,
103        arg: *const c_void,
104        ret: *mut c_void,
105    ) {
106        unsafe {
107            let sig = &*(sig as *const Callback<c_void, 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
116    /// Set callback handler.
117    ///
118    /// The binding has signature fn(user_data)
119    #[unsafe(no_mangle)]
120    pub unsafe extern "C" fn slint_callback_set_handler(
121        sig: *const CallbackOpaque,
122        binding: extern "C" fn(user_data: *mut c_void, arg: *const c_void, ret: *mut c_void),
123        user_data: *mut c_void,
124        drop_user_data: Option<extern "C" fn(*mut c_void)>,
125    ) {
126        unsafe {
127            let sig = &mut *(sig as *mut Callback<c_void, c_void>);
128
129            struct UserData {
130                user_data: *mut c_void,
131                drop_user_data: Option<extern "C" fn(*mut c_void)>,
132                binding:
133                    extern "C" fn(user_data: *mut c_void, arg: *const c_void, ret: *mut c_void),
134            }
135
136            impl Drop for UserData {
137                fn drop(&mut self) {
138                    if let Some(x) = self.drop_user_data {
139                        x(self.user_data)
140                    }
141                }
142            }
143
144            impl UserData {
145                /// Safety: the arguments must be valid pointers
146                unsafe fn call(&self, arg: *const c_void, ret: *mut c_void) {
147                    (self.binding)(self.user_data, arg, ret)
148                }
149            }
150
151            let ud = UserData { user_data, drop_user_data, binding };
152            sig.handler.set(Some(Box::new(move |a, r| ud.call(a, r))));
153        }
154    }
155
156    /// Destroy callback
157    #[unsafe(no_mangle)]
158    pub unsafe extern "C" fn slint_callback_drop(handle: *mut CallbackOpaque) {
159        unsafe { core::ptr::drop_in_place(handle as *mut Callback<c_void, c_void>) };
160    }
161}