diff options
Diffstat (limited to 'src/phosphor.rs')
-rw-r--r-- | src/phosphor.rs | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/src/phosphor.rs b/src/phosphor.rs new file mode 100644 index 0000000..1e50735 --- /dev/null +++ b/src/phosphor.rs @@ -0,0 +1,199 @@ +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<WindowBuilder>, +} + +impl Phosphor { + pub fn new() -> Result<Self, ()> { + 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<WindowBuilder>, + windows: HashMap<WindowId, PhosphorWindow>, + 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); + } +} |