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
    sync::atomic::{AtomicUsize, Ordering},
21
};
22
#[cfg(feature = "std")]
23
use std::sync::mpsc::{Receiver, Sender};
24
#[cfg(feature = "std")]
25
use std::sync::Mutex;
26
#[cfg(feature = "std")]
27
use std::thread::{self, JoinHandle};
28
#[cfg(feature = "std")]
29
use std::time::Duration as StdDuration;
30
#[cfg(feature = "std")]
31
use std::time::Instant as StdInstant;
32

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
308
/// FFI-safe wrapper around std::time::Instant with custom clone/drop callbacks.
309
///
310
/// Allows crossing FFI boundaries while maintaining proper memory management.
311
#[repr(C)]
312
pub struct InstantPtr {
313
    #[cfg(feature = "std")]
314
    pub ptr: Box<StdInstant>,
315
    #[cfg(not(feature = "std"))]
316
    pub ptr: *const c_void,
317
    pub clone_fn: InstantPtrCloneCallback,
318
    pub destructor: InstantPtrDestructorCallback,
319
    pub run_destructor: bool,
320
}
321

            
322
pub type InstantPtrCloneCallbackType = extern "C" fn(*const InstantPtr) -> InstantPtr;
323
#[repr(C)]
324
pub struct InstantPtrCloneCallback {
325
    pub cb: InstantPtrCloneCallbackType,
326
}
327
impl_callback_simple!(InstantPtrCloneCallback);
328

            
329
pub type InstantPtrDestructorCallbackType = extern "C" fn(*mut InstantPtr);
330
#[repr(C)]
331
pub struct InstantPtrDestructorCallback {
332
    pub cb: InstantPtrDestructorCallbackType,
333
}
334
impl_callback_simple!(InstantPtrDestructorCallback);
335

            
336
// ----  LIBSTD implementation for InstantPtr BEGIN
337
#[cfg(feature = "std")]
338
impl core::fmt::Debug for InstantPtr {
339
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
340
        write!(f, "{:?}", self.get())
341
    }
342
}
343

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

            
351
#[cfg(feature = "std")]
352
impl core::hash::Hash for InstantPtr {
353
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
354
        self.get().hash(state);
355
    }
356
}
357

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

            
365
#[cfg(feature = "std")]
366
impl PartialEq for InstantPtr {
367
    fn eq(&self, other: &InstantPtr) -> bool {
368
        self.get() == other.get()
369
    }
370
}
371

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

            
379
impl Eq for InstantPtr {}
380

            
381
#[cfg(feature = "std")]
382
impl PartialOrd for InstantPtr {
383
    fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
384
        Some((self.get()).cmp(&(other.get())))
385
    }
386
}
387

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

            
395
#[cfg(feature = "std")]
396
impl Ord for InstantPtr {
397
    fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
398
        (self.get()).cmp(&(other.get()))
399
    }
400
}
401

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

            
409
#[cfg(feature = "std")]
410
impl InstantPtr {
411
77784
    fn get(&self) -> StdInstant {
412
77784
        *(self.ptr).clone()
413
77784
    }
414
}
415

            
416
impl Clone for InstantPtr {
417
78306
    fn clone(&self) -> Self {
418
78306
        (self.clone_fn.cb)(self)
419
78306
    }
420
}
421

            
422
#[cfg(feature = "std")]
423
78306
extern "C" fn std_instant_clone(ptr: *const InstantPtr) -> InstantPtr {
424
78306
    let az_instant_ptr = unsafe { &*ptr };
425
78306
    InstantPtr {
426
78306
        ptr: az_instant_ptr.ptr.clone(),
427
78306
        clone_fn: az_instant_ptr.clone_fn.clone(),
428
78306
        destructor: az_instant_ptr.destructor.clone(),
429
78306
        run_destructor: true,
430
78306
    }
431
78306
}
432

            
433
#[cfg(feature = "std")]
434
impl From<StdInstant> for InstantPtr {
435
98192
    fn from(s: StdInstant) -> InstantPtr {
436
98192
        Self {
437
98192
            ptr: Box::new(s),
438
98192
            clone_fn: InstantPtrCloneCallback {
439
98192
                cb: std_instant_clone,
440
98192
            },
441
98192
            destructor: InstantPtrDestructorCallback {
442
98192
                cb: std_instant_drop,
443
98192
            },
444
98192
            run_destructor: true,
445
98192
        }
446
98192
    }
447
}
448

            
449
#[cfg(feature = "std")]
450
impl From<InstantPtr> for StdInstant {
451
77784
    fn from(s: InstantPtr) -> StdInstant {
452
77784
        s.get()
453
77784
    }
454
}
455

            
456
impl Drop for InstantPtr {
457
176498
    fn drop(&mut self) {
458
176498
        if self.run_destructor {
459
176498
            self.run_destructor = false;
460
176498
            (self.destructor.cb)(self);
461
176498
        }
462
176498
    }
463
}
464

            
465
#[cfg(feature = "std")]
466
176498
extern "C" fn std_instant_drop(_: *mut InstantPtr) {}
467

            
468
// ----  LIBSTD implementation for InstantPtr END
469

            
470
/// A span of time, either from the system clock or as tick difference.
471
///
472
/// Mirrors `Instant` variants - System durations work with System instants,
473
/// Tick durations work with Tick instants.
474
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
475
#[repr(C, u8)]
476
pub enum Duration {
477
    /// System duration from std::time::Duration (requires "std" feature)
478
    System(SystemTimeDiff),
479
    /// Tick-based duration for embedded systems
480
    Tick(SystemTickDiff),
481
}
482

            
483
impl core::fmt::Display for Duration {
484
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
485
        match self {
486
            #[cfg(feature = "std")]
487
            Duration::System(s) => {
488
                let s: StdDuration = s.clone().into();
489
                write!(f, "{:?}", s)
490
            }
491
            #[cfg(not(feature = "std"))]
492
            Duration::System(s) => write!(f, "({}s, {}ns)", s.secs, s.nanos),
493
            Duration::Tick(tick) => write!(f, "{} ticks", tick.tick_diff),
494
        }
495
    }
