Determine keyboard devices by whether they support KEY_A or not
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
All checks were successful
moparisthebest/rusty-keys/pipeline/head This commit looks good
This commit is contained in:
parent
95833e3a33
commit
fa202be7da
@ -1,5 +1,6 @@
|
|||||||
#![recursion_limit = "1000"]
|
#![recursion_limit = "1000"]
|
||||||
|
|
||||||
|
pub const NAME: &'static str = env!("CARGO_PKG_NAME");
|
||||||
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
111
src/linux/mod.rs
111
src/linux/mod.rs
@ -23,6 +23,8 @@ use std::{env, thread};
|
|||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
|
const INPUT_FOLDER: &str = "/dev/input/";
|
||||||
|
|
||||||
// 1 is down, 0 is up
|
// 1 is down, 0 is up
|
||||||
const DOWN: i32 = 1;
|
const DOWN: i32 = 1;
|
||||||
const UP: i32 = 0;
|
const UP: i32 = 0;
|
||||||
@ -123,7 +125,7 @@ pub fn main_res() -> Result<()> {
|
|||||||
let device = open("/dev/uinput")
|
let device = open("/dev/uinput")
|
||||||
.or_else(|_| open("/dev/input/uinput"))
|
.or_else(|_| open("/dev/input/uinput"))
|
||||||
.or_else(|_| default())?
|
.or_else(|_| default())?
|
||||||
.name("rusty-keys")?
|
.name(NAME)?
|
||||||
.event(key_map.values())?
|
.event(key_map.values())?
|
||||||
.create()?;
|
.create()?;
|
||||||
|
|
||||||
@ -161,7 +163,7 @@ pub fn main_res() -> Result<()> {
|
|||||||
// we want to wait forever starting new threads for any new keyboard devices
|
// we want to wait forever starting new threads for any new keyboard devices
|
||||||
let mut inotify = Inotify::init().expect("Failed to initialize inotify");
|
let mut inotify = Inotify::init().expect("Failed to initialize inotify");
|
||||||
|
|
||||||
inotify.add_watch("/dev/input/", WatchMask::CREATE).expect("Failed to add inotify watch");
|
inotify.add_watch(INPUT_FOLDER, WatchMask::CREATE).expect("Failed to add inotify watch");
|
||||||
|
|
||||||
let device_files = get_keyboard_device_filenames();
|
let device_files = get_keyboard_device_filenames();
|
||||||
println!("Detected devices: {:?}", device_files);
|
println!("Detected devices: {:?}", device_files);
|
||||||
@ -178,12 +180,14 @@ pub fn main_res() -> Result<()> {
|
|||||||
if !event.mask.contains(EventMask::ISDIR) {
|
if !event.mask.contains(EventMask::ISDIR) {
|
||||||
if let Some(device_file) = event.name.and_then(|name|name.to_str()) {
|
if let Some(device_file) = event.name.and_then(|name|name.to_str()) {
|
||||||
// check if this is an eligible keyboard device
|
// check if this is an eligible keyboard device
|
||||||
let device_files = get_keyboard_device_filenames();
|
let mut path = std::path::PathBuf::new();
|
||||||
if !device_files.contains(&device_file.to_string()) {
|
path.push(INPUT_FOLDER);
|
||||||
continue;
|
path.push(device_file);
|
||||||
|
|
||||||
|
if valid_keyboard_device(path) {
|
||||||
|
println!("starting mapping thread for: {}", device_file);
|
||||||
|
inotify_spawn_thread(&tx, device_file.clone());
|
||||||
}
|
}
|
||||||
println!("starting mapping thread for: {}", device_file);
|
|
||||||
inotify_spawn_thread(&tx, device_file.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,7 +214,7 @@ fn send_event(key_map: &mut LinuxKeyMaps, mut event: input_event, device: &Devic
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn inotify_spawn_thread(tx: &Sender<input_event>, device_file: &str) {
|
fn inotify_spawn_thread(tx: &Sender<input_event>, device_file: &str) {
|
||||||
let mut filename = "/dev/input/".to_string();
|
let mut filename = INPUT_FOLDER.to_string();
|
||||||
filename.push_str(&device_file);
|
filename.push_str(&device_file);
|
||||||
let tx = tx.clone();
|
let tx = tx.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
@ -256,7 +260,7 @@ fn parse_args() -> Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if matches.opt_present("v") {
|
if matches.opt_present("v") {
|
||||||
println!("rusty-keys {}", VERSION);
|
println!("{} {}", NAME, VERSION);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,41 +269,69 @@ fn parse_args() -> Config {
|
|||||||
Config::new(matches.free, config_file)
|
Config::new(matches.free, config_file)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detects and returns the name of the keyboard device file. This function uses
|
nix::ioctl_read_buf!(eviocgname, b'E', 0x06, u8);
|
||||||
// the fact that all device information is shown in /proc/bus/input/devices and
|
nix::ioctl_read_buf!(eviocgbit, b'E', 0x20, u8);
|
||||||
// the keyboard device file should always have an EV of 120013
|
nix::ioctl_read_buf!(eviocgbit_ev_key, b'E', 0x20 + EV_KEY, u8);
|
||||||
// grep -E 'Handlers|EV' /proc/bus/input/devices | grep -B1 120013 | grep -Eo event[0-9]+
|
|
||||||
fn get_keyboard_device_filenames() -> Vec<String> {
|
|
||||||
use std::io::BufReader;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::fs::File;
|
|
||||||
|
|
||||||
let f = File::open("/proc/bus/input/devices");
|
fn valid_keyboard_device_res<P: AsRef<Path>>(path: P) -> Result<bool> {
|
||||||
if f.is_err() {
|
use std::fs::File;
|
||||||
return Vec::new();
|
use std::os::unix::fs::FileTypeExt;
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
|
||||||
|
let device_file = File::open(path)?;
|
||||||
|
|
||||||
|
// must be a character device
|
||||||
|
if !device_file.metadata()?.file_type().is_char_device() {
|
||||||
|
return Ok(false);
|
||||||
}
|
}
|
||||||
let f = BufReader::new(f.unwrap());
|
|
||||||
let mut filename = None;
|
// does it support EV_KEY
|
||||||
let mut filenames = Vec::new();
|
let mut evbit = [0u8; 8];
|
||||||
for line in f.lines() {
|
unsafe {
|
||||||
if let Ok(line) = line {
|
eviocgbit(device_file.as_raw_fd(), &mut evbit)?;
|
||||||
if line.starts_with("H: Handlers=") {
|
};
|
||||||
if let Some(event_index) = line.find("event") {
|
let evbit = u64::from_ne_bytes(evbit);
|
||||||
let last_index = line[event_index..line.len()-1].find(" ").and_then(|i| Some(i + event_index)).unwrap_or(line.len() - 1);
|
if (evbit & (1 << EV_KEY)) == 0 {
|
||||||
filename = Some(line[event_index..last_index].to_owned());
|
return Ok(false);
|
||||||
}
|
}
|
||||||
// the following line checked for only 120013 for years, which works across every device I've tested, dozens at least
|
|
||||||
// until I bought a Monoprice Dark Matter Aether keyboard which presents 2 keyboard devices, one with 120013,
|
// does it support KEY_A ? todo: check other keys ?
|
||||||
// another with 100013 the one with 120013 can only be read from a few times before it stops sending events, then events
|
let mut key_bits = [0u8; (KEY_MAX as usize / 8) + 1];
|
||||||
// start coming from 100013, if I lock+read them both, it *just works* as expected... buggy keyboard firmware or ?
|
unsafe {
|
||||||
} else if line.starts_with("B: EV=") && (line.contains("120013") || line.contains("100013")) {
|
eviocgbit_ev_key(device_file.as_raw_fd(), &mut key_bits)?;
|
||||||
if let Some(ref filename) = filename {
|
};
|
||||||
filenames.push(filename.clone());
|
if (key_bits[KEY_A as usize / 8] & (1 << (KEY_A % 8))) == 0 {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// is it another running copy of rusty-keys ?
|
||||||
|
let mut name = [0u8; NAME.len()];
|
||||||
|
unsafe {
|
||||||
|
eviocgname(device_file.as_raw_fd(), &mut name)?
|
||||||
|
};
|
||||||
|
if NAME.as_bytes() == &name {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn valid_keyboard_device<P: AsRef<Path>>(path: P) -> bool {
|
||||||
|
valid_keyboard_device_res(path).unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_keyboard_device_filenames() -> Vec<String> {
|
||||||
|
let mut res = Vec::new();
|
||||||
|
if let Ok(entries) = std::fs::read_dir(INPUT_FOLDER) {
|
||||||
|
for entry in entries {
|
||||||
|
if let Ok(entry) = entry {
|
||||||
|
if valid_keyboard_device(entry.path()) {
|
||||||
|
// these unwrap()'s should not be able to fail if valid_keyboard_device() returns true
|
||||||
|
res.push(entry.path().file_name().unwrap().to_str().unwrap().to_owned());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filenames
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn key_map() -> HashMap<&'static str, u16> {
|
pub fn key_map() -> HashMap<&'static str, u16> {
|
||||||
@ -611,4 +643,5 @@ pub fn key_map() -> HashMap<&'static str, u16> {
|
|||||||
("PDOT", KEY_KPDOT),
|
("PDOT", KEY_KPDOT),
|
||||||
("PENT", KEY_KPENTER),
|
("PENT", KEY_KPENTER),
|
||||||
].iter().cloned().map(|(m, v)| (m, v as u16)).collect()
|
].iter().cloned().map(|(m, v)| (m, v as u16)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user