1
//! Timer and thread management for asynchronous operations.
2
//!
3
//! This module provides:
4
//! - `TimerId` / `ThreadId`: Unique identifiers for timers and background threads
5
//! - `Instant` / `Duration`: Cross-platform time types (works on no_std with tick counters)
6
//! - `ThreadReceiver`: Channel for receiving messages from the main thread
7
//! - Callback types for thread communication and system time queries
8

            
9
#[cfg(not(feature = "std"))]
10
use alloc::string::{String, ToString};
11
use alloc::{
12
    boxed::Box,
13
    collections::btree_map::BTreeMap,
14
    sync::{Arc, Weak},
15
    vec::Vec,
16
};
17
use core::{
18
    ffi::c_void,
19
    fmt,
20
    mem::ManuallyDrop,
21
    sync::atomic::{AtomicUsize, Ordering},
22
};
23
#[cfg(feature = "std")]
24
use std::sync::mpsc::{Receiver, Sender};
25
#[cfg(feature = "std")]
26
use std::sync::Mutex;
27
#[cfg(feature = "std")]
28
use std::thread::{self, JoinHandle};
29
#[cfg(feature = "std")]
30
use std::time::Duration as StdDuration;
31
#[cfg(feature = "std")]
32
use std::time::Instant as StdInstant;
33

            
34
use azul_css::{props::property::CssProperty, AzString};
35
use rust_fontconfig::FcFontCache;
36

            
37
use crate::{
38
    callbacks::{FocusTarget, TimerCallbackReturn, Update},
39
    dom::{DomId, DomNodeId, OptionDomNodeId},
40
    geom::{LogicalPosition, OptionLogicalPosition},
41
    gl::OptionGlContextPtr,
42
    hit_test::ScrollPosition,
43
    id::NodeId,
44
    refany::{OptionRefAny, RefAny},
45
    resources::{ImageCache, ImageMask, ImageRef},
46
    styled_dom::NodeHierarchyItemId,
47
    window::RawWindowHandle,
48
    FastBTreeSet, OrderedMap,
49
};
50

            
51
/// Should a timer terminate or not - used to remove active timers
52
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
53
#[repr(C)]
54
pub enum TerminateTimer {
55
    /// Remove the timer from the list of active timers
56
    Terminate,
57
    /// Do nothing and let the timers continue to run
58
    Continue,
59
}
60

            
61
// ============================================================================
62
// Reserved System Timer IDs (0x0000 - 0x00FF)
63
// ============================================================================
64
// User timers start at 0x0100 to avoid conflicts with system timers.
65
// These constants define well-known timer IDs for internal framework use.
66

            
67
/// Timer ID for cursor blinking in contenteditable elements (~530ms interval)
68
pub const CURSOR_BLINK_TIMER_ID: TimerId = TimerId { id: 0x0001 };
69
/// Timer ID for scroll momentum/inertia animation
70
pub const SCROLL_MOMENTUM_TIMER_ID: TimerId = TimerId { id: 0x0002 };
71
/// Timer ID for auto-scroll during drag operations near edges
72
pub const DRAG_AUTOSCROLL_TIMER_ID: TimerId = TimerId { id: 0x0003 };
73
/// Timer ID for tooltip show delay.
74
///
75
/// Started by the platform event loop when the hover target changes to a node
76
/// that advertises a tooltip source (`aria-label` / `alt` / `title`); fires
77
/// once after `SystemStyle::input_metrics.hover_time_ms` (SPI_GETMOUSEHOVERTIME
78
/// on Windows, default 400ms) and emits a `ShowTooltip` `CallbackChange`. The
79
/// timer is torn down on hover loss, which also emits `HideTooltip`.
80
///
81
/// Double-click detection used to live on a neighbouring reserved ID but is
82
/// now handled entirely by `GestureManager::detect_double_click`, so no
83
/// equivalent `DOUBLE_CLICK_TIMER_ID` exists.
84
pub const TOOLTIP_DELAY_TIMER_ID: TimerId = TimerId { id: 0x0004 };
85

            
86
/// First available ID for user-defined timers
87
pub const USER_TIMER_ID_START: usize = 0x0100;
88

            
89
// User timers start at 0x0100 to avoid conflicts with reserved system timer IDs
90
static MAX_TIMER_ID: AtomicUsize = AtomicUsize::new(USER_TIMER_ID_START);
91

            
92
/// ID for uniquely identifying a timer
93
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
94
#[repr(C)]
95
pub struct TimerId {
96
    pub id: usize,
97
}
98

            
99
impl TimerId {
100
    /// Generates a new, unique `TimerId`.
101
    #[must_use]
102
    pub fn unique() -> Self {
103
        TimerId {
104
            id: MAX_TIMER_ID.fetch_add(1, Ordering::SeqCst),
105
        }
106
    }
107
}
108

            
109
impl_option!(
110
    TimerId,
111
    OptionTimerId,
112
    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
113
);
114

            
115
impl_vec!(TimerId, TimerIdVec, TimerIdVecDestructor, TimerIdVecDestructorType, TimerIdVecSlice, OptionTimerId);
116
impl_vec_debug!(TimerId, TimerIdVec);
117
impl_vec_clone!(TimerId, TimerIdVec, TimerIdVecDestructor);
118
impl_vec_partialeq!(TimerId, TimerIdVec);
119
impl_vec_partialord!(TimerId, TimerIdVec);
120

            
121
// Thread IDs 0-4 are reserved for internal framework use.
122
// User threads start at RESERVED_THREAD_ID_COUNT.
123
const RESERVED_THREAD_ID_COUNT: usize = 5;
124
static MAX_THREAD_ID: AtomicUsize = AtomicUsize::new(RESERVED_THREAD_ID_COUNT);
125

            
126
/// ID for uniquely identifying a background thread
127
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
128
#[repr(C)]
129
pub struct ThreadId {
130
    id: usize,
131
}
132

            
133
impl_option!(
134
    ThreadId,
135
    OptionThreadId,
136
    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
137
);
138

            
139
impl_vec!(ThreadId, ThreadIdVec, ThreadIdVecDestructor, ThreadIdVecDestructorType, ThreadIdVecSlice, OptionThreadId);
140
impl_vec_debug!(ThreadId, ThreadIdVec);
141
impl_vec_clone!(ThreadId, ThreadIdVec, ThreadIdVecDestructor);
142
impl_vec_partialeq!(ThreadId, ThreadIdVec);
143
impl_vec_partialord!(ThreadId, ThreadIdVec);
144

            
145
impl ThreadId {
146
    /// Generates a new, unique `ThreadId`.
147
    #[must_use]
148
    pub fn unique() -> Self {
149
        ThreadId {
150
            id: MAX_THREAD_ID.fetch_add(1, Ordering::SeqCst),
151
        }
152
    }
153
}
154

            
155
/// A point in time, either from the system clock or a tick counter.
156
///
157
/// Use `Instant::System` on platforms with std, `Instant::Tick` on embedded/no_std.
158
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
159
#[repr(C, u8)]
160
pub enum Instant {
161
    /// System time from std::time::Instant (requires "std" feature)
162
    System(InstantPtr),
163
    /// Tick-based time for embedded systems without a real-time clock
164
    Tick(SystemTick),
165
}
166

            
167
#[cfg(feature = "std")]
168
impl From<StdInstant> for Instant {
169
416866
    fn from(s: StdInstant) -> Instant {
170
416866
        Instant::System(s.into())
171
416866
    }
172
}
173

            
174
impl Instant {
175
    /// Returns the current system time.
176
    /// 
177
    /// On systems with std, this uses `std::time::Instant::now()`.
178
    /// On no_std systems, this returns a zero tick.
179
    #[cfg(feature = "std")]
180
1216
    pub fn now() -> Self {
181
1216
        StdInstant::now().into()
182
1216
    }
183

            
184
    /// Returns the current system time (no_std fallback).
185
    #[cfg(not(feature = "std"))]
186
    pub fn now() -> Self {
187
        Instant::Tick(SystemTick::new(0))
188
    }
189

            
190
    /// Returns a number from 0.0 to 1.0 indicating the current
191
    /// linear interpolation value between (start, end)
192
    pub fn linear_interpolate(&self, mut start: Self, mut end: Self) -> f32 {
193
        use core::mem;
194

            
195
        if end < start {
196
            mem::swap(&mut start, &mut end);
197
        }
198

            
199
        if *self < start {
200
            return 0.0;
201
        }
202
        if *self > end {
203
            return 1.0;
204
        }
205

            
206
        let duration_total = end.duration_since(&start);
207
        let duration_current = self.duration_since(&start);
208

            
209
        duration_current.div(&duration_total).max(0.0).min(1.0)
210
    }
211

            
212
    /// Adds a duration to the instant, does nothing in undefined cases
213
    /// (i.e. trying to add a Duration::Tick to an Instant::System)
214
    pub fn add_optional_duration(&self, duration: Option<&Duration>) -> Self {
215
        match duration {
216
            Some(d) => match (self, d) {
217
                (Instant::System(i), Duration::System(d)) => {
218
                    #[cfg(feature = "std")]
219
                    {
220
                        let s: StdInstant = i.clone().into();
221
                        let d: StdDuration = d.clone().into();
222
                        let new: InstantPtr = (s + d).into();
223
                        Instant::System(new)
224
                    }
225
                    #[cfg(not(feature = "std"))]
226
                    {
227
                        unreachable!()
228
                    }
229
                }
230
                (Instant::Tick(s), Duration::Tick(d)) => Instant::Tick(SystemTick {
231
                    tick_counter: s.tick_counter + d.tick_diff,
232
                }),
233
                _ => {
234
                    panic!(
235
                        "invalid: trying to add a duration {:?} to an instant {:?}",
236
                        d, self
237
                    );
238
                }
239
            },
240
            None => self.clone(),
241
        }
242
    }
243

            
244
    /// Converts to std::time::Instant (panics if Tick variant).
245
    #[cfg(feature = "std")]
246
    pub fn into_std_instant(self) -> StdInstant {
247
        match self {
248
            Instant::System(s) => s.into(),
249
            Instant::Tick(_) => unreachable!(),
250
        }
251
    }
252

            
253
    /// Calculates the duration since an earlier point in time
254
    ///
255
    /// - Panics if the earlier Instant was created after the current Instant
256
    /// - Panics if the two enums do not have the same variant (tick / std)
257
195177
    pub fn duration_since(&self, earlier: &Instant) -> Duration {
258
195177
        match (earlier, self) {
259
195177
            (Instant::System(prev), Instant::System(now)) => {
260
                #[cfg(feature = "std")]
261
                {
262
195177
                    let prev_instant: StdInstant = prev.clone().into();
263
195177
                    let now_instant: StdInstant = now.clone().into();
264
195177
                    Duration::System((now_instant.duration_since(prev_instant)).into())
265
                }
266
                #[cfg(not(feature = "std"))]
267
                {
268
                    unreachable!() // cannot construct a System instant on no_std
269
                }
270
            }
271
            (
272
                Instant::Tick(SystemTick { tick_counter: prev }),
273
                Instant::Tick(SystemTick { tick_counter: now }),
274
            ) => {
275
                if prev > now {
276
                    panic!(
277
                        "illegal: subtraction 'Instant - Instant' would result in a negative \
278
                         duration"
279
                    )
280
                } else {
281
                    Duration::Tick(SystemTickDiff {
282
                        tick_diff: now - prev,
283
                    })
284
                }
285
            }
286
            _ => panic!(
287
                "illegal: trying to calculate a Duration from a SystemTime and a Tick instant"
288
            ),
289
        }
290
195177
    }
291
}
292

            
293
/// Tick-based timestamp for systems without a real-time clock.
294
///
295
/// Used on embedded systems where time is measured in frame ticks or cycles.
296
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
297
#[repr(C)]
298
pub struct SystemTick {
299
    pub tick_counter: u64,
300
}
301

            
302
impl SystemTick {
303
    /// Creates a new tick timestamp from a counter value.
304
28
    pub const fn new(tick_counter: u64) -> Self {
305
28
        Self { tick_counter }
306
28
    }
307
}
308

            
309
/// FFI-safe wrapper around std::time::Instant with custom clone/drop callbacks.
310
///
311
/// Allows crossing FFI boundaries while maintaining proper memory management.
312
#[repr(C)]
313
pub struct InstantPtr {
314
    /// `ManuallyDrop` so the owned `Box` is freed ONLY when `run_destructor` is
315
    /// still set (see `Drop`). The codegen FFI wrappers (`AzTimerCallbackInfo`
316
    /// etc.) embed this by value AND have their own `Drop` that `drop_in_place`s
317
    /// the real type first; Rust's drop glue would then drop this `ptr` field a
318
    /// SECOND time on the same bytes. Gating the `Box` free on `run_destructor`
319
    /// (cleared by the first drop) makes that second drop a safe no-op. Layout is
320
    /// unchanged: `ManuallyDrop<Box<T>>` is one pointer, like the old `Box<T>`.
321
    #[cfg(feature = "std")]
322
    pub ptr: ManuallyDrop<Box<StdInstant>>,
323
    #[cfg(not(feature = "std"))]
324
    pub ptr: *const c_void,
325
    pub clone_fn: InstantPtrCloneCallback,
326
    pub destructor: InstantPtrDestructorCallback,
327
    pub run_destructor: bool,
328
}
329

            
330
pub type InstantPtrCloneCallbackType = extern "C" fn(*const InstantPtr) -> InstantPtr;
331
#[repr(C)]
332
pub struct InstantPtrCloneCallback {
333
    pub cb: InstantPtrCloneCallbackType,
334
}
335
impl_callback_simple!(InstantPtrCloneCallback);
336

            
337
pub type InstantPtrDestructorCallbackType = extern "C" fn(*mut InstantPtr);
338
#[repr(C)]
339
pub struct InstantPtrDestructorCallback {
340
    pub cb: InstantPtrDestructorCallbackType,
341
}
342
impl_callback_simple!(InstantPtrDestructorCallback);
343

            
344
// ----  LIBSTD implementation for InstantPtr BEGIN
345
#[cfg(feature = "std")]
346
impl core::fmt::Debug for InstantPtr {
347
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
348
        write!(f, "{:?}", self.get())
349
    }
