use std::marker::PhantomData;
use winit::{
dpi,
event::{ElementState, Event, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent},
event_loop::{ControlFlow, EventLoop},
keyboard::{Key, NamedKey},
};
use crate::graphics::GraphicsContext;
use crate::input::{self, keyboard::KeyInput};
use crate::{
context::{ContextFields, HasMut},
GameResult,
};
use crate::{Context, GameError};
use crate::context::GamepadContext;
#[cfg(feature = "gamepad")]
pub use crate::input::gamepad::GamepadId;
#[cfg(feature = "gamepad")]
pub use gilrs::{Axis, Button};
use winit::application::ApplicationHandler;
use winit::event::{DeviceEvent, DeviceId, StartCause};
use winit::event_loop::ActiveEventLoop;
use winit::window::WindowId;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ErrorOrigin {
Update,
Draw,
MouseButtonDownEvent,
MouseButtonUpEvent,
MouseMotionEvent,
RawMouseMotionEvent,
MouseEnterOrLeave,
MouseWheelEvent,
KeyDownEvent,
KeyUpEvent,
TouchEvent,
GamepadButtonDownEvent,
GamepadButtonUpEvent,
GamepadAxisEvent,
FocusEvent,
QuitEvent,
ResizeEvent,
}
pub trait EventHandler<C = Context, E = GameError>
where
E: std::fmt::Debug,
C: HasMut<ContextFields> + HasMut<input::mouse::MouseContext>,
{
fn update(&mut self, _ctx: &mut C) -> Result<(), E>;
fn draw(&mut self, _ctx: &mut C) -> Result<(), E>;
fn mouse_button_down_event(
&mut self,
_ctx: &mut C,
_button: MouseButton,
_x: f32,
_y: f32,
) -> Result<(), E> {
Ok(())
}
fn mouse_button_up_event(
&mut self,
_ctx: &mut C,
_button: MouseButton,
_x: f32,
_y: f32,
) -> Result<(), E> {
Ok(())
}
fn mouse_motion_event(
&mut self,
_ctx: &mut C,
_x: f32,
_y: f32,
_dx: f32,
_dy: f32,
) -> Result<(), E> {
Ok(())
}
fn raw_mouse_motion_event(&mut self, _ctx: &mut C, _dx: f64, _dy: f64) -> Result<(), E> {
Ok(())
}
fn mouse_enter_or_leave(&mut self, _ctx: &mut C, _entered: bool) -> Result<(), E> {
Ok(())
}
fn mouse_wheel_event(&mut self, _ctx: &mut C, _x: f32, _y: f32) -> Result<(), E> {
Ok(())
}
fn key_down_event(&mut self, ctx: &mut C, input: KeyInput, _repeated: bool) -> Result<(), E> {
if input.event.logical_key == Key::Named(NamedKey::Escape) {
HasMut::<ContextFields>::retrieve_mut(ctx).quit_requested = true;
}
Ok(())
}
fn key_up_event(&mut self, _ctx: &mut C, _input: KeyInput) -> Result<(), E> {
Ok(())
}
fn touch_event(&mut self, ctx: &mut C, phase: TouchPhase, x: f64, y: f64) -> Result<(), E> {
let mouse = HasMut::<input::mouse::MouseContext>::retrieve_mut(ctx);
mouse.handle_move(x as f32, y as f32);
match phase {
TouchPhase::Started => {
mouse.set_button(MouseButton::Left, true);
self.mouse_button_down_event(ctx, MouseButton::Left, x as f32, y as f32)?;
}
TouchPhase::Moved => {
let diff = mouse.last_delta();
self.mouse_motion_event(ctx, x as f32, y as f32, diff.x, diff.y)?;
}
TouchPhase::Ended | TouchPhase::Cancelled => {
mouse.set_button(MouseButton::Left, false);
self.mouse_button_up_event(ctx, MouseButton::Left, x as f32, y as f32)?;
}
}
Ok(())
}
#[cfg(feature = "gamepad")]
fn gamepad_button_down_event(
&mut self,
_ctx: &mut C,
_btn: gilrs::Button,
_id: GamepadId,
) -> Result<(), E> {
Ok(())
}
#[cfg(feature = "gamepad")]
fn gamepad_button_up_event(
&mut self,
_ctx: &mut C,
_btn: gilrs::Button,
_id: GamepadId,
) -> Result<(), E> {
Ok(())
}
#[cfg(feature = "gamepad")]
fn gamepad_axis_event(
&mut self,
_ctx: &mut C,
_axis: gilrs::Axis,
_value: f32,
_id: GamepadId,
) -> Result<(), E> {
Ok(())
}
fn focus_event(&mut self, _ctx: &mut C, _gained: bool) -> Result<(), E> {
Ok(())
}
fn quit_event(&mut self, _ctx: &mut C) -> Result<bool, E> {
debug!("quit_event() callback called, quitting...");
Ok(false)
}
fn resize_event(&mut self, _ctx: &mut C, _width: f32, _height: f32) -> Result<(), E> {
Ok(())
}
fn on_error(&mut self, _ctx: &mut C, _origin: ErrorOrigin, _e: E) -> bool {
true
}
}
pub fn run<S, C, E>(ctx: C, event_loop: EventLoop<()>, state: S) -> GameResult
where
S: EventHandler<C, E> + 'static,
E: std::fmt::Debug,
C: 'static
+ HasMut<ContextFields>
+ HasMut<GraphicsContext>
+ HasMut<input::keyboard::KeyboardContext>
+ HasMut<input::mouse::MouseContext>
+ HasMut<GamepadContext>
+ HasMut<crate::timer::TimeContext>,
{
let mut app = GgezApplicationHandler {
ctx,
state,
_p: PhantomData,
};
event_loop
.run_app(&mut app)
.map_err(GameError::EventLoopError)
}
struct GgezApplicationHandler<S, C, E>
where
S: EventHandler<C, E> + 'static,
E: std::fmt::Debug,
C: 'static
+ HasMut<ContextFields>
+ HasMut<GraphicsContext>
+ HasMut<input::keyboard::KeyboardContext>
+ HasMut<input::mouse::MouseContext>
+ HasMut<GamepadContext>
+ HasMut<crate::timer::TimeContext>,
{
ctx: C,
state: S,
_p: PhantomData<E>,
}
impl<S, C, E> ApplicationHandler<()> for GgezApplicationHandler<S, C, E>
where
S: EventHandler<C, E> + 'static,
E: std::fmt::Debug,
C: 'static
+ HasMut<ContextFields>
+ HasMut<GraphicsContext>
+ HasMut<input::keyboard::KeyboardContext>
+ HasMut<input::mouse::MouseContext>
+ HasMut<GamepadContext>
+ HasMut<crate::timer::TimeContext>,
{
fn new_events(&mut self, event_loop: &ActiveEventLoop, _: StartCause) {
if HasMut::<ContextFields>::retrieve_mut(&mut self.ctx).quit_requested {
let res = self.state.quit_event(&mut self.ctx);
HasMut::<ContextFields>::retrieve_mut(&mut self.ctx).quit_requested = false;
if let Ok(false) = res {
HasMut::<ContextFields>::retrieve_mut(&mut self.ctx).continuing = false;
} else if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::QuitEvent,
) {
event_loop.exit();
}
}
if !HasMut::<ContextFields>::retrieve_mut(&mut self.ctx).continuing {
event_loop.exit();
return;
}
event_loop.set_control_flow(ControlFlow::Poll);
}
fn resumed(&mut self, _event_loop: &ActiveEventLoop) {
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
mut window_id: WindowId,
mut event: WindowEvent,
) {
process_window_event(&mut self.ctx, &mut window_id, &mut event);
match event {
WindowEvent::Resized(logical_size) => {
let res = self.state.resize_event(
&mut self.ctx,
logical_size.width as f32,
logical_size.height as f32,
);
if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::ResizeEvent,
) {}
}
WindowEvent::CloseRequested => {
let res = self.state.quit_event(&mut self.ctx);
if let Ok(false) = res {
HasMut::<ContextFields>::retrieve_mut(&mut self.ctx).continuing = false;
} else if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::QuitEvent,
) {
}
}
WindowEvent::Focused(gained) => {
let res = self.state.focus_event(&mut self.ctx, gained);
if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::FocusEvent,
) {}
}
WindowEvent::ModifiersChanged(modifiers) => {
HasMut::<input::keyboard::KeyboardContext>::retrieve_mut(&mut self.ctx)
.active_modifiers = modifiers.state()
}
WindowEvent::KeyboardInput { event, .. } => {
let mods = HasMut::<input::keyboard::KeyboardContext>::retrieve_mut(&mut self.ctx)
.active_modifiers;
let repeat = event.repeat;
let key_state = event.state;
let input = KeyInput { event, mods };
let (res, origin) = match key_state {
ElementState::Pressed => (
self.state.key_down_event(&mut self.ctx, input, repeat),
ErrorOrigin::KeyDownEvent,
),
ElementState::Released => (
self.state.key_up_event(&mut self.ctx, input),
ErrorOrigin::KeyUpEvent,
),
};
if catch_error(&mut self.ctx, res, &mut self.state, event_loop, origin) {}
}
WindowEvent::MouseWheel { delta, .. } => {
let gfx = HasMut::<GraphicsContext>::retrieve_mut(&mut self.ctx);
let (x, y) = match delta {
MouseScrollDelta::LineDelta(x, y) => (x, y),
MouseScrollDelta::PixelDelta(pos) => {
let scale_factor = gfx.window.scale_factor();
let dpi::LogicalPosition { x, y } = pos.to_logical::<f32>(scale_factor);
(x, y)
}
};
let res = self.state.mouse_wheel_event(&mut self.ctx, x, y);
if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::MouseWheelEvent,
) {}
}
WindowEvent::MouseInput {
state: element_state,
button,
..
} => {
let position =
HasMut::<input::mouse::MouseContext>::retrieve_mut(&mut self.ctx).position();
match element_state {
ElementState::Pressed => {
let res = self.state.mouse_button_down_event(
&mut self.ctx,
button,
position.x,
position.y,
);
if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::MouseButtonDownEvent,
) {}
}
ElementState::Released => {
let res = self.state.mouse_button_up_event(
&mut self.ctx,
button,
position.x,
position.y,
);
if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::MouseButtonUpEvent,
) {}
}
}
}
WindowEvent::CursorMoved { .. } => {
let position =
HasMut::<input::mouse::MouseContext>::retrieve_mut(&mut self.ctx).position();
let delta =
HasMut::<input::mouse::MouseContext>::retrieve_mut(&mut self.ctx).last_delta();
let res = self.state.mouse_motion_event(
&mut self.ctx,
position.x,
position.y,
delta.x,
delta.y,
);
if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::MouseMotionEvent,
) {}
}
WindowEvent::Touch(touch) => {
let res = self.state.touch_event(
&mut self.ctx,
touch.phase,
touch.location.x,
touch.location.y,
);
if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::TouchEvent,
) {}
}
WindowEvent::CursorEntered { device_id: _ } => {
let res = self.state.mouse_enter_or_leave(&mut self.ctx, true);
if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::MouseEnterOrLeave,
) {}
}
WindowEvent::CursorLeft { device_id: _ } => {
let res = self.state.mouse_enter_or_leave(&mut self.ctx, false);
if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::MouseEnterOrLeave,
) {}
}
_x => {
}
}
}
fn device_event(
&mut self,
event_loop: &ActiveEventLoop,
mut device_id: DeviceId,
mut event: DeviceEvent,
) {
process_device_event(&mut self.ctx, &mut device_id, &mut event);
if let DeviceEvent::MouseMotion { delta } = event {
let res = self
.state
.raw_mouse_motion_event(&mut self.ctx, delta.0, delta.1);
if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::RawMouseMotionEvent,
) {}
}
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
let time = HasMut::<crate::timer::TimeContext>::retrieve_mut(&mut self.ctx);
time.tick();
#[cfg(feature = "gamepad")]
while let Some(gilrs::Event { id, event, .. }) =
HasMut::<input::gamepad::GamepadContext>::retrieve_mut(&mut self.ctx).next_event()
{
match event {
gilrs::EventType::ButtonPressed(button, _) => {
let res =
self.state
.gamepad_button_down_event(&mut self.ctx, button, GamepadId(id));
if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::GamepadButtonDownEvent,
) {
return;
};
}
gilrs::EventType::ButtonReleased(button, _) => {
let res =
self.state
.gamepad_button_up_event(&mut self.ctx, button, GamepadId(id));
if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::GamepadButtonUpEvent,
) {
return;
};
}
gilrs::EventType::AxisChanged(axis, value, _) => {
let res =
self.state
.gamepad_axis_event(&mut self.ctx, axis, value, GamepadId(id));
if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::GamepadAxisEvent,
) {
return;
};
}
_ => {}
}
}
let res = self.state.update(&mut self.ctx);
if catch_error(
&mut self.ctx,
res,
&mut self.state,
event_loop,
ErrorOrigin::Update,
) {
return;
};
if let Err(e) = HasMut::<GraphicsContext>::retrieve_mut(&mut self.ctx).begin_frame() {
error!("Error on GraphicsContext::begin_frame(): {e:?}");
eprintln!("Error on GraphicsContext::begin_frame(): {e:?}");
event_loop.exit();
}
if let Err(e) = self.state.draw(&mut self.ctx) {
error!("Error on EventHandler::draw(): {e:?}");
eprintln!("Error on EventHandler::draw(): {e:?}");
if self.state.on_error(&mut self.ctx, ErrorOrigin::Draw, e) {
event_loop.exit();
return;
}
}
if let Err(e) = HasMut::<GraphicsContext>::retrieve_mut(&mut self.ctx).end_frame() {
error!("Error on GraphicsContext::end_frame(): {e:?}");
eprintln!("Error on GraphicsContext::end_frame(): {e:?}");
event_loop.exit();
}
HasMut::<input::mouse::MouseContext>::retrieve_mut(&mut self.ctx).reset_delta();
HasMut::<input::keyboard::KeyboardContext>::retrieve_mut(&mut self.ctx)
.save_keyboard_state();
HasMut::<input::mouse::MouseContext>::retrieve_mut(&mut self.ctx).save_mouse_state();
}
}
fn catch_error<T, C, E, S>(
ctx: &mut C,
event_result: Result<T, E>,
state: &mut S,
event_loop: &ActiveEventLoop,
origin: ErrorOrigin,
) -> bool
where
S: EventHandler<C, E> + 'static,
E: std::fmt::Debug,
C: HasMut<ContextFields> + HasMut<input::mouse::MouseContext>,
{
if let Err(e) = event_result {
error!("Error on EventHandler {origin:?}: {e:?}");
eprintln!("Error on EventHandler {origin:?}: {e:?}");
if state.on_error(ctx, origin, e) {
event_loop.exit();
return true;
}
}
false
}
#[deprecated(
since = "0.10.0",
note = "Use `event::process_device_event` and `event::process_window_event` with a `winit::application::ApplicationHandler` instead."
)]
pub fn process_event<C>(ctx: &mut C, event: &mut Event<()>)
where
C: HasMut<ContextFields>
+ HasMut<GraphicsContext>
+ HasMut<input::keyboard::KeyboardContext>
+ HasMut<input::mouse::MouseContext>,
{
match event {
Event::DeviceEvent { device_id, event } => {
process_device_event(ctx, device_id, event);
}
Event::WindowEvent { window_id, event } => {
process_window_event(ctx, window_id, event);
}
_ => (),
}
}
pub fn process_device_event<C>(ctx: &mut C, _: &mut DeviceId, event: &mut DeviceEvent)
where
C: HasMut<input::mouse::MouseContext>,
{
if let DeviceEvent::MouseMotion { delta } = event {
let mouse = HasMut::<input::mouse::MouseContext>::retrieve_mut(ctx);
mouse.handle_motion(delta.0, delta.1);
}
}
pub fn process_window_event<C>(ctx: &mut C, _: &mut WindowId, event: &mut WindowEvent)
where
C: HasMut<ContextFields>
+ HasMut<GraphicsContext>
+ HasMut<input::keyboard::KeyboardContext>
+ HasMut<input::mouse::MouseContext>,
{
match event {
WindowEvent::Resized(_) => {
let gfx = HasMut::<GraphicsContext>::retrieve_mut(ctx);
gfx.on_resize();
}
WindowEvent::CursorMoved {
position: physical_position,
..
} => {
let mouse = HasMut::<input::mouse::MouseContext>::retrieve_mut(ctx);
mouse.handle_move(physical_position.x as f32, physical_position.y as f32);
}
WindowEvent::MouseInput { button, state, .. } => {
let mouse = HasMut::<input::mouse::MouseContext>::retrieve_mut(ctx);
let pressed = match state {
ElementState::Pressed => true,
ElementState::Released => false,
};
mouse.set_button(*button, pressed);
}
WindowEvent::ModifiersChanged(mods) => {
let keyboard = HasMut::<input::keyboard::KeyboardContext>::retrieve_mut(ctx);
keyboard.active_modifiers = mods.state();
}
WindowEvent::KeyboardInput { event, .. } => {
let keyboard = HasMut::<input::keyboard::KeyboardContext>::retrieve_mut(ctx);
let pressed = event.state == ElementState::Pressed;
keyboard.set_logical_key(&event.logical_key, pressed);
keyboard.set_physical_key(&event.physical_key, pressed);
}
WindowEvent::ScaleFactorChanged {
inner_size_writer, ..
} => {
let fields = HasMut::<ContextFields>::retrieve_mut(ctx);
if !fields.conf.window_mode.resize_on_scale_factor_change {
let _ =
inner_size_writer.request_inner_size(winit::dpi::PhysicalSize::<u32>::from([
fields.conf.window_mode.width,
fields.conf.window_mode.height,
]));
}
}
_ => (),
}
}