basic touch input

This commit is contained in:
2021-08-05 23:33:26 +02:00
parent 02ba2bb95a
commit 01a4e93f3f
6 changed files with 247 additions and 19 deletions

View File

@@ -48,6 +48,7 @@ scan_code = 64
[[button]]
name = "select"
mouse = "left"
touch = 1
[[axis]]
name = "move_forward"

View File

@@ -4,4 +4,5 @@ mesh_load_info = true
[input]
mouse_motion = false
buttons = false
touch = true
missing_bindings = true

31
config/testinput.toml Normal file
View File

@@ -0,0 +1,31 @@
[[button]]
name = "quit"
scan_code = 1
[[button]]
name = "select"
mouse = "left"
[[button]]
name = "touchclick"
touch = 1
[[button]]
name = "doubletouchclick"
touch = 2
[[button]]
name = "button_left"
scan_code = 30
[[button]]
name = "button_right"
scan_code = 32
[[axis]]
name = "move_sideways"
positive_button = "button_right"
negative_button = "button_left"
[config]
line_height_px = 16

View File

@@ -7,6 +7,7 @@ use toml;
pub struct LogConfigInput {
pub mouse_motion: bool,
pub buttons: bool,
pub touch: bool,
pub missing_bindings: bool
}

View File

@@ -2,19 +2,21 @@
use std::collections::{HashMap, HashSet};
use std::fs;
use std::hash::Hash;
use std::iter::FromIterator;
use gilrs;
use gilrs::{EventType, Gilrs};
use serde_derive::{Deserialize, Serialize};
use toml;
use winit::event::{DeviceEvent, ElementState, Event, ModifiersState, MouseButton, MouseScrollDelta, ScanCode, WindowEvent};
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 digital_inputs: Vec<DigitalInput>,
pub touch_count: Option<u8>
}
#[derive(Debug)]
@@ -45,6 +47,7 @@ struct InputConfigButton {
name: String,
scan_code: Option<u32>,
mouse: Option<String>,
touch: Option<u8>,
controller_button: Option<String>,
ctrl: Option<bool>,
shift: Option<bool>,
@@ -75,10 +78,12 @@ pub struct InputState {
input_events: HashSet<DigitalInputEvent>,
pressed_scan_codes: HashSet<ScanCode>,
pressed_mouse_buttons: HashSet<MouseButton>,
pressed_touch_ids: HashSet<u64>,
analog_wheel_state: f32,
config: InputConfigConfig,
log_config: LogConfig,
controller_input: Gilrs,
touch_inputs: Vec<TouchInput>
}
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
@@ -106,10 +111,11 @@ impl InputState {
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,
pressed_mouse_buttons: HashSet::new(), pressed_touch_ids: HashSet::new(), touch_inputs: vec![], analog_wheel_state: 0.0,
config: config.config, mouse_delta_x: 0.0, mouse_delta_y: 0.0, mouse_position: [0., 0.], 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(),
@@ -117,33 +123,55 @@ impl InputState {
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 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();
match button_name_lower.as_str() {
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 }),
}
} else if let Some(controller_button) = &bn.controller_button {
DigitalInput::Controller(ControllerInput {
button: string_to_button(controller_button.as_str()).expect(&format!("Unknown controller button: {}", controller_button.as_str()))
})
} else {
panic!("No mouse or keyboard input for button {:?}", bn.name);
};
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] });
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), .. } => {
@@ -234,6 +262,12 @@ impl InputState {
Event::WindowEvent { event: WindowEvent::CursorMoved { position, .. }, .. } => {
self.mouse_position = [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);
},
_ => {}
}
}
@@ -241,6 +275,11 @@ impl InputState {
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_ids.len() == count as usize {
return true;
}
}
virtual_button.digital_inputs.iter().any(|vi| self.digital_input_pressed(vi))
}
None => {
@@ -255,6 +294,13 @@ impl InputState {
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_ids.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)
@@ -275,6 +321,13 @@ impl InputState {
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_ids.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)
@@ -412,6 +465,24 @@ impl InputState {
};
}
pub fn on_touch_event(&mut self, event: &Touch) {
self.mouse_position = [event.location.x, event.location.y];
match event.phase {
winit::event::TouchPhase::Started => {
self.pressed_touch_ids.insert(event.id);
self.touch_inputs.push(TouchInput { id: event.id, touch_location: [event.location.x, event.location.y], phase: event.phase });
},
winit::event::TouchPhase::Moved => {},
winit::event::TouchPhase::Ended => {
self.pressed_touch_ids.remove(&event.id);
self.touch_inputs.push(TouchInput { id: event.id, touch_location: [event.location.x, event.location.y], phase: event.phase });
},
winit::event::TouchPhase::Cancelled => {
self.pressed_touch_ids.remove(&event.id);
},
}
}
pub fn frame_start(self: &mut Self) {
while let Some(event) = self.controller_input.next_event() {
match event.event {
@@ -440,6 +511,7 @@ impl InputState {
self.mouse_delta_x = 0.0;
self.mouse_delta_y = 0.0;
self.input_events.clear();
self.touch_inputs.clear();
}
}
@@ -490,6 +562,28 @@ pub struct ControllerInput {
button: gilrs::Button,
}
#[derive(Debug, Clone)]
pub struct TouchInput {
id: u64,
touch_location: [f64; 2],
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 WheelInputDirection {
Up,

View File

@@ -1,8 +1,9 @@
#[cfg(test)]
mod tests {
use cgmath::{vec3};
use winit::{dpi::PhysicalPosition, event::{DeviceId, ElementState, Event, Force, KeyboardInput, ModifiersState, Touch, TouchPhase, WindowEvent}, window::WindowId};
use crate::game::player::{intersect_triangle};
use crate::{config::LogConfig, game::player::{intersect_triangle}, input::InputState};
fn epsilon_eq(f1: f32, f2: f32) {
assert!(f32::abs(f1 - f2) < f32::EPSILON, "{} == {}", f1, f2);
@@ -36,4 +37,103 @@ mod tests {
let dist3 = intersect_triangle(zero, vec3(0.9950371902, 0.09950371902, 0.0), a, b, c);
epsilon_eq_option(dist3, Some(2.0099751242));
}
fn create_test_input_state() -> InputState {
InputState::new("config/testinput.toml", LogConfig::from_file("config/log.toml"))
}
#[test]
fn key_test() {
let mut state = create_test_input_state();
state.frame_start();
state.frame_end();
unsafe {
state.on_window_event(&Event::WindowEvent{ window_id: WindowId::dummy(), event: WindowEvent::KeyboardInput {
device_id: DeviceId::dummy(),
input: KeyboardInput { scancode: 1, state: ElementState::Pressed, virtual_keycode: None, modifiers: ModifiersState::empty() },
is_synthetic: true,
}});
}
state.frame_start();
assert_eq!(state.button_just_pressed("quit"), true);
assert_eq!(state.button_down("quit"), true);
state.frame_end();
state.frame_start();
assert_eq!(state.button_just_pressed("quit"), false);
assert_eq!(state.button_down("quit"), true);
state.frame_end();
unsafe {
state.on_window_event(&Event::WindowEvent{ window_id: WindowId::dummy(), event: WindowEvent::KeyboardInput {
device_id: DeviceId::dummy(),
input: KeyboardInput { scancode: 1, state: ElementState::Released, virtual_keycode: None, modifiers: ModifiersState::empty() },
is_synthetic: true,
}});
}
state.frame_start();
assert_eq!(state.button_down("quit"), false);
}
fn touch_event(state: &mut InputState, phase: TouchPhase, id: u64) {
unsafe {
state.on_window_event(&Event::WindowEvent{ window_id: WindowId::dummy(), event: WindowEvent::Touch(Touch {
device_id: DeviceId::dummy(), phase, location: PhysicalPosition::new(0., 0.), force: Some(Force::Normalized(0.5)), id
})});
}
}
#[test]
fn touch_test() {
let mut state = create_test_input_state();
state.frame_start();
state.frame_end();
touch_event(&mut state, TouchPhase::Started, 1);
state.frame_start();
assert_eq!(state.button_just_pressed("touchclick"), true);
assert_eq!(state.button_down("touchclick"), true);
state.frame_end();
touch_event(&mut state, TouchPhase::Moved, 1);
state.frame_start();
assert_eq!(state.button_just_pressed("touchclick"), false);
assert_eq!(state.button_down("touchclick"), true);
state.frame_end();
touch_event(&mut state, TouchPhase::Ended, 1);
state.frame_start();
assert_eq!(state.button_just_released("touchclick"), true);
assert_eq!(state.button_down("touchclick"), false);
state.frame_end();
}
#[test]
fn multi_touch_test() {
let mut state = create_test_input_state();
state.frame_start();
state.frame_end();
touch_event(&mut state, TouchPhase::Started, 2);
// TODO: add tolerance for delay
touch_event(&mut state, TouchPhase::Started, 3);
state.frame_start();
assert_eq!(state.button_just_pressed("touchclick"), false);
assert_eq!(state.button_down("touchclick"), false);
assert_eq!(state.button_just_pressed("doubletouchclick"), true);
assert_eq!(state.button_down("doubletouchclick"), true);
state.frame_end();
touch_event(&mut state, TouchPhase::Moved, 2);
state.frame_start();
assert_eq!(state.button_just_pressed("doubletouchclick"), false);
assert_eq!(state.button_down("doubletouchclick"), true);
state.frame_end();
touch_event(&mut state, TouchPhase::Ended, 2);
state.frame_start();
assert_eq!(state.button_just_released("doubletouchclick"), true);
assert_eq!(state.button_down("doubletouchclick"), false);
assert_eq!(state.button_just_pressed("touchclick"), false);
// TODO: don't enable button_down on release of double touch
// assert_eq!(state.button_down("touchclick"), false);
state.frame_end();
touch_event(&mut state, TouchPhase::Ended, 3);
state.frame_start();
// TODO: only set button_just_released for one frame
// assert_eq!(state.button_just_released("doubletouchclick"), false);
assert_eq!(state.button_down("doubletouchclick"), false);
}
}