350
}
351

            
352
#[cfg(not(feature = "std"))]
353
impl core::fmt::Debug for InstantPtr {
354
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
355
        write!(f, "{:?}", self.ptr as usize)
356
    }
357
}
358

            
359
#[cfg(feature = "std")]
360
impl core::hash::Hash for InstantPtr {
361
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
362
        self.get().hash(state);
363
    }
364
}
365

            
366
#[cfg(not(feature = "std"))]
367
impl core::hash::Hash for InstantPtr {
368
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
369
        (self.ptr as usize).hash(state);
370
    }
371
}
372

            
373
#[cfg(feature = "std")]
374
impl PartialEq for InstantPtr {
375
    fn eq(&self, other: &InstantPtr) -> bool {
376
        self.get() == other.get()
377
    }
378
}
379

            
380
#[cfg(not(feature = "std"))]
381
impl PartialEq for InstantPtr {
382
    fn eq(&self, other: &InstantPtr) -> bool {
383
        (self.ptr as usize).eq(&(other.ptr as usize))
384
    }
385
}
386

            
387
impl Eq for InstantPtr {}
388

            
389
#[cfg(feature = "std")]
390
impl PartialOrd for InstantPtr {
391
    fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
392
        Some((self.get()).cmp(&(other.get())))
393
    }
