mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-28 04:45:13 +00:00
cpp: Make Timer
available to other FFI-exported types
We have a nice FFI-compatible wrapper areound rust timers in C++, so allow to use that in other types exported to C++. This also makes sure the rust and C++ side agree on the size of the type, with the nice side effect that the rust Timer now uses half as much space now as it did before.
This commit is contained in:
parent
3a87342073
commit
3b7e79958a
5 changed files with 179 additions and 114 deletions
|
@ -306,21 +306,22 @@ if (SLINT_BUILD_RUNTIME)
|
|||
endforeach()
|
||||
|
||||
set(generated_headers
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_enums_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_enums.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_builtin_structs_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_builtin_structs.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_string_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_brush_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_sharedvector_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_properties_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_image_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_builtin_structs.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_builtin_structs_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_color_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_pathdata_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_qt_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_platform_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_enums.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_enums_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_generated_public.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_image_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_pathdata_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_platform_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_properties_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_qt_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_sharedvector_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_string_internal.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated_include/slint_timer_internal.h
|
||||
)
|
||||
|
||||
if(SLINT_FEATURE_INTERPRETER)
|
||||
|
|
|
@ -296,7 +296,6 @@ fn gen_corelib(
|
|||
|
||||
// included in generated_public.h
|
||||
let public_exported_types = [
|
||||
"TimerMode",
|
||||
"RenderingState",
|
||||
"SetRenderingNotifierError",
|
||||
"GraphicsAPI",
|
||||
|
@ -320,6 +319,8 @@ fn gen_corelib(
|
|||
"slint_new_path_events",
|
||||
"Property",
|
||||
"Slice",
|
||||
"Timer",
|
||||
"TimerMode",
|
||||
"PropertyHandleOpaque",
|
||||
"Callback",
|
||||
"slint_property_listener_scope_evaluate",
|
||||
|
@ -344,6 +345,12 @@ fn gen_corelib(
|
|||
"slint_image_load_from_embedded_data",
|
||||
"slint_image_from_embedded_textures",
|
||||
"slint_image_compare_equal",
|
||||
"slint_timer_start",
|
||||
"slint_timer_singleshot",
|
||||
"slint_timer_destroy",
|
||||
"slint_timer_stop",
|
||||
"slint_timer_restart",
|
||||
"slint_timer_running",
|
||||
"Coord",
|
||||
"LogicalRect",
|
||||
"LogicalPoint",
|
||||
|
@ -403,6 +410,37 @@ fn gen_corelib(
|
|||
.context("Unable to generate bindings for slint_properties_internal.h")?
|
||||
.write_to_file(include_dir.join("slint_properties_internal.h"));
|
||||
|
||||
// slint_timer_internal.h:
|
||||
let timer_config = {
|
||||
let mut tmp = config.clone();
|
||||
tmp.export.include = vec![
|
||||
"TimerMode",
|
||||
"slint_timer_start",
|
||||
"slint_timer_singleshot",
|
||||
"slint_timer_destroy",
|
||||
"slint_timer_stop",
|
||||
"slint_timer_restart",
|
||||
"slint_timer_running",
|
||||
]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
tmp.export.exclude = config
|
||||
.export
|
||||
.exclude
|
||||
.iter()
|
||||
.filter(|exclusion| !tmp.export.include.iter().any(|inclusion| inclusion == *exclusion))
|
||||
.cloned()
|
||||
.collect();
|
||||
tmp
|
||||
};
|
||||
cbindgen::Builder::new()
|
||||
.with_config(timer_config)
|
||||
.with_src(crate_dir.join("timers.rs"))
|
||||
.generate()
|
||||
.context("Unable to generate bindings for slint_timer_internal.h")?
|
||||
.write_to_file(include_dir.join("slint_timer_internal.h"));
|
||||
|
||||
for (rust_types, extra_excluded_types, internal_header, prelude) in [
|
||||
(
|
||||
vec { (*reinterpret_cast<F *>(data))(); }, new F(std::move(callback)),
|
||||
[](void *data) { delete reinterpret_cast<F *>(data); }))
|
||||
{
|
||||
}
|
||||
Timer(const Timer &) = delete;
|
||||
Timer &operator=(const Timer &) = delete;
|
||||
~Timer() { cbindgen_private::slint_timer_destroy(id); }
|
||||
|
||||
/// Starts the timer with the given \a mode and \a interval, in order for the \a callback to
|
||||
/// called when the timer fires. If the timer has been started previously and not fired yet,
|
||||
/// then it will be restarted.
|
||||
template<std::invocable F>
|
||||
void start(TimerMode mode, std::chrono::milliseconds interval, F callback)
|
||||
{
|
||||
id = cbindgen_private::slint_timer_start(
|
||||
id, mode, interval.count(), [](void *data) { (*reinterpret_cast<F *>(data))(); },
|
||||
new F(std::move(callback)), [](void *data) { delete reinterpret_cast<F *>(data); });
|
||||
}
|
||||
/// Stops the previously started timer. Does nothing if the timer has never been started. A
|
||||
/// stopped timer cannot be restarted with restart(). Use start() instead.
|
||||
void stop() { cbindgen_private::slint_timer_stop(id); }
|
||||
/// Restarts the timer. If the timer was previously started by calling [`Self::start()`]
|
||||
/// with a duration and callback, then the time when the callback will be next invoked
|
||||
/// is re-calculated to be in the specified duration relative to when this function is called.
|
||||
///
|
||||
/// Does nothing if the timer was never started.
|
||||
void restart() { cbindgen_private::slint_timer_restart(id); }
|
||||
/// Returns true if the timer is running; false otherwise.
|
||||
bool running() const { return cbindgen_private::slint_timer_running(id); }
|
||||
|
||||
/// Call the callback after the given duration.
|
||||
template<std::invocable F>
|
||||
static void single_shot(std::chrono::milliseconds duration, F callback)
|
||||
{
|
||||
cbindgen_private::slint_timer_singleshot(
|
||||
duration.count(), [](void *data) { (*reinterpret_cast<F *>(data))(); },
|
||||
new F(std::move(callback)), [](void *data) { delete reinterpret_cast<F *>(data); });
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t id;
|
||||
};
|
||||
|
||||
namespace cbindgen_private {
|
||||
inline LayoutInfo LayoutInfo::merge(const LayoutInfo &other) const
|
||||
{
|
||||
|
|
76
api/cpp/include/slint_timer.h
Normal file
76
api/cpp/include/slint_timer.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
||||
|
||||
// cSpell: ignore singleshot
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <slint_timer_internal.h>
|
||||
|
||||
namespace slint {
|
||||
|
||||
using cbindgen_private::TimerMode;
|
||||
|
||||
/// 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 null timer. Use the start() method to activate the timer with a mode, interval
|
||||
/// and callback.
|
||||
Timer() = default;
|
||||
/// Construct a timer which will repeat the callback every `interval` milliseconds until
|
||||
/// the destructor of the timer is called.
|
||||
///
|
||||
/// This is a convenience function and equivalent to calling
|
||||
/// `start(slint::TimerMode::Repeated, interval, callback);` on a default constructed Timer.
|
||||
template<std::invocable F>
|
||||
Timer(std::chrono::milliseconds interval, F callback)
|
||||
: id(cbindgen_private::slint_timer_start(
|
||||
0, TimerMode::Repeated, interval.count(),
|
||||
[](void *data) { (*reinterpret_cast<F *>(data))(); }, new F(std::move(callback)),
|
||||
[](void *data) { delete reinterpret_cast<F *>(data); }))
|
||||
{
|
||||
}
|
||||
Timer(const Timer &) = delete;
|
||||
Timer &operator=(const Timer &) = delete;
|
||||
~Timer() { cbindgen_private::slint_timer_destroy(id); }
|
||||
|
||||
/// Starts the timer with the given \a mode and \a interval, in order for the \a callback to
|
||||
/// called when the timer fires. If the timer has been started previously and not fired yet,
|
||||
/// then it will be restarted.
|
||||
template<std::invocable F>
|
||||
void start(TimerMode mode, std::chrono::milliseconds interval, F callback)
|
||||
{
|
||||
id = cbindgen_private::slint_timer_start(
|
||||
id, mode, interval.count(), [](void *data) { (*reinterpret_cast<F *>(data))(); },
|
||||
new F(std::move(callback)), [](void *data) { delete reinterpret_cast<F *>(data); });
|
||||
}
|
||||
/// Stops the previously started timer. Does nothing if the timer has never been started. A
|
||||
/// stopped timer cannot be restarted with restart(). Use start() instead.
|
||||
void stop() { cbindgen_private::slint_timer_stop(id); }
|
||||
/// Restarts the timer. If the timer was previously started by calling [`Self::start()`]
|
||||
/// with a duration and callback, then the time when the callback will be next invoked
|
||||
/// is re-calculated to be in the specified duration relative to when this function is called.
|
||||
///
|
||||
/// Does nothing if the timer was never started.
|
||||
void restart() { cbindgen_private::slint_timer_restart(id); }
|
||||
/// Returns true if the timer is running; false otherwise.
|
||||
bool running() const { return cbindgen_private::slint_timer_running(id); }
|
||||
|
||||
/// Call the callback after the given duration.
|
||||
template<std::invocable F>
|
||||
static void single_shot(std::chrono::milliseconds duration, F callback)
|
||||
{
|
||||
cbindgen_private::slint_timer_singleshot(
|
||||
duration.count(), [](void *data) { (*reinterpret_cast<F *>(data))(); },
|
||||
new F(std::move(callback)), [](void *data) { delete reinterpret_cast<F *>(data); });
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t id = 0;
|
||||
};
|
||||
|
||||
} // namespace slint
|
|
@ -12,7 +12,10 @@
|
|||
#![warn(missing_docs)]
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use core::cell::{Cell, RefCell};
|
||||
use core::{
|
||||
cell::{Cell, RefCell},
|
||||
num::NonZeroUsize,
|
||||
};
|
||||
|
||||
use crate::animations::Instant;
|
||||
|
||||
|
@ -58,7 +61,7 @@ pub enum TimerMode {
|
|||
/// ```
|
||||
#[derive(Default)]
|
||||
pub struct Timer {
|
||||
id: Cell<Option<usize>>,
|
||||
id: Cell<Option<NonZeroUsize>>,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
|
@ -79,12 +82,12 @@ impl Timer {
|
|||
CURRENT_TIMERS.with(|timers| {
|
||||
let mut timers = timers.borrow_mut();
|
||||
let id = timers.start_or_restart_timer(
|
||||
self.id.get(),
|
||||
self.id(),
|
||||
mode,
|
||||
interval,
|
||||
CallbackVariant::MultiFire(Box::new(callback)),
|
||||
);
|
||||
self.id.set(Some(id));
|
||||
self.set_id(Some(id));
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -117,7 +120,7 @@ impl Timer {
|
|||
|
||||
/// Stops the previously started timer. Does nothing if the timer has never been started.
|
||||
pub fn stop(&self) {
|
||||
if let Some(id) = self.id.get() {
|
||||
if let Some(id) = self.id() {
|
||||
CURRENT_TIMERS.with(|timers| {
|
||||
timers.borrow_mut().deactivate_timer(id);
|
||||
});
|
||||
|
@ -130,7 +133,7 @@ impl Timer {
|
|||
///
|
||||
/// Does nothing if the timer was never started.
|
||||
pub fn restart(&self) {
|
||||
if let Some(id) = self.id.get() {
|
||||
if let Some(id) = self.id() {
|
||||
CURRENT_TIMERS.with(|timers| {
|
||||
timers.borrow_mut().deactivate_timer(id);
|
||||
timers.borrow_mut().activate_timer(id);
|
||||
|
@ -140,8 +143,7 @@ impl Timer {
|
|||
|
||||
/// Returns true if the timer is running; false otherwise.
|
||||
pub fn running(&self) -> bool {
|
||||
self.id
|
||||
.get()
|
||||
self.id()
|
||||
.map(|timer_id| CURRENT_TIMERS.with(|timers| timers.borrow().timers[timer_id].running))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
@ -156,17 +158,25 @@ impl Timer {
|
|||
/// * `interval`: The duration from now until when the timer should fire. And the period of that timer
|
||||
/// for [`Repeated`](TimerMode::Repeated) timers.
|
||||
pub fn set_interval(&self, interval: core::time::Duration) {
|
||||
if let Some(id) = self.id.get() {
|
||||
if let Some(id) = self.id() {
|
||||
CURRENT_TIMERS.with(|timers| {
|
||||
timers.borrow_mut().set_interval(id, interval);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn id(&self) -> Option<usize> {
|
||||
self.id.get().map(|v| usize::from(v) - 1)
|
||||
}
|
||||
|
||||
fn set_id(&self, id: Option<usize>) {
|
||||
self.id.set(id.and_then(|v| NonZeroUsize::new(v + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Timer {
|
||||
fn drop(&mut self) {
|
||||
if let Some(id) = self.id.get() {
|
||||
if let Some(id) = self.id() {
|
||||
let _ = CURRENT_TIMERS.try_with(|timers| {
|
||||
timers.borrow_mut().remove_timer(id);
|
||||
});
|
||||
|
@ -351,10 +361,10 @@ impl TimerList {
|
|||
}
|
||||
}
|
||||
|
||||
fn activate_timer(&mut self, timer_id: usize) {
|
||||
fn activate_timer(&mut self, id: usize) {
|
||||
self.register_active_timer(ActiveTimer {
|
||||
id: timer_id,
|
||||
timeout: Instant::now() + self.timers[timer_id].duration,
|
||||
id,
|
||||
timeout: Instant::now() + self.timers[id].duration,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -367,29 +377,29 @@ impl TimerList {
|
|||
self.timers[new_active_timer.id].running = true;
|
||||
}
|
||||
|
||||
fn remove_timer(&mut self, timer_id: usize) {
|
||||
self.deactivate_timer(timer_id);
|
||||
let t = &mut self.timers[timer_id];
|
||||
fn remove_timer(&mut self, id: usize) {
|
||||
self.deactivate_timer(id);
|
||||
let t = &mut self.timers[id];
|
||||
if t.being_activated {
|
||||
t.removed = true;
|
||||
} else {
|
||||
self.timers.remove(timer_id);
|
||||
self.timers.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_interval(&mut self, timer_id: usize, duration: core::time::Duration) {
|
||||
let timer = &self.timers[timer_id];
|
||||
fn set_interval(&mut self, id: usize, duration: core::time::Duration) {
|
||||
let timer = &self.timers[id];
|
||||
|
||||
if !matches!(timer.callback, CallbackVariant::MultiFire { .. }) {
|
||||
return;
|
||||
}
|
||||
|
||||
if timer.running {
|
||||
self.deactivate_timer(timer_id);
|
||||
self.timers[timer_id].duration = duration;
|
||||
self.activate_timer(timer_id);
|
||||
self.deactivate_timer(id);
|
||||
self.timers[id].duration = duration;
|
||||
self.activate_timer(id);
|
||||
} else {
|
||||
self.timers[timer_id].duration = duration;
|
||||
self.timers[id].duration = duration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -450,20 +460,20 @@ pub(crate) mod ffi {
|
|||
/// The timer MUST be destroyed with slint_timer_destroy.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn slint_timer_start(
|
||||
id: i64,
|
||||
id: usize,
|
||||
mode: TimerMode,
|
||||
duration: u64,
|
||||
callback: extern "C" fn(*mut c_void),
|
||||
user_data: *mut c_void,
|
||||
drop_user_data: Option<extern "C" fn(*mut c_void)>,
|
||||
) -> i64 {
|
||||
) -> usize {
|
||||
let wrap = WrapFn { callback, user_data, drop_user_data };
|
||||
let timer = Timer::default();
|
||||
if id != -1 {
|
||||
timer.id.set(Some(id as _));
|
||||
if id != 0 {
|
||||
timer.id.set(NonZeroUsize::new(id));
|
||||
}
|
||||
timer.start(mode, core::time::Duration::from_millis(duration), move || wrap.call());
|
||||
timer.id.take().map(|x| x as i64).unwrap_or(-1)
|
||||
timer.id.take().map(|x| usize::from(x)).unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Execute a callback with a delay in millisecond
|
||||
|
@ -480,43 +490,43 @@ pub(crate) mod ffi {
|
|||
|
||||
/// Stop a timer and free its raw data
|
||||
#[no_mangle]
|
||||
pub extern "C" fn slint_timer_destroy(id: i64) {
|
||||
if id == -1 {
|
||||
pub extern "C" fn slint_timer_destroy(id: usize) {
|
||||
if id == 0 {
|
||||
return;
|
||||
}
|
||||
let timer = Timer { id: Cell::new(Some(id as _)) };
|
||||
let timer = Timer { id: Cell::new(NonZeroUsize::new(id)) };
|
||||
drop(timer);
|
||||
}
|
||||
|
||||
/// Stop a timer
|
||||
#[no_mangle]
|
||||
pub extern "C" fn slint_timer_stop(id: i64) {
|
||||
if id == -1 {
|
||||
pub extern "C" fn slint_timer_stop(id: usize) {
|
||||
if id == 0 {
|
||||
return;
|
||||
}
|
||||
let timer = Timer { id: Cell::new(Some(id as _)) };
|
||||
let timer = Timer { id: Cell::new(NonZeroUsize::new(id)) };
|
||||
timer.stop();
|
||||
timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call destroy() in the destructor.
|
||||
}
|
||||
|
||||
/// Restart a repeated timer
|
||||
#[no_mangle]
|
||||
pub extern "C" fn slint_timer_restart(id: i64) {
|
||||
if id == -1 {
|
||||
pub extern "C" fn slint_timer_restart(id: usize) {
|
||||
if id == 0 {
|
||||
return;
|
||||
}
|
||||
let timer = Timer { id: Cell::new(Some(id as _)) };
|
||||
let timer = Timer { id: Cell::new(NonZeroUsize::new(id)) };
|
||||
timer.restart();
|
||||
timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call destroy() in the destructor.
|
||||
}
|
||||
|
||||
/// Returns true if the timer is running; false otherwise.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn slint_timer_running(id: i64) -> bool {
|
||||
if id == -1 {
|
||||
pub extern "C" fn slint_timer_running(id: usize) -> bool {
|
||||
if id == 0 {
|
||||
return false;
|
||||
}
|
||||
let timer = Timer { id: Cell::new(Some(id as _)) };
|
||||
let timer = Timer { id: Cell::new(NonZeroUsize::new(id)) };
|
||||
let running = timer.running();
|
||||
timer.id.take(); // Make sure that dropping the Timer doesn't unregister it. C++ will call destroy() in the destructor.
|
||||
running
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue