Implement MouseMotion and CursorMoved input events
This commit is contained in:
parent
e517852b25
commit
5a37fcf1e6
|
@ -255,7 +255,7 @@ checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.25",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -369,7 +369,7 @@ checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.25",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1044,7 +1044,9 @@ dependencies = [
|
|||
"image",
|
||||
"instant",
|
||||
"petgraph",
|
||||
"quote",
|
||||
"resources",
|
||||
"syn 2.0.26",
|
||||
"tobj",
|
||||
"tracing",
|
||||
"tracing-log",
|
||||
|
@ -1312,7 +1314,7 @@ dependencies = [
|
|||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.25",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1776,9 +1778,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.25"
|
||||
version = "2.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2"
|
||||
checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1811,7 +1813,7 @@ checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.25",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1895,7 +1897,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.25",
|
||||
"syn 2.0.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2020,7 +2022,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.25",
|
||||
"syn 2.0.26",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
@ -2054,7 +2056,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.25",
|
||||
"syn 2.0.26",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
|
|
@ -26,3 +26,5 @@ glam = { version = "0.24.0", features = ["bytemuck"] }
|
|||
petgraph = "0.6.3"
|
||||
resources = "1.1.0"
|
||||
gilrs-core = "0.5.6"
|
||||
syn = "2.0.26"
|
||||
quote = "1.0.29"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use petgraph::{prelude::StableDiGraph, stable_graph::NodeIndex, visit::Topo};
|
||||
use tracing::warn;
|
||||
|
||||
|
@ -8,6 +7,7 @@ use crate::game::Controls;
|
|||
|
||||
pub mod components;
|
||||
pub mod world;
|
||||
pub mod resources;
|
||||
|
||||
use world::World;
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
use std::{any::Any, collections::VecDeque};
|
||||
|
||||
use winit::event::WindowEvent;
|
||||
|
||||
pub mod window_state;
|
||||
pub use window_state::*;
|
||||
|
||||
pub trait Resource: Send + Sync + 'static {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
}
|
||||
|
||||
impl Resource for WindowEvent<'static> {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sync + Send + 'static> Resource for VecDeque<T> {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
use std::any::Any;
|
||||
|
||||
use super::Resource;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct WindowState {
|
||||
pub is_focused: bool,
|
||||
pub is_cursor_inside_window: bool,
|
||||
}
|
||||
|
||||
impl WindowState {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for WindowState {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
|
@ -1,31 +1,6 @@
|
|||
use std::{any::{TypeId, Any}, collections::{HashMap, HashSet, VecDeque}, ops::{Deref, DerefMut}};
|
||||
use std::{any::{TypeId, Any}, collections::{HashMap, HashSet}, ops::{Deref, DerefMut}};
|
||||
|
||||
use winit::event::WindowEvent;
|
||||
|
||||
pub trait Resource: Send + Sync + 'static {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
}
|
||||
|
||||
impl Resource for WindowEvent<'static> {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sync + Send + 'static> Resource for VecDeque<T> {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
use super::resources::Resource;
|
||||
|
||||
pub struct World {
|
||||
inner: hecs::World,
|
||||
|
|
80
src/game.rs
80
src/game.rs
|
@ -12,9 +12,9 @@ use tracing_subscriber::{
|
|||
util::SubscriberInitExt,
|
||||
};
|
||||
|
||||
use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode}, event_loop::{EventLoop, ControlFlow}};
|
||||
use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode, DeviceEvent}, event_loop::{EventLoop, ControlFlow}};
|
||||
|
||||
use crate::{render::{renderer::{Renderer, BasicRenderer}, render_job::RenderJob}, input_event::InputEvent, ecs::{components::{mesh::MeshComponent, transform::TransformComponent}, SimpleSystem, SystemDispatcher, world::World}, input::Input};
|
||||
use crate::{render::{renderer::{Renderer, BasicRenderer}, render_job::RenderJob}, input_event::InputEvent, ecs::{components::{mesh::MeshComponent, transform::TransformComponent}, SimpleSystem, SystemDispatcher, world::World, resources::WindowState}, input::Input};
|
||||
|
||||
pub struct Controls<'a> {
|
||||
pub world: &'a mut World,
|
||||
|
@ -144,6 +144,41 @@ impl GameLoop {
|
|||
Some(ControlFlow::Exit)
|
||||
},
|
||||
|
||||
// TODO: Create system for this? or maybe merge into input system, idk
|
||||
InputEvent::CursorEntered { .. } => {
|
||||
let mut world = self.world.lock().await;
|
||||
let state = match world.query_res_mut::<WindowState>() {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
world.insert_resource(WindowState::new());
|
||||
|
||||
// must succeed since it was just added
|
||||
world.query_res_mut::<WindowState>().unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
state.is_cursor_inside_window = true;
|
||||
|
||||
None
|
||||
},
|
||||
|
||||
InputEvent::CursorLeft { .. } => {
|
||||
let mut world = self.world.lock().await;
|
||||
let state = match world.query_res_mut::<WindowState>() {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
world.insert_resource(WindowState::new());
|
||||
|
||||
// must succeed since it was just added
|
||||
world.query_res_mut::<WindowState>().unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
state.is_cursor_inside_window = false;
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
_ => {
|
||||
//debug!("Got unhandled input event: \"{:?}\"", event);
|
||||
|
||||
|
@ -167,6 +202,32 @@ impl GameLoop {
|
|||
pub async fn run_event_loop(&mut self, event: Event<'_, ()>, control_flow: &mut ControlFlow) {
|
||||
*control_flow = ControlFlow::Poll;
|
||||
match event {
|
||||
Event::DeviceEvent { device_id, event } => match event {
|
||||
// convert a MouseMotion event to an InputEvent
|
||||
DeviceEvent::MouseMotion { delta } => {
|
||||
let mut world = self.world.lock().await;
|
||||
|
||||
// make sure that the mouse is inside the window and the mouse has focus before reporting mouse motion
|
||||
if let Some(window_state) = world.query_res::<WindowState>() {
|
||||
if window_state.is_focused && window_state.is_cursor_inside_window {
|
||||
|
||||
let event_queue = match world.query_res_mut::<VecDeque<InputEvent>>() {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
world.insert_resource(VecDeque::<InputEvent>::new());
|
||||
|
||||
// must succeed since it was just added
|
||||
world.query_res_mut::<VecDeque<InputEvent>>().unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
let input_event = InputEvent::MouseMotion { device_id, delta, };
|
||||
event_queue.push_back(input_event);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
},
|
||||
Event::WindowEvent {
|
||||
ref event,
|
||||
window_id,
|
||||
|
@ -211,6 +272,21 @@ impl GameLoop {
|
|||
self.on_resize(**new_inner_size).await;
|
||||
},
|
||||
|
||||
WindowEvent::Focused(is_focused) => {
|
||||
let mut world = self.world.lock().await;
|
||||
let state = match world.query_res_mut::<WindowState>() {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
world.insert_resource(WindowState::new());
|
||||
|
||||
// must succeed since it was just added
|
||||
world.query_res_mut::<WindowState>().unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
state.is_focused = *is_focused;
|
||||
},
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
80
src/input.rs
80
src/input.rs
|
@ -1,10 +1,11 @@
|
|||
use std::{any::Any, collections::{HashMap, hash_map::DefaultHasher, VecDeque}, hash::{Hash, Hasher}, sync::{Arc, Mutex}};
|
||||
use std::{any::{Any, TypeId}, collections::{HashMap, hash_map::DefaultHasher, VecDeque}, hash::{Hash, Hasher}, sync::{Arc, Mutex}};
|
||||
|
||||
use gilrs_core::Gilrs;
|
||||
use tracing::warn;
|
||||
use glam::Vec2;
|
||||
use tracing::{warn, debug};
|
||||
use winit::event::{VirtualKeyCode, ElementState};
|
||||
|
||||
use crate::{ecs::{SimpleSystem, world::Resource}, input_event::InputEvent};
|
||||
use crate::{ecs::{SimpleSystem, resources::Resource}, input_event::InputEvent};
|
||||
|
||||
pub type KeyCode = winit::event::VirtualKeyCode;
|
||||
|
||||
|
@ -28,10 +29,21 @@ impl SimpleSystem for InputSystem {
|
|||
let world = &mut controls.world;
|
||||
|
||||
if let Some(queue) = world.query_res_mut::<VecDeque<InputEvent>>() {
|
||||
let event = queue.pop_front();
|
||||
// Clone the queue, then clear it
|
||||
let mut queue = {
|
||||
let a = queue.clone();
|
||||
queue.clear();
|
||||
a
|
||||
};
|
||||
|
||||
if let Some(input) = world.query_res_mut::<Input>() {
|
||||
input.update(event.as_ref());
|
||||
// clear input before processing the queue
|
||||
input.clear();
|
||||
|
||||
// process the all events that happened this tick
|
||||
while let Some(event) = queue.pop_front() {
|
||||
input.update(&event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,10 +51,20 @@ impl SimpleSystem for InputSystem {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct MouseMotion {
|
||||
pub delta: Vec2,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MouseExact {
|
||||
pub pos: Vec2,
|
||||
}
|
||||
|
||||
pub struct Input {
|
||||
gilrs: Option<Arc<Mutex<Gilrs>>>, // TODO
|
||||
// the key is a u64. This is a hash of the value stored inside of the Box
|
||||
events: HashMap<u64, Box<dyn Any + Send + Sync>>,
|
||||
button_events: HashMap<u64, Box<dyn Any + Send + Sync>>,
|
||||
typed_events: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
|
||||
}
|
||||
|
||||
impl Resource for Input {
|
||||
|
@ -68,13 +90,14 @@ impl Input {
|
|||
|
||||
Self {
|
||||
gilrs,
|
||||
events: HashMap::new(),
|
||||
button_events: HashMap::new(),
|
||||
typed_events: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, event: Option<&InputEvent>) -> bool {
|
||||
pub fn clear(&mut self) {
|
||||
// Convert JustPressed inputs to Pressed, and remove Released events
|
||||
self.events.retain(|_, v| {
|
||||
self.button_events.retain(|_, v| {
|
||||
if let Some(ev) = v.downcast_mut::<ButtonEvent<KeyCode>>() {
|
||||
match ev {
|
||||
ButtonEvent::Released(_) => {
|
||||
|
@ -90,7 +113,17 @@ impl Input {
|
|||
true
|
||||
});
|
||||
|
||||
if let Some(event) = event {
|
||||
self.typed_events.retain(|_, v| {
|
||||
//if v.is::<MouseExact>() {
|
||||
if v.downcast_ref::<MouseMotion>().is_some() {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
}
|
||||
|
||||
pub fn update(&mut self, event: &InputEvent) -> bool {
|
||||
match event {
|
||||
InputEvent::KeyboardInput { input, .. } => {
|
||||
if let Some(code) = input.virtual_keycode {
|
||||
|
@ -106,9 +139,23 @@ impl Input {
|
|||
ElementState::Released => ButtonEvent::Released(code),
|
||||
});
|
||||
|
||||
self.events.insert(code_hash, buttonev);
|
||||
self.button_events.insert(code_hash, buttonev);
|
||||
}
|
||||
},
|
||||
InputEvent::MouseMotion { delta, .. } => {
|
||||
let delta = MouseMotion {
|
||||
delta: Vec2::new(delta.0 as f32, delta.1 as f32)
|
||||
};
|
||||
|
||||
self.typed_events.insert(delta.type_id(), Box::new(delta));
|
||||
},
|
||||
InputEvent::CursorMoved { position, .. } => {
|
||||
let exact = MouseExact {
|
||||
pos: Vec2::new(position.x as f32, position.y as f32)
|
||||
};
|
||||
|
||||
self.typed_events.insert(exact.type_id(), Box::new(exact.clone()));
|
||||
},
|
||||
/* WindowEvent::CursorMoved { position, .. } => Some(EventType::PositionChanged(position.x, position.y, InputEventCode::CursorMoved)),
|
||||
WindowEvent::CursorEntered { .. } => Some(EventType::Signal(InputEventCode::CursorEnteredWindow)),
|
||||
WindowEvent::CursorLeft { .. } => Some(EventType::Signal(InputEventCode::CursorLeftWindow)), */
|
||||
|
@ -123,18 +170,23 @@ impl Input {
|
|||
WindowEvent::Touch { .. } => Some(InputEventCode::Touch), */
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_event<E: 'static>(&self) -> Option<&E> {
|
||||
self.typed_events.get(&TypeId::of::<E>())
|
||||
.and_then(|e| e.as_ref()
|
||||
.downcast_ref::<E>())
|
||||
}
|
||||
|
||||
pub fn was_just_pressed(&self, code: VirtualKeyCode) -> bool {
|
||||
// get a hash of the key code
|
||||
let mut hasher = DefaultHasher::new();
|
||||
code.hash(&mut hasher);
|
||||
let inner_hash = hasher.finish();
|
||||
|
||||
if let Some(e) = self.events.get(&inner_hash) {
|
||||
if let Some(e) = self.button_events.get(&inner_hash) {
|
||||
if let Some(ev) = e.downcast_ref::<ButtonEvent<KeyCode>>() {
|
||||
match ev {
|
||||
ButtonEvent::JustPressed(v) => {
|
||||
|
@ -158,7 +210,7 @@ impl Input {
|
|||
code.hash(&mut hasher);
|
||||
let inner_hash = hasher.finish();
|
||||
|
||||
if let Some(e) = self.events.get(&inner_hash) {
|
||||
if let Some(e) = self.button_events.get(&inner_hash) {
|
||||
if let Some(ev) = e.downcast_ref::<ButtonEvent<KeyCode>>() {
|
||||
match ev {
|
||||
ButtonEvent::Pressed(v) | ButtonEvent::JustPressed(v) => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use winit::{event::{DeviceId, KeyboardInput, ModifiersState, MouseScrollDelta, TouchPhase, MouseButton, AxisId, Touch, WindowEvent, ElementState}, dpi::PhysicalPosition};
|
||||
|
||||
use crate::ecs::world::Resource;
|
||||
use crate::ecs::resources::Resource;
|
||||
|
||||
/// Wrapper around events from `winit::WindowEvent` that are specific to input related events.
|
||||
///
|
||||
|
@ -30,6 +30,15 @@ pub enum InputEvent {
|
|||
is_synthetic: bool,
|
||||
},
|
||||
|
||||
/// Change in physical position of a pointing device.
|
||||
/// This represents raw, unfiltered physical motion.
|
||||
///
|
||||
/// This is from winit's `DeviceEvent::MouseMotion`
|
||||
MouseMotion {
|
||||
device_id: DeviceId,
|
||||
delta: (f64, f64),
|
||||
},
|
||||
|
||||
/// The cursor has moved on the window.
|
||||
CursorMoved {
|
||||
device_id: DeviceId,
|
||||
|
@ -139,25 +148,6 @@ pub enum InputEventConversionError<'a> {
|
|||
FromError(&'a WindowEvent<'a>)
|
||||
}
|
||||
|
||||
impl<'a> Into<WindowEvent<'a>> for InputEvent {
|
||||
fn into(self) -> WindowEvent<'a> {
|
||||
match self {
|
||||
InputEvent::KeyboardInput { device_id, input, is_synthetic } => WindowEvent::KeyboardInput { device_id, input, is_synthetic },
|
||||
InputEvent::CursorMoved { device_id, position, modifiers } => WindowEvent::CursorMoved { device_id, position, modifiers },
|
||||
InputEvent::CursorEntered { device_id } => WindowEvent::CursorEntered { device_id },
|
||||
InputEvent::CursorLeft { device_id } => WindowEvent::CursorLeft { device_id },
|
||||
InputEvent::MouseWheel { device_id, delta, phase, modifiers } => WindowEvent::MouseWheel { device_id, delta, phase, modifiers },
|
||||
InputEvent::MouseInput { device_id, state, button, modifiers } => WindowEvent::MouseInput { device_id, state, button, modifiers },
|
||||
InputEvent::TouchpadMagnify { device_id, delta, phase } => WindowEvent::TouchpadMagnify { device_id, delta, phase },
|
||||
InputEvent::SmartMagnify { device_id } => WindowEvent::SmartMagnify { device_id },
|
||||
InputEvent::TouchpadRotate { device_id, delta, phase } => WindowEvent::TouchpadRotate { device_id, delta, phase },
|
||||
InputEvent::TouchpadPressure { device_id, pressure, stage } => WindowEvent::TouchpadPressure { device_id, pressure, stage },
|
||||
InputEvent::AxisMotion { device_id, axis, value } => WindowEvent::AxisMotion { device_id, axis, value },
|
||||
InputEvent::Touch(t) => WindowEvent::Touch(t),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a WindowEvent<'a>> for InputEvent {
|
||||
type Error = InputEventConversionError<'a>;
|
||||
|
||||
|
|
|
@ -10,9 +10,10 @@ use ecs::components::mesh::MeshComponent;
|
|||
|
||||
use ecs::components::transform::TransformComponent;
|
||||
use game::Game;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::ecs::world::World;
|
||||
use crate::input::{Input, KeyCode, InputSystem};
|
||||
use crate::input::{Input, KeyCode, InputSystem, MouseMotion};
|
||||
use crate::render::material::Material;
|
||||
use crate::render::texture::Texture;
|
||||
use crate::ecs::components::camera::CameraComponent;
|
||||
|
@ -112,7 +113,9 @@ async fn main() {
|
|||
dir_y += speed;
|
||||
}
|
||||
|
||||
//debug!("dir: ({}, {})", dir_x, dir_y);
|
||||
if let Some(motion) = input.get_event::<MouseMotion>() {
|
||||
debug!("delta: {}", motion.delta);
|
||||
}
|
||||
|
||||
for (_eid, (transform, )) in world.query_mut::<(&mut TransformComponent,)>() {
|
||||
let t = &mut transform.transform;
|
||||
|
|
Loading…
Reference in New Issue