nstd_events/
lib.rs

1use crate::NSTDEvent::*;
2use nstd_input::{
3    deps::{winit, winit_input_helper},
4    key::*,
5    mouse::*,
6    touch::NSTDTouchState,
7    NSTDRawInput,
8};
9use std::{
10    os::raw::{c_double, c_int},
11    ptr::{self, addr_of_mut},
12};
13#[cfg(any(
14    target_os = "windows",
15    target_os = "linux",
16    target_os = "macos",
17    target_os = "android"
18))]
19use winit::platform::run_return::EventLoopExtRunReturn;
20#[cfg(target_os = "linux")]
21use winit::platform::unix::EventLoopExtUnix;
22#[cfg(target_os = "windows")]
23use winit::platform::windows::EventLoopExtWindows;
24use winit::{
25    event::*,
26    event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
27    window::WindowId,
28};
29use winit_input_helper::WinitInputHelper;
30#[cfg(feature = "deps")]
31pub mod deps {
32    pub use nstd_input;
33}
34
35/// An event loop handle.
36pub type NSTDEventLoop = *mut EventLoop<()>;
37
38/// Represents a window ID.
39pub type NSTDWindowID = *mut WindowId;
40
41/// Represents an event loop's control flow.
42#[repr(C)]
43#[allow(non_camel_case_types)]
44pub enum NSTDEventLoopControlFlow {
45    NSTD_EVENT_LOOP_CONTROL_FLOW_POLL,
46    NSTD_EVENT_LOOP_CONTROL_FLOW_WAIT,
47    NSTD_EVENT_LOOP_CONTROL_FLOW_EXIT,
48}
49impl Into<ControlFlow> for NSTDEventLoopControlFlow {
50    #[inline]
51    fn into(self) -> ControlFlow {
52        match self {
53            Self::NSTD_EVENT_LOOP_CONTROL_FLOW_POLL => ControlFlow::Poll,
54            Self::NSTD_EVENT_LOOP_CONTROL_FLOW_WAIT => ControlFlow::Wait,
55            Self::NSTD_EVENT_LOOP_CONTROL_FLOW_EXIT => ControlFlow::Exit,
56        }
57    }
58}
59
60/// Represents an event.
61#[repr(C)]
62#[allow(non_camel_case_types)]
63pub enum NSTDEvent {
64    NSTD_EVENT_NONE,
65    NSTD_EVENT_LOOP_DESTROYED,
66    NSTD_EVENT_EVENTS_CLEARED,
67    NSTD_EVENT_DEVICE_ADDED,
68    NSTD_EVENT_DEVICE_REMOVED,
69    NSTD_EVENT_MOUSE_MOVED,
70    NSTD_EVENT_SCROLL_PIXEL,
71    NSTD_EVENT_SCROLL_LINE,
72    NSTD_EVENT_WINDOW_REDRAW_REQUESTED,
73    NSTD_EVENT_WINDOW_RESIZED,
74    NSTD_EVENT_WINDOW_MOVED,
75    NSTD_EVENT_WINDOW_FOCUS_CHANGED,
76    NSTD_EVENT_WINDOW_KEY,
77    NSTD_EVENT_WINDOW_MOD_KEY,
78    NSTD_EVENT_WINDOW_MOUSE_MOVED,
79    NSTD_EVENT_WINDOW_MOUSE_ENTERED,
80    NSTD_EVENT_WINDOW_MOUSE_LEFT,
81    NSTD_EVENT_WINDOW_SCROLL,
82    NSTD_EVENT_WINDOW_MOUSE_BUTTON,
83    NSTD_EVENT_WINDOW_CLOSE_REQUESTED,
84}
85
86/// Holds an event's data.
87#[repr(C)]
88pub struct NSTDEventData {
89    pub event: NSTDEvent,
90    pub mouse_delta: [c_double; 2],
91    pub size: [u32; 2],
92    pub pos: [i32; 2],
93    pub window_id: NSTDWindowID,
94    pub raw_input: NSTDRawInput,
95    pub touch_state: NSTDTouchState,
96    pub mouse_button_event: NSTDMouseButtonEvent,
97    pub key: NSTDKeyEvent,
98    pub mod_keys: u8,
99    pub has_focus: i8,
100}
101impl Default for NSTDEventData {
102    fn default() -> Self {
103        Self {
104            event: NSTD_EVENT_NONE,
105            mouse_delta: [0.0, 0.0],
106            size: [0, 0],
107            pos: [0, 0],
108            window_id: ptr::null_mut(),
109            raw_input: ptr::null_mut(),
110            touch_state: NSTDTouchState::default(),
111            mouse_button_event: NSTDMouseButtonEvent::default(),
112            key: NSTDKeyEvent::default(),
113            mod_keys: 0,
114            has_focus: 0,
115        }
116    }
117}
118
119/// Creates a new event loop.
120/// Returns: `NSTDEventLoop event_loop` - The event loop.
121#[inline]
122#[cfg_attr(feature = "clib", no_mangle)]
123pub unsafe extern "C" fn nstd_events_event_loop_new() -> NSTDEventLoop {
124    #[cfg(not(any(target_os = "windows", target_os = "linux")))]
125    return Box::into_raw(Box::new(EventLoop::new()));
126    #[cfg(any(target_os = "windows", target_os = "linux"))]
127    return Box::into_raw(Box::new(EventLoop::<()>::new_any_thread()));
128}
129
130/// Runs an event loop, never returning.
131/// Note that this function returns on the following operating systems:
132///     - Windows
133///     - Linux
134///     - MacOS
135///     - Android
136/// Parameters:
137///     `NSTDEventLoop *event_loop` - The event loop to run.
138///     `NSTDEventLoopControlFlow(*callback)(NSTDEventData *)` - Called once per event.
139#[cfg_attr(feature = "clib", no_mangle)]
140pub unsafe extern "C" fn nstd_events_event_loop_run(
141    event_loop: *mut NSTDEventLoop,
142    callback: extern "C" fn(*mut NSTDEventData) -> NSTDEventLoopControlFlow,
143    should_return: c_int,
144) {
145    let mut winit_event_loop = Box::from_raw(*event_loop);
146    *event_loop = ptr::null_mut();
147    let mut winput = Box::new(WinitInputHelper::new());
148    let mut event_data = NSTDEventData::default();
149    event_data.raw_input = winput.as_mut();
150    let closure =
151        move |event: Event<()>, _: &EventLoopWindowTarget<()>, control_flow: &mut ControlFlow| {
152            winput.update(&event);
153            event_data.event = match event {
154                Event::LoopDestroyed => NSTD_EVENT_LOOP_DESTROYED,
155                Event::MainEventsCleared => NSTD_EVENT_EVENTS_CLEARED,
156                Event::RedrawRequested(window_id) => {
157                    event_data.window_id = Box::into_raw(Box::new(window_id));
158                    NSTD_EVENT_WINDOW_REDRAW_REQUESTED
159                }
160                Event::WindowEvent { window_id, event } => {
161                    event_data.window_id = Box::into_raw(Box::new(window_id));
162                    match event {
163                        WindowEvent::Resized(size) => {
164                            event_data.size = [size.width, size.height];
165                            NSTD_EVENT_WINDOW_RESIZED
166                        }
167                        WindowEvent::Moved(pos) => {
168                            event_data.pos = [pos.x, pos.y];
169                            NSTD_EVENT_WINDOW_MOVED
170                        }
171                        WindowEvent::Focused(focused) => {
172                            event_data.has_focus = focused as i8;
173                            NSTD_EVENT_WINDOW_FOCUS_CHANGED
174                        }
175                        WindowEvent::KeyboardInput { input, .. } => {
176                            event_data.key.state = match input.state {
177                                ElementState::Pressed => NSTDKeyState::NSTD_KEY_STATE_PRESSED,
178                                ElementState::Released => NSTDKeyState::NSTD_KEY_STATE_RELEASED,
179                            };
180                            event_data.key.scan_code = input.scancode;
181                            event_data.key.key = match input.virtual_keycode {
182                                Some(key) => NSTDKey::from(key),
183                                _ => NSTDKey::NSTD_KEY_NONE,
184                            };
185                            NSTD_EVENT_WINDOW_KEY
186                        }
187                        WindowEvent::ModifiersChanged(mods) => {
188                            event_data.mod_keys = 0
189                                | NSTD_INPUT_KEY_SHIFT_BIT * mods.shift() as u8
190                                | NSTD_INPUT_KEY_CTRL_BIT * mods.ctrl() as u8
191                                | NSTD_INPUT_KEY_ALT_BIT * mods.alt() as u8
192                                | NSTD_INPUT_KEY_LOGO_BIT * mods.logo() as u8;
193                            NSTD_EVENT_WINDOW_MOD_KEY
194                        }
195                        WindowEvent::CursorMoved { position, .. } => {
196                            event_data.mouse_delta = [position.x, position.y];
197                            NSTD_EVENT_WINDOW_MOUSE_MOVED
198                        }
199                        WindowEvent::CursorEntered { .. } => NSTD_EVENT_WINDOW_MOUSE_ENTERED,
200                        WindowEvent::CursorLeft { .. } => NSTD_EVENT_WINDOW_MOUSE_LEFT,
201                        WindowEvent::MouseWheel { delta, phase, .. } => {
202                            event_data.mouse_delta = match delta {
203                                MouseScrollDelta::PixelDelta(delta) => [delta.x, delta.y],
204                                MouseScrollDelta::LineDelta(x, y) => [x as c_double, y as c_double],
205                            };
206                            event_data.touch_state = match phase {
207                                TouchPhase::Started => NSTDTouchState::NSTD_TOUCH_STATE_STARTED,
208                                TouchPhase::Moved => NSTDTouchState::NSTD_TOUCH_STATE_MOVED,
209                                TouchPhase::Ended => NSTDTouchState::NSTD_TOUCH_STATE_ENDED,
210                                TouchPhase::Cancelled => NSTDTouchState::NSTD_TOUCH_STATE_CANCELLED,
211                            };
212                            NSTD_EVENT_WINDOW_SCROLL
213                        }
214                        WindowEvent::MouseInput { state, button, .. } => {
215                            event_data.mouse_button_event.state = match state {
216                                ElementState::Pressed => {
217                                    NSTDMouseButtonState::NSTD_MOUSE_BUTTON_PRESSED
218                                }
219                                ElementState::Released => {
220                                    NSTDMouseButtonState::NSTD_MOUSE_BUTTON_RELEASED
221                                }
222                            };
223                            event_data.mouse_button_event.button = match button {
224                                MouseButton::Left => NSTDMouseButton::NSTD_MOUSE_BUTTON_LEFT,
225                                MouseButton::Right => NSTDMouseButton::NSTD_MOUSE_BUTTON_RIGHT,
226                                MouseButton::Middle => NSTDMouseButton::NSTD_MOUSE_BUTTON_MIDDLE,
227                                MouseButton::Other(other) => {
228                                    event_data.mouse_button_event.extra_button = other;
229                                    NSTDMouseButton::NSTD_MOUSE_BUTTON_OTHER
230                                }
231                            };
232                            NSTD_EVENT_WINDOW_MOUSE_BUTTON
233                        }
234                        WindowEvent::CloseRequested => NSTD_EVENT_WINDOW_CLOSE_REQUESTED,
235                        _ => NSTD_EVENT_NONE,
236                    }
237                }
238                Event::DeviceEvent { event, .. } => match event {
239                    DeviceEvent::Added => NSTD_EVENT_DEVICE_ADDED,
240                    DeviceEvent::Removed => NSTD_EVENT_DEVICE_REMOVED,
241                    DeviceEvent::MouseMotion { delta } => {
242                        event_data.mouse_delta = [delta.0, delta.1];
243                        NSTD_EVENT_MOUSE_MOVED
244                    }
245                    DeviceEvent::MouseWheel { delta } => match delta {
246                        MouseScrollDelta::PixelDelta(delta) => {
247                            event_data.mouse_delta = [delta.x, delta.y];
248                            NSTD_EVENT_SCROLL_PIXEL
249                        }
250                        MouseScrollDelta::LineDelta(x, y) => {
251                            event_data.mouse_delta = [x as c_double, y as c_double];
252                            NSTD_EVENT_SCROLL_LINE
253                        }
254                    },
255                    _ => NSTD_EVENT_NONE,
256                },
257                _ => NSTD_EVENT_NONE,
258            };
259            *control_flow = callback(addr_of_mut!(event_data)).into();
260            if !event_data.window_id.is_null() {
261                Box::from_raw(event_data.window_id);
262                event_data.window_id = ptr::null_mut();
263            }
264        };
265    #[cfg(not(any(
266        target_os = "windows",
267        target_os = "linux",
268        target_os = "macos",
269        target_os = "android"
270    )))]
271    event_loop.run(closure);
272    #[cfg(any(
273        target_os = "windows",
274        target_os = "linux",
275        target_os = "macos",
276        target_os = "android"
277    ))]
278    if should_return != 0 {
279        winit_event_loop.run_return(closure);
280    } else {
281        winit_event_loop.run(closure);
282    }
283}
284
285/// Frees an event loop without running it.
286/// Parameters:
287///     `NSTDEventLoop *event_loop` - The event loop to free.
288#[inline]
289#[cfg_attr(feature = "clib", no_mangle)]
290pub unsafe extern "C" fn nstd_events_event_loop_free(event_loop: *mut NSTDEventLoop) {
291    Box::from_raw(*event_loop);
292    *event_loop = ptr::null_mut();
293}