394
}
395

            
396
#[cfg(not(feature = "std"))]
397
impl PartialOrd for InstantPtr {
398
    fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
399
        Some((self.ptr as usize).cmp(&(other.ptr as usize)))
400
    }
401
}
402

            
403
#[cfg(feature = "std")]
404
impl Ord for InstantPtr {
405
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
406
        (self.get()).cmp(&(other.get()))
407
    }
408
}
409

            
410
#[cfg(not(feature = "std"))]
411
impl Ord for InstantPtr {
412
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
413
        (self.ptr as usize).cmp(&(other.ptr as usize))
414
    }
415
}
416

            
417
#[cfg(feature = "std")]
418
impl InstantPtr {
419
390354
    fn get(&self) -> StdInstant {
420
390354
        (**self.ptr).clone()
421
390354
    }
422
}
423

            
424
impl Clone for InstantPtr {
425
391335
    fn clone(&self) -> Self {
426
391335
        (self.clone_fn.cb)(self)
427
391335
    }
428
}
429

            
430
#[cfg(feature = "std")]
431
391335
extern "C" fn std_instant_clone(ptr: *const InstantPtr) -> InstantPtr {
432
391335
    let az_instant_ptr = unsafe { &*ptr };
433
391335
    InstantPtr {
434
391335
        ptr: ManuallyDrop::new((*az_instant_ptr.ptr).clone()),
435
391335
        clone_fn: az_instant_ptr.clone_fn.clone(),
436
391335
        destructor: az_instant_ptr.destructor.clone(),
437
391335
        run_destructor: true,
438
391335
    }
439
391335
}
440

            
441
#[cfg(feature = "std")]
442
impl From<StdInstant> for InstantPtr {
443
418498
    fn from(s: StdInstant) -> InstantPtr {
444
418498
        Self {
445
418498
            ptr: ManuallyDrop::new(Box::new(s)),
446
418498
            clone_fn: InstantPtrCloneCallback {
447
418498
                cb: std_instant_clone,
448
418498
            },
449
418498
            destructor: InstantPtrDestructorCallback {
450
418498
                cb: std_instant_drop,
451
418498
            },
452
418498
            run_destructor: true,
453
418498
        }
454
418498
    }
455
}
456

            
457
#[cfg(feature = "std")]
458
impl From<InstantPtr> for StdInstant {
459
390354
    fn from(s: InstantPtr) -> StdInstant {
460
390354
        s.get()
461
390354
    }
462
}
463

            
464
impl Drop for InstantPtr {
465
809833
    fn drop(&mut self) {
466
809833
        if self.run_destructor {
467
809833
            self.run_destructor = false;
468
809833
            (self.destructor.cb)(self);
469
            // Free the owned Box exactly once, here under the run_destructor guard.
470
            // A second drop on the same bytes (the codegen wrapper's field-drop after
471
            // its own `_delete` already ran the real drop) sees run_destructor=false
472
            // and skips this -> no double-free. (non-std `ptr` is a raw POD pointer
473
            // freed by the destructor callback above, so nothing to drop here.)
474
            #[cfg(feature = "std")]
475
809833
            unsafe {
476
809833
                ManuallyDrop::drop(&mut self.ptr);
477
809833
            }
478
        }
479
809833
    }
480
}
481

            
482
#[cfg(feature = "std")]
483
809833
extern "C" fn std_instant_drop(_: *mut InstantPtr) {}
484

            
485
// ----  LIBSTD implementation for InstantPtr END
486

            
487
/// A span of time, either from the system clock or as tick difference.
488
///
489
/// Mirrors `Instant` variants - System durations work with System instants,
490
/// Tick durations work with Tick instants.
491
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
492
#[repr(C, u8)]
493
pub enum Duration {
494
    /// System duration from std::time::Duration (requires "std" feature)
495
    System(SystemTimeDiff),
496
    /// Tick-based duration for embedded systems
497
    Tick(SystemTickDiff),
498
}
499

            
500
impl core::fmt::Display for Duration {
501
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
502
        match self {
503
            #[cfg(feature = "std")]
504
            Duration::System(s) => {
505
                let s: StdDuration = s.clone().into();
506
                write!(f, "{:?}", s)
507
            }
508
            #[cfg(not(feature = "std"))]
509
            Duration::System(s) => write!(f, "({}s, {}ns)", s.secs, s.nanos),
510
            Duration::Tick(tick) => write!(f, "{} ticks", tick.tick_diff),
511
        }
512
    }
