summaryrefslogtreecommitdiff
path: root/src/phosphor.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/phosphor.rs')
-rw-r--r--src/phosphor.rs199
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);
+ }
+}