1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
//! 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; use winit::{self, dpi}; // 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. /// A mouse button. pub use winit::MouseButton; /// An analog axis of some device (gamepad thumbstick, joystick...). pub use gilrs::Axis; /// A button of some device (gamepad, joystick...). pub use gilrs::Button; /// `winit` events; nested in a module for re-export neatness. pub mod winit_event { pub use super::winit::{ DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, MouseScrollDelta, TouchPhase, WindowEvent, }; } pub use crate::input::gamepad::GamepadId; pub use crate::input::keyboard::{KeyCode, KeyMods}; use self::winit_event::*; /// `winit` event loop. pub use winit::EventsLoop; use crate::context::Context; use crate::error::GameResult; /// 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: &mut Context, events_loop: &mut EventsLoop, state: &mut S) -> GameResult where S: EventHandler, { use crate::input::{keyboard, mouse}; while ctx.continuing { // 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. ctx.timer_context.tick(); events_loop.poll_events(|event| { 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::Awakened => (), Event::Suspended(_) => (), } }); // 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)?; state.draw(ctx)?; } Ok(()) }