ferroui_linuxframebuffer/input/ev_dev/
ev_dev_device_description.rs1use super::{
10 EvDevKeyboardDeviceDescription, EvDevMouseDeviceDescription, EvDevMultiTouchScreenDeviceDescription,
11 EvDevTouchScreenDeviceDescription,
12};
13use std::collections::HashMap;
14
15#[derive(Clone, Debug, PartialEq)]
16pub enum EvDevDeviceDescription {
17 TouchScreen(EvDevTouchScreenDeviceDescription),
18 Keyboard(EvDevKeyboardDeviceDescription),
19 Mouse(EvDevMouseDeviceDescription),
20 MultiTouchScreen(EvDevMultiTouchScreenDeviceDescription),
21}
22
23const FORMAT_ERROR: &str =
24 "Invalid device format, expected `(path):type=(touchscreen|multitouch|keyboard|mouse):[calibration=m11,m12,m21,m22,m31,m32]";
25
26impl EvDevDeviceDescription {
27 pub fn path(&self) -> Option<&str> {
28 match self {
29 Self::TouchScreen(description) => description.path.as_deref(),
30 Self::Keyboard(description) => description.path.as_deref(),
31 Self::Mouse(description) => description.path.as_deref(),
32 Self::MultiTouchScreen(description) => description.path.as_deref(),
33 }
34 }
35
36 pub fn set_path(&mut self, value: Option<String>) {
37 match self {
38 Self::TouchScreen(description) => description.path = value,
39 Self::Keyboard(description) => description.path = value,
40 Self::Mouse(description) => description.path = value,
41 Self::MultiTouchScreen(description) => description.path = value,
42 }
43 }
44
45 pub(crate) fn parse_from_env(env: &str) -> Result<Self, String> {
47 let format_ex = || FORMAT_ERROR.to_owned();
48
49 let items: Vec<&str> = env.split(':').filter(|item| !item.is_empty()).collect();
50 if items.len() < 2 {
51 return Err(format_ex());
52 }
53
54 let path = items[0];
55 let mut dic = HashMap::new();
56 for item in &items[1..] {
57 let (key, value) = item.split_once('=').ok_or_else(format_ex)?;
60 if dic.insert(key.to_owned(), value.to_owned()).is_some() {
61 return Err(format_ex());
62 }
63 }
64
65 let type_ = dic.get("type").ok_or_else(format_ex)?;
66
67 match type_.as_str() {
68 "touchscreen" => Ok(Self::TouchScreen(EvDevTouchScreenDeviceDescription::parse_from_env(path, &dic)?)),
69 "multitouch" => {
71 let touch = EvDevTouchScreenDeviceDescription::parse_from_env(path, &dic)?;
72 Ok(Self::MultiTouchScreen(EvDevMultiTouchScreenDeviceDescription {
73 path: touch.path,
74 calibration_matrix: touch.calibration_matrix,
75 }))
76 }
77 "keyboard" => Ok(Self::Keyboard(EvDevKeyboardDeviceDescription { path: Some(path.to_owned()) })),
78 "mouse" => Ok(Self::Mouse(EvDevMouseDeviceDescription { path: Some(path.to_owned()) })),
79 _ => Err(format_ex()),
80 }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
88 use ferroui_base::Matrix;
89
90 #[test]
91 fn a_touch_screen_is_a_path_a_type_and_an_optional_calibration() {
92 let description = EvDevDeviceDescription::parse_from_env("/dev/input/event3:type=touchscreen").unwrap();
93 assert_eq!(description.path(), Some("/dev/input/event3"));
94 assert_eq!(
95 description,
96 EvDevDeviceDescription::TouchScreen(EvDevTouchScreenDeviceDescription {
97 path: Some("/dev/input/event3".to_owned()),
98 calibration_matrix: Matrix::IDENTITY,
99 })
100 );
101
102 let description =
103 EvDevDeviceDescription::parse_from_env("/dev/input/event3:type=touchscreen:calibration=0,1,-1,0,10,20")
104 .unwrap();
105 let EvDevDeviceDescription::TouchScreen(touch) = description else {
106 panic!("not a touch screen");
107 };
108 assert_eq!(touch.calibration_matrix, Matrix::new(0.0, 1.0, -1.0, 0.0, 10.0, 20.0));
109 }
110
111 #[test]
112 fn the_path_is_set_on_the_description_of_every_kind() {
113 for env in [
114 "/dev/input/event0:type=touchscreen",
115 "/dev/input/event0:type=multitouch",
116 "/dev/input/event0:type=keyboard",
117 "/dev/input/event0:type=mouse",
118 ] {
119 let mut description = EvDevDeviceDescription::parse_from_env(env).unwrap();
120 description.set_path(Some("/dev/input/event7".to_owned()));
121 assert_eq!(description.path(), Some("/dev/input/event7"), "{env}");
122 description.set_path(None);
123 assert_eq!(description.path(), None, "{env}");
124 }
125 }
126
127 #[test]
128 fn the_added_types_are_named_by_type() {
129 let parse = |env: &str| EvDevDeviceDescription::parse_from_env(env).unwrap();
130 assert!(matches!(parse("/dev/input/event0:type=keyboard"), EvDevDeviceDescription::Keyboard(_)));
131 assert!(matches!(parse("/dev/input/event1:type=mouse"), EvDevDeviceDescription::Mouse(_)));
132 assert!(matches!(parse("/dev/input/event2:type=multitouch"), EvDevDeviceDescription::MultiTouchScreen(_)));
133 }
134
135 #[test]
136 fn what_is_not_in_the_format_is_an_error() {
137 for env in [
138 "",
139 "/dev/input/event3",
140 "/dev/input/event3:calibration=1,0,0,1,0,0",
141 "/dev/input/event3:type=joystick",
142 "/dev/input/event3:type",
143 "/dev/input/event3:type=touchscreen:calibration=x",
144 ] {
145 assert!(EvDevDeviceDescription::parse_from_env(env).is_err(), "{env}");
146 }
147 }
148}