fennel_core/
events.rs

1//! `sdl3::event::Event`-driven main loop.
2
3use sdl3::event::Event;
4use std::time::{Duration, Instant};
5
6use crate::Window;
7
8use sdl3::{
9    keyboard::{Keycode, Mod, Scancode},
10    mouse::{MouseButton, MouseState, MouseWheelDirection},
11};
12
13pub struct KeyboardEvent {
14    pub timestamp: u64,
15    pub window_id: u32,
16    pub keycode: Option<Keycode>,
17    pub scancode: Option<Scancode>,
18    pub keymod: Mod,
19    pub repeat: bool,
20    pub which: u32,
21    pub raw: u16,
22}
23
24pub struct MouseMotionEvent {
25    pub timestamp: u64,
26    pub window_id: u32,
27    pub which: u32,
28    pub mousestate: MouseState,
29    pub x: f32,
30    pub y: f32,
31    pub xrel: f32,
32    pub yrel: f32,
33}
34
35pub struct MouseClickEvent {
36    pub timestamp: u64,
37    pub window_id: u32,
38    pub which: u32,
39    pub mouse_btn: MouseButton,
40    pub clicks: u8,
41    pub x: f32,
42    pub y: f32,
43}
44
45pub struct MouseWheelEvent {
46    pub timestamp: u64,
47    pub window_id: u32,
48    pub which: u32,
49    pub x: f32,
50    pub y: f32,
51    pub direction: MouseWheelDirection,
52    pub mouse_x: f32,
53    pub mouse_y: f32,
54}
55
56#[async_trait::async_trait]
57pub trait WindowEventHandler: Send + Sync {
58    fn update(&self, _window: &mut Window) -> anyhow::Result<()>;
59    fn draw(&mut self, _window: &mut Window) -> anyhow::Result<()>;
60
61    fn key_down_event(&self, _window: &mut Window, _event: KeyboardEvent) -> anyhow::Result<()> {
62        Ok(())
63    }
64    fn key_up_event(&self, _window: &mut Window, _event: KeyboardEvent) -> anyhow::Result<()> {
65        Ok(())
66    }
67    fn mouse_motion_event(
68        &self,
69        _window: &mut Window,
70        _event: MouseMotionEvent,
71    ) -> anyhow::Result<()> {
72        Ok(())
73    }
74    fn mouse_button_down_event(
75        &self,
76        _window: &mut Window,
77        _event: MouseClickEvent,
78    ) -> anyhow::Result<()> {
79        Ok(())
80    }
81    fn mouse_button_up_event(
82        &self,
83        _window: &mut Window,
84        _event: MouseClickEvent,
85    ) -> anyhow::Result<()> {
86        Ok(())
87    }
88    fn mouse_wheel_event(
89        &self,
90        _window: &mut Window,
91        _event: MouseWheelEvent,
92    ) -> anyhow::Result<()> {
93        Ok(())
94    }
95}
96
97/// Run the main loop.
98///
99/// Parameters:
100/// - `window`: mutable reference to your `Window`. required because [`Window`] contains required
101///   gfx variables
102/// - `state`: boxed implementation of [`EventHandler`] that receives update/draw calls
103///
104/// Behavior:
105/// - Polls SDL events each frame and breaks the loop on `Event::Quit`.
106/// - Calls `state.update(window)` then `state.draw(game)` each frame.
107///
108/// Example:
109/// ```ignore
110/// let mut window = Window::new("cool title".into(), "cool author".into(), graphics);
111/// events::run(&mut window, Box::new(my_handler));
112/// ```
113pub async fn run(window: &mut Window, state:  &'static mut dyn WindowEventHandler) {
114    let mut event_pump = window.graphics.sdl_context.event_pump().unwrap();
115
116    'running: loop {
117        let now = Instant::now();
118
119        // event_PUMP???? HOLY FUCK IS THAT A REFERENCE TO PSYCHOPOMP
120        for event in event_pump.poll_iter() {
121            match event {
122                Event::Quit { .. } => break 'running,
123                Event::KeyDown {
124                    timestamp,
125                    window_id,
126                    keycode: Some(keycode),
127                    scancode: Some(scancode),
128                    keymod,
129                    repeat,
130                    which,
131                    raw,
132                } => state
133                    .key_down_event(
134                        window,
135                        KeyboardEvent {
136                            timestamp,
137                            window_id,
138                            keycode: Some(keycode),
139                            scancode: Some(scancode),
140                            keymod,
141                            repeat,
142                            which,
143                            raw,
144                        },
145                    )
146                    .unwrap(),
147
148                Event::KeyUp {
149                    timestamp,
150                    window_id,
151                    keycode: Some(keycode),
152                    scancode: Some(scancode),
153                    keymod,
154                    repeat,
155                    which,
156                    raw,
157                } => state
158                    .key_up_event(
159                        window,
160                        KeyboardEvent {
161                            timestamp,
162                            window_id,
163                            keycode: Some(keycode),
164                            scancode: Some(scancode),
165                            keymod,
166                            repeat,
167                            which,
168                            raw,
169                        },
170                    )
171                    .unwrap(),
172
173                Event::MouseMotion {
174                    timestamp,
175                    window_id,
176                    which,
177                    mousestate,
178                    x,
179                    y,
180                    xrel,
181                    yrel,
182                } => state
183                    .mouse_motion_event(
184                        window,
185                        MouseMotionEvent {
186                            timestamp,
187                            window_id,
188                            which,
189                            mousestate,
190                            x,
191                            y,
192                            xrel,
193                            yrel,
194                        },
195                    )
196                    .unwrap(),
197
198                Event::MouseButtonDown {
199                    timestamp,
200                    window_id,
201                    which,
202                    mouse_btn,
203                    clicks,
204                    x,
205                    y,
206                } => state
207                    .mouse_button_down_event(
208                        window,
209                        MouseClickEvent {
210                            timestamp,
211                            window_id,
212                            which,
213                            mouse_btn,
214                            clicks,
215                            x,
216                            y,
217                        },
218                    )
219                    .unwrap(),
220
221                Event::MouseButtonUp {
222                    timestamp,
223                    window_id,
224                    which,
225                    mouse_btn,
226                    clicks,
227                    x,
228                    y,
229                } => state
230                    .mouse_button_up_event(
231                        window,
232                        MouseClickEvent {
233                            timestamp,
234                            window_id,
235                            which,
236                            mouse_btn,
237                            clicks,
238                            x,
239                            y,
240                        },
241                    )
242                    .unwrap(),
243
244                Event::MouseWheel {
245                    timestamp,
246                    window_id,
247                    which,
248                    x,
249                    y,
250                    direction,
251                    mouse_x,
252                    mouse_y,
253                } => state
254                    .mouse_wheel_event(
255                        window,
256                        MouseWheelEvent {
257                            timestamp,
258                            window_id,
259                            which,
260                            x,
261                            y,
262                            direction,
263                            mouse_x,
264                            mouse_y,
265                        },
266                    )
267                    .unwrap(),
268                _ => {}
269            }
270        }
271        // Update window logic and render.
272        let _ = state.update(window);
273        let _ = state.draw(window);
274
275        // Simple frame limiter: aim for ~1 millisecond minimum frame time.
276        let elapsed = Instant::now().duration_since(now).as_nanos() as u64;
277        if elapsed < 999_999 {
278            std::thread::sleep(Duration::from_nanos(999_999 - elapsed));
279        }
280    }
281}