#[cfg(feature = "mice-keyboard")]
use winit::{
dpi::PhysicalPosition,
event::*,
};
use crate::input_code::*;
use std::collections::HashMap;
use std::{cmp::Eq, hash::Hash};
#[cfg(not(feature = "glium-types"))]
type Vec2 = (f32, f32);
#[cfg(feature = "glium-types")]
type Vec2 = glium_types::vectors::Vec2;
fn v(a: f32, b: f32) -> Vec2 {
#[cfg(not(feature = "glium-types"))]
{ (a, b) }
#[cfg(feature = "glium-types")]
{ Vec2::new(a, b) }
}
type ActionValue = (f32, bool, bool, Vec<(f32, Vec<f32>)>);
type BindHash<F> = Vec<(F, usize, usize)>;
pub type Binds<F> = Vec<(F, Vec<Vec<InputCode>>)>;
pub struct InputMap<F: Hash + Copy> {
bind_hash: HashMap<InputCode, BindHash<F>>,
action_val: HashMap<F, ActionValue>,
#[cfg(all(feature = "mice-keyboard", feature = "gamepad"))]
focus: bool,
#[cfg(feature = "mice-keyboard")]
pub mouse_pos: Vec2,
pub recently_pressed: Option<InputCode>,
pub text_typed: Option<String>,
#[cfg(feature = "mice-keyboard")]
pub mouse_scale: f32,
#[cfg(feature = "mice-keyboard")]
pub scroll_scale: f32,
pub press_sensitivity: f32
}
impl InputMap<()> {
pub fn empty() -> Self { Self::default() }
}
impl<F: Hash + Copy> Default for InputMap<F> {
fn default() -> Self {
Self {
press_sensitivity: 0.5,
#[cfg(feature = "mice-keyboard")]
mouse_scale: 0.02,
#[cfg(feature = "mice-keyboard")]
scroll_scale: 1.0,
#[cfg(feature = "mice-keyboard")]
mouse_pos: v(0.0, 0.0),
recently_pressed: None,
text_typed: None,
bind_hash: HashMap::<InputCode, BindHash<F>>::new(),
action_val: HashMap::<F, ActionValue>::new(),
#[cfg(all(feature = "mice-keyboard", feature = "gamepad"))]
focus: true,
}
}
}
impl<F: Hash + Copy + Eq> InputMap<F> {
pub fn new(binds: &Binds<F>) -> Self {
let mut result = Self::default();
result.add_binds(binds);
result
}
pub fn add_binds(&mut self, binds: &Binds<F>) {
for (action, binds) in binds {
for (bind_i, bind) in binds.iter().enumerate() {
self.action_val.entry(*action).or_default().3.push((0.0, vec![]));
for (code_i, code) in bind.iter().enumerate() {
self.action_val.entry(*action).or_default().3[bind_i].1.push(0.0);
self.bind_hash.entry(*code).or_default().push((*action, bind_i, code_i));
}
}
}
self.action_val.shrink_to_fit();
self.bind_hash.shrink_to_fit();
}
pub fn set_binds(&mut self, binds: &Binds<F>) {
self.bind_hash.clear();
self.add_binds(binds);
}
pub fn get_binds(&self) -> Binds<F> {
let mut results = Vec::new();
for (input_code, binds) in self.bind_hash.iter() {
for (action, binds_i, bind_i) in binds.iter() {
let action_i = if let Some(result) = results.iter().position(|(a, _)| a == action) { result }
else { results.push((*action, vec![])); results.len() - 1 };
let len = results[action_i].1.len();
if *binds_i >= len { results[action_i].1.append(&mut vec![vec![]; binds_i - len + 1]); }
let vec = &mut results[action_i].1[*binds_i];
let len = vec.len();
if *bind_i >= len { vec.append(&mut vec![*input_code; bind_i - len + 1]); }
}
}
results
}
#[cfg(feature = "mice-keyboard")]
#[deprecated = "use `update_with_window_event` and `update_with_device_event`"]
pub fn update_with_winit(&mut self, event: &Event<()>) {
match event {
Event::WindowEvent { event, .. } => self.update_with_window_event(event),
Event::DeviceEvent { event, device_id, .. } => self.update_with_device_event(*device_id, event),
_ => ()
}
}
#[cfg(feature = "mice-keyboard")]
pub fn update_with_device_event(&mut self, id: DeviceId, event: &DeviceEvent) {
use base_input_codes::*;
match event {
DeviceEvent::MouseMotion { delta } => {
let x = delta.0 as f32 * self.mouse_scale;
let y = delta.1 as f32 * self.mouse_scale;
self.modify_val(MouseMoveRight.with_id(id), |v| v + x.max(0.0));
self.modify_val(MouseMoveLeft .with_id(id), |v| v - x.min(0.0));
self.modify_val(MouseMoveDown .with_id(id), |v| v + y.max(0.0));
self.modify_val(MouseMoveUp .with_id(id), |v| v - y.min(0.0));
},
DeviceEvent::MouseWheel { delta } => self.update_scroll(*delta, id),
_ => (),
}
}
#[cfg(feature = "mice-keyboard")]
pub fn update_with_window_event(&mut self, event: &WindowEvent) {
match event {
WindowEvent::CursorMoved { position, .. } => self.update_mouse(*position),
WindowEvent::MouseWheel { delta, device_id, .. } => self.update_scroll(*delta, *device_id),
WindowEvent::MouseInput { state, button, device_id } => self.update_buttons(state, *device_id, *button),
WindowEvent::KeyboardInput { event, device_id, .. } => self.update_keys(*device_id, event),
WindowEvent::Focused(false) => {
for val in self.action_val.values_mut() {
val.3.iter_mut().for_each(|i| { i.0 = 0.0; i.1.iter_mut().for_each(|i| *i = 0.0) });
val.0 = 0.0;
}
#[cfg(feature = "gamepad")] { self.focus = false; }
},
#[cfg(feature = "gamepad")]
WindowEvent::Focused(true) => self.focus = true,
_ => ()
}
}
#[cfg(feature = "gamepad")]
pub fn update_with_gilrs(&mut self, gilrs: &mut gilrs::Gilrs) {
while let Some(ev) = gilrs.next_event() {
if self.focus { self.update_gamepad(ev); }
}
}
pub fn init(&mut self) {
#[cfg(feature = "mice-keyboard")]
{
use base_input_codes::*;
for i in [MouseMoveLeft, MouseMoveRight,
MouseMoveUp, MouseMoveDown, MouseScrollUp,
MouseScrollDown, MouseScrollLeft,
MouseScrollRight] {
self.update_val(i.into(), 0.0);
}
}
self.action_val.values_mut().for_each(|(_, p, r, _)| (*p, *r) = (false, false));
self.recently_pressed = None;
self.text_typed = None;
}
#[cfg(feature = "mice-keyboard")]
fn update_scroll(&mut self, delta: MouseScrollDelta, id: DeviceId) {
use base_input_codes::*;
let (x, y) = match delta {
MouseScrollDelta::LineDelta(x, y) => (x, y),
MouseScrollDelta::PixelDelta(PhysicalPosition { x, y }) => (x as f32, y as f32)
};
let (x, y) = (x * self.scroll_scale, y * self.scroll_scale);
self.modify_val(MouseScrollUp.with_id(id), |v| v + y.max(0.0));
self.modify_val(MouseScrollDown.with_id(id), |v| v - y.min(0.0));
self.modify_val(MouseScrollLeft.with_id(id), |v| v + x.max(0.0));
self.modify_val(MouseScrollRight.with_id(id), |v| v - x.min(0.0));
}
#[cfg(feature = "mice-keyboard")]
fn update_mouse(&mut self, position: PhysicalPosition<f64>) {
self.mouse_pos = v(position.x as f32, position.y as f32);
}
#[cfg(feature = "mice-keyboard")]
fn update_keys(&mut self, id: DeviceId, event: &KeyEvent) {
let input_code: DeviceInput = event.physical_key.into();
if let (Some(string), Some(new)) = (&mut self.text_typed, &event.text) {
string.push_str(new);
} else { self.text_typed = event.text.as_ref().map(|i| i.to_string()) }
self.update_val(input_code.with_id(id), event.state.is_pressed().into());
}
#[cfg(feature = "mice-keyboard")]
fn update_buttons(&mut self, state: &ElementState, id: DeviceId, button: MouseButton) {
let input_code: DeviceInput = button.into();
self.update_val(input_code.with_id(id), state.is_pressed().into());
}
fn update_val(&mut self, input_code: InputCode, val: f32) {
self.modify_val(input_code, |_| val);
}
fn modify_val<FN: Fn(f32) -> f32>(&mut self, input_code: InputCode, f: FN) {
self.modify_single_val(input_code, &f);
self.modify_single_val(input_code.set_any(), f);
}
fn modify_single_val<FN: Fn(f32) -> f32>(&mut self, input_code: InputCode, f: FN) {
let Some(binds) = self.bind_hash.get(&input_code) else {
if f(0.0) >= self.press_sensitivity && !input_code.is_any() { self.recently_pressed = Some(input_code) }
return;
};
for &(action, index, sub_index) in binds {
let (curr_val, pressing, releasing, sub_values) = &mut self.action_val.get_mut(&action).unwrap();
let old_sub_sub_val = sub_values[index].1[sub_index];
let new_sub_sub_val = f(old_sub_sub_val);
let change = new_sub_sub_val / old_sub_sub_val;
sub_values[index].1[sub_index] = new_sub_sub_val;
let sub_value = sub_values[index].0;
let new_sub_val = if change.is_finite() { sub_value * change }
else { sub_values[index].1.iter().fold(1.0, |a, b| a * b) };
sub_values[index].0 = new_sub_val;
*curr_val += new_sub_val - sub_value;
let now_pressing = *curr_val >= self.press_sensitivity;
if now_pressing && !input_code.is_any() { self.recently_pressed = Some(input_code) }
let jpressed = now_pressing && !*pressing;
let released = !now_pressing && *pressing;
*pressing = jpressed;
*releasing = released;
}
}
#[cfg(feature = "gamepad")]
fn update_gamepad(&mut self, event: gilrs::Event) {
let gilrs::Event { id, event, .. } = event;
use crate::input_code::{axis_pos, axis_neg};
use gilrs::ev::EventType;
match event {
EventType::ButtonChanged(b, v, _) => {
let a: GamepadInput = b.into();
self.update_val(a.with_id(id), v);
},
EventType::AxisChanged(b, v, _) => {
let dir_pos = v.max(0.0);
let dir_neg = (-v).max(0.0);
let input_pos = axis_pos(b);
let input_neg = axis_neg(b);
self.update_val(input_pos.with_id(id), dir_pos);
self.update_val(input_neg.with_id(id), dir_neg);
},
EventType::Disconnected => {
use GamepadInput::*;
for i in [LeftStickLeft, LeftStickRight, LeftStickUp, LeftStickDown, LeftStickPress,
RightStickLeft, RightStickRight, RightStickUp, RightStickDown,
RightStickPress, DPadLeft, DPadRight, DPadUp, DPadDown, LeftZ, RightZ,
South, East, North, West, LeftBumper, LeftTrigger, RightBumper,
RightTrigger, Select, Start, Mode, Other].iter() {
self.update_val(i.with_id(id), 0.0);
}
}
_ => ()
}
}
pub fn pressing(&self, action: F) -> bool {
self.value(action) >= self.press_sensitivity
}
pub fn value(&self, action: F) -> f32 {
if let Some(&(v, _, _, _)) = self.action_val.get(&action) { v } else { 0.0 }
}
pub fn pressed(&self, action: F) -> bool {
if let Some(&(_, v, _, _)) = self.action_val.get(&action) { v } else { false }
}
pub fn released(&self, action: F) -> bool {
if let Some(&(_, _, v, _)) = self.action_val.get(&action) { v } else { false }
}
pub fn axis(&self, pos: F, neg: F) -> f32 {
self.value(pos) - self.value(neg)
}
pub fn dir(&self, pos_x: F, neg_x: F, pos_y: F, neg_y: F) -> Vec2 {
v(self.axis(pos_x, neg_x), self.axis(pos_y, neg_y))
}
pub fn dir_max_len_1(&self, pos_x: F, neg_x: F, pos_y: F, neg_y: F) -> Vec2 {
let (x, y) = (self.axis(pos_x, neg_x), self.axis(pos_y, neg_y));
let length = (x*x + y*y).sqrt().max(1.0);
v(x/length, y/length)
}
}