Format all code

This commit is contained in:
Travis Burtrum 2025-02-07 00:35:49 -05:00
parent 270cdca14f
commit 82421be440
Signed by: moparisthebest
GPG Key ID: 88C93BFE27BC8229
12 changed files with 1599 additions and 1455 deletions

View File

@ -1,7 +1,4 @@
use std::fmt; use std::{error, ffi, fmt, io};
use std::error;
use std::ffi;
use std::io;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use nix; use nix;
@ -9,66 +6,66 @@ use nix;
/// UInput error. /// UInput error.
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// System errors. /// System errors.
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
Nix(nix::Error), Nix(nix::Error),
/// Errors with internal nulls in names. /// Errors with internal nulls in names.
Nul(ffi::NulError), Nul(ffi::NulError),
Io(io::Error), Io(io::Error),
#[cfg(feature = "toml_serde")]
Toml(toml::de::Error),
NotAKeyboard, #[cfg(feature = "toml_serde")]
Toml(toml::de::Error),
/// error reading input_event
ShortRead,
/// epoll already added NotAKeyboard,
EpollAlreadyAdded,
/// error reading input_event
ShortRead,
/// epoll already added
EpollAlreadyAdded,
} }
impl From<ffi::NulError> for Error { impl From<ffi::NulError> for Error {
fn from(value: ffi::NulError) -> Self { fn from(value: ffi::NulError) -> Self {
Error::Nul(value) Error::Nul(value)
} }
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
impl From<nix::Error> for Error { impl From<nix::Error> for Error {
fn from(value: nix::Error) -> Self { fn from(value: nix::Error) -> Self {
Error::Nix(value) Error::Nix(value)
} }
} }
impl From<io::Error> for Error { impl From<io::Error> for Error {
fn from(value: io::Error) -> Self { fn from(value: io::Error) -> Self {
Error::Io(value) Error::Io(value)
} }
} }
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self { match self {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
&Error::Nix(ref err) => err.fmt(f), &Error::Nix(ref err) => err.fmt(f),
&Error::Nul(ref err) => err.fmt(f), &Error::Nul(ref err) => err.fmt(f),
&Error::Io(ref err) => err.fmt(f), &Error::Io(ref err) => err.fmt(f),
#[cfg(feature = "toml_serde")] #[cfg(feature = "toml_serde")]
&Error::Toml(ref err) => err.fmt(f), &Error::Toml(ref err) => err.fmt(f),
&Error::NotAKeyboard => f.write_str("This device file is not a keyboard"),
&Error::ShortRead => f.write_str("Error while reading from device file."), &Error::NotAKeyboard => f.write_str("This device file is not a keyboard"),
&Error::EpollAlreadyAdded => f.write_str("epoll already added, delete first"), &Error::ShortRead => f.write_str("Error while reading from device file."),
}
} &Error::EpollAlreadyAdded => f.write_str("epoll already added, delete first"),
}
}
} }
impl error::Error for Error {} impl error::Error for Error {}

View File

