summaryrefslogtreecommitdiff
path: root/src/input.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/input.rs')
-rw-r--r--src/input.rs220
1 files changed, 218 insertions, 2 deletions
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<KeyCode>,
+ mouse_clicked: bool,
+ mouse_location: Option<Position>,
+ scroll_direction: Option<Direction>,
+ is_focused: bool,
+ is_focused_this_frame: bool,
+ is_unfocused_this_frame: bool,
+ gil_cx: Option<Gilrs>,
+ gamepad_map: FxHashMap<usize, GamepadId>,
+ gamepad_map_rev: FxHashMap<GamepadId, usize>,
+ connected_gamepads: VecDeque<usize>,
+ pressed_buttons: FxHashSet<ControllerButton>,
+ released_buttons: FxHashSet<ControllerButton>,
}
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<Position> {
+ self.mouse_location
+ }
+
+ pub fn scroll_direction_this_frame(&self) -> Option<Direction> {
+ 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<usize> {
+ 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<f32> {
+ 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<PowerInfo> {
+ 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()
+ }
}