From 0c128e472b1b49e32c313874eaeccc8c5f1b1202 Mon Sep 17 00:00:00 2001 From: Mica White Date: Mon, 26 Oct 2026 10:51:49 -0400 Subject: Finish engine --- src/input.rs | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 218 insertions(+), 2 deletions(-) (limited to 'src/input.rs') diff --git a/src/input.rs b/src/input.rs index 90ddbb2..3eea33c 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,15 +1,65 @@ +use std::{collections::VecDeque, fmt::Debug, time::Duration}; + use crossterm::event::KeyCode; -use rustc_hash::FxHashSet; +use gilrs::{ + Axis, Button, GamepadId, Gilrs, PowerInfo, + ff::{BaseEffect, Replay, Ticks}, +}; +use rustc_hash::{FxHashMap, FxHashSet}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Direction { + Up = 1, + Down, + Left, + Right, +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Position { + pub x: u16, + pub y: u16, +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +struct ControllerButton { + gamepad_id: usize, + button: Button, +} -#[derive(Debug, Default, Clone)] +#[derive(Debug)] pub struct InputManager { pressed_keys: FxHashSet, + mouse_clicked: bool, + mouse_location: Option, + scroll_direction: Option, + is_focused: bool, + is_focused_this_frame: bool, + is_unfocused_this_frame: bool, + gil_cx: Option, + gamepad_map: FxHashMap, + gamepad_map_rev: FxHashMap, + connected_gamepads: VecDeque, + pressed_buttons: FxHashSet, + released_buttons: FxHashSet, } impl InputManager { pub fn new() -> Self { Self { pressed_keys: FxHashSet::default(), + mouse_clicked: false, + mouse_location: None, + scroll_direction: None, + is_focused: true, + is_focused_this_frame: false, + is_unfocused_this_frame: false, + gil_cx: Gilrs::new().ok(), + gamepad_map: FxHashMap::default(), + gamepad_map_rev: FxHashMap::default(), + connected_gamepads: VecDeque::new(), + pressed_buttons: FxHashSet::default(), + released_buttons: FxHashSet::default(), } } @@ -17,12 +67,178 @@ impl InputManager { self.pressed_keys.insert(key_code); } + pub fn click_mouse(&mut self) { + self.mouse_clicked = true; + } + + pub fn move_mouse(&mut self, location: Position) { + self.mouse_location = Some(location); + } + + pub fn scroll(&mut self, direction: Direction) { + self.scroll_direction = Some(direction); + } + pub fn reset_next_frame(&mut self) { puffin::profile_function!(); self.pressed_keys.clear(); + self.mouse_clicked = false; + self.scroll_direction = None; + self.is_focused_this_frame = false; + self.is_unfocused_this_frame = false; + self.pressed_buttons.clear(); + self.released_buttons.clear(); + self.connected_gamepads.pop_front(); } pub fn is_key_pressed_this_frame(&self, key_code: KeyCode) -> bool { self.pressed_keys.contains(&key_code) } + + pub fn is_mouse_clicked_this_frame(&self) -> bool { + self.mouse_clicked + } + + pub fn mouse_location(&self) -> Option { + self.mouse_location + } + + pub fn scroll_direction_this_frame(&self) -> Option { + self.scroll_direction + } + + pub fn read_gil_events(&mut self) { + let Some(gil) = self.gil_cx.as_mut() else { + return; + }; + while let Some(event) = gil.next_event() { + let gamepad_id = self.gamepad_map_rev.get(&event.id).copied(); + match event.event { + gilrs::EventType::Connected => { + let gamepad_id = self.gamepad_map.len() + 1; + self.gamepad_map.insert(gamepad_id, event.id); + self.gamepad_map_rev.insert(event.id, gamepad_id); + self.connected_gamepads.push_back(gamepad_id); + } + gilrs::EventType::ButtonPressed(button, _) => { + let Some(gamepad_id) = gamepad_id else { + continue; + }; + self.pressed_buttons + .insert(ControllerButton { gamepad_id, button }); + } + gilrs::EventType::ButtonReleased(button, _) => { + let Some(gamepad_id) = gamepad_id else { + continue; + }; + self.released_buttons + .insert(ControllerButton { gamepad_id, button }); + } + _ => {} + } + } + } + + pub fn is_gamepad_connected_this_frame(&self) -> Option { + self.connected_gamepads.front().copied() + } + + pub fn is_button_pressed_this_frame(&self, gamepad_id: usize, button: Button) -> bool { + self.pressed_buttons + .contains(&ControllerButton { gamepad_id, button }) + } + + pub fn is_button_released_this_frame(&self, gamepad_id: usize, button: Button) -> bool { + self.released_buttons + .contains(&ControllerButton { gamepad_id, button }) + } + + pub fn is_button_down(&self, gamepad_id: usize, button: Button) -> bool { + self.gamepad_map.get(&gamepad_id).is_some_and(|id| { + self.gil_cx + .as_ref() + .is_some_and(|gil| gil.gamepad(*id).is_pressed(button)) + }) + } + + pub fn axis_value(&self, gamepad_id: usize, axis: Axis) -> Option { + self.gamepad_map + .get(&gamepad_id) + .and_then(|id| self.gil_cx.as_ref().map(|gil| gil.gamepad(*id).value(axis))) + } + + pub fn is_gamepad_connected(&self, gamepad_id: usize) -> bool { + self.gamepad_map.get(&gamepad_id).is_some_and(|id| { + self.gil_cx + .as_ref() + .is_some_and(|gil| gil.gamepad(*id).is_connected()) + }) + } + + pub fn gamepad_power_info(&self, gamepad_id: usize) -> Option { + self.gamepad_map.get(&gamepad_id).and_then(|id| { + self.gil_cx + .as_ref() + .map(|gil| gil.gamepad(*id).power_info()) + }) + } + + pub fn vibrate(&mut self, gamepad_id: usize, intensity: f32, duration: Duration) { + let Some(gamepad_id) = self.gamepad_map.get(&gamepad_id) else { + return; + }; + let Some(gil) = self.gil_cx.as_mut() else { + return; + }; + let gamepad = gil.gamepad(*gamepad_id); + let Ok(effect) = gilrs::ff::EffectBuilder::new() + .add_gamepad(&gamepad) + .add_effect(BaseEffect { + kind: gilrs::ff::BaseEffectType::Strong { + magnitude: (intensity * u16::MAX as f32).round() as u16, + }, + scheduling: Replay { + after: Ticks::from_ms(0), + play_for: Ticks::from_ms(duration.as_millis() as u32), + with_delay: Ticks::from_ms(0), + }, + envelope: todo!(), + }) + .repeat(gilrs::ff::Repeat::For(Ticks::from_ms( + duration.as_millis() as u32 + ))) + .finish(gil) + else { + return; + }; + _ = effect.play(); + } + + pub fn focus(&mut self) { + self.is_focused = true; + self.is_focused_this_frame = true; + } + + pub fn unfocus(&mut self) { + self.is_focused = false; + self.is_unfocused_this_frame = true; + } + + pub fn is_focused(&self) -> bool { + self.is_focused + } + + pub fn was_focused_this_frame(&self) -> bool { + self.is_focused_this_frame + } + + pub fn was_unfocused_this_frame(&self) -> bool { + self.is_unfocused_this_frame + } +} + +impl Default for InputManager { + fn default() -> Self { + Self::new() + } } -- cgit v1.3.1