arcane_engine/platform/
window.rs1use std::cell::RefCell;
2use std::path::PathBuf;
3use std::rc::Rc;
4use std::sync::Arc;
5use std::time::Instant;
6
7use anyhow::Result;
8use winit::application::ApplicationHandler;
9use winit::event::{ElementState, KeyEvent, WindowEvent};
10use winit::event_loop::{ActiveEventLoop, EventLoop};
11use winit::keyboard::{Key, NamedKey};
12use winit::window::{Window, WindowId};
13
14use crate::renderer::Renderer;
15
16use super::input::InputState;
17
18pub struct RenderState {
20 pub renderer: Option<Renderer>,
21 pub input: InputState,
22 pub sprite_commands: Vec<crate::renderer::SpriteCommand>,
23 pub camera_x: f32,
24 pub camera_y: f32,
25 pub camera_zoom: f32,
26 pub delta_time: f64,
27}
28
29impl RenderState {
30 pub fn new() -> Self {
31 Self {
32 renderer: None,
33 input: InputState::default(),
34 sprite_commands: Vec::new(),
35 camera_x: 0.0,
36 camera_y: 0.0,
37 camera_zoom: 1.0,
38 delta_time: 0.0,
39 }
40 }
41}
42
43pub struct DevConfig {
45 pub entry_file: PathBuf,
46 pub title: String,
47 pub width: u32,
48 pub height: u32,
49}
50
51pub type FrameCallback = Box<dyn FnMut(&mut RenderState) -> Result<()>>;
54
55struct AppState {
56 window: Option<Arc<Window>>,
57 config: DevConfig,
58 render_state: Rc<RefCell<RenderState>>,
59 frame_callback: FrameCallback,
60 last_frame: Instant,
61}
62
63impl ApplicationHandler for AppState {
64 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
65 if self.window.is_some() {
66 return;
67 }
68
69 let attrs = Window::default_attributes()
70 .with_title(&self.config.title)
71 .with_inner_size(winit::dpi::LogicalSize::new(
72 self.config.width,
73 self.config.height,
74 ));
75
76 let window = Arc::new(
77 event_loop
78 .create_window(attrs)
79 .expect("Failed to create window"),
80 );
81
82 match Renderer::new(window.clone()) {
83 Ok(renderer) => {
84 self.render_state.borrow_mut().renderer = Some(renderer);
85 }
86 Err(e) => {
87 eprintln!("Failed to initialize renderer: {e}");
88 event_loop.exit();
89 return;
90 }
91 }
92
93 self.window = Some(window);
94 self.last_frame = Instant::now();
95 }
96
97 fn window_event(
98 &mut self,
99 event_loop: &ActiveEventLoop,
100 _window_id: WindowId,
101 event: WindowEvent,
102 ) {
103 match event {
104 WindowEvent::CloseRequested => {
105 event_loop.exit();
106 }
107
108 WindowEvent::Resized(new_size) => {
109 let mut state = self.render_state.borrow_mut();
110 if let Some(ref mut renderer) = state.renderer {
111 renderer.resize(new_size.width, new_size.height);
112 }
113 }
114
115 WindowEvent::KeyboardInput {
116 event:
117 KeyEvent {
118 logical_key,
119 state: key_state,
120 ..
121 },
122 ..
123 } => {
124 let key_name = key_to_string(&logical_key);
125 let mut state = self.render_state.borrow_mut();
126 match key_state {
127 ElementState::Pressed => state.input.key_down(&key_name),
128 ElementState::Released => state.input.key_up(&key_name),
129 }
130 }
131
132 WindowEvent::CursorMoved { position, .. } => {
133 let mut state = self.render_state.borrow_mut();
134 state.input.mouse_move(position.x as f32, position.y as f32);
135 }
136
137 WindowEvent::MouseInput { state: button_state, button, .. } => {
138 let mut state = self.render_state.borrow_mut();
139 let button_id: u8 = match button {
140 winit::event::MouseButton::Left => 0,
141 winit::event::MouseButton::Right => 1,
142 winit::event::MouseButton::Middle => 2,
143 winit::event::MouseButton::Back => 3,
144 winit::event::MouseButton::Forward => 4,
145 winit::event::MouseButton::Other(id) => id.min(255) as u8,
146 };
147 match button_state {
148 ElementState::Pressed => {
149 state.input.mouse_buttons.insert(button_id);
150 let key_name = match button_id {
152 0 => "MouseLeft",
153 1 => "MouseRight",
154 2 => "MouseMiddle",
155 _ => return,
156 };
157 state.input.key_down(key_name);
158 }
159 ElementState::Released => {
160 state.input.mouse_buttons.remove(&button_id);
161 let key_name = match button_id {
162 0 => "MouseLeft",
163 1 => "MouseRight",
164 2 => "MouseMiddle",
165 _ => return,
166 };
167 state.input.key_up(key_name);
168 }
169 }
170 }
171
172 WindowEvent::RedrawRequested => {
173 let now = Instant::now();
174 let dt = now.duration_since(self.last_frame).as_secs_f64();
175 self.last_frame = now;
176
177 {
178 let mut state = self.render_state.borrow_mut();
179 state.delta_time = dt;
180 }
181
182 {
184 let mut state = self.render_state.borrow_mut();
185 if let Err(e) = (self.frame_callback)(&mut state) {
186 eprintln!("Frame callback error: {e}");
187 }
188 }
189
190 {
192 let mut state = self.render_state.borrow_mut();
193 state.input.begin_frame();
194 }
195
196 {
198 let mut state = self.render_state.borrow_mut();
199 let cam_x = state.camera_x;
201 let cam_y = state.camera_y;
202 let cam_zoom = state.camera_zoom;
203 let commands = std::mem::take(&mut state.sprite_commands);
204
205 if let Some(ref mut renderer) = state.renderer {
206 renderer.camera.x = cam_x;
207 renderer.camera.y = cam_y;
208 renderer.camera.zoom = cam_zoom;
209 renderer.frame_commands = commands;
210
211 if let Err(e) = renderer.render_frame() {
212 eprintln!("Render error: {e}");
213 }
214 }
215 }
216
217 if let Some(ref window) = self.window {
218 window.request_redraw();
219 }
220 }
221
222 _ => {}
223 }
224 }
225
226 fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
227 if let Some(ref window) = self.window {
228 window.request_redraw();
229 }
230 }
231}
232
233fn key_to_string(key: &Key) -> String {
235 match key {
236 Key::Named(named) => match named {
237 NamedKey::ArrowUp => "ArrowUp".to_string(),
238 NamedKey::ArrowDown => "ArrowDown".to_string(),
239 NamedKey::ArrowLeft => "ArrowLeft".to_string(),
240 NamedKey::ArrowRight => "ArrowRight".to_string(),
241 NamedKey::Space => "Space".to_string(),
242 NamedKey::Enter => "Enter".to_string(),
243 NamedKey::Escape => "Escape".to_string(),
244 NamedKey::Backspace => "Backspace".to_string(),
245 NamedKey::Tab => "Tab".to_string(),
246 NamedKey::Shift => "Shift".to_string(),
247 NamedKey::Control => "Control".to_string(),
248 NamedKey::Alt => "Alt".to_string(),
249 other => format!("{other:?}"),
250 },
251 Key::Character(c) => c.to_string(),
252 _ => "Unknown".to_string(),
253 }
254}
255
256pub fn run_event_loop(
258 config: DevConfig,
259 render_state: Rc<RefCell<RenderState>>,
260 frame_callback: FrameCallback,
261) -> Result<()> {
262 let event_loop = EventLoop::new()?;
263
264 let mut app = AppState {
265 window: None,
266 config,
267 render_state,
268 frame_callback,
269 last_frame: Instant::now(),
270 };
271
272 event_loop.run_app(&mut app)?;
273 Ok(())
274}