513
}
514

            
515
#[cfg(feature = "std")]
516
impl From<StdDuration> for Duration {
517
    fn from(s: StdDuration) -> Self {
518
        Duration::System(s.into())
519
    }
520
}
521

            
522
impl Duration {
523
    /// Returns the maximum possible duration.
524
    pub fn max() -> Self {
525
        #[cfg(feature = "std")]
526
        {
527
            Duration::System(StdDuration::new(core::u64::MAX, NANOS_PER_SEC - 1).into())
528
        }
529
        #[cfg(not(feature = "std"))]
530
        {
531
            Duration::Tick(SystemTickDiff {
532
                tick_diff: u64::MAX,
533
            })
534
        }
535
    }
536

            
537
    /// Divides this duration by another, returning the ratio as f32.
538
51
    pub fn div(&self, other: &Self) -> f32 {
539
        use self::Duration::*;
540
51
        match (self, other) {
541
51
            (System(s), System(s2)) => s.div(s2) as f32,
542
            (Tick(t), Tick(t2)) => t.div(t2) as f32,
543
            _ => 0.0,
544
        }
545
51
    }
546

            
547
    /// Returns the smaller of two durations.
548
    pub fn min(self, other: Self) -> Self {
549
        if self.smaller_than(&other) {
550
            self
551
        } else {
552
            other
553
        }
554
    }
555

            
556
    /// Returns true if self > other (panics if variants differ).
557
    #[allow(unused_variables)]
558
    pub fn greater_than(&self, other: &Self) -> bool {
559
        match (self, other) {
560
            // self > other
561
            (Duration::System(s), Duration::System(o)) => {
562
                #[cfg(feature = "std")]
563
                {
564
                    let s: StdDuration = s.clone().into();
565
                    let o: StdDuration = o.clone().into();
566
                    s > o
567
                }
568
                #[cfg(not(feature = "std"))]
569
                {
570
                    unreachable!()
571
                }
572
            }
573
            (Duration::Tick(s), Duration::Tick(o)) => s.tick_diff > o.tick_diff,
574
            _ => {
575
                panic!("illegal: trying to compare a SystemDuration with a TickDuration");
576
            }
577
        }
578
    }
579

            
580
    /// Returns true if self < other (panics if variants differ).
581
    #[allow(unused_variables)]
582
    pub fn smaller_than(&self, other: &Self) -> bool {
583
        // self < other
584
        match (self, other) {
585
            // self > other
586
            (Duration::System(s), Duration::System(o)) => {
587
                #[cfg(feature = "std")]
588
                {
589
                    let s: StdDuration = s.clone().into();
590
                    let o: StdDuration = o.clone().into();
591
                    s < o
592
                }
593
                #[cfg(not(feature = "std"))]
594
                {
595
                    unreachable!()
596
                }
597
            }
598
            (Duration::Tick(s), Duration::Tick(o)) => s.tick_diff < o.tick_diff,
599
            _ => {
600
                panic!("illegal: trying to compare a SystemDuration with a TickDuration");
601
            }
602
        }
603
    }
604
}
605

            
606
/// Represents a difference in ticks for systems that
607
/// don't support timing
608
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
609
#[repr(C)]
610
pub struct SystemTickDiff {
611
    pub tick_diff: u64,
612
}
613

            
614
impl SystemTickDiff {
615
    /// Divide duration A by duration B.
616
    /// Returns `Inf` or `NaN` if `other` is zero.
617
    pub fn div(&self, other: &Self) -> f64 {
618
        self.tick_diff as f64 / other.tick_diff as f64
619
    }
620
}
621

            
622
/// Duration represented as seconds + nanoseconds (mirrors std::time::Duration).
623
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
624
#[repr(C)]
625
pub struct SystemTimeDiff {
626
    pub secs: u64,
627
    pub nanos: u32,
628
}
629

            
630
impl SystemTimeDiff {
631
    /// Divide duration A by duration B.
632
    /// Returns `Inf` or `NaN` if `other` is zero.
633
51
    pub fn div(&self, other: &Self) -> f64 {
634
51
        self.as_secs_f64() / other.as_secs_f64()
635
51
    }
636
102
    fn as_secs_f64(&self) -> f64 {
637
102
        (self.secs as f64) + ((self.nanos as f64) / (NANOS_PER_SEC as f64))
638
102
    }
639
}
640

            
641
#[cfg(feature = "std")]
642
impl From<StdDuration> for SystemTimeDiff {
643
195279
    fn from(d: StdDuration) -> SystemTimeDiff {
644
195279
        SystemTimeDiff {
645
195279
            secs: d.as_secs(),
646
195279
            nanos: d.subsec_nanos(),
647
195279
        }
648
195279
    }
649
}
650

            
651
#[cfg(feature = "std")]
652
impl From<SystemTimeDiff> for StdDuration {
653
    fn from(d: SystemTimeDiff) -> StdDuration {
654
        StdDuration::new(d.secs, d.nanos)
655
    }
656
}
657

            
658
const MILLIS_PER_SEC: u64 = 1_000;
659
const NANOS_PER_MILLI: u32 = 1_000_000;
660
const NANOS_PER_SEC: u32 = 1_000_000_000;
661

            
662
impl SystemTimeDiff {
663
    /// Creates a duration from whole seconds.
664
    pub const fn from_secs(secs: u64) -> Self {
665
        SystemTimeDiff { secs, nanos: 0 }
666
    }
667
    /// Creates a duration from milliseconds.
668
8145
    pub const fn from_millis(millis: u64) -> Self {
669
8145
        SystemTimeDiff {
670
8145
            secs: millis / MILLIS_PER_SEC,
671
8145
            nanos: ((millis % MILLIS_PER_SEC) as u32) * NANOS_PER_MILLI,
672
8145
        }
673
8145
    }
674
    /// Creates a duration from nanoseconds.
675
    pub const fn from_nanos(nanos: u64) -> Self {
676
        SystemTimeDiff {
677
            secs: nanos / (NANOS_PER_SEC as u64),
678
            nanos: (nanos % (NANOS_PER_SEC as u64)) as u32,
679
        }
680
    }
681
    /// Adds two durations, returning None on overflow.
682
    pub const fn checked_add(self, rhs: Self) -> Option<Self> {
683
        if let Some(mut secs) = self.secs.checked_add(rhs.secs) {
684
            let mut nanos = self.nanos + rhs.nanos;
685
            if nanos >= NANOS_PER_SEC {
686
                nanos -= NANOS_PER_SEC;
687
                if let Some(new_secs) = secs.checked_add(1) {
688
                    secs = new_secs;
689
                } else {
690
                    return None;
691
                }
692
            }
693
            Some(SystemTimeDiff { secs, nanos })
694
        } else {
695
            None
696
        }
697
    }
698

            
699
    /// Returns the total duration in milliseconds.
700
    pub fn millis(&self) -> u64 {
701
        (self.secs * MILLIS_PER_SEC) + (self.nanos / NANOS_PER_MILLI) as u64
702
    }
703

            
704
    /// Converts to std::time::Duration.
705
    #[cfg(feature = "std")]
706
    pub fn get(&self) -> StdDuration {
707
        (*self).into()
708
    }
709
}
710

            
711
impl_option!(
712
    Instant,
713
    OptionInstant,
714
    copy = false,
715
    [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
716
);
717
impl_option!(
718
    Duration,
719
    OptionDuration,
720
    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
721
);
722

            
723
/// Message that can be sent from the main thread to the Thread using the ThreadId.
724
///
725
/// The thread can ignore the event.
726
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
727
#[repr(C, u8)]
728
pub enum ThreadSendMsg {
729
    /// The thread should terminate at the nearest
730
    TerminateThread,
731
    /// Next frame tick
732
    Tick,
733
    /// Custom data
734
    Custom(RefAny),
735
}
736

            
737
impl_option!(
738
    ThreadSendMsg,
739
    OptionThreadSendMsg,
740
    copy = false,
741
    [Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash]
742
);
743

            
744
/// Channel endpoint for receiving messages from the main thread in a background thread.
745
///
746
/// Thread-safe wrapper around the receiver end of a message channel.
747
#[derive(Debug)]
748
#[repr(C)]
749
pub struct ThreadReceiver {
750
    #[cfg(feature = "std")]
751
    pub ptr: Box<Arc<Mutex<ThreadReceiverInner>>>,
752
    #[cfg(not(feature = "std"))]
753
    pub ptr: *const c_void,
754
    pub run_destructor: bool,
755
    /// For FFI: stores the foreign callable (e.g., PyFunction)
756
    pub ctx: OptionRefAny,
757
}
758

            
759
impl Clone for ThreadReceiver {
760
    fn clone(&self) -> Self {
761
        Self {
762
            ptr: self.ptr.clone(),
763
            run_destructor: true,
764
            ctx: self.ctx.clone(),
765
        }
766
    }
767
}
768

            
769
impl Drop for ThreadReceiver {
770
    fn drop(&mut self) {
771
        self.run_destructor = false;
772
    }
773
}
774

            
775
impl ThreadReceiver {
776
    /// Creates a new receiver (no-op on no_std).
777
    #[cfg(not(feature = "std"))]
778
    pub fn new(_t: ThreadReceiverInner) -> Self {
779
        Self {
780
            ptr: core::ptr::null(),
781
            run_destructor: false,
782
            ctx: OptionRefAny::None,
783
        }
784
    }
785

            
786
    /// Creates a new receiver wrapping the inner channel.
787
    #[cfg(feature = "std")]
788
    pub fn new(t: ThreadReceiverInner) -> Self {
789
        Self {
790
            ptr: Box::new(Arc::new(Mutex::new(t))),
791
            run_destructor: true,
792
            ctx: OptionRefAny::None,
793
        }
794
    }
795

            
796
    /// Get the FFI context (e.g., Python callable)
797
    pub fn get_ctx(&self) -> OptionRefAny {
798
        self.ctx.clone()
799
    }
800

            
801
    /// Receives a message (returns None on no_std).
802
    #[cfg(not(feature = "std"))]
803
    pub fn recv(&mut self) -> OptionThreadSendMsg {
804
        None.into()
805
    }
806

            
807
    /// Receives a message from the main thread, if available.
808
    #[cfg(feature = "std")]
809
    pub fn recv(&mut self) -> OptionThreadSendMsg {
810
        let ts = match self.ptr.lock().ok() {
811
            Some(s) => s,
812
            None => return None.into(),
813
        };
814
        (ts.recv_fn.cb)(ts.ptr.as_ref() as *const _ as *const c_void)
815
    }
816
}
817

            
818
/// Inner receiver state containing the actual channel and callbacks.
819
#[derive(Debug)]
820
#[cfg_attr(not(feature = "std"), derive(PartialEq, PartialOrd, Eq, Ord))]
821
#[repr(C)]
822
pub struct ThreadReceiverInner {
823
    #[cfg(feature = "std")]
824
    pub ptr: Box<Receiver<ThreadSendMsg>>,
825
    #[cfg(not(feature = "std"))]
826
    pub ptr: *const c_void,
827
    pub recv_fn: ThreadRecvCallback,
828
    pub destructor: ThreadReceiverDestructorCallback,
829
}
830

            
831
#[cfg(not(feature = "std"))]
832
unsafe impl Send for ThreadReceiverInner {}
833

            
834
#[cfg(feature = "std")]
835
impl core::hash::Hash for ThreadReceiverInner {
836
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
837
        (self.ptr.as_ref() as *const _ as usize).hash(state);
838
    }
839
}
840

            
841
#[cfg(feature = "std")]
842
impl PartialEq for ThreadReceiverInner {
843
    fn eq(&self, other: &Self) -> bool {
844
        (self.ptr.as_ref() as *const _ as usize) == (other.ptr.as_ref() as *const _ as usize)
845
    }
846
}
847

            
848
#[cfg(feature = "std")]
849
impl Eq for ThreadReceiverInner {}
850

            
851
#[cfg(feature = "std")]
852
impl PartialOrd for ThreadReceiverInner {
853
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
854
        Some(
855
            (self.ptr.as_ref() as *const _ as usize)
856
                .cmp(&(other.ptr.as_ref() as *const _ as usize)),
857
        )
858
    }
