1
1
2
2
// vim: shiftwidth=2
3
3
4
- use std:: fs:: { File , canonicalize , read_to_string} ;
4
+ use std:: fs:: { File , read_to_string} ;
5
5
use std:: io:: { self , BufRead } ;
6
6
use std:: num:: ParseIntError ;
7
7
use std:: path:: { Path , PathBuf } ;
@@ -21,11 +21,23 @@ struct ExtractedProcBusKeyboard {
21
21
name : String
22
22
}
23
23
24
+ struct ExtractedProcBusInputDevice {
25
+ sysfs_path : String ,
26
+ name : String ,
27
+ is_keyboard : bool
28
+ }
29
+
24
30
pub struct ExtractedKeyboard {
25
31
pub dev_path : PathBuf ,
26
32
pub name : String
27
33
}
28
34
35
+ pub struct ExtractedInputDevice {
36
+ pub dev_path : PathBuf ,
37
+ pub name : String ,
38
+ pub is_keyboard : bool
39
+ }
40
+
29
41
fn parse_mask_hex ( hex : & str ) -> Result < HashSet < i32 > , ParseIntError > {
30
42
let tokens = hex. rsplit ( ' ' ) ;
31
43
@@ -141,6 +153,101 @@ fn extract_keyboards_from_proc_bus_input_devices(proc_bus_input_devices: &str) -
141
153
res
142
154
}
143
155
156
+ fn extract_input_devices_from_proc_bus_input_devices ( proc_bus_input_devices : & str ) -> Vec < ExtractedProcBusInputDevice > {
157
+ let mut res = Vec :: new ( ) ;
158
+ let lines = proc_bus_input_devices. split ( '\n' ) ;
159
+
160
+ let mut working_sysfs_path = Box :: new ( None ) ;
161
+ let mut working_name = Box :: new ( None ) ;
162
+ let mut working_ev_mask = Box :: new ( None ) ;
163
+
164
+ for line in lines {
165
+ if line. starts_with ( "I:" ) {
166
+ * working_sysfs_path = None ;
167
+ * working_name = None ;
168
+ * working_ev_mask = None ;
169
+ }
170
+ else if line. starts_with ( "S: Sysfs=" ) {
171
+ let new_sysfs_path = line[ 9 ..] . to_string ( ) ;
172
+ * working_sysfs_path = Some ( new_sysfs_path) ;
173
+ }
174
+ else if line. starts_with ( "N: Name=\" " ) {
175
+ let mut name = line[ 9 ..] . to_string ( ) ;
176
+ name = name. trim_end ( ) . to_string ( ) ;
177
+ if name. ends_with ( '"' ) {
178
+ name = name[ ..name. len ( ) -1 ] . to_string ( ) ;
179
+ }
180
+ * working_name = Some ( name) ;
181
+ }
182
+ else if line. starts_with ( "B: EV=" ) {
183
+ * working_ev_mask = Some ( line[ 6 ..] . to_string ( ) ) ;
184
+ }
185
+ else if line. starts_with ( "B: KEY=" ) {
186
+ let mut num_keys = 0 ;
187
+ for c in line[ 7 ..] . chars ( ) {
188
+ num_keys += match c {
189
+ '0' => 0 , '1' => 1 , '2' => 1 , '3' => 2 ,
190
+ '4' => 1 , '5' => 2 , '6' => 2 , '7' => 3 ,
191
+ '8' => 1 , '9' => 2 , 'a' => 2 , 'b' => 3 ,
192
+ 'c' => 2 , 'd' => 3 , 'e' => 3 , 'f' => 4 ,
193
+ _ => 0
194
+ }
195
+ }
196
+
197
+ let key_set = parse_mask_hex ( & line[ 7 ..] ) . unwrap_or ( HashSet :: new ( ) ) ;
198
+
199
+ let ev_set = match & * working_ev_mask {
200
+ None => HashSet :: new ( ) ,
201
+ Some ( mask_hex) => {
202
+ parse_mask_hex ( mask_hex. as_str ( ) ) . unwrap_or ( HashSet :: new ( ) )
203
+ }
204
+ } ;
205
+
206
+ let num_normal_keys =
207
+ ( key_set. contains ( & ( KeyCode :: A as i32 ) ) as i32 )
208
+ + ( key_set. contains ( & ( KeyCode :: B as i32 ) ) as i32 )
209
+ + ( key_set. contains ( & ( KeyCode :: C as i32 ) ) as i32 )
210
+ + ( key_set. contains ( & ( KeyCode :: SPACE as i32 ) ) as i32 )
211
+ + ( key_set. contains ( & ( KeyCode :: LEFTSHIFT as i32 ) ) as i32 )
212
+ + ( key_set. contains ( & ( KeyCode :: RIGHTSHIFT as i32 ) ) as i32 )
213
+ + ( key_set. contains ( & ( KeyCode :: BACKSPACE as i32 ) ) as i32 )
214
+ + ( key_set. contains ( & ( KeyCode :: ENTER as i32 ) ) as i32 )
215
+ + ( key_set. contains ( & ( KeyCode :: ESC as i32 ) ) as i32 )
216
+ + ( key_set. contains ( & ( KeyCode :: PAUSE as i32 ) ) as i32 )
217
+ ;
218
+
219
+ let name = match & * working_name {
220
+ None => "" . to_string ( ) ,
221
+ Some ( name) => name. clone ( )
222
+ } ;
223
+
224
+ let has_scroll_down = key_set. contains ( & ( KeyCode :: SCROLLDOWN as i32 ) ) ;
225
+ let lacks_leds = !ev_set. contains ( & 0x11 ) ;
226
+ let has_mouse_in_name = name. contains ( "Mouse" ) ;
227
+ let is_cros_ec = name == "cros_ec" ;
228
+
229
+ let mousey = ( has_scroll_down as i32 ) + ( lacks_leds as i32 ) + ( has_mouse_in_name as i32 ) >= 2 ;
230
+
231
+ let has_rel_motion = ev_set. contains ( & 0x2 ) ;
232
+
233
+ let is_keyboard = num_keys >= 20 && num_normal_keys >= 3 && !has_rel_motion && !mousey && !is_cros_ec;
234
+
235
+ match & * working_sysfs_path {
236
+ None => ( ) ,
237
+ Some ( p) => {
238
+ res. push ( ExtractedProcBusInputDevice {
239
+ sysfs_path : p. to_string ( ) ,
240
+ name,
241
+ is_keyboard
242
+ } ) ;
243
+ }
244
+ }
245
+ }
246
+ }
247
+
248
+ res
249
+ }
250
+
144
251
pub fn list_keyboards ( ) -> io:: Result < Vec < ExtractedKeyboard > > {
145
252
let mut res = Vec :: new ( ) ;
146
253
@@ -165,6 +272,31 @@ pub fn list_keyboards() -> io::Result<Vec<ExtractedKeyboard>> {
165
272
Ok ( res)
166
273
}
167
274
275
+ pub fn list_input_devices ( ) -> io:: Result < Vec < ExtractedInputDevice > > {
276
+ let mut res = Vec :: new ( ) ;
277
+
278
+ let proc_bus_input_devices = read_to_string ( "/proc/bus/input/devices" ) ?;
279
+ let extracted = extract_input_devices_from_proc_bus_input_devices ( & proc_bus_input_devices) ;
280
+
281
+ for dev in extracted {
282
+ let p = dev. sysfs_path ;
283
+ if !p. starts_with ( "/devices/virtual" ) {
284
+ match dev_path_for_sysfs_name ( & p) ? {
285
+ None => ( ) ,
286
+ Some ( dev_path) => {
287
+ res. push ( ExtractedInputDevice {
288
+ dev_path,
289
+ name : dev. name ,
290
+ is_keyboard : dev. is_keyboard
291
+ } ) ;
292
+ }
293
+ }
294
+ }
295
+ }
296
+
297
+ Ok ( res)
298
+ }
299
+
168
300
fn dev_path_for_sysfs_name ( sysfs_name : & String ) -> io:: Result < Option < PathBuf > > {
169
301
let mut sysfs_path = "/sys" . to_string ( ) ;
170
302
sysfs_path. push_str ( sysfs_name) ;
@@ -197,45 +329,6 @@ fn dev_path_for_sysfs_name(sysfs_name: &String) -> io::Result<Option<PathBuf>> {
197
329
Ok ( None )
198
330
}
199
331
200
- pub fn filter_keyboards_verbose < ' a > ( devices : & Vec < & ' a str > ) -> io:: Result < Vec < & ' a str > > {
201
- let mut res = Vec :: new ( ) ;
202
-
203
- let all_keyboards = list_keyboards ( ) ?;
204
- let mut canonical_set: HashSet < String > = HashSet :: new ( ) ;
205
- for p in all_keyboards {
206
- if let Ok ( q) = canonicalize ( p. dev_path ) {
207
- if let Some ( s) = q. to_str ( ) {
208
- canonical_set. insert ( s. to_string ( ) ) ;
209
- }
210
- }
211
- }
212
-
213
- for s in devices {
214
- match canonicalize ( Path :: new ( s) ) {
215
- Err ( _) => {
216
- eprintln ! ( "Skipping {} because could not canonicalize path" , s) ;
217
- } ,
218
- Ok ( c) => {
219
- match c. to_str ( ) {
220
- None => {
221
- eprintln ! ( "Skipping {} because could not c-strify path" , s) ;
222
- } ,
223
- Some ( l) => {
224
- if canonical_set. contains ( & l. to_string ( ) ) {
225
- res. push ( * s)
226
- }
227
- else {
228
- eprintln ! ( "Skipping {} because {} is not in the list of keyboards" , s, l) ;
229
- }
230
- }
231
- }
232
- }
233
- }
234
- }
235
-
236
- Ok ( res)
237
- }
238
-
239
332
#[ cfg( test) ]
240
333
mod tests {
241
334
use crate :: example_hardware;
0 commit comments