use winit::application::ApplicationHandler;
use winit::dpi::PhysicalSize;
use winit::event::{ElementState, WindowEvent};
use winit::event_loop::ActiveEventLoop;
use winit::window::WindowId;
use crate::input::{InputEvent, KeyCode, MouseButton};
use crate::Result;
pub struct EventLoop {
inner: winit::event_loop::EventLoop<()>,
}
impl EventLoop {
pub fn new() -> Result<Self> {
let inner = winit::event_loop::EventLoop::new()?;
Ok(Self { inner })
}
#[must_use]
pub fn inner(&self) -> &winit::event_loop::EventLoop<()> {
&self.inner
}
pub fn run<F>(self, mut callback: F) -> Result<()>
where
F: FnMut(AppEvent, &mut ControlFlow) + 'static,
{
let mut control_flow = ControlFlow::default();
let mut handler = CallbackHandler {
callback: &mut callback,
control_flow: &mut control_flow,
};
self.inner.run_app(&mut handler)?;
Ok(())
}
}
#[derive(Debug, Clone)]
pub enum AppEvent {
Resized {
width: u32,
height: u32,
},
CloseRequested,
RedrawRequested,
Focused(bool),
Input(InputEvent),
LoopExiting,
NewEvents,
AboutToWait,
}
#[derive(Debug, Default)]
pub struct ControlFlow {
should_exit: bool,
}
impl ControlFlow {
pub fn exit(&mut self) {
self.should_exit = true;
}
#[must_use]
pub fn should_exit(&self) -> bool {
self.should_exit
}
}
struct CallbackHandler<'a, F>
where
F: FnMut(AppEvent, &mut ControlFlow),
{
callback: &'a mut F,
control_flow: &'a mut ControlFlow,
}
impl<F> ApplicationHandler for CallbackHandler<'_, F>
where
F: FnMut(AppEvent, &mut ControlFlow),
{
fn resumed(&mut self, _event_loop: &ActiveEventLoop) {
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
let app_event = match event {
WindowEvent::CloseRequested => Some(AppEvent::CloseRequested),
WindowEvent::Resized(PhysicalSize { width, height }) => {
Some(AppEvent::Resized { width, height })
}
WindowEvent::RedrawRequested => Some(AppEvent::RedrawRequested),
WindowEvent::Focused(focused) => Some(AppEvent::Focused(focused)),
WindowEvent::KeyboardInput { event, .. } => {
if let Some(key_code) = convert_key_code(event.physical_key) {
let input_event = match event.state {
ElementState::Pressed => InputEvent::KeyPressed(key_code),
ElementState::Released => InputEvent::KeyReleased(key_code),
};
Some(AppEvent::Input(input_event))
} else {
None
}
}
WindowEvent::MouseInput { state, button, .. } => {
let mouse_button = convert_mouse_button(button);
let input_event = match state {
ElementState::Pressed => InputEvent::MousePressed(mouse_button),
ElementState::Released => InputEvent::MouseReleased(mouse_button),
};
Some(AppEvent::Input(input_event))
}
WindowEvent::CursorMoved { position, .. } => {
Some(AppEvent::Input(InputEvent::MouseMoved {
x: position.x,
y: position.y,
}))
}
WindowEvent::MouseWheel { delta, .. } => {
let (x, y) = match delta {
winit::event::MouseScrollDelta::LineDelta(x, y) => (f64::from(x), f64::from(y)),
winit::event::MouseScrollDelta::PixelDelta(pos) => (pos.x, pos.y),
};
Some(AppEvent::Input(InputEvent::MouseWheel {
delta_x: x,
delta_y: y,
}))
}
_ => None,
};
if let Some(event) = app_event {
(self.callback)(event, self.control_flow);
}
if self.control_flow.should_exit() {
event_loop.exit();
}
}
fn new_events(&mut self, _event_loop: &ActiveEventLoop, _cause: winit::event::StartCause) {
(self.callback)(AppEvent::NewEvents, self.control_flow);
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
(self.callback)(AppEvent::AboutToWait, self.control_flow);
if self.control_flow.should_exit() {
event_loop.exit();
}
}
fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
(self.callback)(AppEvent::LoopExiting, self.control_flow);
}
}
fn convert_key_code(key: winit::keyboard::PhysicalKey) -> Option<KeyCode> {
use winit::keyboard::{KeyCode as WinitKey, PhysicalKey};
match key {
PhysicalKey::Code(code) => Some(match code {
WinitKey::KeyA => KeyCode::A,
WinitKey::KeyB => KeyCode::B,
WinitKey::KeyC => KeyCode::C,
WinitKey::KeyD => KeyCode::D,
WinitKey::KeyE => KeyCode::E,
WinitKey::KeyF => KeyCode::F,
WinitKey::KeyG => KeyCode::G,
WinitKey::KeyH => KeyCode::H,
WinitKey::KeyI => KeyCode::I,
WinitKey::KeyJ => KeyCode::J,
WinitKey::KeyK => KeyCode::K,
WinitKey::KeyL => KeyCode::L,
WinitKey::KeyM => KeyCode::M,
WinitKey::KeyN => KeyCode::N,
WinitKey::KeyO => KeyCode::O,
WinitKey::KeyP => KeyCode::P,
WinitKey::KeyQ => KeyCode::Q,
WinitKey::KeyR => KeyCode::R,
WinitKey::KeyS => KeyCode::S,
WinitKey::KeyT => KeyCode::T,
WinitKey::KeyU => KeyCode::U,
WinitKey::KeyV => KeyCode::V,
WinitKey::KeyW => KeyCode::W,
WinitKey::KeyX => KeyCode::X,
WinitKey::KeyY => KeyCode::Y,
WinitKey::KeyZ => KeyCode::Z,
WinitKey::Digit0 => KeyCode::Num0,
WinitKey::Digit1 => KeyCode::Num1,
WinitKey::Digit2 => KeyCode::Num2,
WinitKey::Digit3 => KeyCode::Num3,
WinitKey::Digit4 => KeyCode::Num4,
WinitKey::Digit5 => KeyCode::Num5,
WinitKey::Digit6 => KeyCode::Num6,
WinitKey::Digit7 => KeyCode::Num7,
WinitKey::Digit8 => KeyCode::Num8,
WinitKey::Digit9 => KeyCode::Num9,
WinitKey::Escape => KeyCode::Escape,
WinitKey::Enter => KeyCode::Enter,
WinitKey::Space => KeyCode::Space,
WinitKey::Tab => KeyCode::Tab,
WinitKey::Backspace => KeyCode::Backspace,
WinitKey::ShiftLeft | WinitKey::ShiftRight => KeyCode::Shift,
WinitKey::ControlLeft | WinitKey::ControlRight => KeyCode::Control,
WinitKey::AltLeft | WinitKey::AltRight => KeyCode::Alt,
WinitKey::ArrowUp => KeyCode::Up,
WinitKey::ArrowDown => KeyCode::Down,
WinitKey::ArrowLeft => KeyCode::Left,
WinitKey::ArrowRight => KeyCode::Right,
WinitKey::F1 => KeyCode::F1,
WinitKey::F2 => KeyCode::F2,
WinitKey::F3 => KeyCode::F3,
WinitKey::F4 => KeyCode::F4,
WinitKey::F5 => KeyCode::F5,
WinitKey::F6 => KeyCode::F6,
WinitKey::F7 => KeyCode::F7,
WinitKey::F8 => KeyCode::F8,
WinitKey::F9 => KeyCode::F9,
WinitKey::F10 => KeyCode::F10,
WinitKey::F11 => KeyCode::F11,
WinitKey::F12 => KeyCode::F12,
_ => return None,
}),
PhysicalKey::Unidentified(_) => None,
}
}
fn convert_mouse_button(button: winit::event::MouseButton) -> MouseButton {
match button {
winit::event::MouseButton::Left => MouseButton::Left,
winit::event::MouseButton::Right => MouseButton::Right,
winit::event::MouseButton::Middle => MouseButton::Middle,
winit::event::MouseButton::Back => MouseButton::Back,
winit::event::MouseButton::Forward => MouseButton::Forward,
winit::event::MouseButton::Other(id) => MouseButton::Other(id),
}
}