use crate::*; use winit::application::ApplicationHandler; use winit::dpi::{PhysicalPosition, PhysicalSize}; use winit::event::{MouseButton as WinitMouseButton, MouseScrollDelta, StartCause, WindowEvent}; use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; use winit::keyboard::PhysicalKey; use winit::window::WindowId; use winit::error::EventLoopError; use std::collections::HashMap; use std::time::{Duration, Instant}; pub struct Phosphor { event_loop: EventLoop<()>, builders: Vec, } impl Phosphor { pub fn new() -> Result { if let Ok(event_loop) = EventLoop::new() { Ok( Self { event_loop, builders: Vec::new(), } ) } else { Err(()) } } pub fn create_window(&mut self, window: WindowBuilder) { self.builders.push(window); } pub fn run(self) -> Result<(), EventLoopError> { let mut application = PhosphorApplication { builders: self.builders, windows: HashMap::new(), frame_start: Instant::now(), }; self.event_loop.run_app(&mut application) } } struct PhosphorApplication { builders: Vec, windows: HashMap, frame_start: Instant, } impl PhosphorApplication { pub fn handle_builders_and_destructors(&mut self, event_loop: &ActiveEventLoop) { // Find marked windows, handle final requests and destroy. let mut marked_ids = Vec::new(); for (id, window) in &mut self.windows { if window.marked_for_destruction { window.handle_requests(&mut self.builders); marked_ids.push(id.clone()); } } for id in marked_ids { self.windows.remove(&id); } // Build any newly-requested windows. while !self.builders.is_empty() { for builder in std::mem::take(&mut self.builders) { let mut window = PhosphorWindow::from_builder(builder, event_loop); window.program.handle_event(Event::Initialise, &mut window.requests.as_writer()); window.handle_requests(&mut self.builders); self.windows.insert(window.winit.id(), window); } } // If all windows have been destroyed, close the event loop. if self.windows.is_empty() { event_loop.exit(); } } pub fn handle_window_event(&mut self, event: WindowEvent, id: WindowId) { let window = match self.windows.get_mut(&id) { Some(w) => w, None => return, }; macro_rules! handle { ($event:expr) => { window.program.handle_event($event, &mut window.requests.as_writer()) }; } match event { WindowEvent::Resized( PhysicalSize { width, height } ) => { window.update_buffer_size(Dimensions::new(width, height)); } WindowEvent::CloseRequested => { handle!(Event::CloseRequest); } WindowEvent::Destroyed => { handle!(Event::Close); } WindowEvent::DroppedFile(path) => { handle!(Event::FileDrop(path)); } WindowEvent::Focused(focused) => { handle!(Event::FocusChange(focused)); } WindowEvent::KeyboardInput { event, .. } => { if let Some(smol_str) = event.text { if event.state.is_pressed() { for c in smol_str.chars() { handle!(Event::CharacterInput(c)); } } } if let PhysicalKey::Code(code) = event.physical_key { handle!(Event::KeyboardInput { key: code, action: event.state.into(), }); } } WindowEvent::ModifiersChanged(modifiers) => { handle!(Event::ModifierChange(modifiers.state())); } WindowEvent::CursorMoved { position, .. } => { let pointer = Position::new( position.x as i32 / window.scale() as i32, position.y as i32 / window.scale() as i32, ); if window.pointer != Some(pointer) { window.pointer = Some(pointer); handle!(Event::CursorMove(pointer)); } } WindowEvent::CursorEntered { .. } => { handle!(Event::CursorEnter); } WindowEvent::CursorLeft { .. } => { handle!(Event::CursorExit); } WindowEvent::MouseWheel { delta, .. } => match delta { MouseScrollDelta::LineDelta(x, y) => { if x != 0.0 { handle!(Event::ScrollLines { axis: Axis::Horizontal, distance: -x }); } if y != 0.0 { handle!(Event::ScrollLines { axis: Axis::Vertical, distance: -y }); } } MouseScrollDelta::PixelDelta(PhysicalPosition {x, y}) => { if x != 0.0 { handle!(Event::ScrollPixels { axis: Axis::Horizontal, distance: -x as f32 }); } if y != 0.0 { handle!(Event::ScrollPixels { axis: Axis::Vertical, distance: -y as f32 }); } } } WindowEvent::MouseInput { state, button, .. } => { let action = state.into(); match button { WinitMouseButton::Left => handle!(Event::MouseButton { button: MouseButton::Left, action }), WinitMouseButton::Middle => handle!(Event::MouseButton { button: MouseButton::Middle, action }), WinitMouseButton::Right => handle!(Event::MouseButton { button: MouseButton::Right, action }), _ => (), } } WindowEvent::RedrawRequested => { window.redraw(); }, _ => (), } } } impl ApplicationHandler for PhosphorApplication { fn resumed(&mut self, _event_loop: &ActiveEventLoop) {} fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) { if let StartCause::Init = cause { // Ensure a minimum duration between frames. const MINIMUM_WAIT: Duration = Duration::from_millis(1); std::thread::sleep(MINIMUM_WAIT.saturating_sub(self.frame_start.elapsed())); self.frame_start = Instant::now(); event_loop.set_control_flow(ControlFlow::Poll); self.handle_builders_and_destructors(event_loop); } } fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { for (_, window) in &mut self.windows { window.program.process(&mut window.requests.as_writer()); window.handle_requests(&mut self.builders); } self.handle_builders_and_destructors(event_loop); } fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) { self.handle_window_event(event, id); if let Some(window) = self.windows.get_mut(&id) { window.handle_requests(&mut self.builders); } self.handle_builders_and_destructors(event_loop); } }