/* LICENSE BEGIN This file is part of the SixtyFPS Project -- https://sixtyfps.io Copyright (c) 2020 Olivier Goffart Copyright (c) 2020 Simon Hausmann SPDX-License-Identifier: GPL-3.0-only This file is also available under commercial licensing terms. Please contact info@sixtyfps.io for more information. LICENSE END */ /*! Callback that can be connected to one sigle handler. TODO: reconsider if we should rename that to `Event` but then it should also be renamed everywhere, including in the language grammar */ #![warn(missing_docs)] use core::cell::Cell; /// A Callback that can be connected to a handler. /// /// The Arg represents the argument. It should always be a tuple /// #[repr(C)] pub struct Callback { /// FIXME: Box is a fat object and we probaly want to put an erased type in there handler: Cell>>, } impl Default for Callback { fn default() -> Self { Self { handler: Default::default() } } } impl Callback { /// Call the callback with the given argument. pub fn call(&self, a: &Arg) -> Ret { let mut r = Ret::default(); if let Some(h) = self.handler.take() { h(a, &mut r); assert!(self.handler.take().is_none(), "Callback Handler set while callted"); self.handler.set(Some(h)); } r } /// Set an handler to be called when the callback is called /// /// There can only be one single handler per callback. pub fn set_handler(&self, f: impl Fn(&Arg) -> Ret + 'static) { self.handler.set(Some(Box::new(move |a: &Arg, r: &mut Ret| *r = f(a)))); } } #[test] fn callback_simple_test() { use std::rc::Rc; #[derive(Default)] struct Component { pressed: core::cell::Cell, clicked: Callback<()>, } let c = Rc::new(Component::default()); let weak = Rc::downgrade(&c); c.clicked.set_handler(move |()| weak.upgrade().unwrap().pressed.set(true)); c.clicked.call(&()); assert_eq!(c.pressed.get(), true); } #[cfg(not(target_arch = "wasm32"))] pub(crate) mod ffi { #![allow(unsafe_code)] use super::*; #[allow(non_camel_case_types)] type c_void = (); #[repr(C)] /// Has the same layout as Callback<_> pub struct CallbackOpaque(*const c_void, *const c_void); static_assertions::assert_eq_align!(CallbackOpaque, Callback<()>); static_assertions::assert_eq_size!(CallbackOpaque, Callback<()>); static_assertions::assert_eq_align!(CallbackOpaque, Callback<(String,)>); static_assertions::assert_eq_size!(CallbackOpaque, Callback<(String,)>); /// Initialize the callback. /// sixtyfps_callback_drop must be called. #[no_mangle] pub unsafe extern "C" fn sixtyfps_callback_init(out: *mut CallbackOpaque) { assert_eq!(core::mem::size_of::(), core::mem::size_of::>()); core::ptr::write(out as *mut Callback<()>, Default::default()); } /// Emit the callback #[no_mangle] pub unsafe extern "C" fn sixtyfps_callback_call( sig: *const CallbackOpaque, arg: *const c_void, ret: *mut c_void, ) { let sig = &*(sig as *const Callback); if let Some(h) = sig.handler.take() { h(&*arg, &mut *ret); assert!(sig.handler.take().is_none(), "Callback Handler set while callted"); sig.handler.set(Some(h)); } } /// Set callback handler. /// /// The binding has signature fn(user_data) #[no_mangle] pub unsafe extern "C" fn sixtyfps_callback_set_handler( sig: *const CallbackOpaque, binding: extern "C" fn(user_data: *mut c_void, arg: *const c_void, ret: *mut c_void), user_data: *mut c_void, drop_user_data: Option, ) { let sig = &mut *(sig as *mut Callback); struct UserData { user_data: *mut c_void, drop_user_data: Option, } impl Drop for UserData { fn drop(&mut self) { if let Some(x) = self.drop_user_data { x(self.user_data) } } } let ud = UserData { user_data, drop_user_data }; sig.handler.set(Some(Box::new(move |a: &(), r: &mut ()| { binding(ud.user_data, a as *const c_void, r as *mut c_void) }))); } /// Destroy callback #[no_mangle] pub unsafe extern "C" fn sixtyfps_callback_drop(handle: *mut CallbackOpaque) { core::ptr::read(handle as *mut Callback<()>); } }