#![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, pub touch_count: Option, } #[derive(Debug)] pub struct VirtualAxis { pub axis_inputs: Vec, } #[derive(Debug, PartialEq, Eq, Hash)] pub enum DigitalInputEvent { Pressed(DigitalInput), Released(DigitalInput), } #[derive(Debug, Serialize, Deserialize)] struct InputConfig { config: InputConfigConfig, button: Vec, axis: Vec, } #[derive(Debug, Serialize, Deserialize)] struct InputConfigConfig { line_height_px: f32, } #[derive(Debug, Serialize, Deserialize)] struct InputConfigButton { name: String, scan_code: Option, mouse: Option, touch: Option, controller_button: Option, ctrl: Option, shift: Option, alt: Option, logo: Option, } #[derive(Debug, Serialize, Deserialize)] struct InputConfigAxis { name: String, positive_button: Option, negative_button: Option, mouse_axis: Option, controller_axis: Option, touch_axis: Option, ctrl: Option, shift: Option, alt: Option, logo: Option, } #[derive(Debug)] pub struct InputState { pub virtual_buttons: HashMap, pub virtual_axes: HashMap, pub mouse_delta_x: f64, pub mouse_delta_y: f64, pub mouse_position: Vector2, input_events: HashSet, pressed_scan_codes: HashSet, pressed_mouse_buttons: HashSet, pressed_touch_positions: HashMap>, analog_wheel_state: f32, config: InputConfigConfig, log_config: LogConfig, controller_input: Gilrs, touch_inputs: Vec } #[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 { 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, b: &Vector2) -> Option> { 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, 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(&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 { 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 { 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 { match touch_axis_name { "horizontal" => Some(TouchAxis::Horizontal), "vertical" => Some(TouchAxis::Vertical), "rotate" => Some(TouchAxis::Rotate), _ => None, } }