@ -1,7 +1,4 @@
use std::{collections::HashMap, convert::TryFrom, hash::Hash};
use std::collections::HashMap;
use std::hash::Hash;
use std::convert::TryFrom;
#[cfg(feature = "toml_serde")] #[cfg(feature = "toml_serde")]
use std::path::Path; use std::path::Path;
@ -23,17 +20,17 @@ pub enum KeyState {
} }
pub trait KeyEvent<T> pub trait KeyEvent<T>
where where
T: Into<usize>, T: Into<usize>,
{ {
fn code(&self) -> T; fn code(&self) -> T;
fn value(&self) -> KeyState; fn value(&self) -> KeyState;
} }
pub trait Keyboard<T, E, R = ()> pub trait Keyboard<T, E, R = ()>
where where
T: Into<usize> + Copy, T: Into<usize> + Copy,
E: KeyEvent<T>, E: KeyEvent<T>,
{ {
fn send(&self, event: &mut E) -> Result<R>; fn send(&self, event: &mut E) -> Result<R>;
fn send_mod_code(&self, code: T, event: &mut E) -> Result<R>; fn send_mod_code(&self, code: T, event: &mut E) -> Result<R>;
@ -44,7 +41,14 @@ pub trait Keyboard<T, E, R = ()>
fn caps_lock_code(&self) -> T; fn caps_lock_code(&self) -> T;
fn block_key(&self) -> Result<R>; fn block_key(&self) -> Result<R>;
fn send_half_inverted_key(&self, half_inverted_key: &HalfInvertedKey<T>, event: &mut E, left_shift: bool, right_shift: bool, caps_lock: bool) -> Result<R> { fn send_half_inverted_key(
&self,
half_inverted_key: &HalfInvertedKey<T>,
event: &mut E,
left_shift: bool,
right_shift: bool,
caps_lock: bool,
) -> Result<R> {
let value = event.value(); let value = event.value();
let mut invert_shift = half_inverted_key.invert_shift; let mut invert_shift = half_inverted_key.invert_shift;
if value == KeyState::DOWN { if value == KeyState::DOWN {
@ -87,19 +91,19 @@ pub trait Keyboard<T, E, R = ()>
} }
pub trait KeyMapper<K, T, E, R> pub trait KeyMapper<K, T, E, R>
where where
T: Into<usize> + Copy, T: Into<usize> + Copy,
E: KeyEvent<T>, E: KeyEvent<T>,
K: Keyboard<T, E, R>, K: Keyboard<T, E, R>,
{ {
fn send_event(&self, key_state: &[bool], event: &mut E, device: &K) -> Result<R>; fn send_event(&self, key_state: &[bool], event: &mut E, device: &K) -> Result<R>;
} }
pub struct KeyMaps<K, T, E, R = ()> pub struct KeyMaps<K, T, E, R = ()>
where where
T: Into<usize> + Copy + Clone + Eq + Hash, T: Into<usize> + Copy + Clone + Eq + Hash,
E: KeyEvent<T>, E: KeyEvent<T>,
K: Keyboard<T, E, R>, K: Keyboard<T, E, R>,
{ {
keymaps: Vec<Box<dyn KeyMapper<K, T, E, R>>>, keymaps: Vec<Box<dyn KeyMapper<K, T, E, R>>>,
keymap_index_keys: HashMap<T, usize>, keymap_index_keys: HashMap<T, usize>,
@ -113,17 +117,25 @@ pub struct KeyMaps<K, T, E, R = ()>
} }
fn parse_key<T: Clone + Copy>(key_map: &HashMap<&'static str, T>, key: &str) -> T { fn parse_key<T: Clone + Copy>(key_map: &HashMap<&'static str, T>, key: &str) -> T {
match key_map.get(key.trim_matches(|c: char| c.is_whitespace() || c == INVERT_KEY_FLAG || c == CAPS_MODIFY_KEY_FLAG)) { match key_map.get(key.trim_matches(|c: char| {
c.is_whitespace() || c == INVERT_KEY_FLAG || c == CAPS_MODIFY_KEY_FLAG
})) {
Some(key_code) => *key_code, Some(key_code) => *key_code,
None => panic!("unknown key: {}", key.trim()) None => panic!("unknown key: {}", key.trim()),
} }
} }
fn parse_keymap_numeric<T: Clone + Copy>(key_map: &HashMap<&'static str, T>, keymap: &str) -> Vec<T> { fn parse_keymap_numeric<T: Clone + Copy>(
key_map: &HashMap<&'static str, T>,
keymap: &str,
) -> Vec<T> {
keymap.split(",").map(|k| parse_key(key_map, k)).collect() keymap.split(",").map(|k| parse_key(key_map, k)).collect()
} }
fn parse_key_half_inverted<T: Clone + Copy>(key_map: &HashMap<&'static str, T>, key: &str) -> HalfInvertedKey<T> { fn parse_key_half_inverted<T: Clone + Copy>(
key_map: &HashMap<&'static str, T>,
key: &str,
) -> HalfInvertedKey<T> {
HalfInvertedKey { HalfInvertedKey {
code: parse_key(key_map, key), code: parse_key(key_map, key),
invert_shift: key.contains(INVERT_KEY_FLAG), invert_shift: key.contains(INVERT_KEY_FLAG),
@ -138,53 +150,70 @@ fn parse_keymap_u16<T: Clone + Copy>(key_map: &HashMap<&'static str, T>, keymap:
// todo: how do I return an iterator here instead of .collect to Vec? // todo: how do I return an iterator here instead of .collect to Vec?
fn parse_keymap<T: Copy>(key_map: &HashMap<&'static str, T>, keymap: &str) -> Vec<Key<T>> { fn parse_keymap<T: Copy>(key_map: &HashMap<&'static str, T>, keymap: &str) -> Vec<Key<T>> {
keymap.split(",").map(|k| { keymap
let ret: Key<T> = if k.contains(HALF_KEY_SEPARATOR) { .split(",")
let keys: Vec<&str> = k.split(HALF_KEY_SEPARATOR).collect(); .map(|k| {
if keys.len() != 2 { let ret: Key<T> = if k.contains(HALF_KEY_SEPARATOR) {
panic!("split key can only have 2 keys, 1 :, has {} keys", keys.len()); let keys: Vec<&str> = k.split(HALF_KEY_SEPARATOR).collect();
} if keys.len() != 2 {
let mut shift_half = parse_key_half_inverted(key_map, keys[1]); panic!(
shift_half.invert_shift = !shift_half.invert_shift; "split key can only have 2 keys, 1 :, has {} keys",
Key::FullKey(parse_key_half_inverted(key_map, keys[0]), shift_half) keys.len()
} else if k.contains(INVERT_KEY_FLAG) || k.contains(CAPS_MODIFY_KEY_FLAG) { );
Key::HalfKey(parse_key_half_inverted(key_map, k)) }
} else { let mut shift_half = parse_key_half_inverted(key_map, keys[1]);
Key::Direct(parse_key(key_map, k)) shift_half.invert_shift = !shift_half.invert_shift;
}; Key::FullKey(parse_key_half_inverted(key_map, keys[0]), shift_half)
ret } else if k.contains(INVERT_KEY_FLAG) || k.contains(CAPS_MODIFY_KEY_FLAG) {
}).collect() Key::HalfKey(parse_key_half_inverted(key_map, k))
} else {
Key::Direct(parse_key(key_map, k))
};
ret
})
.collect()
} }
impl<K, T, E, R> KeyMaps<K, T, E, R> impl<K, T, E, R> KeyMaps<K, T, E, R>
where where
T: Into<usize> + TryFrom<usize> + Copy + Clone + Eq + Hash + Default + 'static, T: Into<usize> + TryFrom<usize> + Copy + Clone + Eq + Hash + Default + 'static,
E: KeyEvent<T>, E: KeyEvent<T>,
K: Keyboard<T, E, R>, K: Keyboard<T, E, R>,
{ {
#[cfg(feature = "toml_serde")] #[cfg(feature = "toml_serde")]
pub fn from_cfg<P: AsRef<Path>>(key_map: &HashMap<&'static str, T>, path: P) -> KeyMaps<K, T, E, R> { pub fn from_cfg<P: AsRef<Path>>(
key_map: &HashMap<&'static str, T>,
path: P,
) -> KeyMaps<K, T, E, R> {
let key_map_config = parse_cfg(path).expect("provided config cannot be found/parsed"); let key_map_config = parse_cfg(path).expect("provided config cannot be found/parsed");
KeyMaps::new(key_map, key_map_config) KeyMaps::new(key_map, key_map_config)
} }
pub fn new(key_map: &HashMap<&'static str, T>, config: KeymapConfig) -> KeyMaps<K, T, E, R> { pub fn new(key_map: &HashMap<&'static str, T>, config: KeymapConfig) -> KeyMaps<K, T, E, R> {
if config.keymaps.len() < 2 { if config.keymaps.len() < 2 {
panic!("must have at least 2 keymaps (original and mapped) but only have {},", config.keymaps.len()); panic!(
"must have at least 2 keymaps (original and mapped) but only have {},",
config.keymaps.len()
);
} }
if config.default_keymap_index >= config.keymaps.len() || config.revert_keymap_index >= config.keymaps.len() { if config.default_keymap_index >= config.keymaps.len()
|| config.revert_keymap_index >= config.keymaps.len()
{
panic!("default_keymap_index ({}) and revert_keymap_index ({}) must be less than keymaps length ({}),", config.default_keymap_index, config.revert_keymap_index, config.keymaps.len()); panic!("default_keymap_index ({}) and revert_keymap_index ({}) must be less than keymaps length ({}),", config.default_keymap_index, config.revert_keymap_index, config.keymaps.len());
} }
let base_keymap = parse_keymap_numeric(key_map, &config.keymaps[0]); let base_keymap = parse_keymap_numeric(key_map, &config.keymaps[0]);
//println!("base_keymap : {:?}", base_keymap); //println!("base_keymap : {:?}", base_keymap);
let mut keymaps: Vec<Box<dyn KeyMapper<K, T, E, R>>> = vec!(Box::new(Key::Noop)); // todo: can we share the box? let mut keymaps: Vec<Box<dyn KeyMapper<K, T, E, R>>> = vec![Box::new(Key::Noop)]; // todo: can we share the box?
let mut keymap_index_keys: HashMap<T, usize> = HashMap::new(); let mut keymap_index_keys: HashMap<T, usize> = HashMap::new();
for (x, v) in config.keymaps.iter().enumerate() { for (x, v) in config.keymaps.iter().enumerate() {
keymap_index_keys.insert(*key_map.get(&*x.to_string()).unwrap(), x); keymap_index_keys.insert(*key_map.get(&*x.to_string()).unwrap(), x);
if x == 0 { if x == 0 {
continue; continue;
} }
if v.contains(HALF_KEY_SEPARATOR) || v.contains(INVERT_KEY_FLAG) || v.contains(CAPS_MODIFY_KEY_FLAG) { if v.contains(HALF_KEY_SEPARATOR)
|| v.contains(INVERT_KEY_FLAG)
|| v.contains(CAPS_MODIFY_KEY_FLAG)
{
// we need KeyMap, the complicated more memory taking one // we need KeyMap, the complicated more memory taking one
let v = parse_keymap(key_map, v); let v = parse_keymap(key_map, v);
let mut keymap = KeyMap::new(); let mut keymap = KeyMap::new();
@ -241,7 +270,11 @@ impl<K, T, E, R> KeyMaps<K, T, E, R>
KeyMaps { KeyMaps {
keymaps: keymaps, keymaps: keymaps,
keymap_index_keys: keymap_index_keys, keymap_index_keys: keymap_index_keys,
switch_layout_keys: config.switch_layout_keys.iter().map(|k| parse_key(key_map, k).into()).collect(), switch_layout_keys: config
.switch_layout_keys
.iter()
.map(|k| parse_key(key_map, k).into())
.collect(),
key_state: [false; KEY_MAX], key_state: [false; KEY_MAX],
// todo: detect key state? at least CAPSLOCK... // todo: detect key state? at least CAPSLOCK...
revert_default_keys, revert_default_keys,
@ -250,17 +283,18 @@ impl<K, T, E, R> KeyMaps<K, T, E, R>
current_keymap_index: config.default_keymap_index, current_keymap_index: config.default_keymap_index,
} }
} }
//} //}
//impl KeyMapper for KeyMaps { //impl KeyMapper for KeyMaps {
//impl KeyMaps { //impl KeyMaps {
pub fn send_event(&mut self, mut event: &mut E, device: &K) -> Result<R> { pub fn send_event(&mut self, mut event: &mut E, device: &K) -> Result<R> {
let value = event.value(); let value = event.value();
if value != KeyState::OTHER { if value != KeyState::OTHER {
// todo: index check here... // todo: index check here...
if event.code() == device.caps_lock_code() { if event.code() == device.caps_lock_code() {
if value == KeyState::DOWN { if value == KeyState::DOWN {
self.key_state[device.caps_lock_code().into()] = !self.key_state[device.caps_lock_code().into()]; self.key_state[device.caps_lock_code().into()] =
!self.key_state[device.caps_lock_code().into()];
} }
} else { } else {
let idx = event.code().into(); let idx = event.code().into();
@ -286,28 +320,35 @@ pub fn send_event(&mut self, mut event: &mut E, device: &K) -> Result<R> {
return device.block_key(); // we don't want to also send this keypress, so bail return device.block_key(); // we don't want to also send this keypress, so bail
} }
} }
if self.revert_default_keys.contains(&event.code()) { if self.revert_default_keys.contains(&event.code()) {
match value { match value {
KeyState::DOWN => { KeyState::DOWN => {
// todo: should we release currently held keys and then press them back down here, kinda the opposite of below? not for now... // todo: should we release currently held keys and then press them back down here, kinda the opposite of below? not for now...
self.current_keymap_index = self.revert_keymap_index self.current_keymap_index = self.revert_keymap_index
}, }
KeyState::UP => { KeyState::UP => {
self.current_keymap_index = self.chosen_keymap_index; self.current_keymap_index = self.chosen_keymap_index;
#[cfg(not(target_os = "macos"))] { #[cfg(not(target_os = "macos"))]
// need to release all currently held down keys, except this one, otherwise ctrl+c will get c stuck because code c value 1 will be sent, but then we'll let go of ctrl, and code j value 0 is sent, so c is never released {
let orig_code = event.code(); // need to release all currently held down keys, except this one, otherwise ctrl+c will get c stuck because code c value 1 will be sent, but then we'll let go of ctrl, and code j value 0 is sent, so c is never released
for (idx, key_down) in self.key_state.iter_mut().enumerate() { let orig_code = event.code();
if *key_down { for (idx, key_down) in self.key_state.iter_mut().enumerate() {
device.send_mod_code_value(T::try_from(idx).unwrap_or_else(|_| panic!("cannot convert from usize to T ????")), true, event)?; if *key_down {
*key_down = false; device.send_mod_code_value(
T::try_from(idx).unwrap_or_else(|_| {
panic!("cannot convert from usize to T ????")
}),
true,
event,
)?;
*key_down = false;
}
}
// todo: seems like we should not send this here, and instead just set the original code back, and pass it through the keymaps?
return device.send_mod_code_value(orig_code, true, event);
} }
} }
// todo: seems like we should not send this here, and instead just set the original code back, and pass it through the keymaps? _ => (), // do nothing for 2
return device.send_mod_code_value(orig_code, true, event)
}
},
_ => () // do nothing for 2
} }
} }
} }
@ -325,7 +366,7 @@ struct KeyMap<T: Into<usize> + Copy> {
impl<T: Into<usize> + Copy> KeyMap<T> { impl<T: Into<usize> + Copy> KeyMap<T> {
pub fn new() -> Self { pub fn new() -> Self {
KeyMap { KeyMap {
keymap: [Key::Noop; KEY_MAX] keymap: [Key::Noop; KEY_MAX],
} }
} }
@ -335,10 +376,10 @@ impl<T: Into<usize> + Copy> KeyMap<T> {
} }
impl<K, T, E, R> KeyMapper<K, T, E, R> for KeyMap<T> impl<K, T, E, R> KeyMapper<K, T, E, R> for KeyMap<T>
where where
T: Into<usize> + Copy, T: Into<usize> + Copy,
E: KeyEvent<T>, E: KeyEvent<T>,
K: Keyboard<T, E, R>, K: Keyboard<T, E, R>,
{ {
fn send_event(&self, key_state: &[bool], event: &mut E, device: &K) -> Result<R> { fn send_event(&self, key_state: &[bool], event: &mut E, device: &K) -> Result<R> {
self.keymap[event.code().into()].send_event(key_state, event, device) self.keymap[event.code().into()].send_event(key_state, event, device)
@ -356,9 +397,7 @@ impl<T: Into<usize> + TryFrom<usize> + Copy + Default> CodeKeyMap<T> {
*v = T::try_from(x).unwrap_or_else(|_| panic!("cannot convert from usize to T ????")); *v = T::try_from(x).unwrap_or_else(|_| panic!("cannot convert from usize to T ????"));
} }
//println!("keymap: {:?}", &keymap[..]); //println!("keymap: {:?}", &keymap[..]);
CodeKeyMap { CodeKeyMap { keymap }
keymap
}
} }
pub fn map(&mut self, from: T, to: T) { pub fn map(&mut self, from: T, to: T) {
@ -367,10 +406,10 @@ impl<T: Into<usize> + TryFrom<usize> + Copy + Default> CodeKeyMap<T> {
} }
impl<K, T, E, R> KeyMapper<K, T, E, R> for CodeKeyMap<T> impl<K, T, E, R> KeyMapper<K, T, E, R> for CodeKeyMap<T>
where where
T: Into<usize> + TryFrom<usize> + Copy + Default, T: Into<usize> + TryFrom<usize> + Copy + Default,
E: KeyEvent<T>, E: KeyEvent<T>,
K: Keyboard<T, E, R>, K: Keyboard<T, E, R>,
{ {
fn send_event(&self, _key_state: &[bool], event: &mut E, device: &K) -> Result<R> { fn send_event(&self, _key_state: &[bool], event: &mut E, device: &K) -> Result<R> {
device.send_mod_code(self.keymap[event.code().into()], event) device.send_mod_code(self.keymap[event.code().into()], event)
@ -390,10 +429,10 @@ pub struct HalfInvertedKey<T: Clone + Copy> {
} }
impl<K, T, E, R> KeyMapper<K, T, E, R> for HalfInvertedKey<T> impl<K, T, E, R> KeyMapper<K, T, E, R> for HalfInvertedKey<T>
where where
T: Into<usize> + Clone + Copy, T: Into<usize> + Clone + Copy,
E: KeyEvent<T>, E: KeyEvent<T>,
K: Keyboard<T, E, R>, K: Keyboard<T, E, R>,
{ {
fn send_event(&self, key_state: &[bool], event: &mut E, device: &K) -> Result<R> { fn send_event(&self, key_state: &[bool], event: &mut E, device: &K) -> Result<R> {
let left_shift = key_state[device.left_shift_code().into()]; let left_shift = key_state[device.left_shift_code().into()];
@ -405,8 +444,8 @@ impl<K, T, E, R> KeyMapper<K, T, E, R> for HalfInvertedKey<T>
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
enum Key<T> enum Key<T>
where where
T: Copy + Clone T: Copy + Clone,
{ {
Noop, Noop,
Direct(T), Direct(T),
@ -415,32 +454,38 @@ enum Key<T>
} }
impl<K, T, E, R> KeyMapper<K, T, E, R> for Key<T> impl<K, T, E, R> KeyMapper<K, T, E, R> for Key<T>
where where
T: Into<usize> + Copy, T: Into<usize> + Copy,
E: KeyEvent<T>, E: KeyEvent<T>,
K: Keyboard<T, E, R>, K: Keyboard<T, E, R>,
{ {
fn send_event(&self, key_state: &[bool], event: &mut E, device: &K) -> Result<R> { fn send_event(&self, key_state: &[bool], event: &mut E, device: &K) -> Result<R> {
match *self { match *self {
Key::Noop => { Key::Noop => device.send(event),
device.send(event) Key::Direct(code) => device.send_mod_code(code, event),
}, Key::HalfKey(ref key_half) => key_half.send_event(key_state, event, device),
Key::Direct(code) => {
device.send_mod_code(code, event)
},
Key::HalfKey(ref key_half) => {
key_half.send_event(key_state, event, device)
},
Key::FullKey(ref noshift_half, ref shift_half) => { Key::FullKey(ref noshift_half, ref shift_half) => {
let left_shift = key_state[device.left_shift_code().into()]; let left_shift = key_state[device.left_shift_code().into()];
let right_shift = key_state[device.right_shift_code().into()]; let right_shift = key_state[device.right_shift_code().into()];
let caps_lock = key_state[device.caps_lock_code().into()]; let caps_lock = key_state[device.caps_lock_code().into()];
if caps_lock != (left_shift || right_shift) { if caps_lock != (left_shift || right_shift) {
device.send_half_inverted_key(shift_half, event, left_shift, right_shift, caps_lock) device.send_half_inverted_key(
shift_half,
event,
left_shift,
right_shift,
caps_lock,
)
} else { } else {
device.send_half_inverted_key(noshift_half, event, left_shift, right_shift, caps_lock) device.send_half_inverted_key(
noshift_half,
event,
left_shift,
right_shift,
caps_lock,
)
} }
}, }
} }
} }
} }
@ -453,7 +498,7 @@ pub struct KeymapConfig {
revert_default_keys: Option<Vec<String>>, revert_default_keys: Option<Vec<String>>,
revert_keymap_index: usize, revert_keymap_index: usize,
default_keymap_index: usize, default_keymap_index: usize,
keymaps: Vec<String> keymaps: Vec<String>,
} }
#[cfg(feature = "toml_serde")] #[cfg(feature = "toml_serde")]
@ -473,7 +518,7 @@ pub struct KeymapConfig {
revert_default_keys: Option<Vec<&'static str>>, revert_default_keys: Option<Vec<&'static str>>,
revert_keymap_index: usize, revert_keymap_index: usize,
default_keymap_index: usize, default_keymap_index: usize,
keymaps: Vec<&'static str> keymaps: Vec<&'static str>,
} }
#[cfg(not(feature = "toml_serde"))] #[cfg(not(feature = "toml_serde"))]
@ -528,4 +573,3 @@ impl Default for KeymapConfig {
} }
} }
} }

View File

@ -1,12 +1,7 @@
use std::path::Path; use crate::{Device, Result};
use std::{mem, slice};
use std::ffi::CString;
use libc::c_int; use libc::c_int;
use nix::{self, fcntl, unistd, ioctl_write_ptr, ioctl_none}; use nix::{self, fcntl, ioctl_none, ioctl_write_ptr, sys::stat, unistd};
use nix::sys::stat; use std::{collections::hash_map::Values, ffi::CString, mem, os::raw::c_char, path::Path, slice};
use crate::{Result, Device};
use std::collections::hash_map::Values;
use std::os::raw::c_char;
use crate::linux::device::codes::*; use crate::linux::device::codes::*;
@ -21,131 +16,134 @@ pub const ABS_CNT: c_int = ABS_MAX + 1;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[repr(C)] #[repr(C)]
pub struct input_id { pub struct input_id {
pub bustype: u16, pub bustype: u16,
pub vendor: u16, pub vendor: u16,
pub product: u16, pub product: u16,
pub version: u16, pub version: u16,
} }
#[repr(C)] #[repr(C)]
pub struct uinput_user_dev { pub struct uinput_user_dev {
pub name: [c_char; UINPUT_MAX_NAME_SIZE as usize], pub name: [c_char; UINPUT_MAX_NAME_SIZE as usize],
pub id: input_id, pub id: input_id,
pub ff_effects_max: u32, pub ff_effects_max: u32,
pub absmax: [i32; ABS_CNT as usize], pub absmax: [i32; ABS_CNT as usize],
pub absmin: [i32; ABS_CNT as usize], pub absmin: [i32; ABS_CNT as usize],
pub absfuzz: [i32; ABS_CNT as usize], pub absfuzz: [i32; ABS_CNT as usize],
pub absflat: [i32; ABS_CNT as usize], pub absflat: [i32; ABS_CNT as usize],
} }
/// Device builder. /// Device builder.
pub struct Builder { pub struct Builder {
fd: c_int, fd: c_int,
def: uinput_user_dev, def: uinput_user_dev,
abs: Option<c_int>, abs: Option<c_int>,
} }
impl Builder { impl Builder {
/// Create a builder from the specified path. /// Create a builder from the specified path.
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> { pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
Ok(Builder { Ok(Builder {
fd: fcntl::open(path.as_ref(), fcntl::OFlag::O_WRONLY | fcntl::OFlag::O_NONBLOCK, stat::Mode::empty())?, fd: fcntl::open(
def: unsafe { mem::zeroed() }, path.as_ref(),
abs: None, fcntl::OFlag::O_WRONLY | fcntl::OFlag::O_NONBLOCK,
}) stat::Mode::empty(),
} )?,
def: unsafe { mem::zeroed() },
abs: None,
})
}
/// Create a builder from `/dev/uinput`. /// Create a builder from `/dev/uinput`.
pub fn default() -> Result<Self> { pub fn default() -> Result<Self> {
Builder::open("/dev/uinput") Builder::open("/dev/uinput")
} }
/// Set the name. /// Set the name.
pub fn name<T: AsRef<str>>(mut self, value: T) -> Result<Self> { pub fn name<T: AsRef<str>>(mut self, value: T) -> Result<Self> {
let string = CString::new(value.as_ref())?; let string = CString::new(value.as_ref())?;
let bytes = string.as_bytes_with_nul(); let bytes = string.as_bytes_with_nul();
if bytes.len() > UINPUT_MAX_NAME_SIZE as usize { if bytes.len() > UINPUT_MAX_NAME_SIZE as usize {
Err(nix::Error::EINVAL)?; Err(nix::Error::EINVAL)?;
} }
(&mut self.def.name)[..bytes.len()] (&mut self.def.name)[..bytes.len()].clone_from_slice(unsafe { mem::transmute(bytes) });
.clone_from_slice(unsafe { mem::transmute(bytes) });
Ok(self) Ok(self)
} }
/// Set the bus type. /// Set the bus type.
pub fn bus(mut self, value: u16) -> Self { pub fn bus(mut self, value: u16) -> Self {
self.def.id.bustype = value; self.def.id.bustype = value;
self self
} }
/// Set the vendor ID. /// Set the vendor ID.
pub fn vendor(mut self, value: u16) -> Self { pub fn vendor(mut self, value: u16) -> Self {
self.def.id.vendor = value; self.def.id.vendor = value;
self self
} }
/// Set the product ID. /// Set the product ID.
pub fn product(mut self, value: u16) -> Self { pub fn product(mut self, value: u16) -> Self {
self.def.id.product = value; self.def.id.product = value;
self self
} }
/// Set the version. /// Set the version.
pub fn version(mut self, value: u16) -> Self { pub fn version(mut self, value: u16) -> Self {
self.def.id.version = value; self.def.id.version = value;
self self
} }
pub fn event(mut self, key_codes: Values<&str, u16>) -> Result<Self> { pub fn event(mut self, key_codes: Values<&str, u16>) -> Result<Self> {
self.abs = None; self.abs = None;
unsafe { unsafe {
ui_set_evbit(self.fd, EV_KEY as *const c_int)?; ui_set_evbit(self.fd, EV_KEY as *const c_int)?;
for key_code in key_codes { for key_code in key_codes {
ui_set_keybit(self.fd, *key_code as *const c_int)?; ui_set_keybit(self.fd, *key_code as *const c_int)?;
} }
} }
Ok(self) Ok(self)
} }
/// Set the maximum value for the previously enabled absolute event. /// Set the maximum value for the previously enabled absolute event.
pub fn max(mut self, value: i32) -> Self { pub fn max(mut self, value: i32) -> Self {
self.def.absmax[self.abs.unwrap() as usize] = value; self.def.absmax[self.abs.unwrap() as usize] = value;
self self
} }
/// Set the minimum value for the previously enabled absolute event. /// Set the minimum value for the previously enabled absolute event.
pub fn min(mut self, value: i32) -> Self { pub fn min(mut self, value: i32) -> Self {
self.def.absmin[self.abs.unwrap() as usize] = value; self.def.absmin[self.abs.unwrap() as usize] = value;
self self
} }
/// Set the fuzz value for the previously enabled absolute event. /// Set the fuzz value for the previously enabled absolute event.
pub fn fuzz(mut self, value: i32) -> Self { pub fn fuzz(mut self, value: i32) -> Self {
self.def.absfuzz[self.abs.unwrap() as usize] = value; self.def.absfuzz[self.abs.unwrap() as usize] = value;
self self
} }
/// Set the flat value for the previously enabled absolute event. /// Set the flat value for the previously enabled absolute event.
pub fn flat(mut self, value: i32) -> Self { pub fn flat(mut self, value: i32) -> Self {
self.def.absflat[self.abs.unwrap() as usize] = value; self.def.absflat[self.abs.unwrap() as usize] = value;
self self
} }
/// Create the defined device. /// Create the defined device.
pub fn create(self) -> Result<Device> { pub fn create(self) -> Result<Device> {
unsafe { unsafe {
let ptr = &self.def as *const _ as *const u8; let ptr = &self.def as *const _ as *const u8;
let size = mem::size_of_val(&self.def); let size = mem::size_of_val(&self.def);
unistd::write(self.fd, slice::from_raw_parts(ptr, size))?; unistd::write(self.fd, slice::from_raw_parts(ptr, size))?;
ui_dev_create(self.fd)?; ui_dev_create(self.fd)?;
} }
Ok(Device::new(self.fd)) Ok(Device::new(self.fd))
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
use std::{mem, ptr, slice};
use libc::{timeval, gettimeofday, input_event, c_int};
use nix::{unistd, ioctl_none};
use crate::Result; use crate::Result;
use libc::{c_int, gettimeofday, input_event, timeval};
use nix::{ioctl_none, unistd};
use std::{mem, ptr, slice};
use crate::linux::device::codes::*; use crate::linux::device::codes::*;
@ -9,80 +9,81 @@ ioctl_none!(ui_dev_destroy, b'U', 2);
/// The virtual device. /// The virtual device.
pub struct Device { pub struct Device {
fd: c_int, fd: c_int,
} }
impl Device { impl Device {
/// Wrap a file descriptor in a `Device`. /// Wrap a file descriptor in a `Device`.
pub fn new(fd: c_int) -> Self { pub fn new(fd: c_int) -> Self {
Device { Device { fd: fd }
fd: fd }
}
}
#[doc(hidden)] #[doc(hidden)]
pub fn write(&self, kind: c_int, code: c_int, value: c_int) -> Result<()> { pub fn write(&self, kind: c_int, code: c_int, value: c_int) -> Result<()> {
let mut event = input_event { let mut event = input_event {
time: timeval { tv_sec: 0, tv_usec: 0 }, time: timeval {
type_: kind as u16, tv_sec: 0,
code: code as u16, tv_usec: 0,
value: value as i32, },
}; type_: kind as u16,
code: code as u16,
value: value as i32,
};
self.write_event(&mut event) self.write_event(&mut event)
} }
#[doc(hidden)] #[doc(hidden)]
pub fn write_event(&self, event: &mut input_event) -> Result<()> { pub fn write_event(&self, event: &mut input_event) -> Result<()> {
unsafe { unsafe {
gettimeofday(&mut event.time, ptr::null_mut()); gettimeofday(&mut event.time, ptr::null_mut());
let ptr = event as *const _ as *const u8; let ptr = event as *const _ as *const u8;
let size = mem::size_of_val(event); let size = mem::size_of_val(event);
unistd::write(self.fd, slice::from_raw_parts(ptr, size))?; unistd::write(self.fd, slice::from_raw_parts(ptr, size))?;
} }
Ok(()) Ok(())
} }
/// Synchronize the device. /// Synchronize the device.
pub fn synchronize(&self) -> Result<()> { pub fn synchronize(&self) -> Result<()> {
self.write(EV_SYN, SYN_REPORT, 0) self.write(EV_SYN, SYN_REPORT, 0)
} }
/// Send an event. /// Send an event.
pub fn send(&self, kind: c_int, code: c_int, value: i32) -> Result<()> { pub fn send(&self, kind: c_int, code: c_int, value: i32) -> Result<()> {
self.write(kind, code, value) self.write(kind, code, value)
} }
/// Send a press event. /// Send a press event.
pub fn press(&self, kind: c_int, code: c_int) -> Result<()> { pub fn press(&self, kind: c_int, code: c_int) -> Result<()> {
self.write(kind, code, 1) self.write(kind, code, 1)
} }
/// Send a release event. /// Send a release event.
pub fn release(&self, kind: c_int, code: c_int) -> Result<()> { pub fn release(&self, kind: c_int, code: c_int) -> Result<()> {
self.write(kind, code, 0) self.write(kind, code, 0)
} }
/// Send a press and release event. /// Send a press and release event.
pub fn click(&self, kind: c_int, code: c_int) -> Result<()> { pub fn click(&self, kind: c_int, code: c_int) -> Result<()> {
self.press(kind, code)?; self.press(kind, code)?;
self.release(kind, code) self.release(kind, code)
} }
/// Send a relative or absolute positioning event. /// Send a relative or absolute positioning event.
pub fn position(&self, kind: c_int, code: c_int, value: i32) -> Result<()> { pub fn position(&self, kind: c_int, code: c_int, value: i32) -> Result<()> {
self.write(kind, code, value) self.write(kind, code, value)
} }
} }
impl Drop for Device { impl Drop for Device {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
// ignore error here so as to not panic in a drop // ignore error here so as to not panic in a drop
ui_dev_destroy(self.fd).ok(); ui_dev_destroy(self.fd).ok();
} }
} }
} }

View File

@ -1,15 +1,14 @@
use std::mem; use libc::{c_int, input_event};
use std::fs::File; use nix::{ioctl_read_buf, ioctl_write_ptr};
use std::io::Read; use std::{fs::File, io::Read, mem, os::unix::io::AsRawFd};
use std::os::unix::io::AsRawFd;
use libc::{input_event, c_int};
use nix::{ioctl_write_ptr, ioctl_read_buf};
#[cfg(feature = "epoll_inotify")] #[cfg(feature = "epoll_inotify")]
use std::os::unix::prelude::RawFd; use std::os::unix::prelude::RawFd;
use crate::{Error,Result}; use crate::{
use crate::linux::{EV_KEY, KEY_MAX, NAME, KEY_W, KEY_A, KEY_S, KEY_D, BTN_LEFT}; linux::{BTN_LEFT, EV_KEY, KEY_A, KEY_D, KEY_MAX, KEY_S, KEY_W, NAME},
Error, Result,
};
ioctl_write_ptr!(eviocgrab, b'E', 0x90, c_int); ioctl_write_ptr!(eviocgrab, b'E', 0x90, c_int);
ioctl_read_buf!(eviocgname, b'E', 0x06, u8); ioctl_read_buf!(eviocgname, b'E', 0x06, u8);
@ -34,7 +33,7 @@ impl InputDevice {
epoll_fd: None, epoll_fd: None,
}) })
} }
pub fn new_input_event_buf() -> [u8; SIZE_OF_INPUT_EVENT] { pub fn new_input_event_buf() -> [u8; SIZE_OF_INPUT_EVENT] {
[0u8; SIZE_OF_INPUT_EVENT] [0u8; SIZE_OF_INPUT_EVENT]
} }
@ -55,7 +54,7 @@ impl InputDevice {
if !self.device_file.metadata()?.file_type().is_char_device() { if !self.device_file.metadata()?.file_type().is_char_device() {
return Err(Error::NotAKeyboard); return Err(Error::NotAKeyboard);
} }
let raw_fd = self.device_file.as_raw_fd(); let raw_fd = self.device_file.as_raw_fd();
// does it support EV_KEY // does it support EV_KEY
@ -73,16 +72,19 @@ impl InputDevice {
unsafe { unsafe {
eviocgbit_ev_key(raw_fd, &mut key_bits)?; eviocgbit_ev_key(raw_fd, &mut key_bits)?;
}; };
let key_unsupported = |key : c_int| (key_bits[key as usize / 8] & (1 << (key % 8))) == 0; let key_unsupported = |key: c_int| (key_bits[key as usize / 8] & (1 << (key % 8))) == 0;
if key_unsupported(KEY_W) || key_unsupported(KEY_A) || key_unsupported(KEY_S) || key_unsupported(KEY_D) || !key_unsupported(BTN_LEFT) { if key_unsupported(KEY_W)
|| key_unsupported(KEY_A)
|| key_unsupported(KEY_S)
|| key_unsupported(KEY_D)
|| !key_unsupported(BTN_LEFT)
{
return Err(Error::NotAKeyboard); return Err(Error::NotAKeyboard);
} }
// is it another running copy of rusty-keys ? // is it another running copy of rusty-keys ?
let mut name = [0u8; NAME.len()]; let mut name = [0u8; NAME.len()];
unsafe { unsafe { eviocgname(raw_fd, &mut name)? };
eviocgname(raw_fd, &mut name)?
};
// exclude anything starting with "Yubico" also // exclude anything starting with "Yubico" also
if NAME.as_bytes() == &name || "Yubico".as_bytes() == &name[0..6] { if NAME.as_bytes() == &name || "Yubico".as_bytes() == &name[0..6] {
return Err(Error::NotAKeyboard); return Err(Error::NotAKeyboard);
@ -107,10 +109,10 @@ impl InputDevice {
} }
Ok(()) Ok(())
} }
#[cfg(feature = "epoll_inotify")] #[cfg(feature = "epoll_inotify")]
pub fn epoll_add(mut self, epoll_fd: RawFd, data: u64) -> Result<Self> { pub fn epoll_add(mut self, epoll_fd: RawFd, data: u64) -> Result<Self> {
use nix::fcntl::{OFlag, fcntl, FcntlArg}; use nix::fcntl::{fcntl, FcntlArg, OFlag};
if None != self.epoll_fd { if None != self.epoll_fd {
return Err(Error::EpollAlreadyAdded); return Err(Error::EpollAlreadyAdded);
@ -123,7 +125,12 @@ impl InputDevice {
fcntl(raw_fd, FcntlArg::F_SETFL(flags | OFlag::O_NONBLOCK))?; fcntl(raw_fd, FcntlArg::F_SETFL(flags | OFlag::O_NONBLOCK))?;
let epoll_event = epoll::Event::new(epoll::Events::EPOLLIN | epoll::Events::EPOLLET, data); let epoll_event = epoll::Event::new(epoll::Events::EPOLLIN | epoll::Events::EPOLLET, data);
epoll::ctl(epoll_fd, epoll::ControlOptions::EPOLL_CTL_ADD, raw_fd, epoll_event)?; epoll::ctl(
epoll_fd,
epoll::ControlOptions::EPOLL_CTL_ADD,
raw_fd,
epoll_event,
)?;
self.epoll_fd = Some(epoll_fd); self.epoll_fd = Some(epoll_fd);
Ok(self) Ok(self)
} }
@ -134,7 +141,12 @@ impl InputDevice {
// set this to None first, if we end up returning an Err early, we can't do anything else anyway... // set this to None first, if we end up returning an Err early, we can't do anything else anyway...
self.epoll_fd = None; self.epoll_fd = None;
let empty_event = epoll::Event::new(epoll::Events::empty(), 0); let empty_event = epoll::Event::new(epoll::Events::empty(), 0);
epoll::ctl(epoll_fd, epoll::ControlOptions::EPOLL_CTL_DEL, self.device_file.as_raw_fd(), empty_event)?; epoll::ctl(
epoll_fd,
epoll::ControlOptions::EPOLL_CTL_DEL,
self.device_file.as_raw_fd(),
empty_event,
)?;
} }
Ok(self) Ok(self)
} }

View File

@ -1,15 +1,12 @@
use crate::*; use crate::*;
use crate::linux::device::codes::*; use crate::linux::device::codes::*;
pub mod device; pub mod device;
pub use device::{Device,InputDevice, Builder}; pub use device::{Builder, Device, InputDevice};
use libc::input_event; use libc::input_event;
use std::process::exit; use std::{collections::HashMap, env, process::exit};
use std::env;
use std::collections::HashMap;
#[cfg(feature = "epoll_inotify")] #[cfg(feature = "epoll_inotify")]
const INPUT_FOLDER: &str = "/dev/input/"; const INPUT_FOLDER: &str = "/dev/input/";
@ -48,7 +45,12 @@ impl Keyboard<u16, input_event> for Device {
Keyboard::send(self, event) Keyboard::send(self, event)
} }
fn send_mod_code_value(&self, code: u16, up_not_down: bool, event: &mut input_event) -> Result<()> { fn send_mod_code_value(
&self,
code: u16,
up_not_down: bool,
event: &mut input_event,
) -> Result<()> {
event.code = code; event.code = code;
let value = event.value; let value = event.value;
if up_not_down { if up_not_down {
@ -86,12 +88,15 @@ impl Keyboard<u16, input_event> for Device {
#[derive(Debug)] #[derive(Debug)]
struct Config { struct Config {
device_files: Vec<String>, device_files: Vec<String>,
config_file: String config_file: String,
} }
impl Config { impl Config {
fn new(device_files: Vec<String>, config_file: String) -> Self { fn new(device_files: Vec<String>, config_file: String) -> Self {
Config { device_files: device_files, config_file: config_file } Config {
device_files: device_files,
config_file: config_file,
}
} }
} }
@ -130,91 +135,120 @@ pub fn main_res() -> Result<()> {
panic!("without epoll_inotify feature, only exactly 1 device is supported"); panic!("without epoll_inotify feature, only exactly 1 device is supported");
#[cfg(feature = "epoll_inotify")] #[cfg(feature = "epoll_inotify")]
{ {
use inotify::{Inotify, WatchMask}; use inotify::{Inotify, WatchMask};
let epoll_fd = epoll::create(true)?; let epoll_fd = epoll::create(true)?;
const INOTIFY_DATA: u64 = u64::max_value(); const INOTIFY_DATA: u64 = u64::max_value();
let (device_files, mut inotify) = if config.device_files.len() > 0 { let (device_files, mut inotify) = if config.device_files.len() > 0 {
// we operate on exactly the devices sent in and never watch for new devices // we operate on exactly the devices sent in and never watch for new devices
(config.device_files.iter().map(|device_file| InputDevice::open(&device_file).expect("device_file does not exist!")).collect(), None) (
} else { config
use std::os::unix::io::AsRawFd; .device_files
// we want to wait forever starting new threads for any new keyboard devices .iter()
// there is a slight race condition here, if a keyboard is plugged in between the time we .map(|device_file| {
// enumerate the devices and set up the inotify watch, we'll miss it, doing it the other way InputDevice::open(&device_file).expect("device_file does not exist!")
// can bring duplicates though, todo: think about this... })
let device_files = get_keyboard_devices(); .collect(),
let mut inotify = Inotify::init()?; None,
inotify.add_watch(INPUT_FOLDER, WatchMask::CREATE)?; )
let epoll_event = epoll::Event::new(epoll::Events::EPOLLIN | epoll::Events::EPOLLET, INOTIFY_DATA); } else {
epoll::ctl(epoll_fd, epoll::ControlOptions::EPOLL_CTL_ADD, inotify.as_raw_fd(), epoll_event)?; use std::os::unix::io::AsRawFd;
(device_files, Some(inotify)) // we want to wait forever starting new threads for any new keyboard devices
}; // there is a slight race condition here, if a keyboard is plugged in between the time we
let mut input_devices = Vec::with_capacity(device_files.len()); // enumerate the devices and set up the inotify watch, we'll miss it, doing it the other way
for (idx, device_file) in device_files.into_iter().enumerate() { // can bring duplicates though, todo: think about this...
input_devices.push(Some(device_file.grab()?.epoll_add(epoll_fd, idx as u64)?)); let device_files = get_keyboard_devices();
} let mut inotify = Inotify::init()?;
inotify.add_watch(INPUT_FOLDER, WatchMask::CREATE)?;
let epoll_event = epoll::Event::new(
epoll::Events::EPOLLIN | epoll::Events::EPOLLET,
INOTIFY_DATA,
);
epoll::ctl(
epoll_fd,
epoll::ControlOptions::EPOLL_CTL_ADD,
inotify.as_raw_fd(),
epoll_event,
)?;
(device_files, Some(inotify))
};
let mut input_devices = Vec::with_capacity(device_files.len());
for (idx, device_file) in device_files.into_iter().enumerate() {
input_devices.push(Some(device_file.grab()?.epoll_add(epoll_fd, idx as u64)?));
}
let mut epoll_buf = [epoll::Event::new(epoll::Events::empty(), 0); 4]; let mut epoll_buf = [epoll::Event::new(epoll::Events::empty(), 0); 4];
let mut inotify_buf = [0u8; 256]; let mut inotify_buf = [0u8; 256];
loop { loop {
let num_events = epoll::wait(epoll_fd, -1, &mut epoll_buf)?; let num_events = epoll::wait(epoll_fd, -1, &mut epoll_buf)?;
for event in &epoll_buf[0..num_events] { for event in &epoll_buf[0..num_events] {
let idx = event.data as usize; let idx = event.data as usize;
if let Some(Some(input_device)) = &mut input_devices.get_mut(idx) { if let Some(Some(input_device)) = &mut input_devices.get_mut(idx) {
loop { loop {
match input_device.read_event(&mut input_event_buf) { match input_device.read_event(&mut input_event_buf) {
Ok(event) => { Ok(event) => {
//println!("input event: {:?}", event); //println!("input event: {:?}", event);
send_event(&mut key_map, event, &device)? send_event(&mut key_map, event, &device)?
} }
Err(err) => { Err(err) => {
if let Error::Io(ref err) = err { if let Error::Io(ref err) = err {
if err.kind() == std::io::ErrorKind::WouldBlock { if err.kind() == std::io::ErrorKind::WouldBlock {
// go back to epoll event loop // go back to epoll event loop
break; break;
}
} }
// otherwise it's some other error, don't read anything from this again
println!("input err: {:?}", err);
// remove it from input_devices and drop it
let _ = std::mem::replace(&mut input_devices[idx], None);
if inotify.is_none() {
// if we aren't watching with inotify, and the last device is removed (Vec only has None's in it), exit the program
if input_devices.iter().all(|id| id.is_none()) {
println!("last device went away, exiting...");
return Ok(());
}
}
break;
} }
// otherwise it's some other error, don't read anything from this again
println!("input err: {:?}", err);
// remove it from input_devices and drop it
let _ = std::mem::replace(&mut input_devices[idx], None);
if inotify.is_none() {
// if we aren't watching with inotify, and the last device is removed (Vec only has None's in it), exit the program
if input_devices.iter().all(|id| id.is_none()) {
println!("last device went away, exiting...");
return Ok(());
}
}
break;
} }
} }
} else if event.data == INOTIFY_DATA { }
if let Some(inotify) = &mut inotify { } else if event.data == INOTIFY_DATA {
for event in inotify.read_events(&mut inotify_buf)? { if let Some(inotify) = &mut inotify {
if let Some(device_file) = event.name.and_then(|name| name.to_str()) { for event in inotify.read_events(&mut inotify_buf)? {
// check if this is an eligible keyboard device if let Some(device_file) = event.name.and_then(|name| name.to_str())
let mut path = std::path::PathBuf::new(); {
path.push(INPUT_FOLDER); // check if this is an eligible keyboard device
path.push(device_file); let mut path = std::path::PathBuf::new();
path.push(INPUT_FOLDER);
path.push(device_file);
if let Ok(input_device) = InputDevice::open(path).and_then(|id| id.valid_keyboard_device()) { if let Ok(input_device) = InputDevice::open(path)
println!("starting mapping for new keyboard: {}", device_file); .and_then(|id| id.valid_keyboard_device())
{
println!(
"starting mapping for new keyboard: {}",
device_file
);
let idx = input_devices.iter().position(|id| id.is_none()).unwrap_or(input_devices.len()); let idx = input_devices
.iter()
.position(|id| id.is_none())
.unwrap_or(input_devices.len());
let input_device = input_device.grab()?.epoll_add(epoll_fd, idx as u64)?; let input_device =
input_device.grab()?.epoll_add(epoll_fd, idx as u64)?;
if idx == input_devices.len() { if idx == input_devices.len() {
input_devices.push(Some(input_device)); input_devices.push(Some(input_device));
} else { } else {
// simply replacing None here // simply replacing None here
let _ = std::mem::replace(&mut input_devices[idx], Some(input_device)); let _ = std::mem::replace(
} &mut input_devices[idx],
Some(input_device),
);
} }
} }
} }
@ -223,6 +257,7 @@ pub fn main_res() -> Result<()> {
} }
} }
} }
}
} }
} }
@ -247,7 +282,12 @@ fn parse_args() -> Config {
let mut opts = Options::new(); let mut opts = Options::new();
opts.optflag("h", "help", "prints this help message"); opts.optflag("h", "help", "prints this help message");
opts.optflag("v", "version", "prints the version"); opts.optflag("v", "version", "prints the version");
opts.optopt("c", "config", "specify the keymap config file to use (default: /etc/rusty-keys/keymap.toml)", "FILE"); opts.optopt(
"c",
"config",
"specify the keymap config file to use (default: /etc/rusty-keys/keymap.toml)",
"FILE",
);
let matches = opts.parse(&args[1..]); let matches = opts.parse(&args[1..]);
if matches.is_err() { if matches.is_err() {
@ -265,7 +305,9 @@ fn parse_args() -> Config {
exit(0); exit(0);
} }
let config_file = matches.opt_str("c").unwrap_or("/etc/rusty-keys/keymap.toml".to_owned()); let config_file = matches
.opt_str("c")
.unwrap_or("/etc/rusty-keys/keymap.toml".to_owned());
Config::new(matches.free, config_file) Config::new(matches.free, config_file)
} }
@ -276,7 +318,9 @@ fn get_keyboard_devices() -> Vec<InputDevice> {
if let Ok(entries) = std::fs::read_dir(INPUT_FOLDER) { if let Ok(entries) = std::fs::read_dir(INPUT_FOLDER) {
for entry in entries { for entry in entries {
if let Ok(entry) = entry { if let Ok(entry) = entry {
if let Ok(input_device) = InputDevice::open(entry.path()).and_then(|id|id.valid_keyboard_device()) { if let Ok(input_device) =
InputDevice::open(entry.path()).and_then(|id| id.valid_keyboard_device())
{
res.push(input_device); res.push(input_device);
} }
} }
@ -286,314 +330,317 @@ fn get_keyboard_devices() -> Vec<InputDevice> {
} }
pub fn key_map() -> HashMap<&'static str, u16> { pub fn key_map() -> HashMap<&'static str, u16> {
[ [
// generated like: // generated like:
// grep -o 'KEY_[^ :;]*' ~/.cargo/registry/src/github.com-1ecc6299db9ec823/uinput-sys-0.1.3/src/codes | sed 's/^KEY_//' | awk '{print "(\""$1"\", KEY_"$1"),"}' // grep -o 'KEY_[^ :;]*' ~/.cargo/registry/src/github.com-1ecc6299db9ec823/uinput-sys-0.1.3/src/codes | sed 's/^KEY_//' | awk '{print "(\""$1"\", KEY_"$1"),"}'
("RESERVED", KEY_RESERVED), ("RESERVED", KEY_RESERVED),
("ESC", KEY_ESC), ("ESC", KEY_ESC),
("1", KEY_1), ("1", KEY_1),
("2", KEY_2), ("2", KEY_2),
("3", KEY_3), ("3", KEY_3),
("4", KEY_4), ("4", KEY_4),
("5", KEY_5), ("5", KEY_5),
("6", KEY_6), ("6", KEY_6),
("7", KEY_7), ("7", KEY_7),
("8", KEY_8), ("8", KEY_8),
("9", KEY_9), ("9", KEY_9),
("10", KEY_10), ("10", KEY_10),
("MINUS", KEY_MINUS), ("MINUS", KEY_MINUS),
("EQUAL", KEY_EQUAL), ("EQUAL", KEY_EQUAL),
("BACKSPACE", KEY_BACKSPACE), ("BACKSPACE", KEY_BACKSPACE),
("TAB", KEY_TAB), ("TAB", KEY_TAB),
("Q", KEY_Q), ("Q", KEY_Q),
("W", KEY_W), ("W", KEY_W),
("E", KEY_E), ("E", KEY_E),
("R", KEY_R), ("R", KEY_R),
("T", KEY_T), ("T", KEY_T),
("Y", KEY_Y), ("Y", KEY_Y),
("U", KEY_U), ("U", KEY_U),
("I", KEY_I), ("I", KEY_I),
("O", KEY_O), ("O", KEY_O),
("P", KEY_P), ("P", KEY_P),
("LEFTBRACE", KEY_LEFTBRACE), ("LEFTBRACE", KEY_LEFTBRACE),
("RIGHTBRACE", KEY_RIGHTBRACE), ("RIGHTBRACE", KEY_RIGHTBRACE),
("ENTER", KEY_ENTER), ("ENTER", KEY_ENTER),
("LEFTCTRL", KEY_LEFTCTRL), ("LEFTCTRL", KEY_LEFTCTRL),
("A", KEY_A), ("A", KEY_A),
("S", KEY_S), ("S", KEY_S),
("D", KEY_D), ("D", KEY_D),
("F", KEY_F), ("F", KEY_F),
("G", KEY_G), ("G", KEY_G),
("H", KEY_H), ("H", KEY_H),
("J", KEY_J), ("J", KEY_J),
("K", KEY_K), ("K", KEY_K),
("L", KEY_L), ("L", KEY_L),
("SEMICOLON", KEY_SEMICOLON), ("SEMICOLON", KEY_SEMICOLON),
("APOSTROPHE", KEY_APOSTROPHE), ("APOSTROPHE", KEY_APOSTROPHE),
("GRAVE", KEY_GRAVE), ("GRAVE", KEY_GRAVE),
("LEFTSHIFT", KEY_LEFTSHIFT), ("LEFTSHIFT", KEY_LEFTSHIFT),
("BACKSLASH", KEY_BACKSLASH), ("BACKSLASH", KEY_BACKSLASH),
("Z", KEY_Z), ("Z", KEY_Z),
("X", KEY_X), ("X", KEY_X),
("C", KEY_C), ("C", KEY_C),
("V", KEY_V), ("V", KEY_V),
("B", KEY_B), ("B", KEY_B),
("N", KEY_N), ("N", KEY_N),
("M", KEY_M), ("M", KEY_M),
("COMMA", KEY_COMMA), ("COMMA", KEY_COMMA),
("DOT", KEY_DOT), ("DOT", KEY_DOT),
("SLASH", KEY_SLASH), ("SLASH", KEY_SLASH),
("RIGHTSHIFT", KEY_RIGHTSHIFT), ("RIGHTSHIFT", KEY_RIGHTSHIFT),
("KPASTERISK", KEY_KPASTERISK), ("KPASTERISK", KEY_KPASTERISK),
("LEFTALT", KEY_LEFTALT), ("LEFTALT", KEY_LEFTALT),
("SPACE", KEY_SPACE), ("SPACE", KEY_SPACE),
("CAPSLOCK", KEY_CAPSLOCK), ("CAPSLOCK", KEY_CAPSLOCK),
("F1", KEY_F1), ("F1", KEY_F1),
("F2", KEY_F2), ("F2", KEY_F2),
("F3", KEY_F3), ("F3", KEY_F3),
("F4", KEY_F4), ("F4", KEY_F4),
("F5", KEY_F5), ("F5", KEY_F5),
("F6", KEY_F6), ("F6", KEY_F6),
("F7", KEY_F7), ("F7", KEY_F7),
("F8", KEY_F8), ("F8", KEY_F8),
("F9", KEY_F9), ("F9", KEY_F9),
("F10", KEY_F10), ("F10", KEY_F10),
("NUMLOCK", KEY_NUMLOCK), ("NUMLOCK", KEY_NUMLOCK),
("SCROLLLOCK", KEY_SCROLLLOCK), ("SCROLLLOCK", KEY_SCROLLLOCK),
("KP7", KEY_KP7), ("KP7", KEY_KP7),
("KP8", KEY_KP8), ("KP8", KEY_KP8),
("KP9", KEY_KP9), ("KP9", KEY_KP9),
("KPMINUS", KEY_KPMINUS), ("KPMINUS", KEY_KPMINUS),
("KP4", KEY_KP4), ("KP4", KEY_KP4),
("KP5", KEY_KP5), ("KP5", KEY_KP5),
("KP6", KEY_KP6), ("KP6", KEY_KP6),
("KPPLUS", KEY_KPPLUS), ("KPPLUS", KEY_KPPLUS),
("KP1", KEY_KP1), ("KP1", KEY_KP1),
("KP2", KEY_KP2), ("KP2", KEY_KP2),
("KP3", KEY_KP3), ("KP3", KEY_KP3),
("KP0", KEY_KP0), ("KP0", KEY_KP0),
("KPDOT", KEY_KPDOT), ("KPDOT", KEY_KPDOT),
("ZENKAKUHANKAKU", KEY_ZENKAKUHANKAKU), ("ZENKAKUHANKAKU", KEY_ZENKAKUHANKAKU),
("102ND", KEY_102ND), ("102ND", KEY_102ND),
("F11", KEY_F11), ("F11", KEY_F11),
("F12", KEY_F12), ("F12", KEY_F12),
("RO", KEY_RO), ("RO", KEY_RO),
("KATAKANA", KEY_KATAKANA), ("KATAKANA", KEY_KATAKANA),
("HIRAGANA", KEY_HIRAGANA), ("HIRAGANA", KEY_HIRAGANA),
("HENKAN", KEY_HENKAN), ("HENKAN", KEY_HENKAN),
("KATAKANAHIRAGANA", KEY_KATAKANAHIRAGANA), ("KATAKANAHIRAGANA", KEY_KATAKANAHIRAGANA),
("MUHENKAN", KEY_MUHENKAN), ("MUHENKAN", KEY_MUHENKAN),
("KPJPCOMMA", KEY_KPJPCOMMA), ("KPJPCOMMA", KEY_KPJPCOMMA),
("KPENTER", KEY_KPENTER), ("KPENTER", KEY_KPENTER),
("RIGHTCTRL", KEY_RIGHTCTRL), ("RIGHTCTRL", KEY_RIGHTCTRL),
("KPSLASH", KEY_KPSLASH), ("KPSLASH", KEY_KPSLASH),
("SYSRQ", KEY_SYSRQ), ("SYSRQ", KEY_SYSRQ),
("RIGHTALT", KEY_RIGHTALT), ("RIGHTALT", KEY_RIGHTALT),
("LINEFEED", KEY_LINEFEED), ("LINEFEED", KEY_LINEFEED),
("HOME", KEY_HOME), ("HOME", KEY_HOME),
("UP", KEY_UP), ("UP", KEY_UP),
("PAGEUP", KEY_PAGEUP), ("PAGEUP", KEY_PAGEUP),
("LEFT", KEY_LEFT), ("LEFT", KEY_LEFT),
("RIGHT", KEY_RIGHT), ("RIGHT", KEY_RIGHT),
("END", KEY_END), ("END", KEY_END),
("DOWN", KEY_DOWN), ("DOWN", KEY_DOWN),
("PAGEDOWN", KEY_PAGEDOWN), ("PAGEDOWN", KEY_PAGEDOWN),
("INSERT", KEY_INSERT), ("INSERT", KEY_INSERT),
("DELETE", KEY_DELETE), ("DELETE", KEY_DELETE),
("MACRO", KEY_MACRO), ("MACRO", KEY_MACRO),
("MUTE", KEY_MUTE), ("MUTE", KEY_MUTE),
("VOLUMEDOWN", KEY_VOLUMEDOWN), ("VOLUMEDOWN", KEY_VOLUMEDOWN),
("VOLUMEUP", KEY_VOLUMEUP), ("VOLUMEUP", KEY_VOLUMEUP),
("POWER", KEY_POWER), ("POWER", KEY_POWER),
("KPEQUAL", KEY_KPEQUAL), ("KPEQUAL", KEY_KPEQUAL),
("KPPLUSMINUS", KEY_KPPLUSMINUS), ("KPPLUSMINUS", KEY_KPPLUSMINUS),
("PAUSE", KEY_PAUSE), ("PAUSE", KEY_PAUSE),
("SCALE", KEY_SCALE), ("SCALE", KEY_SCALE),
("KPCOMMA", KEY_KPCOMMA), ("KPCOMMA", KEY_KPCOMMA),
("HANGEUL", KEY_HANGEUL), ("HANGEUL", KEY_HANGEUL),
("HANGUEL", KEY_HANGUEL), ("HANGUEL", KEY_HANGUEL),
("HANGEUL", KEY_HANGEUL), ("HANGEUL", KEY_HANGEUL),
("HANJA", KEY_HANJA), ("HANJA", KEY_HANJA),
("YEN", KEY_YEN), ("YEN", KEY_YEN),
("LEFTMETA", KEY_LEFTMETA), ("LEFTMETA", KEY_LEFTMETA),
("RIGHTMETA", KEY_RIGHTMETA), ("RIGHTMETA", KEY_RIGHTMETA),
("COMPOSE", KEY_COMPOSE), ("COMPOSE", KEY_COMPOSE),
("STOP", KEY_STOP), ("STOP", KEY_STOP),
("AGAIN", KEY_AGAIN), ("AGAIN", KEY_AGAIN),
("PROPS", KEY_PROPS), ("PROPS", KEY_PROPS),
("UNDO", KEY_UNDO), ("UNDO", KEY_UNDO),
("FRONT", KEY_FRONT), ("FRONT", KEY_FRONT),
("COPY", KEY_COPY), ("COPY", KEY_COPY),
("OPEN", KEY_OPEN), ("OPEN", KEY_OPEN),
("PASTE", KEY_PASTE), ("PASTE", KEY_PASTE),
("FIND", KEY_FIND), ("FIND", KEY_FIND),
("CUT", KEY_CUT), ("CUT", KEY_CUT),
("HELP", KEY_HELP), ("HELP", KEY_HELP),
("MENU", KEY_MENU), ("MENU", KEY_MENU),
("CALC", KEY_CALC), ("CALC", KEY_CALC),
("SETUP", KEY_SETUP), ("SETUP", KEY_SETUP),
("SLEEP", KEY_SLEEP), ("SLEEP", KEY_SLEEP),
("WAKEUP", KEY_WAKEUP), ("WAKEUP", KEY_WAKEUP),
("FILE", KEY_FILE), ("FILE", KEY_FILE),
("SENDFILE", KEY_SENDFILE), ("SENDFILE", KEY_SENDFILE),
("DELETEFILE", KEY_DELETEFILE), ("DELETEFILE", KEY_DELETEFILE),
("XFER", KEY_XFER), ("XFER", KEY_XFER),
("PROG1", KEY_PROG1), ("PROG1", KEY_PROG1),
("PROG2", KEY_PROG2), ("PROG2", KEY_PROG2),
("WWW", KEY_WWW), ("WWW", KEY_WWW),
("MSDOS", KEY_MSDOS), ("MSDOS", KEY_MSDOS),
("COFFEE", KEY_COFFEE), ("COFFEE", KEY_COFFEE),
("SCREENLOCK", KEY_SCREENLOCK), ("SCREENLOCK", KEY_SCREENLOCK),
("COFFEE", KEY_COFFEE), ("COFFEE", KEY_COFFEE),
("ROTATE_DISPLAY", KEY_ROTATE_DISPLAY), ("ROTATE_DISPLAY", KEY_ROTATE_DISPLAY),
("DIRECTION", KEY_DIRECTION), ("DIRECTION", KEY_DIRECTION),
("ROTATE_DISPLAY", KEY_ROTATE_DISPLAY), ("ROTATE_DISPLAY", KEY_ROTATE_DISPLAY),
("CYCLEWINDOWS", KEY_CYCLEWINDOWS), ("CYCLEWINDOWS", KEY_CYCLEWINDOWS),
("MAIL", KEY_MAIL), ("MAIL", KEY_MAIL),
("BOOKMARKS", KEY_BOOKMARKS), ("BOOKMARKS", KEY_BOOKMARKS),
("COMPUTER", KEY_COMPUTER), ("COMPUTER", KEY_COMPUTER),
("BACK", KEY_BACK), ("BACK", KEY_BACK),
("FORWARD", KEY_FORWARD), ("FORWARD", KEY_FORWARD),
("CLOSECD", KEY_CLOSECD), ("CLOSECD", KEY_CLOSECD),
("EJECTCD", KEY_EJECTCD), ("EJECTCD", KEY_EJECTCD),
("EJECTCLOSECD", KEY_EJECTCLOSECD), ("EJECTCLOSECD", KEY_EJECTCLOSECD),
("NEXTSONG", KEY_NEXTSONG), ("NEXTSONG", KEY_NEXTSONG),
("PLAYPAUSE", KEY_PLAYPAUSE), ("PLAYPAUSE", KEY_PLAYPAUSE),
("PREVIOUSSONG", KEY_PREVIOUSSONG), ("PREVIOUSSONG", KEY_PREVIOUSSONG),
("STOPCD", KEY_STOPCD), ("STOPCD", KEY_STOPCD),
("RECORD", KEY_RECORD), ("RECORD", KEY_RECORD),
("REWIND", KEY_REWIND), ("REWIND", KEY_REWIND),
("PHONE", KEY_PHONE), ("PHONE", KEY_PHONE),
("ISO", KEY_ISO), ("ISO", KEY_ISO),
("CONFIG", KEY_CONFIG), ("CONFIG", KEY_CONFIG),
("HOMEPAGE", KEY_HOMEPAGE), ("HOMEPAGE", KEY_HOMEPAGE),
("REFRESH", KEY_REFRESH), ("REFRESH", KEY_REFRESH),
("EXIT", KEY_EXIT), ("EXIT", KEY_EXIT),
("MOVE", KEY_MOVE), ("MOVE", KEY_MOVE),
("EDIT", KEY_EDIT), ("EDIT", KEY_EDIT),
("SCROLLUP", KEY_SCROLLUP), ("SCROLLUP", KEY_SCROLLUP),
("SCROLLDOWN", KEY_SCROLLDOWN), ("SCROLLDOWN", KEY_SCROLLDOWN),
("KPLEFTPAREN", KEY_KPLEFTPAREN), ("KPLEFTPAREN", KEY_KPLEFTPAREN),
("KPRIGHTPAREN", KEY_KPRIGHTPAREN), ("KPRIGHTPAREN", KEY_KPRIGHTPAREN),
("NEW", KEY_NEW), ("NEW", KEY_NEW),
("REDO", KEY_REDO), ("REDO", KEY_REDO),
("F13", KEY_F13), ("F13", KEY_F13),
("F14", KEY_F14), ("F14", KEY_F14),
("F15", KEY_F15), ("F15", KEY_F15),
("F16", KEY_F16), ("F16", KEY_F16),
("F17", KEY_F17), ("F17", KEY_F17),
("F18", KEY_F18), ("F18", KEY_F18),
("F19", KEY_F19), ("F19", KEY_F19),
("F20", KEY_F20), ("F20", KEY_F20),
("F21", KEY_F21), ("F21", KEY_F21),
("F22", KEY_F22), ("F22", KEY_F22),
("F23", KEY_F23), ("F23", KEY_F23),
("F24", KEY_F24), ("F24", KEY_F24),
("PLAYCD", KEY_PLAYCD), ("PLAYCD", KEY_PLAYCD),
("PAUSECD", KEY_PAUSECD), ("PAUSECD", KEY_PAUSECD),
("PROG3", KEY_PROG3), ("PROG3", KEY_PROG3),
("PROG4", KEY_PROG4), ("PROG4", KEY_PROG4),
("DASHBOARD", KEY_DASHBOARD), ("DASHBOARD", KEY_DASHBOARD),
("SUSPEND", KEY_SUSPEND), ("SUSPEND", KEY_SUSPEND),
("CLOSE", KEY_CLOSE), ("CLOSE", KEY_CLOSE),
("PLAY", KEY_PLAY), ("PLAY", KEY_PLAY),
("FASTFORWARD", KEY_FASTFORWARD), ("FASTFORWARD", KEY_FASTFORWARD),
("BASSBOOST", KEY_BASSBOOST), ("BASSBOOST", KEY_BASSBOOST),
("PRINT", KEY_PRINT), ("PRINT", KEY_PRINT),
("HP", KEY_HP), ("HP", KEY_HP),
("CAMERA", KEY_CAMERA), ("CAMERA", KEY_CAMERA),
("SOUND", KEY_SOUND), ("SOUND", KEY_SOUND),
("QUESTION", KEY_QUESTION), ("QUESTION", KEY_QUESTION),
("EMAIL", KEY_EMAIL), ("EMAIL", KEY_EMAIL),
("CHAT", KEY_CHAT), ("CHAT", KEY_CHAT),
("SEARCH", KEY_SEARCH), ("SEARCH", KEY_SEARCH),
("CONNECT", KEY_CONNECT), ("CONNECT", KEY_CONNECT),
("FINANCE", KEY_FINANCE), ("FINANCE", KEY_FINANCE),
("SPORT", KEY_SPORT), ("SPORT", KEY_SPORT),
("SHOP", KEY_SHOP), ("SHOP", KEY_SHOP),
("ALTERASE", KEY_ALTERASE), ("ALTERASE", KEY_ALTERASE),
("CANCEL", KEY_CANCEL), ("CANCEL", KEY_CANCEL),
("BRIGHTNESSDOWN", KEY_BRIGHTNESSDOWN), ("BRIGHTNESSDOWN", KEY_BRIGHTNESSDOWN),
("BRIGHTNESSUP", KEY_BRIGHTNESSUP), ("BRIGHTNESSUP", KEY_BRIGHTNESSUP),
("MEDIA", KEY_MEDIA), ("MEDIA", KEY_MEDIA),
("SWITCHVIDEOMODE", KEY_SWITCHVIDEOMODE), ("SWITCHVIDEOMODE", KEY_SWITCHVIDEOMODE),
("KBDILLUMTOGGLE", KEY_KBDILLUMTOGGLE), ("KBDILLUMTOGGLE", KEY_KBDILLUMTOGGLE),
("KBDILLUMDOWN", KEY_KBDILLUMDOWN), ("KBDILLUMDOWN", KEY_KBDILLUMDOWN),
("KBDILLUMUP", KEY_KBDILLUMUP), ("KBDILLUMUP", KEY_KBDILLUMUP),
("SEND", KEY_SEND), ("SEND", KEY_SEND),
("REPLY", KEY_REPLY), ("REPLY", KEY_REPLY),
("FORWARDMAIL", KEY_FORWARDMAIL), ("FORWARDMAIL", KEY_FORWARDMAIL),
("SAVE", KEY_SAVE), ("SAVE", KEY_SAVE),
("DOCUMENTS", KEY_DOCUMENTS), ("DOCUMENTS", KEY_DOCUMENTS),
("BATTERY", KEY_BATTERY), ("BATTERY", KEY_BATTERY),
("BLUETOOTH", KEY_BLUETOOTH), ("BLUETOOTH", KEY_BLUETOOTH),
("WLAN", KEY_WLAN), ("WLAN", KEY_WLAN),
("UWB", KEY_UWB), ("UWB", KEY_UWB),
("UNKNOWN", KEY_UNKNOWN), ("UNKNOWN", KEY_UNKNOWN),
("VIDEO_NEXT", KEY_VIDEO_NEXT), ("VIDEO_NEXT", KEY_VIDEO_NEXT),
("VIDEO_PREV", KEY_VIDEO_PREV), ("VIDEO_PREV", KEY_VIDEO_PREV),
("BRIGHTNESS_CYCLE", KEY_BRIGHTNESS_CYCLE), ("BRIGHTNESS_CYCLE", KEY_BRIGHTNESS_CYCLE),
("BRIGHTNESS_AUTO", KEY_BRIGHTNESS_AUTO), ("BRIGHTNESS_AUTO", KEY_BRIGHTNESS_AUTO),
("BRIGHTNESS_ZERO", KEY_BRIGHTNESS_ZERO), ("BRIGHTNESS_ZERO", KEY_BRIGHTNESS_ZERO),
("BRIGHTNESS_AUTO", KEY_BRIGHTNESS_AUTO), ("BRIGHTNESS_AUTO", KEY_BRIGHTNESS_AUTO),
("DISPLAY_OFF", KEY_DISPLAY_OFF), ("DISPLAY_OFF", KEY_DISPLAY_OFF),
("WWAN", KEY_WWAN), ("WWAN", KEY_WWAN),
("WIMAX", KEY_WIMAX), ("WIMAX", KEY_WIMAX),
("WWAN", KEY_WWAN), ("WWAN", KEY_WWAN),
("RFKILL", KEY_RFKILL), ("RFKILL", KEY_RFKILL),
("MICMUTE", KEY_MICMUTE), ("MICMUTE", KEY_MICMUTE),
// below manual shortcuts // below manual shortcuts
("PSCR", KEY_SYSRQ), ("PSCR", KEY_SYSRQ),
("SLCK", KEY_SCROLLLOCK), ("SLCK", KEY_SCROLLLOCK),
("BRK", KEY_PAUSE), ("BRK", KEY_PAUSE),
("GRV", KEY_GRAVE), ("GRV", KEY_GRAVE),
("0", KEY_10), // dumb or named wrong? ("0", KEY_10), // dumb or named wrong?
("MINS", KEY_MINUS), ("MINS", KEY_MINUS),
("EQL", KEY_EQUAL), ("EQL", KEY_EQUAL),
("BSPC", KEY_BACKSPACE), ("BSPC", KEY_BACKSPACE),
("LBRC", KEY_LEFTBRACE), ("LBRC", KEY_LEFTBRACE),
("RBRC", KEY_RIGHTBRACE), ("RBRC", KEY_RIGHTBRACE),
("BSLS", KEY_BACKSLASH), ("BSLS", KEY_BACKSLASH),
("SCLN", KEY_SEMICOLON), ("SCLN", KEY_SEMICOLON),
("QUOT", KEY_APOSTROPHE), ("QUOT", KEY_APOSTROPHE),
("ENT", KEY_ENTER), ("ENT", KEY_ENTER),
("COMM", KEY_COMMA), ("COMM", KEY_COMMA),
("DOT", KEY_DOT), ("DOT", KEY_DOT),
("SLSH", KEY_SLASH), ("SLSH", KEY_SLASH),
("CAPS", KEY_CAPSLOCK), ("CAPS", KEY_CAPSLOCK),
("LSFT", KEY_LEFTSHIFT), ("LSFT", KEY_LEFTSHIFT),
("RSFT", KEY_RIGHTSHIFT), ("RSFT", KEY_RIGHTSHIFT),
("SPC", KEY_SPACE), ("SPC", KEY_SPACE),
("APP", KEY_COMPOSE), ("APP", KEY_COMPOSE),
("LCTL", KEY_LEFTCTRL), ("LCTL", KEY_LEFTCTRL),
("RCTL", KEY_RIGHTCTRL), ("RCTL", KEY_RIGHTCTRL),
("LALT", KEY_LEFTALT), ("LALT", KEY_LEFTALT),
("RALT", KEY_RIGHTALT), ("RALT", KEY_RIGHTALT),
("LGUI", KEY_LEFTMETA), ("LGUI", KEY_LEFTMETA),
("RGUI", KEY_RIGHTMETA), ("RGUI", KEY_RIGHTMETA),
("INS", KEY_INSERT), ("INS", KEY_INSERT),
("PGUP", KEY_PAGEUP), ("PGUP", KEY_PAGEUP),
("PGDN", KEY_PAGEDOWN), ("PGDN", KEY_PAGEDOWN),
("DEL", KEY_DELETE), ("DEL", KEY_DELETE),
("RGHT", KEY_RIGHT), ("RGHT", KEY_RIGHT),
("NLCK", KEY_NUMLOCK), ("NLCK", KEY_NUMLOCK),
("PSLS", KEY_KPSLASH), ("PSLS", KEY_KPSLASH),
("PAST", KEY_KPASTERISK), ("PAST", KEY_KPASTERISK),
("PMNS", KEY_KPMINUS), ("PMNS", KEY_KPMINUS),
("P7", KEY_KP7), ("P7", KEY_KP7),
("P8", KEY_KP8), ("P8", KEY_KP8),
("P9", KEY_KP9), ("P9", KEY_KP9),
("P4", KEY_KP4), ("P4", KEY_KP4),
("P5", KEY_KP5), ("P5", KEY_KP5),
("P6", KEY_KP6), ("P6", KEY_KP6),
("PPLS", KEY_KPPLUS), ("PPLS", KEY_KPPLUS),
("P1", KEY_KP1), ("P1", KEY_KP1),
("P2", KEY_KP2), ("P2", KEY_KP2),
("P3", KEY_KP3), ("P3", KEY_KP3),
("P0", KEY_KP0), ("P0", KEY_KP0),
("PDOT", KEY_KPDOT), ("PDOT", KEY_KPDOT),
("PENT", KEY_KPENTER), ("PENT", KEY_KPENTER),
("TOUCHPAD_TOGGLE", KEY_TOUCHPAD_TOGGLE), ("TOUCHPAD_TOGGLE", KEY_TOUCHPAD_TOGGLE),
].iter().cloned().map(|(m, v)| (m, v as u16)).collect() ]
} .iter()
.cloned()
.map(|(m, v)| (m, v as u16))
.collect()
}

View File

@ -1,6 +1,5 @@
use std::collections::HashMap;
use core_graphics::event::CGKeyCode; use core_graphics::event::CGKeyCode;
use std::collections::HashMap;
pub const KEY_RESERVED: CGKeyCode = 0x31; pub const KEY_RESERVED: CGKeyCode = 0x31;
pub const KEY_ESC: CGKeyCode = 0x00; pub const KEY_ESC: CGKeyCode = 0x00;
@ -564,5 +563,9 @@ pub fn key_map() -> HashMap<&'static str, CGKeyCode> {
("P0", KEY_KP0), ("P0", KEY_KP0),
("PDOT", KEY_KPDOT), ("PDOT", KEY_KPDOT),
("PENT", KEY_KPENTER), ("PENT", KEY_KPENTER),
].iter().cloned().map(|(m, v)| (m, v)).collect() ]
.iter()
.cloned()
.map(|(m, v)| (m, v))
.collect()
} }

View File

@ -4,8 +4,7 @@
#![allow(improper_ctypes)] #![allow(improper_ctypes)]
use crate::*; use crate::*;
use std::env; use std::{env, process::exit};
use std::process::exit;
use getopts::Options; use getopts::Options;
use std::fs::File; use std::fs::File;
@ -13,9 +12,10 @@ use std::fs::File;
pub mod codes; pub mod codes;
use codes::*; use codes::*;
use core_graphics::event::CGKeyCode; use core_graphics::{
use core_graphics::event::*; event::{CGKeyCode, *},
use core_graphics::event_source::*; event_source::*,
};
use core_graphics::event::{CGEventTapLocation, CGEventType}; use core_graphics::event::{CGEventTapLocation, CGEventType};
@ -62,8 +62,12 @@ impl KeyEvent<CGKeyCode> for CGEvent {
KEY_CAPSLOCK => NX_DEVICECAPSLOCKMASK, KEY_CAPSLOCK => NX_DEVICECAPSLOCKMASK,
_ => panic!("unhandled key: {}", self.code()), _ => panic!("unhandled key: {}", self.code()),
}; };
if (flags & mask) != 0 { KeyState::DOWN } else { KeyState::UP } if (flags & mask) != 0 {
}, KeyState::DOWN
} else {
KeyState::UP
}
}
CGEventType::KeyDown => KeyState::DOWN, CGEventType::KeyDown => KeyState::DOWN,
CGEventType::KeyUp => KeyState::UP, CGEventType::KeyUp => KeyState::UP,
CGEventType::TapDisabledByTimeout => { CGEventType::TapDisabledByTimeout => {
@ -71,11 +75,11 @@ impl KeyEvent<CGKeyCode> for CGEvent {
todo!("implement register listener"); todo!("implement register listener");
//register_listener(channel); //register_listener(channel);
//KeyState::OTHER //KeyState::OTHER
}, }
_ => { _ => {
println!("Received unknown EventType: {:?}", event_type); println!("Received unknown EventType: {:?}", event_type);
KeyState::OTHER KeyState::OTHER
}, }
} }
} }
} }
@ -99,15 +103,21 @@ impl Keyboard<CGKeyCode, CGEvent, Option<CGEvent>> for CGEventSource {
self.send_mod_code_value(code, event.value() == KeyState::UP, event) self.send_mod_code_value(code, event.value() == KeyState::UP, event)
} }
fn send_mod_code_value(&self, code: CGKeyCode, up_not_down: bool, _event: &mut CGEvent) -> Result<Option<CGEvent>> { fn send_mod_code_value(
&self,
code: CGKeyCode,
up_not_down: bool,
_event: &mut CGEvent,
) -> Result<Option<CGEvent>> {
//println!("send_mod_code_value orig: {} code: {}, up_not_down: {}", event.code(), code, up_not_down); //println!("send_mod_code_value orig: {} code: {}, up_not_down: {}", event.code(), code, up_not_down);
//return Ok(None); //return Ok(None);
let event = let event = CGEvent::new_keyboard_event(self.clone(), code, !up_not_down)
CGEvent::new_keyboard_event(self.clone(), code, !up_not_down) .expect("Failed creating event");
.expect("Failed creating event");
match tapLocation { match tapLocation {
CGEventTapLocation::HID => event.set_integer_value_field(EventField::EVENT_SOURCE_USER_DATA, uniqueHIDUserData), CGEventTapLocation::HID => {
event.set_integer_value_field(EventField::EVENT_SOURCE_USER_DATA, uniqueHIDUserData)
}
_ => {} _ => {}
}; };
event.post(tapLocation); event.post(tapLocation);
@ -146,12 +156,14 @@ pub fn main_res() -> Result<()> {
let key_maps = MacOSKeyMaps::from_cfg(&key_map, &config.config_file); let key_maps = MacOSKeyMaps::from_cfg(&key_map, &config.config_file);
//println!("key_maps: {}", key_maps); //println!("key_maps: {}", key_maps);
let callback_pointer: CallbackPointer = (key_maps, CGEventSource::new(CGEventSourceStateID::Private).expect("Failed creating event source")); let callback_pointer: CallbackPointer = (
key_maps,
CGEventSource::new(CGEventSourceStateID::Private).expect("Failed creating event source"),
);
let mask = CGEventMaskBit(CGEventType::KeyDown) let mask = CGEventMaskBit(CGEventType::KeyDown)
| CGEventMaskBit(CGEventType::KeyUp) | CGEventMaskBit(CGEventType::KeyUp)
| CGEventMaskBit(CGEventType::FlagsChanged) | CGEventMaskBit(CGEventType::FlagsChanged);
;
unsafe { unsafe {
let options = 0; let options = 0;
@ -191,12 +203,14 @@ pub fn main_res() -> Result<()> {
#[derive(Debug)] #[derive(Debug)]
struct Config { struct Config {
config_file: String config_file: String,
} }
impl Config { impl Config {
fn new(config_file: String) -> Self { fn new(config_file: String) -> Self {
Config { config_file: config_file } Config {
config_file: config_file,
}
} }
} }
@ -224,7 +238,10 @@ fn parse_args() -> Config {
default_configs.push("keymap.toml".to_string()); default_configs.push("keymap.toml".to_string());
let c_msg = format!("specify the keymap config file to use (default in order: {:?})", default_configs); let c_msg = format!(
"specify the keymap config file to use (default in order: {:?})",
default_configs
);
let mut opts = Options::new(); let mut opts = Options::new();
opts.optflag("h", "help", "prints this help message"); opts.optflag("h", "help", "prints this help message");
@ -278,117 +295,119 @@ type CGEventTapOptions = u32;
type CGEventTapPlacement = u32; type CGEventTapPlacement = u32;
// Callback Type // Callback Type
type CGEventTapCallBack = extern "C" fn(Pointer, CGEventType, CGEvent, &mut CallbackPointer) -> CGEvent; type CGEventTapCallBack =
extern "C" fn(Pointer, CGEventType, CGEvent, &mut CallbackPointer) -> CGEvent;
// Constants // Constants
const kCGSessionEventTap: CGEventTapLocation = CGEventTapLocation::HID; const kCGSessionEventTap: CGEventTapLocation = CGEventTapLocation::HID;
const kCGHeadInsertEventTap: CGEventTapPlacement = 0; const kCGHeadInsertEventTap: CGEventTapPlacement = 0;
// Link to ApplicationServices/ApplicationServices.h and Carbon/Carbon.h // Link to ApplicationServices/ApplicationServices.h and Carbon/Carbon.h
#[link(name = "ApplicationServices", kind = "framework")] #[link(name = "ApplicationServices", kind = "framework")]
#[link(name = "Carbon", kind = "framework")] #[link(name = "Carbon", kind = "framework")]
extern { extern "C" {
/// Pass through to the default loop modes /// Pass through to the default loop modes
pub static kCFRunLoopCommonModes: Pointer; pub static kCFRunLoopCommonModes: Pointer;
/// Pass through to the default allocator /// Pass through to the default allocator
pub static kCFAllocatorDefault: Pointer; pub static kCFAllocatorDefault: Pointer;
/// Run the current threads loop in default mode /// Run the current threads loop in default mode
pub fn CFRunLoopRun(); pub fn CFRunLoopRun();
/// Obtain the current threads loop /// Obtain the current threads loop
pub fn CFRunLoopGetCurrent() -> Pointer; pub fn CFRunLoopGetCurrent() -> Pointer;
/// Create an event tap /// Create an event tap
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `place` - The location of the new event tap. Pass one of /// * `place` - The location of the new event tap. Pass one of
/// the constants listed in Event Tap Locations. Only /// the constants listed in Event Tap Locations. Only
/// processes running as the root user may locate an /// processes running as the root user may locate an
/// event tap at the point where HID events enter the /// event tap at the point where HID events enter the
/// window server; for other users, this function /// window server; for other users, this function
/// returns NULL. /// returns NULL.
/// ///
/// * `options` - The placement of the new event tap in the /// * `options` - The placement of the new event tap in the
/// list of active event taps. Pass one of the /// list of active event taps. Pass one of the
/// constants listed in Event Tap Placement. /// constants listed in Event Tap Placement.
/// ///
/// * `eventsOfInterest` - A constant that specifies whether /// * `eventsOfInterest` - A constant that specifies whether
/// the new event tap is a passive listener or an /// the new event tap is a passive listener or an
/// active filter. /// active filter.
/// ///
/// * `callback` - A bit mask that specifies the set of events /// * `callback` - A bit mask that specifies the set of events
/// to be observed. For a list of possible events, /// to be observed. For a list of possible events,
/// see Event Types. For information on how to /// see Event Types. For information on how to
/// specify the mask, see CGEventMask. If the event /// specify the mask, see CGEventMask. If the event
/// tap is not permitted to monitor one or more of /// tap is not permitted to monitor one or more of
/// the events specified in the eventsOfInterest /// the events specified in the eventsOfInterest
/// parameter, then the appropriate bits in the mask /// parameter, then the appropriate bits in the mask
/// are cleared. If that action results in an empty /// are cleared. If that action results in an empty
/// mask, this function returns NULL. callback /// mask, this function returns NULL. callback
/// ///
/// * `refcon` - An event tap callback function that you /// * `refcon` - An event tap callback function that you
/// provide. Your callback function is invoked from /// provide. Your callback function is invoked from
/// the run loop to which the event tap is added as a /// the run loop to which the event tap is added as a
/// source. The thread safety of the callback is /// source. The thread safety of the callback is
/// defined by the run loops environment. To learn /// defined by the run loops environment. To learn
/// more about event tap callbacks, see /// more about event tap callbacks, see
/// CGEventTapCallBack. refcon /// CGEventTapCallBack. refcon
/// ///
/// * `channel` - A pointer to user-defined data. This pointer /// * `channel` - A pointer to user-defined data. This pointer
/// is passed into the callback function specified in /// is passed into the callback function specified in
/// the callback parameter. Here we use it as a mpsc /// the callback parameter. Here we use it as a mpsc
/// channel. /// channel.
pub fn CGEventTapCreate( pub fn CGEventTapCreate(
tap: CGEventTapLocation, tap: CGEventTapLocation,
place: CGEventTapPlacement, place: CGEventTapPlacement,
options: CGEventTapOptions, options: CGEventTapOptions,
eventsOfInterest: CGEventMask, eventsOfInterest: CGEventMask,
callback: CGEventTapCallBack, callback: CGEventTapCallBack,
channel: &CallbackPointer, channel: &CallbackPointer,
) -> CFMachPortRef; ) -> CFMachPortRef;
/// Creates a CFRunLoopSource object for a CFMachPort /// Creates a CFRunLoopSource object for a CFMachPort
/// object. /// object.
/// ///
/// The run loop source is not automatically added to /// The run loop source is not automatically added to
/// a run loop. To add the source to a run loop, use /// a run loop. To add the source to a run loop, use
/// CFRunLoopAddSource /// CFRunLoopAddSource
pub fn CFMachPortCreateRunLoopSource( pub fn CFMachPortCreateRunLoopSource(
allocator: Pointer, allocator: Pointer,
port: CFMachPortRef, port: CFMachPortRef,
order: libc::c_int, order: libc::c_int,
) -> Pointer; ) -> Pointer;
/// Adds a CFRunLoopSource object to a run loop mode. /// Adds a CFRunLoopSource object to a run loop mode.
pub fn CFRunLoopAddSource( pub fn CFRunLoopAddSource(run_loop: Pointer, run_loop_source: Pointer, mode: Pointer);
run_loop: Pointer,
run_loop_source: Pointer,
mode: Pointer,
);
pub fn CGEventTapEnable(port: CFMachPortRef, enable: bool); pub fn CGEventTapEnable(port: CFMachPortRef, enable: bool);
} }
const NX_DEVICELCTLKEYMASK: u64 = 0x00000001; const NX_DEVICELCTLKEYMASK: u64 = 0x00000001;
const NX_DEVICERCTLKEYMASK: u64 = 0x00002000; const NX_DEVICERCTLKEYMASK: u64 = 0x00002000;
const NX_DEVICELSHIFTKEYMASK: u64 = 0x00000002; const NX_DEVICELSHIFTKEYMASK: u64 = 0x00000002;
const NX_DEVICERSHIFTKEYMASK: u64 = 0x00000004; const NX_DEVICERSHIFTKEYMASK: u64 = 0x00000004;
const NX_DEVICELCMDKEYMASK: u64 = 0x00000008; const NX_DEVICELCMDKEYMASK: u64 = 0x00000008;
const NX_DEVICERCMDKEYMASK: u64 = 0x00000010; const NX_DEVICERCMDKEYMASK: u64 = 0x00000010;
const NX_DEVICELALTKEYMASK: u64 = 0x00000020; const NX_DEVICELALTKEYMASK: u64 = 0x00000020;
const NX_DEVICERALTKEYMASK: u64 = 0x00000040; const NX_DEVICERALTKEYMASK: u64 = 0x00000040;
const NX_DEVICECAPSLOCKMASK: u64 = 1 << 16; const NX_DEVICECAPSLOCKMASK: u64 = 1 << 16;
/// This callback will be registered to be invoked from the run loop /// This callback will be registered to be invoked from the run loop
/// to which the event tap is added as a source. /// to which the event tap is added as a source.
#[no_mangle] #[no_mangle]
#[allow(unused_variables, improper_ctypes_definitions)] #[allow(unused_variables, improper_ctypes_definitions)]
pub extern fn callback(proxy: Pointer, event_type: CGEventType, mut event: CGEvent, callback_pointer: &mut CallbackPointer) -> CGEvent { pub extern "C" fn callback(
proxy: Pointer,
event_type: CGEventType,
mut event: CGEvent,
callback_pointer: &mut CallbackPointer,
) -> CGEvent {
let (key_maps, event_source) = callback_pointer; let (key_maps, event_source) = callback_pointer;
match tapLocation { match tapLocation {
CGEventTapLocation::HID => { CGEventTapLocation::HID => {
@ -400,15 +419,16 @@ pub extern fn callback(proxy: Pointer, event_type: CGEventType, mut event: CGEve
_ => {} _ => {}
}; };
key_maps.send_event(&mut event, &event_source).expect("macos shouldn't error...") key_maps
.send_event(&mut event, &event_source)
.expect("macos shouldn't error...")
.unwrap_or_else(|| { .unwrap_or_else(|| {
event.set_type(CGEventType::Null); event.set_type(CGEventType::Null);
event event
}) // None means return NULL }) // None means return NULL
} }
/// Redefine macro for bitshifting from header as function here
pub fn CGEventMaskBit(eventType: CGEventType) -> CGEventMask {
1 << (eventType as u32)
}
/// Redefine macro for bitshifting from header as function here
pub fn CGEventMaskBit(eventType: CGEventType) -> CGEventMask {
1 << (eventType as u32)
}

View File

@ -1,4 +1,3 @@
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
fn main() { fn main() {
let ret = rusty_keys::main_res(); let ret = rusty_keys::main_res();

View File

@ -1,6 +1,5 @@
use std::{collections::HashMap, convert::TryFrom};
use winapi::shared::minwindef::DWORD; use winapi::shared::minwindef::DWORD;
use std::collections::HashMap;
use std::convert::TryFrom;
// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes // https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
@ -557,7 +556,11 @@ pub fn key_map() -> HashMap<&'static str, USizeableDWORD> {
("P0", KEY_KP0), ("P0", KEY_KP0),
("PDOT", KEY_KPDOT), ("PDOT", KEY_KPDOT),
("PENT", KEY_KPENTER), ("PENT", KEY_KPENTER),
].iter().cloned().map(|(m, v)| (m, USizeableDWORD(v))).collect() ]
.iter()
.cloned()
.map(|(m, v)| (m, USizeableDWORD(v)))
.collect()
} }
// https://www.reddit.com/r/rust/comments/9xth8g/why_no_from_conversion_from_u32_to_usize/ // https://www.reddit.com/r/rust/comments/9xth8g/why_no_from_conversion_from_u32_to_usize/
@ -576,4 +579,4 @@ impl TryFrom<usize> for USizeableDWORD {
fn try_from(value: usize) -> ::std::result::Result<Self, Self::Error> { fn try_from(value: usize) -> ::std::result::Result<Self, Self::Error> {
Ok(USizeableDWORD(value as DWORD)) Ok(USizeableDWORD(value as DWORD))
} }
} }

View File

@ -1,24 +1,32 @@
#![windows_subsystem = "windows"] #![windows_subsystem = "windows"]
use crate::*; use crate::*;
use std::env; use std::{env, process::exit};
use std::process::exit;
use getopts::Options; use getopts::Options;
use std::fs::File; use std::fs::File;
use winapi::um::winuser::{KBDLLHOOKSTRUCT, WH_KEYBOARD_LL, MSG, GetMessageW, CallNextHookEx, SetWindowsHookExW, INPUT_KEYBOARD, MapVirtualKeyW, LPINPUT, INPUT, KEYBDINPUT, SendInput, KEYEVENTF_SCANCODE, KEYEVENTF_KEYUP, WM_KEYUP, WM_KEYDOWN, MAPVK_VK_TO_VSC, ShowWindow, SW_HIDE}; use winapi::{
use winapi::shared::windef::{HHOOK__, HWND}; ctypes::c_int,
use winapi::shared::minwindef::{LRESULT, WPARAM, LPARAM, HINSTANCE}; shared::{
use winapi::shared::basetsd::ULONG_PTR; basetsd::ULONG_PTR,
use winapi::ctypes::c_int; minwindef::{HINSTANCE, LPARAM, LRESULT, WPARAM},
windef::{HHOOK__, HWND},
},
um::winuser::{
CallNextHookEx, GetMessageW, MapVirtualKeyW, SendInput, SetWindowsHookExW, ShowWindow,
INPUT, INPUT_KEYBOARD, KBDLLHOOKSTRUCT, KEYBDINPUT, KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE,
LPINPUT, MAPVK_VK_TO_VSC, MSG, SW_HIDE, WH_KEYBOARD_LL, WM_KEYDOWN, WM_KEYUP,
},
};
pub mod codes; pub mod codes;
use codes::*; use codes::*;
use std::sync::atomic::{AtomicPtr, Ordering}; use std::{
use std::mem::{zeroed, size_of}; mem::{size_of, zeroed},
use winapi::_core::ptr::null_mut; sync::atomic::{AtomicPtr, Ordering},
use winapi::_core::mem::transmute_copy; };
use winapi::_core::{mem::transmute_copy, ptr::null_mut};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::sync::Mutex; use std::sync::Mutex;
@ -65,7 +73,12 @@ impl Keyboard<USizeableDWORD, InputEvent, LRESULT> for Device {
self.send_mod_code_value(code, event.value as u32 == WM_KEYUP, event) self.send_mod_code_value(code, event.value as u32 == WM_KEYUP, event)
} }
fn send_mod_code_value(&self, code: USizeableDWORD, up_not_down: bool, _event: &mut InputEvent) -> Result<LRESULT> { fn send_mod_code_value(
&self,
code: USizeableDWORD,
up_not_down: bool,
_event: &mut InputEvent,
) -> Result<LRESULT> {
let flags = if up_not_down { let flags = if up_not_down {
KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP
} else { } else {
@ -132,7 +145,7 @@ unsafe extern "system" fn keybd_proc(code: c_int, w_param: WPARAM, l_param: LPAR
kb_struct.vkCode, kb_struct.scanCode, kb_struct.flags, kb_struct.time, kb_struct.dwExtraInfo); kb_struct.vkCode, kb_struct.scanCode, kb_struct.flags, kb_struct.time, kb_struct.dwExtraInfo);
*/ */
let mut input_event = InputEvent{ let mut input_event = InputEvent {
code, code,
value: w_param, value: w_param,
kb_hook_pointer: l_param, kb_hook_pointer: l_param,
@ -142,13 +155,17 @@ unsafe extern "system" fn keybd_proc(code: c_int, w_param: WPARAM, l_param: LPAR
// .unwrap() is ok because windows impl can actually never can fail // .unwrap() is ok because windows impl can actually never can fail
//DEVICE.send(&mut input_event).unwrap() //DEVICE.send(&mut input_event).unwrap()
//KEY_MAPPER.send_event(&mut input_event, &DEVICE).unwrap() //KEY_MAPPER.send_event(&mut input_event, &DEVICE).unwrap()
KEY_MAPPER.lock().unwrap().send_event(&mut input_event, &DEVICE).unwrap() KEY_MAPPER
.lock()
.unwrap()
.send_event(&mut input_event, &DEVICE)
.unwrap()
} }
fn set_hook( fn set_hook(
hook_id: i32, hook_id: i32,
hook_ptr: &AtomicPtr<HHOOK__>, hook_ptr: &AtomicPtr<HHOOK__>,
hook_proc: unsafe extern "system" fn (c_int, WPARAM, LPARAM) -> LRESULT, hook_proc: unsafe extern "system" fn(c_int, WPARAM, LPARAM) -> LRESULT,
) { ) {
hook_ptr.store( hook_ptr.store(
unsafe { SetWindowsHookExW(hook_id, Some(hook_proc), 0 as HINSTANCE, 0) }, unsafe { SetWindowsHookExW(hook_id, Some(hook_proc), 0 as HINSTANCE, 0) },
@ -172,7 +189,6 @@ fn send_keybd_input(flags: u32, key_code: USizeableDWORD) {
unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) }; unsafe { SendInput(1, &mut input as LPINPUT, size_of::<INPUT>() as c_int) };
} }
pub fn main_res() -> Result<()> { pub fn main_res() -> Result<()> {
// this is just to cause the lazy_static init to run first, so if -h or -v is wanted, we do that // this is just to cause the lazy_static init to run first, so if -h or -v is wanted, we do that
// and exit immediately... todo: how to avoid mutex/lazy_static entirely??? // and exit immediately... todo: how to avoid mutex/lazy_static entirely???
@ -188,25 +204,27 @@ pub fn main_res() -> Result<()> {
// hide window // hide window
// todo: probably should be tray icon someplace in future to quit, and error messages as windows popups etc... // todo: probably should be tray icon someplace in future to quit, and error messages as windows popups etc...
let hwnd = GetConsoleWindow(); let hwnd = GetConsoleWindow();
ShowWindow( hwnd, SW_HIDE ); ShowWindow(hwnd, SW_HIDE);
} }
let mut msg: MSG = unsafe { zeroed() }; let mut msg: MSG = unsafe { zeroed() };
unsafe { GetMessageW(&mut msg, 0 as HWND, 0, 0) }; unsafe { GetMessageW(&mut msg, 0 as HWND, 0, 0) };
//std::thread::sleep(std::time::Duration::from_millis(400000)); //std::thread::sleep(std::time::Duration::from_millis(400000));
Ok(()) Ok(())
} }
#[derive(Debug)] #[derive(Debug)]
struct Config { struct Config {
config_file: String config_file: String,
} }
impl Config { impl Config {
fn new(config_file: String) -> Self { fn new(config_file: String) -> Self {
Config { config_file: config_file } Config {
config_file: config_file,
}
} }
} }
@ -227,14 +245,17 @@ fn parse_args() -> Config {
} }
let args: Vec<_> = env::args().collect(); let args: Vec<_> = env::args().collect();
let mut default_configs = Vec::new(); let mut default_configs = Vec::new();
get_env_push("USERPROFILE", "\\keymap.toml", &mut default_configs); get_env_push("USERPROFILE", "\\keymap.toml", &mut default_configs);
get_env_push("APPDATA", "\\keymap.toml", &mut default_configs); get_env_push("APPDATA", "\\keymap.toml", &mut default_configs);
default_configs.push("keymap.toml".to_string()); default_configs.push("keymap.toml".to_string());
let c_msg = format!("specify the keymap config file to use (default in order: {:?})", default_configs); let c_msg = format!(
"specify the keymap config file to use (default in order: {:?})",
default_configs
);
let mut opts = Options::new(); let mut opts = Options::new();
opts.optflag("h", "help", "prints this help message"); opts.optflag("h", "help", "prints this help message");