1use std::{
2 sync::{
3 atomic::{AtomicBool, Ordering},
4 Arc,
5 },
6 time::Instant,
7};
8
9use parking_lot::{Mutex, RwLock};
10use winit::{
11 application::ApplicationHandler,
12 dpi::{LogicalPosition, LogicalSize},
13 event::{KeyEvent, WindowEvent},
14 event_loop::{ControlFlow, EventLoop},
15 keyboard::{Key as WKey, NamedKey, SmolStr},
16 window::{Window, WindowAttributes},
17};
18
19use crate::{math::Vec2, prelude::Key};
20
21static HAS_INITIALIZED: AtomicBool = AtomicBool::new(false);
22static INIT_COMPLETE: AtomicBool = AtomicBool::new(false);
23thread_local! {
24 static EVENT_LOOP: RwLock<Option<EventLoop<()>>> = const { RwLock::new(None) };
25}
26
27pub(crate) static CREATE_WINDOWS: Mutex<Vec<WindowAttributes>> = Mutex::new(Vec::new());
28pub(crate) static WINDOWS: RwLock<Vec<Arc<Window>>> = RwLock::new(Vec::new());
29
30fn init() {
31 #[cfg(any(target_os = "linux", target_os = "windows"))]
32 assert!(
33 std::thread::current().name() == Some("main"),
34 "Window init must be called on the main thread!"
35 );
36 if HAS_INITIALIZED
37 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
38 .is_ok()
39 {
40 INIT_COMPLETE.store(false, Ordering::Release);
41 EVENT_LOOP.with(|el_cell| {
42 let mut el = el_cell.write();
43 if el.is_none() {
44 let tmp = EventLoop::new().unwrap();
45 tmp.set_control_flow(ControlFlow::Poll);
46 *el = Some(tmp);
47 }
48 });
49 }
50}
51
52#[derive(Debug)]
54pub struct WindowSettings<'a> {
55 name: &'a str,
57 size: Option<Vec2>,
59 resizable: bool,
61 pos: Option<Vec2>,
63}
64
65impl Default for WindowSettings<'_> {
66 fn default() -> Self {
67 Self {
68 name: "CÄRE game",
69 size: Some((800, 600).into()),
70 resizable: false,
71 pos: None,
72 }
73 }
74}
75
76pub fn open(name: &str) {
81 open_with_settings(WindowSettings {
82 name,
83 ..WindowSettings::default()
84 })
85}
86
87pub fn open_with_settings(settings: WindowSettings) {
89 let mut attribs = Window::default_attributes()
90 .with_title(settings.name)
91 .with_resizable(settings.resizable);
92 if let Some(size) = settings.size {
93 attribs = attribs.with_inner_size(LogicalSize::new(size.0.x, size.0.y));
94 }
95 if let Some(pos) = settings.pos {
96 attribs = attribs.with_position(LogicalPosition::new(pos.0.x, pos.0.y));
97 }
98 CREATE_WINDOWS.lock().push(attribs);
99}
100
101pub fn set_window_size(size: impl Into<Vec2>) {
103 let size = size.into();
104 let mut windows = WINDOWS.write();
105 let window = windows.first_mut().unwrap();
106 let _ = window.request_inner_size(LogicalSize::new(size.x(), size.y()));
107}
108
109fn convert_key(key: winit::keyboard::Key<SmolStr>) -> Key {
110 match key {
111 WKey::Named(NamedKey::ArrowUp) => Key::Up,
112 WKey::Named(NamedKey::ArrowDown) => Key::Down,
113 WKey::Named(NamedKey::ArrowLeft) => Key::Left,
114 WKey::Named(NamedKey::ArrowRight) => Key::Right,
115 WKey::Named(NamedKey::Space) => Key::Space,
116 WKey::Named(NamedKey::Enter) => Key::Enter,
117 WKey::Named(NamedKey::Escape) => Key::Escape,
118 WKey::Named(NamedKey::Backspace) => Key::Backspace,
119 WKey::Named(NamedKey::Delete) => Key::Delete,
120 WKey::Named(NamedKey::Shift) => Key::Shift,
121 WKey::Named(NamedKey::Control) => Key::Control,
122 WKey::Named(NamedKey::Alt) => Key::Alt,
123 WKey::Named(NamedKey::Meta) => Key::Meta,
124 WKey::Character(ch) => Key::Char(ch.chars().next().unwrap()),
125 _ => Key::Unknown,
126 }
127}
128
129enum AppData<T, F: FnOnce() -> T> {
130 Init(Option<F>),
131 Data(T),
132}
133
134struct AppHandler<T, F: FnMut(&mut T), I: FnOnce() -> T> {
135 data: AppData<T, I>,
136 loop_fn: F,
137}
138
139impl<T, F: FnMut(&mut T), I: FnOnce() -> T> ApplicationHandler for AppHandler<T, F, I> {
140 fn resumed(&mut self, _event_loop: &winit::event_loop::ActiveEventLoop) {}
141
142 fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
143 for attribs in CREATE_WINDOWS.lock().drain(..) {
144 WINDOWS.write().push(Arc::new(
145 event_loop
146 .create_window(attribs)
147 .expect("Failed to open window"),
148 ));
149 }
150 if let AppData::Init(init) = &mut self.data {
151 self.data = AppData::Data((init.take().unwrap())());
152 };
153 let AppData::Data(data) = &mut self.data else {
154 panic!("Impossible");
155 };
156 (self.loop_fn)(data);
157 }
158
159 fn window_event(
160 &mut self,
161 event_loop: &winit::event_loop::ActiveEventLoop,
162 window_id: winit::window::WindowId,
163 ev: WindowEvent,
164 ) {
165 match ev {
166 WindowEvent::CloseRequested => {
167 event_loop.exit();
168 }
169 WindowEvent::KeyboardInput {
170 event:
171 KeyEvent {
172 logical_key,
173 state,
174 repeat,
175 ..
176 },
177 ..
178 } => {
179 if !repeat {
180 crate::event::handle_event(crate::event::Event {
181 timestamp: Instant::now(),
182 data: crate::event::EventData::KeyEvent {
183 key: convert_key(logical_key),
184 pressed: state.is_pressed(),
185 },
186 });
187 }
188 }
189 WindowEvent::CursorMoved { position, .. } => {
190 let position: LogicalPosition<f64> = position.to_logical(
191 WINDOWS
192 .read()
193 .iter()
194 .find(|w| w.id() == window_id)
195 .map(|w| w.scale_factor())
196 .unwrap_or(1.0),
197 );
198 crate::event::handle_event(crate::event::Event {
199 timestamp: Instant::now(),
200 data: crate::event::EventData::MouseMoved {
201 position: Vec2::new(position.x, position.y),
202 },
203 });
204 }
205 WindowEvent::MouseInput { state, button, .. } => {
206 crate::event::handle_event(crate::event::Event {
207 timestamp: Instant::now(),
208 data: crate::event::EventData::MouseClick {
209 button: match button {
210 winit::event::MouseButton::Left => 1,
211 winit::event::MouseButton::Right => 2,
212 winit::event::MouseButton::Middle => 3,
213 winit::event::MouseButton::Back => 4,
214 winit::event::MouseButton::Forward => 5,
215 winit::event::MouseButton::Other(n) => n as i32 + 6,
216 },
217 pressed: state.is_pressed(),
218 },
219 });
220 }
221 _ => {}
222 }
223 }
224}
225
226pub(crate) fn run<T>(init_fn: impl FnOnce() -> T, loop_fn: impl FnMut(&mut T)) {
230 init();
231 EVENT_LOOP.with(move |el_call| {
232 let el = el_call
233 .write()
234 .take()
235 .expect("Event loop must be run from the main thread");
236 el.run_app(&mut AppHandler {
237 data: AppData::Init(Some(init_fn)),
238 loop_fn,
239 })
240 .unwrap();
241 });
242}