859
}
860

            
861
#[cfg(feature = "std")]
862
impl Ord for ThreadReceiverInner {
863
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
864
        (self.ptr.as_ref() as *const _ as usize).cmp(&(other.ptr.as_ref() as *const _ as usize))
865
    }
866
}
867

            
868
impl Drop for ThreadReceiverInner {
869
    fn drop(&mut self) {
870
        (self.destructor.cb)(self);
871
    }
872
}
873

            
874
/// Get the current system type, equivalent to `std::time::Instant::now()`, except it
875
/// also works on systems that don't have a clock (such as embedded timers)
876
pub type GetSystemTimeCallbackType = extern "C" fn() -> Instant;
877
#[repr(C)]
878
pub struct GetSystemTimeCallback {
879
    pub cb: GetSystemTimeCallbackType,
880
}
881
impl_callback_simple!(GetSystemTimeCallback);
882

            
883
/// Default implementation that gets the current system time.
884
///
885
/// On WASM targets `std::time::Instant::now()` panics, so we fall back to
886
/// a zero-tick instant instead.
887
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
888
415293
pub extern "C" fn get_system_time_libstd() -> Instant {
889
415293
    StdInstant::now().into()
890
415293
}
891

            
892
/// Fallback for WASM (where `Instant::now()` panics) and no-std targets.
893
#[cfg(any(not(feature = "std"), target_arch = "wasm32"))]
894
pub extern "C" fn get_system_time_libstd() -> Instant {
895
    Instant::Tick(SystemTick::new(0))
896
}
897

            
898
/// Callback to check if a thread has finished execution.
899
pub type CheckThreadFinishedCallbackType =
900
    extern "C" fn(/* dropcheck */ *const c_void) -> bool;
