771 lines
32 KiB
Rust
771 lines
32 KiB
Rust
#![allow(deprecated)]
|
|
|
|
use std::collections::{HashMap, HashSet};
|
|
use std::fs;
|
|
use std::hash::Hash;
|
|
use std::iter::FromIterator;
|
|
|
|
use cgmath::{InnerSpace, Vector2, Zero, vec2};
|
|
use gilrs;
|
|
use gilrs::{EventType, Gilrs};
|
|
use serde_derive::{Deserialize, Serialize};
|
|
use toml;
|
|
use winit::event::{DeviceEvent, ElementState, Event, ModifiersState, MouseButton, MouseScrollDelta, ScanCode, Touch, TouchPhase, WindowEvent};
|
|
|
|
use crate::config::LogConfig;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct VirtualButton {
|
|
pub digital_inputs: Vec<DigitalInput>,
|
|
pub touch_count: Option<u8>,
|
|
}
|
|
|
|
#[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>,
|
|
touch: Option<u8>,
|
|
controller_button: 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>,
|
|
controller_axis: Option<String>,
|
|
touch_axis: Option<String>,
|
|
ctrl: Option<bool>,
|
|
shift: Option<bool>,
|
|
alt: Option<bool>,
|
|
logo: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct InputState {
|
|
pub virtual_buttons: HashMap<String, VirtualButton>,
|
|
pub virtual_axes: HashMap<String, VirtualAxis>,
|
|
pub mouse_delta_x: f64,
|
|
pub mouse_delta_y: f64,
|
|
pub mouse_position: Vector2<f64>,
|
|
input_events: HashSet<DigitalInputEvent>,
|
|
pressed_scan_codes: HashSet<ScanCode>,
|
|
pressed_mouse_buttons: HashSet<MouseButton>,
|
|
pressed_touch_positions: HashMap<u64, Vector2<f64>>,
|
|
analog_wheel_state: f32,
|
|
config: InputConfigConfig,
|
|
log_config: LogConfig,
|
|
controller_input: Gilrs,
|
|
touch_inputs: Vec<TouchInput>
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
pub struct KeyboardModifierState {
|
|
shift: bool,
|
|
ctrl: bool,
|
|
alt: bool,
|
|
logo: bool
|
|
}
|
|
|
|
impl KeyboardModifierState {
|
|
pub fn from_deprecated_state(old_state: &ModifiersState) -> KeyboardModifierState {
|
|
KeyboardModifierState {
|
|
shift: old_state.shift(),
|
|
ctrl: old_state.ctrl(),
|
|
alt: old_state.alt(),
|
|
logo: old_state.logo()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl InputState {
|
|
pub fn new(toml_path: &str, log_config: LogConfig) -> InputState {
|
|
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(), pressed_touch_positions: HashMap::new(), touch_inputs: vec![], analog_wheel_state: 0.0,
|
|
config: config.config, mouse_delta_x: 0.0, mouse_delta_y: 0.0, mouse_position: Vector2::zero(), log_config,
|
|
controller_input: Gilrs::new().unwrap() };
|
|
|
|
// Create virtual buttons from config
|
|
config.button.iter().for_each(|bn| {
|
|
let modifiers = KeyboardModifierState {
|
|
shift: bn.shift.is_some(),
|
|
ctrl: bn.ctrl.is_some(),
|
|
alt: bn.alt.is_some(),
|
|
logo: bn.logo.is_some()
|
|
};
|
|
|
|
let mut inputs = vec![];
|
|
|
|
// Keyboard buttons
|
|
if let Some(scan_code) = bn.scan_code {
|
|
inputs.push(DigitalInput::Keyboard(KeyboardInput { scan_code, modifiers: modifiers.clone() }));
|
|
}
|
|
|
|
// Mouse buttons
|
|
if let Some(button_name) = &bn.mouse {
|
|
let button_name_lower = button_name.to_lowercase();
|
|
let button_input = 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 }),
|
|
};
|
|
inputs.push(button_input);
|
|
}
|
|
|
|
// Controller buttons
|
|
if let Some(controller_button) = &bn.controller_button {
|
|
inputs.push(DigitalInput::Controller(ControllerInput {
|
|
button: string_to_button(controller_button.as_str()).expect(&format!("Unknown controller button: {}", controller_button.as_str()))
|
|
}));
|
|
}
|
|
|
|
// Touch clicks
|
|
if let Some(_) = bn.touch {
|
|
if let Some(virtual_button) = state.virtual_buttons.get_mut(&bn.name) {
|
|
virtual_button.touch_count = bn.touch;
|
|
} else {
|
|
state.virtual_buttons.insert(bn.name.clone(), VirtualButton { digital_inputs: vec![], touch_count: bn.touch });
|
|
}
|
|
}
|
|
|
|
// Insert digital inputs
|
|
for input in inputs {
|
|
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], touch_count: bn.touch });
|
|
}
|
|
}
|
|
});
|
|
|
|
// Create virtual axes from config
|
|
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 = KeyboardModifierState {
|
|
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),
|
|
}
|
|
},
|
|
InputConfigAxis { controller_axis: Some(controller_axis_name), .. } => {
|
|
AxisInput::Controller(AnalogControllerInput { axis: string_to_axis(controller_axis_name).expect(&format!("Unknown controller axis: {}", controller_axis_name)) })
|
|
},
|
|
InputConfigAxis { touch_axis: Some(touch_axis_name), .. } => {
|
|
if let Some(axis) = string_to_touch_axis(touch_axis_name) {
|
|
AxisInput::Touch(axis)
|
|
} else {
|
|
panic!("Unknown touch axis {:?}", touch_axis_name);
|
|
}
|
|
},
|
|
other => panic!("Axis {:?} needs either (positive_button and negative_button) or (touch_axis) or (mouse_wheel) or (controller_axis 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.buttons {
|
|
let mods = mods_to_string(&KeyboardModifierState::from_deprecated_state(&input.modifiers));
|
|
if mods.len() > 0 {
|
|
println!("Keyboard {:?} {:?} {:?} + {:?} {:?}", device_id, input.state, &mods, input.virtual_keycode, input.scancode)
|
|
} else {
|
|
println!("Keyboard {:?} {:?} {:?} {:?}", device_id, input.state, input.virtual_keycode, input.scancode)
|
|
}
|
|
}
|
|
|
|
self.on_keyboard_event(input.state, input.scancode, KeyboardModifierState::from_deprecated_state(&input.modifiers));
|
|
},
|
|
Event::WindowEvent { event: WindowEvent::MouseInput { device_id, state, button, modifiers }, .. } => {
|
|
if self.log_config.input.buttons {
|
|
let mods = mods_to_string(&KeyboardModifierState::from_deprecated_state(&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, &KeyboardModifierState::from_deprecated_state(&modifiers));
|
|
},
|
|
Event::WindowEvent { event: WindowEvent::MouseWheel { device_id, delta, phase, modifiers }, .. } => {
|
|
if self.log_config.input.buttons {
|
|
let mods = mods_to_string(&KeyboardModifierState::from_deprecated_state(&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, &KeyboardModifierState::from_deprecated_state(&modifiers));
|
|
},
|
|
Event::DeviceEvent { device_id, event: DeviceEvent::MouseMotion { delta: (delta_x, delta_y) } } => {
|
|
if self.log_config.input.mouse_motion {
|
|
println!("MouseMotion {:?}, ({:?},{:?})", device_id, delta_x, delta_y);
|
|
}
|
|
self.mouse_delta_x += *delta_x;
|
|
self.mouse_delta_y += *delta_y;
|
|
},
|
|
Event::WindowEvent { event: WindowEvent::CursorMoved { position, .. }, .. } => {
|
|
self.mouse_position = vec2(position.x, position.y);
|
|
},
|
|
Event::WindowEvent { event: WindowEvent::Touch(touch), .. } => {
|
|
if self.log_config.input.touch {
|
|
println!("Touch {:?}, at {:?}, id: {:?}, force: {:?}", touch.phase, touch.location, touch.id, touch.force);
|
|
}
|
|
self.on_touch_event(touch);
|
|
},
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
pub fn button_down(self: &Self, button_code: &str) -> bool {
|
|
match self.virtual_buttons.get(button_code) {
|
|
Some(virtual_button) => {
|
|
if let Some(count) = virtual_button.touch_count {
|
|
if self.pressed_touch_positions.len() == count as usize {
|
|
return true;
|
|
}
|
|
}
|
|
virtual_button.digital_inputs.iter().any(|vi| self.digital_input_pressed(vi))
|
|
}
|
|
None => {
|
|
if self.log_config.input.buttons {
|
|
println!("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) => {
|
|
if let Some(count) = virtual_button.touch_count {
|
|
if self.pressed_touch_positions.len() == count as usize
|
|
&& self.touch_inputs.iter().any(|ti| ti.phase == TouchPhase::Started) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
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 => {
|
|
if self.log_config.input.missing_bindings {
|
|
println!("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) => {
|
|
if let Some(count) = virtual_button.touch_count {
|
|
if self.pressed_touch_positions.len() < count as usize
|
|
&& self.touch_inputs.iter().any(|ti| ti.phase == TouchPhase::Ended) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
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 => {
|
|
if self.log_config.input.missing_bindings {
|
|
println!("Button {:?} not found!", button_code);
|
|
}
|
|
false
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_touch_drag_distance(&self) -> Vector2<f64> {
|
|
if let Some(old_pos) = self.pressed_touch_positions.values().next() {
|
|
if let Some(newest_input) = self.touch_inputs.iter().filter(|ti| ti.phase == TouchPhase::Started || ti.phase == TouchPhase::Moved).last() {
|
|
newest_input.touch_location - old_pos
|
|
} else {
|
|
vec2(0.0, 0.0)
|
|
}
|
|
} else {
|
|
vec2(0.0, 0.0)
|
|
}
|
|
}
|
|
|
|
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)
|
|
},
|
|
AxisInput::Controller(controller_input) => {
|
|
self.controller_input.gamepads()
|
|
.map(|(_id, gamepad)| gamepad.value(controller_input.axis))
|
|
.fold(0.0, fold_axis_value)
|
|
},
|
|
&AxisInput::Touch(touch_axis) => {
|
|
match touch_axis {
|
|
TouchAxis::Horizontal => {
|
|
if self.pressed_touch_positions.len() == 1 {
|
|
self.get_touch_drag_distance().x as f32
|
|
} else {
|
|
0.0
|
|
}
|
|
},
|
|
TouchAxis::Vertical => {
|
|
if self.pressed_touch_positions.len() == 1 {
|
|
self.get_touch_drag_distance().y as f32
|
|
} else {
|
|
0.0
|
|
}
|
|
},
|
|
TouchAxis::Rotate => {
|
|
if self.pressed_touch_positions.len() == 2 {
|
|
let mut positions = self.pressed_touch_positions.iter();
|
|
let (id_1, pos_1) = positions.next().unwrap();
|
|
let (id_2, pos_2) = positions.next().unwrap();
|
|
let mut touch_loc_1 = vec2(0., 0.);
|
|
let mut touch_loc_2 = vec2(0., 0.);
|
|
let mut touch_1 = None;
|
|
let mut touch_2 = None;
|
|
|
|
fn filtered_vector_sub(a: Vector2<f64>, b: &Vector2<f64>) -> Option<Vector2<f64>> {
|
|
Some(a - b).filter(|v| v.magnitude() > 0.01)
|
|
}
|
|
|
|
for input in &self.touch_inputs {
|
|
if input.id == *id_1 {
|
|
touch_1 = filtered_vector_sub(input.touch_location, &pos_1);
|
|
touch_loc_1 = input.touch_location;
|
|
}
|
|
if input.id == *id_2 {
|
|
touch_2 = filtered_vector_sub(input.touch_location, &pos_2);
|
|
touch_loc_2 = input.touch_location;
|
|
}
|
|
}
|
|
|
|
let diff = touch_loc_2 - touch_loc_1;
|
|
let norm_n = vec2(-diff.y, diff.x).normalize();
|
|
|
|
match (touch_1, touch_2) {
|
|
(Some(v1), Some(v2)) => {
|
|
let v1_n = v1.normalize();
|
|
let v2_n = v2.normalize();
|
|
|
|
let direction = v1_n.dot(norm_n) - v2_n.dot(norm_n);
|
|
let distance = (v1.magnitude() + v2.magnitude()) / 2.;
|
|
(direction * distance) as f32
|
|
},
|
|
(Some(v1), None) => {
|
|
let direction = v1.normalize().dot(norm_n);
|
|
(direction * v1.magnitude()) as f32
|
|
},
|
|
(None, Some(v2)) => {
|
|
let direction = v2.normalize().dot(norm_n);
|
|
(-direction * v2.magnitude()) as f32
|
|
},
|
|
(None, None) => { 0.0 }
|
|
}
|
|
} else {
|
|
0.0
|
|
}
|
|
},
|
|
}
|
|
},
|
|
}
|
|
}).fold(0.0, fold_axis_value)
|
|
} else {
|
|
if self.log_config.input.missing_bindings {
|
|
println!("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())))
|
|
},
|
|
DigitalInput::Controller(controller_input) => {
|
|
self.controller_input.gamepads().any(|(_id, gamepad)| gamepad.is_pressed(controller_input.button))
|
|
}
|
|
}
|
|
}
|
|
|
|
fn modifiers_are_pressed(self: &Self, modifiers: &KeyboardModifierState) -> 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: KeyboardModifierState) {
|
|
let input = DigitalInput::Keyboard(KeyboardInput { scan_code, modifiers });
|
|
match state {
|
|
ElementState::Pressed => {
|
|
if self.pressed_scan_codes.contains(&scan_code) { return; }
|
|
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: &KeyboardModifierState) {
|
|
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: &KeyboardModifierState) {
|
|
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 on_touch_event(&mut self, event: &Touch) {
|
|
self.mouse_position = vec2(event.location.x, event.location.y);
|
|
match event.phase {
|
|
winit::event::TouchPhase::Started => {
|
|
self.pressed_touch_positions.insert(event.id, self.mouse_position);
|
|
self.touch_inputs.push(TouchInput { id: event.id, touch_location: self.mouse_position, phase: event.phase });
|
|
},
|
|
winit::event::TouchPhase::Moved => {
|
|
self.touch_inputs.push(TouchInput { id: event.id, touch_location: self.mouse_position, phase: event.phase });
|
|
},
|
|
winit::event::TouchPhase::Ended => {
|
|
self.pressed_touch_positions.remove(&event.id);
|
|
self.touch_inputs.push(TouchInput { id: event.id, touch_location: self.mouse_position, phase: event.phase });
|
|
},
|
|
winit::event::TouchPhase::Cancelled => {
|
|
self.pressed_touch_positions.remove(&event.id);
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn frame_start(self: &mut Self) {
|
|
// Read out all controller events
|
|
while let Some(event) = self.controller_input.next_event() {
|
|
match event.event {
|
|
EventType::ButtonPressed(button, _) => {
|
|
if self.log_config.input.buttons { println!("ControllerButton Pressed {:?} {:?}", event.id, button); }
|
|
self.input_events.insert(DigitalInputEvent::Pressed(DigitalInput::Controller(ControllerInput { button })));
|
|
},
|
|
EventType::ButtonRepeated(_, _) => {},
|
|
EventType::ButtonReleased(button, _) => {
|
|
if self.log_config.input.buttons { println!("ControllerButton Released {:?} {:?}", event.id, button); }
|
|
self.input_events.insert(DigitalInputEvent::Released(DigitalInput::Controller(ControllerInput { button })));
|
|
},
|
|
EventType::ButtonChanged(_, _, _) => {},
|
|
EventType::AxisChanged(axis, value, _) => {
|
|
if self.log_config.input.buttons { println!("ControllerAxis {:?} {:?} {:?}", event.id, axis, value); }
|
|
},
|
|
EventType::Connected => {},
|
|
EventType::Disconnected => {},
|
|
EventType::Dropped => {},
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
// Store final touch positions as base for next frame
|
|
for touch_input in &mut self.touch_inputs {
|
|
self.pressed_touch_positions.get_mut(&touch_input.id).map(|pos| *pos = touch_input.touch_location);
|
|
}
|
|
self.touch_inputs.clear();
|
|
}
|
|
}
|
|
|
|
fn mods_to_string(modifiers: &KeyboardModifierState) -> 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(KeyboardModifierState),
|
|
MouseMove(MouseMoveDirection, KeyboardModifierState),
|
|
Digital(VirtualButton, VirtualButton),
|
|
Controller(AnalogControllerInput),
|
|
Touch(TouchAxis)
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
pub enum DigitalInput {
|
|
Keyboard(KeyboardInput),
|
|
Wheel(WheelInput),
|
|
Mouse(MouseInput),
|
|
Controller(ControllerInput),
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
pub struct KeyboardInput {
|
|
scan_code: ScanCode,
|
|
modifiers: KeyboardModifierState,
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
pub struct MouseInput {
|
|
button: MouseButton,
|
|
modifiers: KeyboardModifierState,
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
pub struct WheelInput {
|
|
direction: WheelInputDirection,
|
|
modifiers: KeyboardModifierState,
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
pub struct ControllerInput {
|
|
button: gilrs::Button,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct TouchInput {
|
|
id: u64,
|
|
touch_location: Vector2<f64>,
|
|
phase: TouchPhase
|
|
}
|
|
|
|
impl Eq for TouchInput {}
|
|
|
|
impl PartialEq for TouchInput {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.id.eq(&other.id) && self.phase.eq(&other.phase)
|
|
}
|
|
}
|
|
|
|
impl Hash for TouchInput {
|
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
state.write_u64(self.id);
|
|
self.phase.hash(state)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
pub enum TouchAxis {
|
|
Horizontal,
|
|
Vertical,
|
|
Rotate
|
|
}
|
|
|
|
#[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: KeyboardModifierState,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub struct AnalogControllerInput {
|
|
axis: gilrs::Axis,
|
|
}
|
|
|
|
fn fold_axis_value(fold: f32, it: f32) -> f32 {
|
|
if f32::abs(it) > f32::abs(fold) { it } else { fold }
|
|
}
|
|
|
|
fn string_to_button(button_name: &str) -> Option<gilrs::Button> {
|
|
match button_name {
|
|
"South" => Some(gilrs::Button::South),
|
|
"East" => Some(gilrs::Button::East),
|
|
"North" => Some(gilrs::Button::North),
|
|
"West" => Some(gilrs::Button::West),
|
|
"C" => Some(gilrs::Button::C),
|
|
"Z" => Some(gilrs::Button::Z),
|
|
// Triggers
|
|
"LeftTrigger" => Some(gilrs::Button::LeftTrigger),
|
|
"LeftTrigger2" => Some(gilrs::Button::LeftTrigger2),
|
|
"RightTrigger" => Some(gilrs::Button::RightTrigger),
|
|
"RightTrigger2" => Some(gilrs::Button::RightTrigger2),
|
|
// Menu Pad
|
|
"Select" => Some(gilrs::Button::Select),
|
|
"Start" => Some(gilrs::Button::Start),
|
|
"Mode" => Some(gilrs::Button::Mode),
|
|
// Sticks
|
|
"LeftThumb" => Some(gilrs::Button::LeftThumb),
|
|
"RightThumb" => Some(gilrs::Button::RightThumb),
|
|
// D-Pad
|
|
"DPadUp" => Some(gilrs::Button::DPadUp),
|
|
"DPadDown" => Some(gilrs::Button::DPadDown),
|
|
"DPadLeft" => Some(gilrs::Button::DPadLeft),
|
|
"DPadRight" => Some(gilrs::Button::DPadRight),
|
|
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn string_to_axis(axis_name: &str) -> Option<gilrs::Axis> {
|
|
match axis_name {
|
|
"LeftStickX" => Some(gilrs::Axis::LeftStickX),
|
|
"LeftStickY" => Some(gilrs::Axis::LeftStickY),
|
|
"LeftZ" => Some(gilrs::Axis::LeftZ),
|
|
"RightStickX" => Some(gilrs::Axis::RightStickX),
|
|
"RightStickY" => Some(gilrs::Axis::RightStickY),
|
|
"RightZ" => Some(gilrs::Axis::RightZ),
|
|
"DPadX" => Some(gilrs::Axis::DPadX),
|
|
"DPadY" => Some(gilrs::Axis::DPadY),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn string_to_touch_axis(touch_axis_name: &str) -> Option<TouchAxis> {
|
|
match touch_axis_name {
|
|
"horizontal" => Some(TouchAxis::Horizontal),
|
|
"vertical" => Some(TouchAxis::Vertical),
|
|
"rotate" => Some(TouchAxis::Rotate),
|
|
_ => None,
|
|
}
|
|
} |