1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use js::*;
use std::collections::HashMap;
use std::sync::Mutex;

use crate::{get_property_f64, get_property_i64};

pub type TimerHandle = f64;

static ANIMATION_FRAME_EVENT_HANDLERS: Mutex<
    Option<HashMap<i64, Box<dyn FnMut() + Send + 'static>>>,
> = Mutex::new(None);

#[no_mangle]
pub extern "C" fn web_one_time_empty_handler(id: i64) {
    let mut c = None;
    {
        let mut handlers = ANIMATION_FRAME_EVENT_HANDLERS.lock().unwrap();
        if let Some(h) = handlers.as_mut() {
            // remove
            if let Some(handler) = h.remove(&id) {
                c = Some(handler);
            }
        }
    }
    if let Some(mut c) = c {
        c();
    }
}

pub fn request_animation_frame(handler: impl FnMut() + Send + Sync + 'static) {
    let function_handle = js!(r#"
        function(){
            const handler = () => {
                this.module.instance.exports.web_one_time_empty_handler(id);
                this.releaseObject(id);
            };
            const id = this.storeObject(handler);
            requestAnimationFrame(handler);
            return id;
        }"#)
    .invoke_and_return_bigint(&[]);
    let mut h = ANIMATION_FRAME_EVENT_HANDLERS.lock().unwrap();
    if h.is_none() {
        *h = Some(HashMap::new());
    }
    h.as_mut()
        .unwrap()
        .insert(function_handle, Box::new(handler));
}

pub fn set_timeout(
    handler: impl FnMut() + 'static + Send + Sync,
    ms: impl Into<f64>,
) -> TimerHandle {
    let obj_handle = js!(r#"
        function(ms){
            const handler = () => {
                this.module.instance.exports.web_one_time_empty_handler(id);
                this.releaseObject(id);
            };
            const id = this.storeObject(handler);
            const handle = window.setTimeout(handler, ms);
            return {id,handle};
        }"#)
    .invoke_and_return_object(&[ms.into().into()]);
    let function_handle = get_property_i64(&obj_handle, "id");
    let timer_handle = get_property_f64(&obj_handle, "handle");
    let mut h = ANIMATION_FRAME_EVENT_HANDLERS.lock().unwrap();
    if h.is_none() {
        *h = Some(HashMap::new());
    }
    h.as_mut()
        .unwrap()
        .insert(function_handle, Box::new(handler));
    timer_handle
}

pub fn clear_timeout(interval_id: impl Into<f64>) {
    let clear_interval = js!(r#"
        function(interval_id){
            window.clearTimeout(interval_id);
        }"#);
    clear_interval.invoke(&[interval_id.into().into()]);
}