diff --git a/api/sixtyfps-cpp/include/sixtyfps.h b/api/sixtyfps-cpp/include/sixtyfps.h index bb0199ded..d5d6f4120 100644 --- a/api/sixtyfps-cpp/include/sixtyfps.h +++ b/api/sixtyfps-cpp/include/sixtyfps.h @@ -18,6 +18,7 @@ LICENSE END */ #include #include #include // FIXME: remove: iostream always bring it lots of code so we should not have it in this header +#include namespace sixtyfps::cbindgen_private { // Workaround https://github.com/eqrion/cbindgen/issues/43 @@ -248,6 +249,41 @@ public: } }; +/// A Timer that can call a callback at repeated interval +/// +/// Use the static single_shot function to make a single shot timer +struct Timer { + /// Construct a timer which will repeat the callback every `duration` milliseconds until + /// the destructor of the timer is called. + template + Timer(std::chrono::milliseconds duration, F callback) + : id(cbindgen_private::sixtyfps_timer_start( + duration.count(), + [](void *data) { (*reinterpret_cast(data))(); }, + new F(std::move(callback)), + [](void *data) { delete reinterpret_cast(data); })) + {} + Timer(const Timer&) = delete; + Timer &operator=(const Timer&) = delete; + ~Timer() { + cbindgen_private::sixtyfps_timer_stop(id); + } + + /// Call the callback after the given duration. + template + static void single_shot(std::chrono::milliseconds duration, F callback) { + cbindgen_private::sixtyfps_timer_singleshot( + duration.count(), + [](void *data) { (*reinterpret_cast(data))(); }, + new F(std::move(callback)), + [](void *data) { delete reinterpret_cast(data); }); + } + +private: + int64_t id; +}; + + // layouts: using cbindgen_private::box_layout_info; using cbindgen_private::BoxLayoutCellData; diff --git a/sixtyfps_runtime/corelib/lib.rs b/sixtyfps_runtime/corelib/lib.rs index 4f665d74b..fa89aaa79 100644 --- a/sixtyfps_runtime/corelib/lib.rs +++ b/sixtyfps_runtime/corelib/lib.rs @@ -85,4 +85,5 @@ pub fn use_modules() -> usize { + string::ffi::sixtyfps_shared_string_bytes as usize + eventloop::ffi::sixtyfps_component_window_drop as usize + component::ffi::sixtyfps_component_init_items as usize + + timers::ffi::sixtyfps_timer_start as usize } diff --git a/sixtyfps_runtime/corelib/timers.rs b/sixtyfps_runtime/corelib/timers.rs index a12ded43a..3bdbd9f90 100644 --- a/sixtyfps_runtime/corelib/timers.rs +++ b/sixtyfps_runtime/corelib/timers.rs @@ -302,3 +302,65 @@ fn lower_bound(vec: &Vec, mut less_than: impl FnMut(&T) -> bool) -> usize left } + +pub(crate) mod ffi { + use super::*; + #[allow(non_camel_case_types)] + type c_void = (); + + struct WrapFn { + callback: extern "C" fn(*mut c_void), + user_data: *mut c_void, + drop_user_data: Option, + } + + impl Drop for WrapFn { + fn drop(&mut self) { + if let Some(x) = self.drop_user_data { + x(self.user_data) + } + } + } + + /// Start a timer with the given duration in millisecond. + /// Returns the timer id. + /// The timer MUST be stopped with sixtyfps_timer_stop + #[no_mangle] + pub extern "C" fn sixtyfps_timer_start( + duration: u64, + callback: extern "C" fn(*mut c_void), + user_data: *mut c_void, + drop_user_data: Option, + ) -> i64 { + let wrap = WrapFn { callback, user_data, drop_user_data }; + let timer = Timer::default(); + timer.start(TimerMode::Repeated, core::time::Duration::from_millis(duration), move || { + (wrap.callback)(wrap.user_data) + }); + timer.id.get().map(|x| x as i64).unwrap_or(-1) + } + + /// Execute a callback with a delay in milisecond + #[no_mangle] + pub extern "C" fn sixtyfps_timer_singleshot( + delay: u64, + callback: extern "C" fn(*mut c_void), + user_data: *mut c_void, + drop_user_data: Option, + ) { + let wrap = WrapFn { callback, user_data, drop_user_data }; + Timer::single_shot(core::time::Duration::from_millis(delay), move || { + (wrap.callback)(wrap.user_data) + }); + } + + /// Stop a timer and free its raw data + #[no_mangle] + pub extern "C" fn sixtyfps_timer_stop(id: i64) { + if id == -1 { + return; + } + let timer = Timer { id: Cell::new(Some(id as _)) }; + timer.stop() + } +}