425 lines
17 KiB
Rust
425 lines
17 KiB
Rust
use winit::{ScanCode, ModifiersState, MouseButton, ElementState, MouseScrollDelta, Event, WindowEvent, DeviceEvent};
|
|
|
|
use std::collections::{HashMap, HashSet};
|
|
use std::fs;
|
|
|
|
use toml;
|
|
|
|
use serde_derive::{Serialize, Deserialize};
|
|
use std::iter::FromIterator;
|
|
use crate::config::LogConfig;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct VirtualButton {
|
|
pub digital_inputs: Vec<DigitalInput>
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct VirtualAxis {
|
|
pub axis_inputs: Vec<AxisInput>
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
|
pub enum DigitalInputEvent {
|
|
Pressed(DigitalInput),
|
|
Released(DigitalInput),
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
struct InputConfig {
|
|
config: InputConfigConfig,
|
|
button: Vec<InputConfigButton>,
|
|
axis: Vec<InputConfigAxis>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
struct InputConfigConfig {
|
|
line_height_px: f32,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
struct InputConfigButton {
|
|
name: String,
|
|
scan_code: Option<u32>,
|
|
mouse: Option<String>,
|
|
ctrl: Option<bool>,
|
|
shift: Option<bool>,
|
|
alt: Option<bool>,
|
|
logo: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
struct InputConfigAxis {
|
|
name: String,
|
|
positive_button: Option<String>,
|
|
negative_button: Option<String>,
|
|
mouse_axis: Option<String>,
|
|
ctrl: Option<bool>,
|
|
shift: Option<bool>,
|
|
alt: Option<bool>,
|
|
logo: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct InputState<'a> {
|
|
pub virtual_buttons: HashMap<String, VirtualButton>,
|
|
pub virtual_axes: HashMap<String, VirtualAxis>,
|
|
pub mouse_delta_x: f64,
|
|
pub mouse_delta_y: f64,
|
|
input_events: HashSet<DigitalInputEvent>,
|
|
pressed_scan_codes: HashSet<ScanCode>,
|
|
pressed_mouse_buttons: HashSet<MouseButton>,
|
|
analog_wheel_state: f32,
|
|
config: InputConfigConfig,
|
|
log_config: &'a LogConfig,
|
|
}
|
|
|
|
impl <'a> InputState<'a> {
|
|
pub fn new(toml_path: &str, log_config: &'a LogConfig) -> InputState<'a> {
|
|
let config: InputConfig = toml::from_slice(&fs::read(toml_path).expect("Failed to read input config!")).expect("Failed to parse input config!");
|
|
|
|
let mut state = InputState { virtual_buttons: HashMap::new(), virtual_axes: HashMap::new(),
|
|
input_events: HashSet::new(), pressed_scan_codes: HashSet::new(),
|
|
pressed_mouse_buttons: HashSet::new(), analog_wheel_state: 0.0,
|
|
config: config.config, mouse_delta_x: 0.0, mouse_delta_y: 0.0, log_config };
|
|
|
|
config.button.iter().for_each(|bn| {
|
|
let modifiers = ModifiersState {
|
|
shift: bn.shift.is_some(),
|
|
ctrl: bn.ctrl.is_some(),
|
|
alt: bn.alt.is_some(),
|
|
logo: bn.logo.is_some()
|
|
};
|
|
let input = if let Some(scan_code) = bn.scan_code {
|
|
DigitalInput::Keyboard(KeyboardInput { scan_code, modifiers })
|
|
} else if let Some(button_name) = &bn.mouse {
|
|
let button_name_lower = button_name.to_lowercase();
|
|
match button_name_lower.as_str() {
|
|
"left" => DigitalInput::Mouse(MouseInput { button: MouseButton::Left, modifiers }),
|
|
"middle" => DigitalInput::Mouse(MouseInput { button: MouseButton::Middle, modifiers }),
|
|
"right" => DigitalInput::Mouse(MouseInput { button: MouseButton::Right, modifiers }),
|
|
"wheelup" => DigitalInput::Wheel(WheelInput { direction: WheelInputDirection::Up, modifiers }),
|
|
"wheeldown" => DigitalInput::Wheel(WheelInput { direction: WheelInputDirection::Down, modifiers }),
|
|
other => DigitalInput::Mouse(MouseInput { button: MouseButton::Other(other.parse().expect(&format!("Unknown button: {:?}", other))), modifiers }),
|
|
}
|
|
} else {
|
|
panic!("No mouse or keyboard input for button {:?}", bn.name);
|
|
};
|
|
|
|
if let Some(virtual_button) = state.virtual_buttons.get_mut(&bn.name) {
|
|
virtual_button.digital_inputs.push(input);
|
|
} else {
|
|
state.virtual_buttons.insert(bn.name.clone(), VirtualButton { digital_inputs: vec![input] });
|
|
}
|
|
});
|
|
|
|
config.axis.iter().for_each(|axis| {
|
|
let axis_input = match axis {
|
|
InputConfigAxis { positive_button: Some(pos_button_name), negative_button: Some(neg_button_name), .. } => {
|
|
let positive_button = state.virtual_buttons.get(pos_button_name)
|
|
.expect(&format!("Button {:?} of axis {:?} not found!", axis.positive_button, axis.name))
|
|
.clone();
|
|
let negative_button = state.virtual_buttons.get(neg_button_name)
|
|
.expect(&format!("Button {:?} of axis {:?} not found!", axis.positive_button, axis.name))
|
|
.clone();
|
|
|
|
AxisInput::Digital(positive_button, negative_button)
|
|
},
|
|
InputConfigAxis { mouse_axis: Some(axis_name), .. } => {
|
|
let modifiers = ModifiersState {
|
|
shift: axis.shift.is_some(),
|
|
ctrl: axis.ctrl.is_some(),
|
|
alt: axis.alt.is_some(),
|
|
logo: axis.logo.is_some(),
|
|
};
|
|
match axis_name.to_lowercase().as_str() {
|
|
"wheel" => AxisInput::Wheel(modifiers),
|
|
"x" => AxisInput::MouseMove(MouseMoveDirection::X, modifiers),
|
|
"y" => AxisInput::MouseMove(MouseMoveDirection::Y, modifiers),
|
|
other => panic!("Axis {:?} has unknown mouse axis name {:?}!", axis_name, other),
|
|
}
|
|
},
|
|
other => panic!("Axis {:?} needs either positive_button and negative_button or mouse_wheel must be set to true!", other.name)
|
|
};
|
|
|
|
if let Some(virtual_axis) = state.virtual_axes.get_mut(&axis.name) {
|
|
virtual_axis.axis_inputs.push(axis_input);
|
|
} else {
|
|
state.virtual_axes.insert(axis.name.clone(), VirtualAxis { axis_inputs: vec![axis_input] });
|
|
}
|
|
});
|
|
|
|
return state;
|
|
}
|
|
|
|
pub fn on_window_event(self: &mut Self, event: &Event) {
|
|
match event {
|
|
Event::WindowEvent { event: WindowEvent::KeyboardInput { device_id, input }, .. } => {
|
|
if self.log_config.input {
|
|
let mods = mods_to_string(&input.modifiers);
|
|
if mods.len() > 0 {
|
|
println!("Keyboard {:?} {:?} {:?} + {:?}", device_id, input.state, &mods, input.scancode)
|
|
} else {
|
|
println!("Keyboard {:?} {:?} {:?}", device_id, input.state, input.scancode)
|
|
}
|
|
}
|
|
|
|
self.on_keyboard_event(input.state, input.scancode, input.modifiers);
|
|
},
|
|
Event::WindowEvent { event: WindowEvent::MouseInput { device_id, state, button, modifiers }, .. } => {
|
|
if self.log_config.input {
|
|
let mods = mods_to_string(&modifiers);
|
|
if mods.len() > 0 {
|
|
println!("Mouse {:?} {:?} {:?} + {:?}", device_id, state, &mods, button)
|
|
} else {
|
|
println!("Mouse {:?} {:?} {:?}", device_id, state, button)
|
|
}
|
|
}
|
|
|
|
self.on_mouse_event(state, button, modifiers);
|
|
},
|
|
Event::WindowEvent { event: WindowEvent::MouseWheel { device_id, delta, phase, modifiers }, .. } => {
|
|
if self.log_config.input {
|
|
let mods = mods_to_string(&modifiers);
|
|
if mods.len() > 0 {
|
|
println!("Scroll {:?} {:?} {:?} + {:?}", device_id, phase, &mods, delta)
|
|
} else {
|
|
println!("Scroll {:?} {:?} {:?}", device_id, phase, delta)
|
|
}
|
|
}
|
|
|
|
self.on_mouse_wheel_event(delta, modifiers);
|
|
},
|
|
Event::DeviceEvent { device_id, event: DeviceEvent::MouseMotion { delta: (delta_x, delta_y) } } => {
|
|
if self.log_config.input {
|
|
println!("MouseMotion {:?}, ({:?},{:?})", device_id, delta_x, delta_y);
|
|
}
|
|
self.mouse_delta_x += *delta_x;
|
|
self.mouse_delta_y += *delta_y;
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
pub fn button_down(self: &Self, button_code: &str) -> bool {
|
|
match self.virtual_buttons.get(button_code) {
|
|
Some(virtual_button) => {
|
|
virtual_button.digital_inputs.iter().any(|vi| self.digital_input_pressed(vi))
|
|
}
|
|
None => {
|
|
assert!(false, format!("Button {:?} not found!", button_code));
|
|
false
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn button_just_pressed(self: &Self, button_code: &str) -> bool {
|
|
match self.virtual_buttons.get(button_code) {
|
|
Some(virtual_button) => {
|
|
self.input_events.iter().any(|input_event| {
|
|
if let DigitalInputEvent::Pressed(digital_input) = input_event {
|
|
virtual_button.digital_inputs.iter().any(|virtual_button_input| virtual_button_input == digital_input)
|
|
} else {
|
|
false
|
|
}
|
|
})
|
|
}
|
|
None => {
|
|
assert!(false, format!("Button {:?} not found!", button_code));
|
|
false
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn button_just_released(self: &Self, button_code: &str) -> bool {
|
|
match self.virtual_buttons.get(button_code) {
|
|
Some(virtual_button) => {
|
|
self.input_events.iter().any(|input_event| {
|
|
if let DigitalInputEvent::Released(digital_input) = input_event {
|
|
virtual_button.digital_inputs.iter().any(|virtual_button_input| virtual_button_input == digital_input)
|
|
} else {
|
|
false
|
|
}
|
|
})
|
|
}
|
|
None => {
|
|
assert!(false, format!("Button {:?} not found!", button_code));
|
|
false
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_axis(self: &Self, axis_code: &str) -> f32 {
|
|
if let Some(axis) = self.virtual_axes.get(axis_code) {
|
|
axis.axis_inputs.iter().map(|item| {
|
|
match item {
|
|
AxisInput::Wheel(modifiers) => {
|
|
if self.modifiers_are_pressed(*modifiers) { self.analog_wheel_state } else { 0.0 }
|
|
},
|
|
AxisInput::MouseMove(direction, modifiers) => {
|
|
if self.modifiers_are_pressed(*modifiers) {
|
|
match direction {
|
|
MouseMoveDirection::X => self.mouse_delta_x as f32,
|
|
MouseMoveDirection::Y => self.mouse_delta_y as f32,
|
|
}
|
|
} else {
|
|
0.0
|
|
}
|
|
},
|
|
AxisInput::Digital(positive_button, negative_button) => {
|
|
self.virtual_button_to_float(positive_button) - self.virtual_button_to_float(negative_button)
|
|
},
|
|
}
|
|
}).fold(0.0, |fold, it| if f32::abs(it) > f32::abs(fold) { it } else { fold })
|
|
} else {
|
|
assert!(false, format!("Axis {:?} not found!", axis_code));
|
|
0.0
|
|
}
|
|
}
|
|
|
|
pub fn digital_input_pressed(self: &Self, digital_input: &DigitalInput) -> bool {
|
|
match digital_input {
|
|
DigitalInput::Keyboard(keyboard_input) => {
|
|
self.pressed_scan_codes.contains(&keyboard_input.scan_code)
|
|
&& self.modifiers_are_pressed(keyboard_input.modifiers)
|
|
},
|
|
DigitalInput::Mouse(mouse_input) => {
|
|
self.pressed_mouse_buttons.contains(&mouse_input.button)
|
|
&& self.modifiers_are_pressed(mouse_input.modifiers)
|
|
},
|
|
DigitalInput::Wheel(wheel_input) => {
|
|
self.input_events.contains(&DigitalInputEvent::Pressed(DigitalInput::Wheel(wheel_input.clone())))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn modifiers_are_pressed(self: &Self, modifiers: ModifiersState) -> bool {
|
|
(!modifiers.ctrl || self.pressed_scan_codes.contains(&29))
|
|
&& (!modifiers.shift || self.pressed_scan_codes.contains(&42) || self.pressed_scan_codes.contains(&54))
|
|
&& (!modifiers.alt || self.pressed_scan_codes.contains(&56))
|
|
&& (!modifiers.logo || self.pressed_scan_codes.contains(&91) || self.pressed_scan_codes.contains(&92))
|
|
}
|
|
|
|
fn virtual_button_to_float(self: &Self, input: &VirtualButton) -> f32 {
|
|
if input.digital_inputs.iter().any(|di| self.digital_input_pressed(di)) { 1.0 } else { 0.0 }
|
|
}
|
|
|
|
pub fn on_keyboard_event(self: &mut Self, state: ElementState, scan_code: ScanCode, modifiers: ModifiersState) {
|
|
let input = DigitalInput::Keyboard(KeyboardInput { scan_code, modifiers });
|
|
match state {
|
|
ElementState::Pressed => {
|
|
self.input_events.insert(DigitalInputEvent::Pressed(input));
|
|
self.pressed_scan_codes.insert(scan_code);
|
|
},
|
|
ElementState::Released => {
|
|
self.input_events.insert(DigitalInputEvent::Released(input));
|
|
self.pressed_scan_codes.remove(&scan_code);
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn on_mouse_event(self: &mut Self, state: &ElementState, button: &MouseButton, modifiers: &ModifiersState) {
|
|
let input = DigitalInput::Mouse(MouseInput { button: button.clone(), modifiers: modifiers.clone() });
|
|
match state {
|
|
ElementState::Pressed => {
|
|
self.input_events.insert(DigitalInputEvent::Pressed(input));
|
|
self.pressed_mouse_buttons.insert(button.clone());
|
|
},
|
|
ElementState::Released => {
|
|
self.input_events.insert(DigitalInputEvent::Released(input));
|
|
self.pressed_mouse_buttons.remove(button);
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn on_mouse_wheel_event(self: &mut Self, delta: &MouseScrollDelta, modifiers: &ModifiersState) {
|
|
let vertical_direction = match delta {
|
|
MouseScrollDelta::LineDelta(_x, y) => {
|
|
if *y > 0.0 { Some(WheelInputDirection::Up) }
|
|
else if *y < 0.0 { Some(WheelInputDirection::Down) }
|
|
else { None }
|
|
},
|
|
MouseScrollDelta::PixelDelta(pixels) => {
|
|
if pixels.y > 0.0 { Some(WheelInputDirection::Up) }
|
|
else if pixels.y < 0.0 { Some(WheelInputDirection::Down) }
|
|
else { None }
|
|
}
|
|
};
|
|
|
|
if let Some(direction) = vertical_direction {
|
|
let input = DigitalInput::Wheel(WheelInput { direction, modifiers: modifiers.clone() });
|
|
self.input_events.insert(DigitalInputEvent::Pressed(input.clone()));
|
|
self.input_events.insert(DigitalInputEvent::Released(input));
|
|
}
|
|
|
|
self.analog_wheel_state = match delta {
|
|
MouseScrollDelta::LineDelta(_x, y) => *y * self.config.line_height_px,
|
|
MouseScrollDelta::PixelDelta(pixels) => pixels.y as f32,
|
|
};
|
|
}
|
|
|
|
pub fn frame_end(self: &mut Self) {
|
|
self.analog_wheel_state = 0.0;
|
|
self.mouse_delta_x = 0.0;
|
|
self.mouse_delta_y = 0.0;
|
|
self.input_events.clear();
|
|
}
|
|
}
|
|
|
|
fn mods_to_string(modifiers: &ModifiersState) -> String {
|
|
String::from_iter(
|
|
vec!["shift", "ctrl", "alt", "logo"].iter()
|
|
.zip(vec![modifiers.shift, modifiers.ctrl, modifiers.alt, modifiers.logo])
|
|
.filter(|(&_name, state)| *state)
|
|
.map(|(&name, _state)| name))
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum AxisInput {
|
|
Wheel(ModifiersState),
|
|
MouseMove(MouseMoveDirection, ModifiersState),
|
|
Digital(VirtualButton, VirtualButton),
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
pub enum DigitalInput {
|
|
Keyboard(KeyboardInput),
|
|
Wheel(WheelInput),
|
|
Mouse(MouseInput),
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
pub struct KeyboardInput {
|
|
scan_code: ScanCode,
|
|
modifiers: ModifiersState,
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
pub struct MouseInput {
|
|
button: MouseButton,
|
|
modifiers: ModifiersState,
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
pub struct WheelInput {
|
|
direction: WheelInputDirection,
|
|
modifiers: ModifiersState,
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
pub enum WheelInputDirection {
|
|
Up,
|
|
Down,
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
pub enum MouseMoveDirection {
|
|
X,
|
|
Y,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub struct AnalogWheelInput {
|
|
value: f32,
|
|
modifiers: ModifiersState,
|
|
} |