Skip to main content

care_game/
event.rs

1use std::{future::Future, time::Instant};
2
3use crate::{
4    graphics,
5    keyboard::{self, Key},
6    math::Vec2,
7    mouse,
8};
9
10#[cfg(feature = "async-custom")]
11mod custom_async;
12#[cfg(not(any(feature = "async-custom", feature = "_async-tokio-internal")))]
13mod polling;
14#[cfg(feature = "_async-tokio-internal")]
15mod tokio_event;
16
17#[derive(Debug)]
18/// Data for an event
19pub enum EventData {
20    /// A key pressed/released event
21    KeyEvent {
22        /// The key
23        key: Key,
24        /// Whether it was pressed (true) or released (false)
25        pressed: bool,
26    },
27    /// A mouse moved event
28    MouseMoved {
29        /// The absolute screen position for the event
30        position: Vec2,
31    },
32    /// A mouse click event
33    MouseClick {
34        /// The mouse button
35        button: i32,
36        /// Whether it's currently pressed
37        pressed: bool,
38    },
39}
40
41#[derive(Debug)]
42/// An event that has occurred, usually from user input
43pub struct Event {
44    /// The time the event was created
45    pub timestamp: Instant,
46    /// The data associated with the event
47    pub data: EventData,
48}
49
50/// Initialize the care game engine, including all loaded modules
51///
52/// This is normally called automatically
53pub fn init() {
54    graphics::init();
55}
56
57/// End the frame, resetting everything for the next frame
58///
59/// This is normally called automatically
60pub fn end_frame() {
61    #[cfg(feature = "graphics")]
62    graphics::present();
63    keyboard::reset();
64    mouse::reset();
65}
66
67/// Run the game main loop, using a specific function that gets called once per frame
68pub fn main_loop<T>(init_fn: impl FnOnce() -> T + 'static, mut loop_fn: impl FnMut(&mut T) + 'static) {
69    main_loop_manual(move || {
70        init();
71        init_fn()
72    }, move |data| {
73        loop_fn(data);
74        end_frame();
75    });
76}
77
78/// Like [main_loop], but you have to call [end_frame] stuff yourself
79pub fn main_loop_manual<T>(init_fn: impl FnOnce() -> T + 'static, loop_fn: impl FnMut(&mut T) + 'static) {
80    #[cfg(feature = "window")]
81    crate::window::run(init_fn, loop_fn);
82    #[cfg(not(feature = "window"))]
83    {
84        let mut data = init_fn();
85        loop {
86            loop_fn(&mut data);
87        }
88    }
89}
90
91#[cfg(all(feature = "async-custom", feature = "_async-tokio-internal"))]
92compile_error!("Only one async executor feature can be enabled at a time.");
93
94/// Run the game main function, as a single async function
95///
96/// This supports multiple async executors as backends
97pub fn main_async(fut: impl Future<Output = ()> + 'static + Send) {
98    #[cfg(not(any(feature = "async-custom", feature = "_async-tokio-internal")))]
99    polling::async_executor(fut, true);
100    #[cfg(feature = "async-custom")]
101    custom_async::async_executor(fut, true);
102    #[cfg(feature = "_async-tokio-internal")]
103    tokio_event::async_executor(fut, true);
104}
105
106/// Like [main_async], but you have to call [end_frame] stuff yourself
107/// after every frame
108pub fn main_async_manual(fut: impl Future<Output = ()> + 'static + Send) {
109    #[cfg(not(any(feature = "async-custom", feature = "_async-tokio-internal")))]
110    polling::async_executor(fut, false);
111    #[cfg(feature = "async-custom")]
112    custom_async::async_executor(fut, false);
113    #[cfg(feature = "_async-tokio-internal")]
114    tokio_event::async_executor(fut, false);
115}
116
117/// Await until the next frame
118pub async fn next_frame() {
119    #[cfg(not(any(feature = "async-custom", feature = "_async-tokio-internal")))]
120    return polling::next_frame().await;
121    #[cfg(feature = "async-custom")]
122    return custom_async::next_frame().await;
123    #[cfg(feature = "_async-tokio-internal")]
124    return tokio_event::next_frame().await;
125}
126
127/// Await, immediately readying, so that other tasks can run along side this task without waiting
128/// for anything in particular
129pub async fn async_yield() {
130    #[cfg(feature = "async-custom")]
131    return custom_async::async_yield().await;
132    #[cfg(feature = "_async-tokio-internal")]
133    return tokio_event::async_yield().await;
134}
135
136/// Spawn an async task on the current executor
137///
138/// Panics on the "polling" executor
139pub fn spawn(task: impl Future<Output = ()> + 'static + Send) {
140    #[cfg(not(any(feature = "async-custom", feature = "_async-tokio-internal")))]
141    panic!("The polling/null executor does not support spawning multiple tasks.");
142    #[cfg(feature = "async-custom")]
143    return custom_async::spawn(task);
144    #[cfg(feature = "_async-tokio-internal")]
145    return tokio_event::spawn(task);
146}
147
148/// Process an event, this can only send events within the game, not emulate actual mouse motion or
149/// keyboard buttons
150pub fn handle_event(ev: Event) {
151    match ev.data {
152        EventData::KeyEvent { key, pressed } => crate::keyboard::process_key_event(key, pressed),
153        EventData::MouseMoved { position } => crate::mouse::process_mouse_moved_event(position),
154        EventData::MouseClick { button, pressed } => {
155            crate::mouse::process_mouse_click_event(button, pressed)
156        }
157    }
158}