901
/// Wrapper for thread completion check callback.
902
#[repr(C)]
903
pub struct CheckThreadFinishedCallback {
904
    pub cb: CheckThreadFinishedCallbackType,
905
}
906
impl_callback_simple!(CheckThreadFinishedCallback);
907

            
908
/// Callback to send a message to a background thread.
909
pub type LibrarySendThreadMsgCallbackType =
910
    extern "C" fn(/* Sender<ThreadSendMsg> */ *const c_void, ThreadSendMsg) -> bool;
911
/// Wrapper for thread message send callback.
912
#[repr(C)]
913
pub struct LibrarySendThreadMsgCallback {
914
    pub cb: LibrarySendThreadMsgCallbackType,
915
}
916
impl_callback_simple!(LibrarySendThreadMsgCallback);
917

            
918
/// Callback for a running thread to receive messages from the main thread.
919
pub type ThreadRecvCallbackType =
920
    extern "C" fn(/* receiver.ptr */ *const c_void) -> OptionThreadSendMsg;
921
/// Wrapper for thread message receive callback.
922
#[repr(C)]
923
pub struct ThreadRecvCallback {
924
    pub cb: ThreadRecvCallbackType,
925
}
926
impl_callback_simple!(ThreadRecvCallback);
927

            
928
/// Callback to destroy a ThreadReceiver.
929
pub type ThreadReceiverDestructorCallbackType = extern "C" fn(*mut ThreadReceiverInner);
930
/// Wrapper for thread receiver destructor callback.
931
#[repr(C)]
932
pub struct ThreadReceiverDestructorCallback {
933
    pub cb: ThreadReceiverDestructorCallbackType,
934
}
935
impl_callback_simple!(ThreadReceiverDestructorCallback);