496
}
497

            
498
#[cfg(feature = "std")]
499
impl From<StdDuration> for Duration {
500
    fn from(s: StdDuration) -> Self {
501
        Duration::System(s.into())
502
    }
503
}
504

            
505
impl Duration {
506
    /// Returns the maximum possible duration.
507
    pub fn max() -> Self {
508
        #[cfg(feature = "std")]
509
        {
510
            Duration::System(StdDuration::new(core::u64::MAX, NANOS_PER_SEC - 1).into())
511
        }
512
        #[cfg(not(feature = "std"))]
513
        {
514
            Duration::Tick(SystemTickDiff {
515
                tick_diff: u64::MAX,
516
            })
517
        }
518
    }
519

            
520
    /// Divides this duration by another, returning the ratio as f32.
521
42
    pub fn div(&self, other: &Self) -> f32 {
522
        use self::Duration::*;
523
42
        match (self, other) {
524
42
            (System(s), System(s2)) => s.div(s2) as f32,
525
            (Tick(t), Tick(t2)) => t.div(t2) as f32,
526
            _ => 0.0,
527
        }
528
42
    }
529

            
530
    /// Returns the smaller of two durations.
531
    pub fn min(self, other: Self) -> Self {
532
        if self.smaller_than(&other) {
533
            self
534
        } else {
535
            other
536
        }
537
    }
538

            
539
    /// Returns true if self > other (panics if variants differ).
540
    #[allow(unused_variables)]
541
    pub fn greater_than(&self, other: &Self) -> bool {
542
        match (self, other) {
543
            // self > other
544
            (Duration::System(s), Duration::System(o)) => {
545
                #[cfg(feature = "std")]
546
                {
547
                    let s: StdDuration = s.clone().into();
548
                    let o: StdDuration = o.clone().into();
549
                    s > o
550
                }
551
                #[cfg(not(feature = "std"))]
552
                {
553
                    unreachable!()
554
                }
555
            }
556
            (Duration::Tick(s), Duration::Tick(o)) => s.tick_diff > o.tick_diff,
557
            _ => {
558
                panic!("illegal: trying to compare a SystemDuration with a TickDuration");
559
            }
560
        }
561
    }
562

            
563
    /// Returns true if self < other (panics if variants differ).
564
    #[allow(unused_variables)]
565
    pub fn smaller_than(&self, other: &Self) -> bool {
566
        // self < other
567
        match (self, other) {
568
            // self > other
569
            (Duration::System(s), Duration::System(o)) => {
570
                #[cfg(feature = "std")]
571
                {
572
                    let s: StdDuration = s.clone().into();
573
                    let o: StdDuration = o.clone().into();
574
                    s < o
575
                }
576
                #[cfg(not(feature = "std"))]
577
                {
578
                    unreachable!()
579
                }
580
            }
581
            (Duration::Tick(s), Duration::Tick(o)) => s.tick_diff < o.tick_diff,
582
            _ => {
583
                panic!("illegal: trying to compare a SystemDuration with a TickDuration");
584
            }
585
        }
586
    }
587
}
588

            
589
/// Represents a difference in ticks for systems that
590
/// don't support timing
591
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
592
#[repr(C)]
593
pub struct SystemTickDiff {
594
    pub tick_diff: u64,
595
}
596

            
597
impl SystemTickDiff {
598
    /// Divide duration A by duration B.
599
    /// Returns `Inf` or `NaN` if `other` is zero.
600
    pub fn div(&self, other: &Self) -> f64 {
601
        self.tick_diff as f64 / other.tick_diff as f64
602
    }
603
}
604

            
605
/// Duration represented as seconds + nanoseconds (mirrors std::time::Duration).
606
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
607
#[repr(C)]
608
pub struct SystemTimeDiff {
609
    pub secs: u64,
610
    pub nanos: u32,
611
}
612

            
613
impl SystemTimeDiff {
614
    /// Divide duration A by duration B.
615
    /// Returns `Inf` or `NaN` if `other` is zero.
616
42
    pub fn div(&self, other: &Self) -> f64 {
617
42
        self.as_secs_f64() / other.as_secs_f64()
618
42
    }
619
84
    fn as_secs_f64(&self) -> f64 {
620
84
        (self.secs as f64) + ((self.nanos as f64) / (NANOS_PER_SEC as f64))
621
84
    }
622
}
623

            
624
#[cfg(feature = "std")]
625
impl From<StdDuration> for SystemTimeDiff {
626
38976
    fn from(d: StdDuration) -> SystemTimeDiff {
627
38976
        SystemTimeDiff {
628
38976
            secs: d.as_secs(),
629
38976
            nanos: d.subsec_nanos(),
630
38976
        }
631
38976
    }
632
}
633

            
634
#[cfg(feature = "std")]
635
impl From<SystemTimeDiff> for StdDuration {
636
    fn from(d: SystemTimeDiff) -> StdDuration {
637
        StdDuration::new(d.secs, d.nanos)
638
    }
639
}
640

            
641
const MILLIS_PER_SEC: u64 = 1_000;
642
const NANOS_PER_MILLI: u32 = 1_000_000;
643
const NANOS_PER_SEC: u32 = 1_000_000_000;
644

            
645
impl SystemTimeDiff {
646
    /// Creates a duration from whole seconds.
647
    pub const fn from_secs(secs: u64) -> Self {
648
        SystemTimeDiff { secs, nanos: 0 }
649
    }
650
    /// Creates a duration from milliseconds.
651
5580
    pub const fn from_millis(millis: u64) -> Self {
652
5580
        SystemTimeDiff {
653
5580
            secs: millis / MILLIS_PER_SEC,
654
5580
            nanos: ((millis % MILLIS_PER_SEC) as u32) * NANOS_PER_MILLI,
655
5580
        }
656
5580
    }
657
    /// Creates a duration from nanoseconds.
658
    pub const fn from_nanos(nanos: u64) -> Self {
659
        SystemTimeDiff {
660
            secs: nanos / (NANOS_PER_SEC as u64),
661
            nanos: (nanos % (NANOS_PER_SEC as u64)) as u32,
662
        }
663
    }
664
    /// Adds two durations, returning None on overflow.
665
    pub const fn checked_add(self, rhs: Self) -> Option<Self> {
666
        if let Some(mut secs) = self.secs.checked_add(rhs.secs) {
667
            let mut nanos = self.nanos + rhs.nanos;
668
            if nanos >= NANOS_PER_SEC {
669
                nanos -= NANOS_PER_SEC;
670
                if let Some(new_secs) = secs.checked_add(1) {
671
                    secs = new_secs;
672
                } else {
673
                    return None;
674
                }
675
            }
676
            Some(SystemTimeDiff { secs, nanos })
677
        } else {
678
            None
679
        }
680
    }
681

            
682
    /// Returns the total duration in milliseconds.
683
    pub fn millis(&self) -> u64 {
684
        (self.secs * MILLIS_PER_SEC) + (self.nanos / NANOS_PER_MILLI) as u64
685
    }
686

            
687
    /// Converts to std::time::Duration.
688
    #[cfg(feature = "std")]
689
    pub fn get(&self) -> StdDuration {
690
        (*self).into()
691
    }
692
}
693

            
694
impl_option!(
695
    Instant,
696
    OptionInstant,
697
    copy = false,
698
    [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
699
);
700
impl_option!(
701
    Duration,
702
    OptionDuration,
703
    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
704
);
705

            
706
/// Message that can be sent from the main thread to the Thread using the ThreadId.
707
///
708
/// The thread can ignore the event.
709
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
710
#[repr(C, u8)]
711
pub enum ThreadSendMsg {
712
    /// The thread should terminate at the nearest
713
    TerminateThread,
714
    /// Next frame tick
715
    Tick,
716
    /// Custom data
717
    Custom(RefAny),
718
}
719

            
720
impl_option!(
721
    ThreadSendMsg,
722
    OptionThreadSendMsg,
723
    copy = false,
724
    [Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash]
725
);
726

            
727
/// Channel endpoint for receiving messages from the main thread in a background thread.
728
///
729
/// Thread-safe wrapper around the receiver end of a message channel.
730
#[derive(Debug)]
731
#[repr(C)]
732
pub struct ThreadReceiver {
733
    #[cfg(feature = "std")]
734
    pub ptr: Box<Arc<Mutex<ThreadReceiverInner>>>,
735
    #[cfg(not(feature = "std"))]
736
    pub ptr: *const c_void,
737
    pub run_destructor: bool,
738
    /// For FFI: stores the foreign callable (e.g., PyFunction)
739
    pub ctx: OptionRefAny,
740
}
741

            
742
impl Clone for ThreadReceiver {
743
    fn clone(&self) -> Self {
744
        Self {
745
            ptr: self.ptr.clone(),
746
            run_destructor: true,
747
            ctx: self.ctx.clone(),
748
        }
749
    }
750
}
751

            
752
impl Drop for ThreadReceiver {
753
    fn drop(&mut self) {
754
        self.run_destructor = false;
755
    }
756
}
757

            
758
impl ThreadReceiver {
759
    /// Creates a new receiver (no-op on no_std).
760
    #[cfg(not(feature = "std"))]
761
    pub fn new(_t: ThreadReceiverInner) -> Self {
762
        Self {
763
            ptr: core::ptr::null(),
764
            run_destructor: false,
765
            ctx: OptionRefAny::None,
766
        }
767
    }
768

            
769
    /// Creates a new receiver wrapping the inner channel.
770
    #[cfg(feature = "std")]
771
    pub fn new(t: ThreadReceiverInner) -> Self {
772
        Self {
773
            ptr: Box::new(Arc::new(Mutex::new(t))),
774
            run_destructor: true,
775
            ctx: OptionRefAny::None,
776
        }
777
    }
778

            
779
    /// Get the FFI context (e.g., Python callable)
780
    pub fn get_ctx(&self) -> OptionRefAny {
781
        self.ctx.clone()
782
    }
783

            
784
    /// Receives a message (returns None on no_std).
785
    #[cfg(not(feature = "std"))]
786
    pub fn recv(&mut self) -> OptionThreadSendMsg {
787
        None.into()
788
    }
789

            
790
    /// Receives a message from the main thread, if available.
791
    #[cfg(feature = "std")]
792
    pub fn recv(&mut self) -> OptionThreadSendMsg {
793
        let ts = match self.ptr.lock().ok() {
794
            Some(s) => s,
795
            None => return None.into(),
796
        };
797
        (ts.recv_fn.cb)(ts.ptr.as_ref() as *const _ as *const c_void)
798
    }
799
}
800

            
801
/// Inner receiver state containing the actual channel and callbacks.
802
#[derive(Debug)]
803
#[cfg_attr(not(feature = "std"), derive(PartialEq, PartialOrd, Eq, Ord))]
804
#[repr(C)]
805
pub struct ThreadReceiverInner {
806
    #[cfg(feature = "std")]
807
    pub ptr: Box<Receiver<ThreadSendMsg>>,
808
    #[cfg(not(feature = "std"))]
809
    pub ptr: *const c_void,
810
    pub recv_fn: ThreadRecvCallback,
811
    pub destructor: ThreadReceiverDestructorCallback,
812
}
813

            
814
#[cfg(not(feature = "std"))]
815
unsafe impl Send for ThreadReceiverInner {}
816

            
817
#[cfg(feature = "std")]
818
impl core::hash::Hash for ThreadReceiverInner {
819
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
820
        (self.ptr.as_ref() as *const _ as usize).hash(state);
821
    }
822
}
823

            
824
#[cfg(feature = "std")]
825
impl PartialEq for ThreadReceiverInner {
826
    fn eq(&self, other: &Self) -> bool {
827
        (self.ptr.as_ref() as *const _ as usize) == (other.ptr.as_ref() as *const _ as usize)
828
    }
829
}
830

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

            
834
#[cfg(feature = "std")]
835
impl PartialOrd for ThreadReceiverInner {
836
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
837
        Some(
838
            (self.ptr.as_ref() as *const _ as usize)
839
                .cmp(&(other.ptr.as_ref() as *const _ as usize)),
840
        )
841
    }
