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 } #[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, 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, ctrl: Option, shift: Option, alt: Option, logo: Option, } #[derive(Debug)] pub struct InputState<'a> { pub virtual_buttons: HashMap, pub virtual_axes: HashMap, pub mouse_delta_x: f64, pub mouse_delta_y: f64, input_events: HashSet, pressed_scan_codes: HashSet, pressed_mouse_buttons: HashSet, 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, }