Skip to content

Commit 5c0bc40

Browse files
author
Owen Healy
committed
Fix --exclude with --dev-file
1 parent f6e3254 commit 5c0bc40

File tree

2 files changed

+207
-42
lines changed

2 files changed

+207
-42
lines changed

src/keyboard_listing.rs

Lines changed: 133 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
// vim: shiftwidth=2
33

4-
use std::fs::{File, canonicalize, read_to_string};
4+
use std::fs::{File, read_to_string};
55
use std::io::{self, BufRead};
66
use std::num::ParseIntError;
77
use std::path::{Path, PathBuf};
@@ -21,11 +21,23 @@ struct ExtractedProcBusKeyboard {
2121
name: String
2222
}
2323

24+
struct ExtractedProcBusInputDevice {
25+
sysfs_path: String,
26+
name: String,
27+
is_keyboard: bool
28+
}
29+
2430
pub struct ExtractedKeyboard {
2531
pub dev_path: PathBuf,
2632
pub name: String
2733
}
2834

35+
pub struct ExtractedInputDevice {
36+
pub dev_path: PathBuf,
37+
pub name: String,
38+
pub is_keyboard: bool
39+
}
40+
2941
fn parse_mask_hex(hex: &str) -> Result<HashSet<i32>, ParseIntError> {
3042
let tokens = hex.rsplit(' ');
3143

@@ -141,6 +153,101 @@ fn extract_keyboards_from_proc_bus_input_devices(proc_bus_input_devices: &str) -
141153
res
142154
}
143155

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+
144251
pub fn list_keyboards() -> io::Result<Vec<ExtractedKeyboard>> {
145252
let mut res = Vec::new();
146253

@@ -165,6 +272,31 @@ pub fn list_keyboards() -> io::Result<Vec<ExtractedKeyboard>> {
165272
Ok(res)
166273
}
167274

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+
168300
fn dev_path_for_sysfs_name(sysfs_name: &String) -> io::Result<Option<PathBuf>> {
169301
let mut sysfs_path = "/sys".to_string();
170302
sysfs_path.push_str(sysfs_name);
@@ -197,45 +329,6 @@ fn dev_path_for_sysfs_name(sysfs_name: &String) -> io::Result<Option<PathBuf>> {
197329
Ok(None)
198330
}
199331

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-
239332
#[cfg(test)]
240333
mod tests {
241334
use crate::example_hardware;

src/remapping_loop.rs

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ use nix::Error;
66
use nix::errno::Errno::ENODEV;
77
use wildmatch::WildMatch;
88
use crate::key_transforms;
9-
use crate::keyboard_listing::{filter_keyboards_verbose, list_keyboards, ExtractedKeyboard};
9+
use crate::keyboard_listing::{list_keyboards, ExtractedKeyboard, list_input_devices, ExtractedInputDevice};
1010
use crate::dev_input_rw::{DevInputReader, DevInputWriter, Exclusion};
11+
use std::collections::HashMap;
1112
use std::thread::{spawn, JoinHandle};
1213
use std::sync::Mutex;
1314
use std::sync::Arc;
@@ -160,15 +161,75 @@ pub fn do_remapping_loop_multiple_devices(devices: &Vec<&str>, skip_non_keyboard
160161
)
161162
}
162163

163-
let filter_devices_verbose<'s>(devices: &Vec<&'s str>, skip_non_keyboard: bool, excludes: &[&str]) -> Result<Vec<&'s str>, String> {
164+
fn filter_devices_verbose<'s>(devices: &Vec<&'s str>, skip_non_keyboard: bool, excludes: &[&str], verbose: bool) -> Result<Vec<&'s str>, String> {
165+
use std::fs::canonicalize;
166+
let mut res = Vec::new();
164167

168+
let all_input_devices = list_input_devices()
169+
.map_err(|e| format!("Failed to get the list of keyboards: {}", e))?;
170+
171+
let devs_with_exclusions = flag_excluded_input_devices(all_input_devices, excludes);
172+
173+
let mut canonical_set: HashMap<String, PossiblyExcludedInputDevice> = HashMap::new();
174+
for p in devs_with_exclusions {
175+
if let Ok(q) = canonicalize(p.extracted_keyboard.dev_path.clone()) {
176+
if let Some(s) = q.to_str() {
177+
canonical_set.insert(s.to_string(), p);
178+
}
179+
}
180+
else {
181+
if verbose {
182+
eprint!("Skipping {:?} because could not canonicalize path", p.extracted_keyboard.dev_path);
183+
}
184+
}
185+
}
186+
187+
for s in devices {
188+
match canonicalize(Path::new(s)) {
189+
Err(_) => {
190+
eprintln!("Skipping {} because could not canonicalize path", s);
191+
},
192+
Ok(c) => {
193+
match c.to_str() {
194+
None => {
195+
eprintln!("Skipping {} because could not c-strify path", s);
196+
},
197+
Some(l) => {
198+
if let Some(dev) = canonical_set.get(&l.to_string()) {
199+
if skip_non_keyboard && !dev.extracted_keyboard.is_keyboard {
200+
if verbose { eprintln!("Skipping {} ({}) ({}) beacuse it does not appear to be a keyboard", s, l, dev.extracted_keyboard.name); }
201+
}
202+
else {
203+
if dev.excluded {
204+
if verbose { eprintln!("Skipping {} ({}) ({}) beacuse it was excluded by a pattern", s, l, dev.extracted_keyboard.name); }
205+
}
206+
else {
207+
res.push(*s)
208+
}
209+
}
210+
}
211+
else {
212+
if verbose { eprintln!("Skipping {} ({}) beacuse it was not found in /proc/bus/input/devices", s, l); }
213+
}
214+
}
215+
}
216+
}
217+
}
218+
}
219+
220+
Ok(res)
165221
}
166222

167223
struct PossiblyExcludedDevice {
168224
extracted_keyboard: ExtractedKeyboard,
169225
excluded: bool
170226
}
171227

228+
struct PossiblyExcludedInputDevice {
229+
extracted_keyboard: ExtractedInputDevice,
230+
excluded: bool
231+
}
232+
172233
fn flag_excluded(devices: Vec<ExtractedKeyboard>, excludes: &[&str]) -> Vec<PossiblyExcludedDevice> {
173234
let wilds: Vec<WildMatch> = excludes.iter().map(|e| WildMatch::new(e)).collect();
174235
devices.into_iter().map(|d| {
@@ -180,6 +241,17 @@ fn flag_excluded(devices: Vec<ExtractedKeyboard>, excludes: &[&str]) -> Vec<Poss
180241
}).collect()
181242
}
182243

244+
fn flag_excluded_input_devices(devices: Vec<ExtractedInputDevice>, excludes: &[&str]) -> Vec<PossiblyExcludedInputDevice> {
245+
let wilds: Vec<WildMatch> = excludes.iter().map(|e| WildMatch::new(e)).collect();
246+
devices.into_iter().map(|d| {
247+
let excluded = wilds.iter().any(|w| w.matches(&d.name));
248+
PossiblyExcludedInputDevice {
249+
extracted_keyboard: d,
250+
excluded
251+
}
252+
}).collect()
253+
}
254+
183255
fn open_device(path: &Path, tablet_mode_switch_device: &Option<PathBuf>) -> Result<RealDriver, String> {
184256
let r = match DevInputReader::open(path, Exclusion::WaitReleaseAndExclude, true) {
185257
Err(e) => Err(format!("Failed to open {:?} for reading: {}", path, e)),

0 commit comments

Comments
 (0)