842
}
843

            
844
#[cfg(feature = "std")]
845
impl Ord for ThreadReceiverInner {
846
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
847
        (self.ptr.as_ref() as *const _ as usize).cmp(&(other.ptr.as_ref() as *const _ as usize))
848
    }
849
}
850

            
851
impl Drop for ThreadReceiverInner {
852
    fn drop(&mut self) {
853
        (self.destructor.cb)(self);
854
    }
855
}
856

            
857
/// Get the current system type, equivalent to `std::time::Instant::now()`, except it
858
/// also works on systems that don't have a clock (such as embedded timers)
859
pub type GetSystemTimeCallbackType = extern "C" fn() -> Instant;
860
#[repr(C)]
861
pub struct GetSystemTimeCallback {
862
    pub cb: GetSystemTimeCallbackType,
863
}
864
impl_callback_simple!(GetSystemTimeCallback);
865

            
866
/// Default implementation that gets the current system time.
867
///
868
/// On WASM targets `std::time::Instant::now()` panics, so we fall back to
869
/// a zero-tick instant instead.
870
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
871
95634
pub extern "C" fn get_system_time_libstd() -> Instant {
872
95634
    StdInstant::now().into()
873
95634
}
874

            
875
/// Fallback for WASM (where `Instant::now()` panics) and no-std targets.
876
#[cfg(any(not(feature = "std"), target_arch = "wasm32"))]
877
pub extern "C" fn get_system_time_libstd() -> Instant {
878
    Instant::Tick(SystemTick::new(0))
879
}
880

            
881
/// Callback to check if a thread has finished execution.
882
pub type CheckThreadFinishedCallbackType =
883
    extern "C" fn(/* dropcheck */ *const c_void) -> bool;
