291 lines
12 KiB
Rust
291 lines
12 KiB
Rust
//! The `event` module contains traits and structs to actually run your game mainloop
|
|
//! and handle top-level state, as well as handle input events such as keyboard
|
|
//! and mouse.
|
|
//!
|
|
//! If you don't want to use `ggez`'s built in event loop, you can
|
|
//! write your own mainloop and check for events on your own. This is
|
|
//! not particularly hard, there's nothing special about the
|
|
//! `EventHandler` trait. It just tries to simplify the process a
|
|
//! little. For examples of how to write your own main loop, see the
|
|
//! source code for this module, or the [`eventloop`
|
|
//! example](https://github.com/ggez/ggez/blob/master/examples/eventloop.rs).
|
|
|
|
use gilrs;
|
|
/// An analog axis of some device (gamepad thumbstick, joystick...).
|
|
pub use gilrs::Axis;
|
|
/// A button of some device (gamepad, joystick...).
|
|
pub use gilrs::Button;
|
|
use winit::{self, dpi};
|
|
/// A mouse button.
|
|
pub use winit::event::MouseButton;
|
|
/// `winit` event loop.
|
|
pub use winit::event_loop::EventLoop;
|
|
|
|
use crate::ggez::context::Context;
|
|
use crate::ggez::error::GameResult;
|
|
pub use crate::ggez::input::gamepad::GamepadId;
|
|
pub use crate::ggez::input::keyboard::{KeyCode, KeyMods};
|
|
|
|
use self::winit_event::*;
|
|
use crate::ggez::graphics::window;
|
|
|
|
// TODO LATER: I kinda hate all these re-exports. I kinda hate
|
|
// a lot of the details of the `EventHandler` and input now though,
|
|
// and look forward to ripping it all out and replacing it with newer winit.
|
|
|
|
/// `winit` events; nested in a module for re-export neatness.
|
|
pub mod winit_event {
|
|
pub use winit::event::{
|
|
DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, MouseScrollDelta,
|
|
TouchPhase, WindowEvent,
|
|
};
|
|
}
|
|
|
|
/// A trait defining event callbacks. This is your primary interface with
|
|
/// `ggez`'s event loop. Implement this trait for a type and
|
|
/// override at least the [`update()`](#tymethod.update) and
|
|
/// [`draw()`](#tymethod.draw) methods, then pass it to
|
|
/// [`event::run()`](fn.run.html) to run the game's mainloop.
|
|
///
|
|
/// The default event handlers do nothing, apart from
|
|
/// [`key_down_event()`](#tymethod.key_down_event), which will by
|
|
/// default exit the game if the escape key is pressed. Just
|
|
/// override the methods you want to use.
|
|
pub trait EventHandler {
|
|
/// Called upon each logic update to the game.
|
|
/// This should be where the game's logic takes place.
|
|
fn update(&mut self, _ctx: &mut Context) -> GameResult;
|
|
|
|
/// Called to do the drawing of your game.
|
|
/// You probably want to start this with
|
|
/// [`graphics::clear()`](../graphics/fn.clear.html) and end it
|
|
/// with [`graphics::present()`](../graphics/fn.present.html) and
|
|
/// maybe [`timer::yield_now()`](../timer/fn.yield_now.html).
|
|
fn draw(&mut self, _ctx: &mut Context) -> GameResult;
|
|
|
|
/// A mouse button was pressed
|
|
fn mouse_button_down_event(
|
|
&mut self,
|
|
_ctx: &mut Context,
|
|
_button: MouseButton,
|
|
_x: f32,
|
|
_y: f32,
|
|
) {}
|
|
|
|
/// A mouse button was released
|
|
fn mouse_button_up_event(
|
|
&mut self,
|
|
_ctx: &mut Context,
|
|
_button: MouseButton,
|
|
_x: f32,
|
|
_y: f32,
|
|
) {}
|
|
|
|
/// The mouse was moved; it provides both absolute x and y coordinates in the window,
|
|
/// and relative x and y coordinates compared to its last position.
|
|
fn mouse_motion_event(&mut self, _ctx: &mut Context, _x: f32, _y: f32, _dx: f32, _dy: f32) {}
|
|
|
|
/// The mousewheel was scrolled, vertically (y, positive away from and negative toward the user)
|
|
/// or horizontally (x, positive to the right and negative to the left).
|
|
fn mouse_wheel_event(&mut self, _ctx: &mut Context, _x: f32, _y: f32) {}
|
|
|
|
/// A keyboard button was pressed.
|
|
///
|
|
/// The default implementation of this will call `ggez::event::quit()`
|
|
/// when the escape key is pressed. If you override this with
|
|
/// your own event handler you have to re-implment that
|
|
/// functionality yourself.
|
|
fn key_down_event(
|
|
&mut self,
|
|
ctx: &mut Context,
|
|
keycode: KeyCode,
|
|
_keymods: KeyMods,
|
|
_repeat: bool,
|
|
) {
|
|
if keycode == KeyCode::Escape {
|
|
quit(ctx);
|
|
}
|
|
}
|
|
|
|
/// A keyboard button was released.
|
|
fn key_up_event(&mut self, _ctx: &mut Context, _keycode: KeyCode, _keymods: KeyMods) {}
|
|
|
|
/// A unicode character was received, usually from keyboard input.
|
|
/// This is the intended way of facilitating text input.
|
|
fn text_input_event(&mut self, _ctx: &mut Context, _character: char) {}
|
|
|
|
/// A gamepad button was pressed; `id` identifies which gamepad.
|
|
/// Use [`input::gamepad()`](../input/fn.gamepad.html) to get more info about
|
|
/// the gamepad.
|
|
fn gamepad_button_down_event(&mut self, _ctx: &mut Context, _btn: Button, _id: GamepadId) {}
|
|
|
|
/// A gamepad button was released; `id` identifies which gamepad.
|
|
/// Use [`input::gamepad()`](../input/fn.gamepad.html) to get more info about
|
|
/// the gamepad.
|
|
fn gamepad_button_up_event(&mut self, _ctx: &mut Context, _btn: Button, _id: GamepadId) {}
|
|
|
|
/// A gamepad axis moved; `id` identifies which gamepad.
|
|
/// Use [`input::gamepad()`](../input/fn.gamepad.html) to get more info about
|
|
/// the gamepad.
|
|
fn gamepad_axis_event(&mut self, _ctx: &mut Context, _axis: Axis, _value: f32, _id: GamepadId) {}
|
|
|
|
/// Called when the window is shown or hidden.
|
|
fn focus_event(&mut self, _ctx: &mut Context, _gained: bool) {}
|
|
|
|
/// Called upon a quit event. If it returns true,
|
|
/// the game does not exit (the quit event is cancelled).
|
|
fn quit_event(&mut self, _ctx: &mut Context) -> bool {
|
|
debug!("quit_event() callback called, quitting...");
|
|
false
|
|
}
|
|
|
|
/// Called when the user resizes the window, or when it is resized
|
|
/// via [`graphics::set_mode()`](../graphics/fn.set_mode.html).
|
|
fn resize_event(&mut self, _ctx: &mut Context, _width: f32, _height: f32) {}
|
|
}
|
|
|
|
/// Terminates the [`ggez::event::run()`](fn.run.html) loop by setting
|
|
/// [`Context.continuing`](struct.Context.html#structfield.continuing)
|
|
/// to `false`.
|
|
pub fn quit(ctx: &mut Context) {
|
|
ctx.continuing = false;
|
|
}
|
|
|
|
/*
|
|
/// Runs the game's main loop, calling event callbacks on the given state
|
|
/// object as events occur.
|
|
///
|
|
/// It does not try to do any type of framerate limiting. See the
|
|
/// documentation for the [`timer`](../timer/index.html) module for more info.
|
|
pub fn run<S>(ctx: &'static mut Context, events_loop: &mut EventLoop<()>, state: &'static mut S) -> GameResult
|
|
where
|
|
S: EventHandler,
|
|
{
|
|
use crate::ggez::input::{keyboard, mouse};
|
|
|
|
// If you are writing your own event loop, make sure
|
|
// you include `timer_context.tick()` and
|
|
// `ctx.process_event()` calls. These update ggez's
|
|
// internal state however necessary.
|
|
while ctx.continuing {
|
|
events_loop.run_return(|event, _target, _flow| {
|
|
ctx.timer_context.tick();
|
|
ctx.process_event(&event);
|
|
match event {
|
|
Event::WindowEvent { event, .. } => match event {
|
|
WindowEvent::Resized(logical_size) => {
|
|
// let actual_size = logical_size;
|
|
state.resize_event(
|
|
ctx,
|
|
logical_size.width as f32,
|
|
logical_size.height as f32,
|
|
);
|
|
}
|
|
WindowEvent::CloseRequested => {
|
|
if !state.quit_event(ctx) {
|
|
quit(ctx);
|
|
}
|
|
}
|
|
WindowEvent::Focused(gained) => {
|
|
state.focus_event(ctx, gained);
|
|
}
|
|
WindowEvent::ReceivedCharacter(ch) => {
|
|
state.text_input_event(ctx, ch);
|
|
}
|
|
WindowEvent::KeyboardInput {
|
|
input:
|
|
KeyboardInput {
|
|
state: ElementState::Pressed,
|
|
virtual_keycode: Some(keycode),
|
|
modifiers,
|
|
..
|
|
},
|
|
..
|
|
} => {
|
|
let repeat = keyboard::is_key_repeated(ctx);
|
|
state.key_down_event(ctx, keycode, modifiers.into(), repeat);
|
|
}
|
|
WindowEvent::KeyboardInput {
|
|
input:
|
|
KeyboardInput {
|
|
state: ElementState::Released,
|
|
virtual_keycode: Some(keycode),
|
|
modifiers,
|
|
..
|
|
},
|
|
..
|
|
} => {
|
|
state.key_up_event(ctx, keycode, modifiers.into());
|
|
}
|
|
WindowEvent::MouseWheel { delta, .. } => {
|
|
let (x, y) = match delta {
|
|
MouseScrollDelta::LineDelta(x, y) => (x, y),
|
|
MouseScrollDelta::PixelDelta(dpi::LogicalPosition { x, y }) => {
|
|
(x as f32, y as f32)
|
|
}
|
|
};
|
|
state.mouse_wheel_event(ctx, x, y);
|
|
}
|
|
WindowEvent::MouseInput {
|
|
state: element_state,
|
|
button,
|
|
..
|
|
} => {
|
|
let position = mouse::position(ctx);
|
|
match element_state {
|
|
ElementState::Pressed => {
|
|
state.mouse_button_down_event(ctx, button, position.x, position.y)
|
|
}
|
|
ElementState::Released => {
|
|
state.mouse_button_up_event(ctx, button, position.x, position.y)
|
|
}
|
|
}
|
|
}
|
|
WindowEvent::CursorMoved { .. } => {
|
|
let position = mouse::position(ctx);
|
|
let delta = mouse::delta(ctx);
|
|
state.mouse_motion_event(ctx, position.x, position.y, delta.x, delta.y);
|
|
}
|
|
_x => {
|
|
// trace!("ignoring window event {:?}", x);
|
|
}
|
|
},
|
|
Event::DeviceEvent { event, .. } => match event {
|
|
_ => (),
|
|
},
|
|
Event::Resumed => (),
|
|
Event::Suspended => (),
|
|
Event::RedrawRequested(win) => {
|
|
if win == ctx.gfx_context.window.window().id() {
|
|
state.draw(ctx).unwrap();
|
|
}
|
|
}
|
|
Event::MainEventsCleared => {
|
|
// Handle gamepad events if necessary.
|
|
if ctx.conf.modules.gamepad {
|
|
while let Some(gilrs::Event { id, event, .. }) = ctx.gamepad_context.next_event() {
|
|
match event {
|
|
gilrs::EventType::ButtonPressed(button, _) => {
|
|
state.gamepad_button_down_event(ctx, button, GamepadId(id));
|
|
}
|
|
gilrs::EventType::ButtonReleased(button, _) => {
|
|
state.gamepad_button_up_event(ctx, button, GamepadId(id));
|
|
}
|
|
gilrs::EventType::AxisChanged(axis, value, _) => {
|
|
state.gamepad_axis_event(ctx, axis, value, GamepadId(id));
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
state.update(ctx).unwrap();
|
|
window(ctx).window().request_redraw();
|
|
}
|
|
_ => {}
|
|
}
|
|
});
|
|
}
|
|
|
|
Ok(())
|
|
}*/
|