use std::collections::HashSet; use glutin::{ event::{ ElementState, Event, KeyboardInput, MouseScrollDelta, WindowEvent as GlutinWindowEvent, }, event_loop::{ControlFlow, EventLoop}, platform::desktop::EventLoopExtDesktop, }; use shrev::{EventChannel, ReaderId}; use space_crush_common::misc::{LogResult, WorldHelper}; use specs::World; use crate::{misc::Window, Error}; pub use glutin::event::{MouseButton, VirtualKeyCode}; pub struct Events { keys: HashSet, event_loop: EventLoop<()>, mouse_pos: Option<(f64, f64)>, mouse_lock: Option<(f64, f64)>, control_event_id: ReaderId, } impl Events { pub fn new(world: &mut World) -> Result { world.insert(EventChannel::::default()); world.insert(EventChannel::::default()); world.insert(EventChannel::::default()); world.insert(EventChannel::::default()); let control_event_id = world.register_event_reader::()?; Ok(Self { keys: HashSet::new(), event_loop: EventLoop::new(), mouse_pos: None, mouse_lock: None, control_event_id, }) } } impl Events { pub fn handle(&self) -> &EventLoop<()> { &self.event_loop } pub fn process(&mut self, world: &World, window: &Window) { let control_events = world.fetch::>(); let mut mouse_events = world.fetch_mut::>(); let mut window_events = world.fetch_mut::>(); let mut keyboard_events = world.fetch_mut::>(); let keys = &mut self.keys; let mouse_pos = &mut self.mouse_pos; let event_loop = &mut self.event_loop; let mouse_lock = &mut self.mouse_lock; let events = control_events.read(&mut self.control_event_id); for event in events { match event { ControlEvent::LockMouse => { *mouse_lock = *mouse_pos; window .set_mouse_locked(true) .warn("Unable to lock mouse to window"); } ControlEvent::UnlockMouse => { *mouse_lock = None; window .set_mouse_locked(false) .warn("Unable to lock mouse to window"); } } } let mut has_mouse_moved = false; event_loop.run_return(|event, _target, flow_control| { *flow_control = ControlFlow::Poll; match event { Event::MainEventsCleared | Event::RedrawRequested(_) => { *flow_control = ControlFlow::Exit; } Event::WindowEvent { event, .. } => match event { /* Mouse Events */ GlutinWindowEvent::CursorMoved { position, .. } => { if mouse_lock.is_none() { mouse_events .single_write(MouseEvent::Move(position.x as _, position.y as _)); } if let Some((x, y)) = mouse_pos { let dx = position.x - *x; let dy = position.y - *y; mouse_events.single_write(MouseEvent::Delta(dx as _, dy as _)); } has_mouse_moved = true; *mouse_pos = Some((position.x as _, position.y as _)); } GlutinWindowEvent::MouseInput { state: ElementState::Pressed, button, .. } => { mouse_events.single_write(MouseEvent::ButtonDown(button)); } GlutinWindowEvent::MouseInput { state: ElementState::Released, button, .. } => { mouse_events.single_write(MouseEvent::ButtonUp(button)); } GlutinWindowEvent::MouseWheel { delta: MouseScrollDelta::LineDelta(x, y), .. } => { if x != 0.0 { mouse_events.single_write(MouseEvent::ScrollX(x)); } if y != 0.0 { mouse_events.single_write(MouseEvent::ScrollY(y)); } } GlutinWindowEvent::CursorLeft { .. } => { *mouse_pos = None; } /* Key Event */ GlutinWindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, virtual_keycode: Some(key), .. }, .. } => { if keys.insert(key) { keyboard_events.single_write(KeyboardEvent::KeyDown(key)); } keyboard_events.single_write(KeyboardEvent::KeyPress(key)); } GlutinWindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Released, virtual_keycode: Some(key), .. }, .. } => { if keys.remove(&key) { keyboard_events.single_write(KeyboardEvent::KeyUp(key)); } } /* Window Event */ GlutinWindowEvent::Resized(pos) => { window_events.single_write(WindowEvent::Resize(pos.width, pos.height)); } GlutinWindowEvent::CloseRequested => { window_events.single_write(WindowEvent::Close); } /* Ignore */ _ => (), }, _ => (), } }); if let Some((x, y)) = self.mouse_lock { if has_mouse_moved { window.set_mouse_pos(x, y).warn("Unable to set mouse pos"); self.mouse_pos = Some((x, y)); } } } } pub enum ControlEvent { LockMouse, UnlockMouse, } pub enum WindowEvent { Resize(u32, u32), Close, } pub enum MouseEvent { Move(f32, f32), Delta(f32, f32), ButtonDown(MouseButton), ButtonUp(MouseButton), ScrollX(f32), ScrollY(f32), } pub enum KeyboardEvent { KeyUp(VirtualKeyCode), KeyDown(VirtualKeyCode), KeyPress(VirtualKeyCode), }