884
/// Wrapper for thread completion check callback.
885
#[repr(C)]
886
pub struct CheckThreadFinishedCallback {
887
    pub cb: CheckThreadFinishedCallbackType,
888
}
889
impl_callback_simple!(CheckThreadFinishedCallback);
890

            
891
/// Callback to send a message to a background thread.
892
pub type LibrarySendThreadMsgCallbackType =
893
    extern "C" fn(/* Sender<ThreadSendMsg> */ *const c_void, ThreadSendMsg) -> bool;
894
/// Wrapper for thread message send callback.
895
#[repr(C)]
896
pub struct LibrarySendThreadMsgCallback {
897
    pub cb: LibrarySendThreadMsgCallbackType,
898
}
899
impl_callback_simple!(LibrarySendThreadMsgCallback);
900

            
901
/// Callback for a running thread to receive messages from the main thread.
902
pub type ThreadRecvCallbackType =
903
    extern "C" fn(/* receiver.ptr */ *const c_void) -> OptionThreadSendMsg;
904
/// Wrapper for thread message receive callback.
905
#[repr(C)]
906
pub struct ThreadRecvCallback {
907
    pub cb: ThreadRecvCallbackType,
908
}
909
impl_callback_simple!(ThreadRecvCallback);
910

            
911
/// Callback to destroy a ThreadReceiver.
912
pub type ThreadReceiverDestructorCallbackType = extern "C" fn(*mut ThreadReceiverInner);
913
/// Wrapper for thread receiver destructor callback.
914
#[repr(C)]
915
pub struct ThreadReceiverDestructorCallback {
916
    pub cb: ThreadReceiverDestructorCallbackType,
917
}
918
impl_callback_simple!(ThreadReceiverDestructorCallback);