use super::{
EvDevKeyboardDeviceDescription, EvDevMouseDeviceDescription, EvDevMultiTouchScreenDeviceDescription,
EvDevTouchScreenDeviceDescription,
};
use std::collections::HashMap;
#[derive(Clone, Debug, PartialEq)]
pub enum EvDevDeviceDescription {
TouchScreen(EvDevTouchScreenDeviceDescription),
Keyboard(EvDevKeyboardDeviceDescription),
Mouse(EvDevMouseDeviceDescription),
MultiTouchScreen(EvDevMultiTouchScreenDeviceDescription),
}
const FORMAT_ERROR: &str =
"Invalid device format, expected `(path):type=(touchscreen|multitouch|keyboard|mouse):[calibration=m11,m12,m21,m22,m31,m32]";
impl EvDevDeviceDescription {
pub fn path(&self) -> Option<&str> {
match self {
Self::TouchScreen(description) => description.path.as_deref(),
Self::Keyboard(description) => description.path.as_deref(),
Self::Mouse(description) => description.path.as_deref(),
Self::MultiTouchScreen(description) => description.path.as_deref(),
}
}
pub fn set_path(&mut self, value: Option<String>) {
match self {
Self::TouchScreen(description) => description.path = value,
Self::Keyboard(description) => description.path = value,
Self::Mouse(description) => description.path = value,
Self::MultiTouchScreen(description) => description.path = value,
}
}
pub(crate) fn parse_from_env(env: &str) -> Result<Self, String> {
let format_ex = || FORMAT_ERROR.to_owned();
let items: Vec<&str> = env.split(':').filter(|item| !item.is_empty()).collect();
if items.len() < 2 {
return Err(format_ex());
}
let path = items[0];
let mut dic = HashMap::new();
for item in &items[1..] {
let (key, value) = item.split_once('=').ok_or_else(format_ex)?;
if dic.insert(key.to_owned(), value.to_owned()).is_some() {
return Err(format_ex());
}
}
let type_ = dic.get("type").ok_or_else(format_ex)?;
match type_.as_str() {
"touchscreen" => Ok(Self::TouchScreen(EvDevTouchScreenDeviceDescription::parse_from_env(path, &dic)?)),
"multitouch" => {
let touch = EvDevTouchScreenDeviceDescription::parse_from_env(path, &dic)?;
Ok(Self::MultiTouchScreen(EvDevMultiTouchScreenDeviceDescription {
path: touch.path,
calibration_matrix: touch.calibration_matrix,
}))
}
"keyboard" => Ok(Self::Keyboard(EvDevKeyboardDeviceDescription { path: Some(path.to_owned()) })),
"mouse" => Ok(Self::Mouse(EvDevMouseDeviceDescription { path: Some(path.to_owned()) })),
_ => Err(format_ex()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use ferroui_base::Matrix;
#[test]
fn a_touch_screen_is_a_path_a_type_and_an_optional_calibration() {
let description = EvDevDeviceDescription::parse_from_env("/dev/input/event3:type=touchscreen").unwrap();
assert_eq!(description.path(), Some("/dev/input/event3"));
assert_eq!(
description,
EvDevDeviceDescription::TouchScreen(EvDevTouchScreenDeviceDescription {
path: Some("/dev/input/event3".to_owned()),
calibration_matrix: Matrix::IDENTITY,
})
);
let description =
EvDevDeviceDescription::parse_from_env("/dev/input/event3:type=touchscreen:calibration=0,1,-1,0,10,20")
.unwrap();
let EvDevDeviceDescription::TouchScreen(touch) = description else {
panic!("not a touch screen");
};
assert_eq!(touch.calibration_matrix, Matrix::new(0.0, 1.0, -1.0, 0.0, 10.0, 20.0));
}
#[test]
fn the_path_is_set_on_the_description_of_every_kind() {
for env in [
"/dev/input/event0:type=touchscreen",
"/dev/input/event0:type=multitouch",
"/dev/input/event0:type=keyboard",
"/dev/input/event0:type=mouse",
] {
let mut description = EvDevDeviceDescription::parse_from_env(env).unwrap();
description.set_path(Some("/dev/input/event7".to_owned()));
assert_eq!(description.path(), Some("/dev/input/event7"), "{env}");
description.set_path(None);
assert_eq!(description.path(), None, "{env}");
}
}
#[test]
fn the_added_types_are_named_by_type() {
let parse = |env: &str| EvDevDeviceDescription::parse_from_env(env).unwrap();
assert!(matches!(parse("/dev/input/event0:type=keyboard"), EvDevDeviceDescription::Keyboard(_)));
assert!(matches!(parse("/dev/input/event1:type=mouse"), EvDevDeviceDescription::Mouse(_)));
assert!(matches!(parse("/dev/input/event2:type=multitouch"), EvDevDeviceDescription::MultiTouchScreen(_)));
}
#[test]
fn what_is_not_in_the_format_is_an_error() {
for env in [
"",
"/dev/input/event3",
"/dev/input/event3:calibration=1,0,0,1,0,0",
"/dev/input/event3:type=joystick",
"/dev/input/event3:type",
"/dev/input/event3:type=touchscreen:calibration=x",
] {
assert!(EvDevDeviceDescription::parse_from_env(env).is_err(), "{env}");
}
}
}