use std::collections::HashMap; use std::ops::Deref; use std::sync::Arc; use ctx::WgpuCtx; use tracing::{debug, error, info, trace, warn}; use wgpu::rwh::HasWindowHandle; use winit::application::ApplicationHandler; use winit::event::{KeyEvent, WindowEvent}; use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; use winit::window::Window; use winit::window::WindowId; pub mod ctx; struct WindowContext<'window> { window: Arc, ctx: WgpuCtx<'window>, main_window: bool, } impl Deref for WindowContext<'_> { type Target = winit::window::Window; fn deref(&self) -> &Self::Target { self.window.as_ref() } } impl WindowContext<'_> { pub fn is_main_window(&self) -> bool { self.main_window } } #[derive(Default)] pub struct App<'window> { windows: HashMap>, } impl App<'_> { fn create_main_window(&mut self, event_loop: &ActiveEventLoop) { let win_attr = Window::default_attributes().with_title("Zenyx"); match event_loop.create_window(win_attr) { Ok(window) => { let window = Arc::new(window); let window_id = window.id(); match WgpuCtx::new_blocking(window.clone()) { Ok(wgpu_ctx) => { self.windows.insert( window_id, WindowContext { window, ctx: wgpu_ctx, main_window: true, }, ); info!("Main window created: {:?}", window_id); } Err(e) => error!("Failed to create WGPU context: {:?}", e), } } Err(e) => error!("Failed to create main window: {:?}", e), } } fn handle_close_requested(&mut self, window_id: WindowId) { if self.windows.remove(&window_id).is_some() { debug!("Window {:?} closed", window_id); } else { warn!("Tried to close non-existent window {:?}", window_id); } } fn handle_keyboard_input( &mut self, event_loop: &ActiveEventLoop, window_id: WindowId, key_event: KeyEvent, ) { if !key_event.state.is_pressed() { return; } match key_event.physical_key { winit::keyboard::PhysicalKey::Code(code) => match code { winit::keyboard::KeyCode::Space => { self.toggle_background(window_id); } winit::keyboard::KeyCode::Escape => { self.spawn_child_window(event_loop); } other => error!("Unimplemented keycode: {:?}", other), }, _ => debug!("Received a keyboard event with no physical key"), } } fn toggle_background(&mut self, window_id: WindowId) { if let Some(window_context) = self.windows.get_mut(&window_id) { let current_color = window_context.ctx.bg_color(); let new_color = match current_color { wgpu::Color::WHITE => wgpu::Color::BLACK, wgpu::Color::BLACK => wgpu::Color::WHITE, _ => wgpu::Color::WHITE, }; window_context.ctx.change_bg_color(new_color); debug!("Toggled background color for window {:?}", window_id); } else { warn!("No window context for toggling background: {:?}", window_id); } } fn spawn_child_window(&mut self, event_loop: &ActiveEventLoop) { if let Some(main_ctx) = self.windows.values().find(|ctx| ctx.is_main_window()) { let title = format!("Zenyx - New Window {}", self.windows.len()); //TODO: Verify that this is safe instead of matching on it let win_attr = unsafe { let base = Window::default_attributes().with_title(title); match main_ctx.window_handle() { Ok(handle) => base.with_parent_window(Some(handle.as_raw())), Err(_) => base, } }; match event_loop.create_window(win_attr) { Ok(window) => { let window = Arc::new(window); let window_id = window.id(); match WgpuCtx::new_blocking(window.clone()) { Ok(wgpu_ctx) => { self.windows.insert( window_id, WindowContext { window, ctx: wgpu_ctx, main_window: false, }, ); debug!("Spawned new child window: {:?}", window_id); } Err(e) => error!("Failed to create WGPU context for child window: {:?}", e), } } Err(e) => error!("Failed to create child window: {:?}", e), } } else { error!("No main window found. Cannot spawn a child window."); } } fn handle_redraw_requested(&mut self, window_id: WindowId) { if let Some(window_context) = self.windows.get_mut(&window_id) { window_context.ctx.draw(); window_context.request_redraw(); trace!( "Redrew window {:?} with title: {}", window_id, window_context.window.title() ); } else { warn!("Received redraw for unknown window {:?}", window_id); } } fn handle_resize(&mut self, window_id: WindowId, new_size: winit::dpi::PhysicalSize) { if let Some(window_context) = self.windows.get_mut(&window_id) { window_context.ctx.resize(new_size.into()); window_context.window.request_redraw(); debug!( "Resized window {:?} to {}x{}", window_id, new_size.width, new_size.height ); } else { warn!("Received resize for unknown window {:?}", window_id); } } fn handle_destroyed(&mut self, event_loop: &ActiveEventLoop) { if self.windows.is_empty() || !self.windows.iter().any(|(_, ctx)| ctx.is_main_window()) { self.windows.clear(); debug!("All main windows are closed. Exiting event loop."); event_loop.exit(); } } } impl ApplicationHandler for App<'_> { fn resumed(&mut self, event_loop: &ActiveEventLoop) { if self.windows.is_empty() { self.create_main_window(event_loop); } } fn window_event( &mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent, ) { match event { WindowEvent::CloseRequested => { self.handle_close_requested(window_id); } WindowEvent::KeyboardInput { event: key_event, .. } => { self.handle_keyboard_input(event_loop, window_id, key_event); } WindowEvent::RedrawRequested => { self.handle_redraw_requested(window_id); } WindowEvent::Resized(new_size) => { self.handle_resize(window_id, new_size); } WindowEvent::Destroyed => { self.handle_destroyed(event_loop); } _ => trace!("Unhandled window event for window {:?}", window_id), } } } pub fn init_renderer(event_loop: EventLoop<()>) { event_loop.set_control_flow(ControlFlow::Wait); let mut app = App::default(); if let Err(e) = event_loop.run_app(&mut app) { error!("Failed to run application: {:?}", e); } }