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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
 * Created on Mon Aug 07 2023
 *
 * Copyright (c) storycraft. Licensed under the MIT Licence.
 */

//! Implementation of winit Executor

pub mod event;
pub mod handle;

use std::sync::OnceLock;

use async_task::Task;
use event_source::emit;
use futures_lite::Future;
use instant::Duration;
use scoped_tls_hkt::scoped_thread_local;
use winit::{
    error::EventLoopError,
    event::Event,
    event_loop::{ControlFlow, EventLoopBuilder, EventLoopWindowTarget},
};

use crate::{device, resumed, suspended, timer::UpdateState, window};

use self::{event::ExecutorEvent, handle::ExecutorHandle};

pub type EventLoopTarget = EventLoopWindowTarget<ExecutorEvent>;

static HANDLE: OnceLock<ExecutorHandle> = OnceLock::new();

/// Get current [`ExecutorHandle`]
/// 
/// There can be only one [`ExecutorHandle`] and will panic if executor did not start.
pub fn executor_handle() -> &'static ExecutorHandle {
    HANDLE.get().expect("Executor is not started")
}

scoped_thread_local!(static EL_TARGET: EventLoopTarget);

/// Run closure using current [`EventLoopTarget`]
/// 
/// Will panic if it called on outside of runtime thread
pub fn with_eventloop_target<R>(func: impl FnOnce(&EventLoopTarget) -> R) -> R {
    EL_TARGET.with(func)
}

#[derive(Debug)]
struct Executor {
    _main: Task<()>,
    handle: &'static ExecutorHandle,
}

impl Executor {
    fn on_event(
        &mut self,
        event: Event<ExecutorEvent>,
        target: &EventLoopTarget,
        control_flow: &mut ControlFlow,
    ) {
        EL_TARGET.set(target, move || match event {
            Event::UserEvent(ExecutorEvent::Wake) => {}

            Event::UserEvent(ExecutorEvent::PollTask(runnable)) => {
                runnable.run();
            }

            Event::UserEvent(ExecutorEvent::Exit(code)) => {
                *control_flow = ControlFlow::ExitWithCode(code);
            }

            Event::DeviceEvent { device_id, event } => {
                emit!(device(), (device_id, &event));
            }

            Event::WindowEvent { window_id, mut event } => {
                emit!(window(), (window_id, &mut event));
            }

            Event::Resumed => {
                emit!(resumed(), ());
            }

            Event::Suspended => {
                emit!(suspended(), ());
            }

            Event::AboutToWait => {
                if let UpdateState::WaitTimeout(next_delay) = self.handle.timer.update_next() {
                    control_flow.set_wait_timeout(Duration::from_millis(next_delay.get()));
                } else if *control_flow == ControlFlow::Poll {
                    *control_flow = ControlFlow::Wait;
                }
            }

            _ => {}
        });
    }
}

/// Entrypoint for runtime
pub fn run(main: impl Future<Output = ()>) -> Result<(), EventLoopError> {
    let event_loop = EventLoopBuilder::with_user_event().build()?;

    let handle = {
        if HANDLE.set(ExecutorHandle::new(&event_loop)).is_err() {
            panic!("This cannot be happen");
        }

        HANDLE.get().unwrap()
    };

    let (runnable, task) = {
        let proxy = event_loop.create_proxy();

        // SAFETY: EventLoop created on same function, closure does not need to be Send and task and references to Future outlive event loop
        unsafe {
            handle.spawn_raw_unchecked(async move {
                main.await;
                let _ = proxy.send_event(ExecutorEvent::Exit(0));
            })
        }
    };

    let mut executor = Executor {
        _main: task,
        handle,
    };

    EL_TARGET.set(&event_loop, move || runnable.run());

    event_loop
        .run(move |event, target, control_flow| executor.on_event(event, target, control_flow))
}