#[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) }
}
pub struct InputMap<F: Hash + Eq + Clone + Copy> {
pub binds: HashMap<InputCode, (f32, Vec<F>)>,
action_val: HashMap<F, (f32, bool, 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<F: Hash + Eq + Copy> Default for InputMap<F> {
fn default() -> Self {
Self {
#[cfg(feature = "mice-keyboard")]
mouse_scale: 0.01,
press_sensitivity: 0.5,
#[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,
binds: HashMap::<InputCode, (f32, Vec<F>)>::new(),
action_val: HashMap::<F, (f32, bool, bool)>::new()
}
}
}
impl<F: Hash + Eq + Copy> InputMap<F> {
pub fn new(binds: &[(F, Vec<InputCode>)]) -> Self {
let mut result = Self::default();
for (i, binds) in binds {
for bind in binds {
result.mut_bind(*bind).push(*i);
}
}
result.binds.shrink_to_fit();
result
}
pub fn empty() -> InputMap<()> {
InputMap::<()>::default()
}
pub fn mut_bind(&mut self, input_code: InputCode) -> &mut Vec<F> {
let has_val = self.binds.contains_key(&input_code);
&mut (if has_val { self.binds.get_mut(&input_code) } else {
self.binds.insert(input_code, (0.0, vec![]));
self.binds.get_mut(&input_code)
}).unwrap().1
}
#[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(MouseMoveLeft.with_id(id), |v| v + x.max(0.0));
self.modify_val(MouseMoveRight.with_id(id), |v| v - x.min(0.0));
self.modify_val(MouseMoveUp.with_id(id), |v| v + y.max(0.0));
self.modify_val(MouseMoveDown.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),
_ => ()
}
}
#[cfg(feature = "gamepad")]
pub fn update_with_gilrs(&mut self, gilrs: &mut gilrs::Gilrs) {
while let Some(ev) = gilrs.next_event() {
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.iter_mut().for_each(|(_, i)|
*i = (i.0, 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) {
let pressing = val >= self.press_sensitivity;
if pressing && !input_code.is_any() { self.recently_pressed = Some(input_code) }
let Some((bind_val, binds)) = self.binds.get(&input_code) else {
if !input_code.is_any() {
let any = input_code.set_any();
if self.binds.contains_key(&any) {
self.modify_any_val(any, |_| val);
}
}
return
};
let diff = val - bind_val; for &action in binds {
let pressed = self.pressing(action);
let jpressed = !pressed && pressing;
let released = pressed && !pressing;
let mut val = self.action_val(action) + diff;
if val <= f32::EPSILON { val = 0.0 }
self.action_val.insert(action, (val, jpressed, released));
}
self.binds.get_mut(&input_code).unwrap().0 = val;
if !input_code.is_any() {
let any = input_code.set_any();
if self.binds.contains_key(&any) {
self.modify_val(any, |v| v + diff);
}
}
}
fn modify_val<FN: Fn(f32) -> f32>(&mut self, input_code: InputCode, f: FN) {
let Some((bind_val, binds)) = self.binds.get(&input_code) else {
if f(0.0) >= self.press_sensitivity && !input_code.is_any() { self.recently_pressed = Some(input_code) }
if !input_code.is_any() {
let any = input_code.set_any();
if self.binds.contains_key(&any) {
self.modify_any_val(any, f);
}
}
return;
};
let val = f(*bind_val);
let diff = val - *bind_val;
for &action in binds {
let pressing = val >= self.press_sensitivity;
if pressing && !input_code.is_any() { self.recently_pressed = Some(input_code) }
let pressed = self.pressing(action);
let jpressed = pressing && !pressed;
let released = !pressing && pressed;
let val = self.action_val(action) + diff;
self.action_val.insert(action, (val, jpressed, released));
}
self.binds.get_mut(&input_code).unwrap().0 = val;
if !input_code.is_any() {
let any = input_code.set_any();
if self.binds.contains_key(&any) {
self.modify_any_val(any, |v| v + diff);
}
}
}
fn modify_any_val<FN: Fn(f32) -> f32>(&mut self, input_code: InputCode, f: FN) {
let Some((bind_val, binds)) = self.binds.get(&input_code) else {
if input_code.is_any() { return }
if f(0.0) >= self.press_sensitivity && !input_code.is_any() { self.recently_pressed = Some(input_code) }
return;
};
let val = f(*bind_val);
let diff = val - *bind_val;
for &action in binds {
let pressing = val >= self.press_sensitivity;
if pressing && !input_code.is_any() { self.recently_pressed = Some(input_code) }
let pressed = self.pressing(action);
let jpressed = pressing && !pressed;
let released = !pressing && pressed;
let val = self.action_val(action) + diff;
self.action_val.insert(action, (val, jpressed, released));
}
self.binds.get_mut(&input_code).unwrap().0 = val;
}
#[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, LeftTrigger, LeftTrigger2, RightTrigger,
RightTrigger2, Select, Start, Mode, Other].iter() {
self.update_val(i.with_id(id), 0.0);
}
}
_ => ()
}
}
pub fn pressing(&self, action: F) -> bool {
self.action_val(action) >= self.press_sensitivity
}
pub fn action_val(&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.action_val(pos) - self.action_val(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)
}
}