#![warn(missing_docs)]
use core::cell::Cell;
#[repr(C)]
pub struct Signal<Arg: ?Sized> {
handler: Cell<Option<Box<dyn Fn(&Arg)>>>,
}
impl<Arg: ?Sized> Default for Signal<Arg> {
fn default() -> Self {
Self { handler: Default::default() }
}
}
impl<Arg: ?Sized> Signal<Arg> {
pub fn emit(&self, a: &Arg) {
if let Some(h) = self.handler.take() {
h(a);
assert!(self.handler.take().is_none(), "Signal Handler set while emitted");
self.handler.set(Some(h))
}
}
pub fn set_handler(&self, f: impl Fn(&Arg) + 'static) {
self.handler.set(Some(Box::new(f)));
}
}
#[test]
fn signal_simple_test() {
use std::rc::Rc;
#[derive(Default)]
struct Component {
pressed: core::cell::Cell<bool>,
clicked: Signal<()>,
}
let c = Rc::new(Component::default());
let weak = Rc::downgrade(&c);
c.clicked.set_handler(move |()| weak.upgrade().unwrap().pressed.set(true));
c.clicked.emit(&());
assert_eq!(c.pressed.get(), true);
}
pub(crate) mod ffi {
#![allow(unsafe_code)]
use super::*;
#[allow(non_camel_case_types)]
type c_void = ();
#[repr(C)]
pub struct SignalOpaque(*const c_void, *const c_void);
static_assertions::assert_eq_align!(SignalOpaque, Signal<()>);
static_assertions::assert_eq_size!(SignalOpaque, Signal<()>);
static_assertions::assert_eq_align!(SignalOpaque, Signal<(String,)>);
static_assertions::assert_eq_size!(SignalOpaque, Signal<(String,)>);
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_signal_init(out: *mut SignalOpaque) {
assert_eq!(core::mem::size_of::<SignalOpaque>(), core::mem::size_of::<Signal<()>>());
core::ptr::write(out as *mut Signal<()>, Default::default());
}
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_signal_emit(sig: *const SignalOpaque, arg: *const c_void) {
let sig = &*(sig as *const Signal<c_void>);
sig.emit(&*arg);
}
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_signal_set_handler(
sig: *mut SignalOpaque,
binding: extern "C" fn(user_data: *mut c_void, arg: *const c_void),
user_data: *mut c_void,
drop_user_data: Option<extern "C" fn(*mut c_void)>,
) {
let sig = &mut *(sig as *mut Signal<c_void>);
struct UserData {
user_data: *mut c_void,
drop_user_data: Option<extern "C" fn(*mut c_void)>,
}
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 };
let real_binding = move |arg: &()| {
binding(ud.user_data, arg as *const c_void);
};
sig.set_handler(real_binding);
}
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_signal_drop(handle: *mut SignalOpaque) {
core::ptr::read(handle as *mut Signal<()>);
}
}