move command line parsing to clap; address clippy

pull/77/head
Roland Fredenhagen 2 years ago
parent 937841532d
commit b3c3c10755
No known key found for this signature in database
GPG Key ID: 094AF99241035EB6

98
Cargo.lock generated

@ -132,6 +132,36 @@ dependencies = [
"winapi",
]
[[package]]
name = "clap"
version = "3.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63edc3f163b3c71ec8aa23f9bd6070f77edbf3d1d198b164afa90ff00e4ec62"
dependencies = [
"atty",
"bitflags",
"clap_derive",
"indexmap",
"lazy_static",
"os_str_bytes",
"strsim",
"termcolor",
"textwrap",
]
[[package]]
name = "clap_derive"
version = "3.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a1132dc3944b31c20dd8b906b3a9f0a5d0243e092d59171414969657ac6aa85"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "concurrent-queue"
version = "1.2.2"
@ -379,15 +409,6 @@ dependencies = [
"slab",
]
[[package]]
name = "getopts"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
dependencies = [
"unicode-width",
]
[[package]]
name = "gimli"
version = "0.26.1"
@ -400,6 +421,12 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.1.19"
@ -577,6 +604,15 @@ version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "os_str_bytes"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]]
name = "parking"
version = "2.0.0"
@ -633,6 +669,30 @@ dependencies = [
"toml",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.34"
@ -853,6 +913,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
[[package]]
name = "thiserror"
version = "1.0.30"
@ -882,12 +948,6 @@ dependencies = [
"serde",
]
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.2"
@ -900,6 +960,12 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "waker-fn"
version = "1.1.0"
@ -969,9 +1035,9 @@ dependencies = [
name = "xremap"
version = "0.2.5"
dependencies = [
"clap",
"env_logger",
"evdev",
"getopts",
"indoc",
"lazy_static",
"log",

@ -4,13 +4,12 @@ version = "0.2.5"
edition = "2021"
description = "Dynamic key remapp for X and Wayland"
license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "3.0.14", features = ["derive"] }
env_logger = "0.9.0"
evdev = "0.11.3"
getopts = "0.2"
indoc = "1.0"
lazy_static = "1.4.0"
log = "0.4.14"
@ -19,9 +18,9 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_with = { version = "1.11", features = ["chrono"] }
serde_yaml = "0.8"
zbus = { version = "1.9.2", optional = true }
x11_rs = { package = "x11", version = "2.19.1", features = ["xlib"], optional = true }
swayipc = { version = "2.7.2", optional = true }
x11_rs = { package = "x11", version = "2.19.1", features = ["xlib"], optional = true }
zbus = { version = "1.9.2", optional = true }
[features]
gnome = ["zbus"]

@ -21,7 +21,7 @@ impl WMClient {
}
pub fn current_application(&mut self) -> Option<String> {
if let None = self.supported {
if self.supported.is_none() {
let supported = self.client.supported();
self.supported = Some(supported);
println!("application-client: {} (supported: {})", self.name, supported);

@ -29,7 +29,7 @@ where
let mut action = HashMap::<String, HashMap<KeyPress, Actions>>::deserialize(deserializer)?;
if let Some(remap) = action.remove("remap") {
if action.is_empty() {
return Ok(remap.into_iter().map(|(k, v)| (k, v.to_vec())).collect());
return Ok(remap.into_iter().map(|(k, v)| (k, v.into_vec())).collect());
}
}
Err(de::Error::custom("not a map with a single \"remap\" key"))
@ -96,7 +96,7 @@ pub enum Actions {
}
impl Actions {
pub fn to_vec(self) -> Vec<Action> {
pub fn into_vec(self) -> Vec<Action> {
match self {
Actions::Action(action) => vec![action],
Actions::Actions(actions) => actions,

@ -8,7 +8,7 @@ where
D: Deserializer<'de>,
{
let key = String::deserialize(deserializer)?;
Ok(parse_key(&key).map_err(serde::de::Error::custom)?)
parse_key(&key).map_err(serde::de::Error::custom)
}
pub fn parse_key(input: &str) -> Result<Key, Box<dyn Error>> {

@ -33,12 +33,12 @@ impl<'de> Deserialize<'de> for KeyPress {
D: Deserializer<'de>,
{
let key_press = String::deserialize(deserializer)?;
Ok(parse_key_press(&key_press).map_err(serde::de::Error::custom)?)
parse_key_press(&key_press).map_err(serde::de::Error::custom)
}
}
fn parse_key_press(input: &str) -> Result<KeyPress, Box<dyn error::Error>> {
let keys: Vec<&str> = input.split("-").collect();
let keys: Vec<&str> = input.split('-').collect();
if let Some((key, modifiers)) = keys.split_last() {
let mut shift = ModifierState::None;
let mut control = ModifierState::None;

@ -24,7 +24,7 @@ where
.flat_map(|(key_press, actions)| {
expand_modifiers(key_press)
.into_iter()
.map(|k| (k, actions.clone().to_vec()))
.map(|k| (k, actions.clone().into_vec()))
.collect::<Vec<(KeyPress, Vec<Action>)>>()
})
.collect())
@ -52,7 +52,7 @@ fn expand_modifier(key_press: KeyPress, modifier: &Modifier) -> Vec<KeyPress> {
change_modifier(key_press, modifier, ModifierState::Right),
]
.into_iter()
.flat_map(|key_press| expand_modifiers(key_press))
.flat_map(expand_modifiers)
.collect()
}
@ -70,7 +70,7 @@ fn change_modifier(key_press: KeyPress, modifier: &Modifier, state: ModifierStat
}
KeyPress {
key: key_press.key.clone(),
key: key_press.key,
shift,
control,
alt,

@ -14,7 +14,7 @@ extern crate serde_yaml;
use keymap::Keymap;
use modmap::Modmap;
use serde::Deserialize;
use std::{error, fs};
use std::{error, fs, path::Path};
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
@ -25,8 +25,8 @@ pub struct Config {
pub keymap: Vec<Keymap>,
}
pub fn load_config(filename: &str) -> Result<Config, Box<dyn error::Error>> {
pub fn load_config(filename: &Path) -> Result<Config, Box<dyn error::Error>> {
let yaml = fs::read_to_string(&filename)?;
let config: Config = serde_yaml::from_str(&yaml)?;
return Ok(config);
Ok(config)
}

@ -139,7 +139,7 @@ fn test_keymap_mark() {
}
fn assert_parse(yaml: &str) {
let result: Result<Config, Error> = serde_yaml::from_str(&yaml);
let result: Result<Config, Error> = serde_yaml::from_str(yaml);
if let Err(e) = result {
assert!(false, "{}", e)
}

@ -39,7 +39,7 @@ pub fn output_device() -> Result<VirtualDevice, Box<dyn Error>> {
for code in Key::KEY_RESERVED.code()..Key::BTN_TRIGGER_HAPPY40.code() {
let key = Key::new(code);
let name = format!("{:?}", key);
if name.starts_with("KEY_") || MOUSE_BTNS.contains(&&**&name) {
if name.starts_with("KEY_") || MOUSE_BTNS.contains(&&*name) {
keys.insert(key);
}
}
@ -70,12 +70,12 @@ pub fn device_watcher(watch: bool) -> Result<Option<Inotify>, Box<dyn Error>> {
}
pub fn input_devices(
device_opts: &Vec<String>,
ignore_opts: &Vec<String>,
device_opts: &[String],
ignore_opts: &[String],
watch: bool,
) -> Result<Vec<Device>, Box<dyn Error>> {
let mut path_devices = list_devices()?;
let mut paths: Vec<String> = path_devices.keys().map(|e| e.clone()).collect();
let mut paths: Vec<String> = path_devices.keys().cloned().collect();
paths.sort_by(|a, b| device_index(a).partial_cmp(&device_index(b)).unwrap());
println!("Selecting devices from the following list:");
@ -130,14 +130,14 @@ pub fn input_devices(
.grab()
.map_err(|e| format!("Failed to grab device '{}': {}", device_name(device), e))?;
}
return Ok(devices);
Ok(devices)
}
// We can't know the device path from evdev::enumerate(). So we re-implement it.
fn list_devices() -> Result<HashMap<String, Device>, Box<dyn Error>> {
let mut path_devices: HashMap<String, Device> = HashMap::new();
if let Some(dev_input) = read_dir("/dev/input").as_mut().ok() {
while let Some(entry) = dev_input.next() {
if let Ok(dev_input) = read_dir("/dev/input").as_mut() {
for entry in dev_input {
let path = entry?.path();
if let Some(fname) = path.file_name() {
if fname.as_bytes().starts_with(b"event") {
@ -151,7 +151,7 @@ fn list_devices() -> Result<HashMap<String, Device>, Box<dyn Error>> {
}
}
}
return Ok(path_devices);
Ok(path_devices)
}
fn device_name(device: &Device) -> &str {
@ -166,9 +166,9 @@ fn current_device_name() -> String {
format!("xremap pid={}", process::id())
}
fn match_device(path: &str, device: &Device, device_opts: &Vec<String>) -> bool {
fn match_device(path: &str, device: &Device, device_opts: &[String]) -> bool {
// Force unmatch its own device
if device_name(device) == &current_device_name() {
if device_name(device) == current_device_name() {
return false;
}
@ -186,7 +186,7 @@ fn match_device(path: &str, device: &Device, device_opts: &Vec<String>) -> bool
return true;
}
}
return false;
false
}
fn is_keyboard(device: &Device) -> bool {

@ -55,7 +55,7 @@ impl EventHandler {
debug!("=> {}: {:?}", event.value(), &key);
// Apply modmap
let mut key_values = if let Some(key_action) = self.find_modmap(&config, &key) {
let mut key_values = if let Some(key_action) = self.find_modmap(config, &key) {
self.dispatch_keys(key_action, key, event.value())
} else {
vec![(key, event.value())]
@ -97,11 +97,11 @@ impl EventHandler {
fn dispatch_keys(&mut self, key_action: KeyAction, key: Key, value: i32) -> Vec<(Key, i32)> {
match key_action {
KeyAction::Key(modmap_key) => vec![(modmap_key.clone(), value)],
KeyAction::Key(modmap_key) => vec![(modmap_key, value)],
KeyAction::MultiPurposeKey(multi_purpose_key) => {
if value == PRESS {
self.multi_purpose_keys.insert(
key.clone(),
key,
MultiPurposeKeyState {
held: multi_purpose_key.held,
alone: multi_purpose_key.alone,
@ -149,7 +149,7 @@ impl EventHandler {
fn find_modmap(&mut self, config: &Config, key: &Key) -> Option<KeyAction> {
for modmap in &config.modmap {
if let Some(key_action) = modmap.remap.get(&key) {
if let Some(key_action) = modmap.remap.get(key) {
if let Some(application_matcher) = &modmap.application {
if !self.match_application(application_matcher) {
continue;
@ -163,7 +163,7 @@ impl EventHandler {
fn find_keymap(&mut self, config: &Config, key: &Key) -> Option<Vec<Action>> {
let key_press = KeyPress {
key: key.clone(),
key: *key,
shift: self.shift.to_modifier_state(),
control: self.control.to_modifier_state(),
alt: self.alt.to_modifier_state(),
@ -183,7 +183,7 @@ impl EventHandler {
continue;
}
}
return Some(actions.iter().map(|a| a.clone()).collect());
return Some(actions.to_vec());
}
}
None
@ -195,7 +195,7 @@ impl EventHandler {
Action::Remap(remap) => {
let mut override_remap: HashMap<KeyPress, Vec<Action>> = HashMap::new();
for (key_press, actions) in remap.iter() {
override_remap.insert(key_press.clone(), actions.iter().map(|a| a.clone()).collect());
override_remap.insert(key_press.clone(), actions.to_vec());
}
self.override_remap = Some(override_remap)
}
@ -318,7 +318,7 @@ impl EventHandler {
shift = ModifierState::Left;
}
KeyPress {
key: key_press.key.clone(),
key: key_press.key,
shift,
control: key_press.control.clone(),
alt: key_press.alt.clone(),
@ -351,7 +351,7 @@ impl EventHandler {
fn match_application(&mut self, application_matcher: &Application) -> bool {
// Lazily fill the wm_class cache
if let None = self.application_cache {
if self.application_cache.is_none() {
match self.application_client.current_application() {
Some(application) => self.application_cache = Some(application),
None => self.application_cache = Some(String::new()),
@ -479,10 +479,10 @@ impl MultiPurposeKeyState {
vec![] // still delay the press
} else {
self.alone_timeout_at = None; // timeout
vec![(self.held.clone(), PRESS)]
vec![(self.held, PRESS)]
}
} else {
vec![(self.held.clone(), REPEAT)]
vec![(self.held, REPEAT)]
}
}
@ -490,20 +490,20 @@ impl MultiPurposeKeyState {
if let Some(alone_timeout_at) = &self.alone_timeout_at {
if Instant::now() < *alone_timeout_at {
// dispatch the delayed press and this release
vec![(self.alone.clone(), PRESS), (self.alone.clone(), RELEASE)]
vec![(self.alone, PRESS), (self.alone, RELEASE)]
} else {
// too late. dispatch the held key
vec![(self.held.clone(), PRESS), (self.held.clone(), RELEASE)]
vec![(self.held, PRESS), (self.held, RELEASE)]
}
} else {
vec![(self.held.clone(), RELEASE)]
vec![(self.held, RELEASE)]
}
}
fn force_held(&mut self) -> Vec<(Key, i32)> {
if self.alone_timeout_at.is_some() {
self.alone_timeout_at = None;
vec![(self.held.clone(), PRESS)]
vec![(self.held, PRESS)]
} else {
vec![]
}

@ -1,70 +1,89 @@
use crate::config::Config;
use crate::device::{device_watcher, input_devices, output_device};
use crate::event_handler::EventHandler;
use clap::{ArgEnum, Parser};
use evdev::uinput::VirtualDevice;
use evdev::{Device, EventType};
use getopts::Options;
use nix::sys::inotify::Inotify;
use nix::sys::select::select;
use nix::sys::select::FdSet;
use std::env;
use std::error::Error;
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use std::process::exit;
extern crate getopts;
mod client;
mod config;
mod device;
mod event_handler;
#[derive(Parser, Debug)]
#[clap(version)]
struct Opts {
/// Include a device name or path
#[clap(long, use_delimiter = true)]
device: Vec<String>,
/// Ignore a device name or path
#[clap(long, use_delimiter = true)]
ignore: Vec<String>,
#[clap(
long,
arg_enum,
min_values = 0,
use_delimiter = true,
require_equals = true,
default_missing_value = "device",
verbatim_doc_comment,
hide_possible_values = true,
// Separating the help like this is necessary due to
// https://github.com/clap-rs/clap/issues/3312
help = "Targets to watch [possible values: device, config]"
)]
/// Targets to watch
///
/// - Device to add new devices automatically
/// - Config to reload the config automatically
watch: Vec<WatchTargets>,
/// Config file
config: PathBuf,
}
#[derive(ArgEnum, Clone, Copy, Debug, PartialEq, Eq)]
enum WatchTargets {
/// Device to add new devices automatically
Device,
/// Config to reload the config automatically
Config,
}
fn main() {
env_logger::init();
let argv: Vec<String> = env::args().collect();
let program = argv[0].clone();
let mut opts = Options::new();
opts.optmulti("", "device", "Include a device name or path", "NAME");
opts.optmulti("", "ignore", "Exclude a device name or path", "NAME");
opts.optflag("", "watch", "Add new devices automatically");
opts.optflag("h", "help", "print this help menu");
opts.optflag("", "version", "show version");
let Opts {
device,
ignore,
watch,
config,
} = dbg!(Opts::parse());
let args = match opts.parse(&argv[1..]) {
Ok(args) => args,
Err(e) => abort(&e.to_string()),
};
if args.opt_present("h") {
println!("{}", &usage(&program, opts));
return;
}
if args.opt_present("version") {
println!("xremap version {}", env!("CARGO_PKG_VERSION"));
return;
}
let filename = match &args.free.iter().map(String::as_str).collect::<Vec<&str>>()[..] {
&[filename] => filename,
&[..] => abort(&usage(&program, opts)),
};
let config = match config::load_config(&filename) {
let config = match config::load_config(&config) {
Ok(config) => config,
Err(e) => abort(&format!("Failed to load config '{}': {}", filename, e)),
Err(e) => abort(&format!("Failed to load config '{}': {}", config.display(), e)),
};
let watch = args.opt_present("watch");
let watch_devices = watch.contains(&WatchTargets::Device);
loop {
let output_device = match output_device() {
Ok(output_device) => output_device,
Err(e) => abort(&format!("Failed to prepare an output device: {}", e)),
};
let input_devices = match input_devices(&args.opt_strs("device"), &args.opt_strs("ignore"), watch) {
let input_devices = match input_devices(&device, &ignore, watch_devices) {
Ok(input_devices) => input_devices,
Err(e) => abort(&format!("Failed to prepare input devices: {}", e)),
};
if let Err(e) = event_loop(output_device, input_devices, &config, watch) {
if let Err(e) = event_loop(output_device, input_devices, &config, watch_devices) {
if e.to_string().starts_with("No such device") {
println!("Found a removed device. Reselecting devices.");
continue;
@ -104,7 +123,7 @@ fn event_loop(
}
}
fn select_readable(devices: &Vec<Device>, watcher: &Option<Inotify>) -> Result<FdSet, Box<dyn Error>> {
fn select_readable(devices: &[Device], watcher: &Option<Inotify>) -> Result<FdSet, Box<dyn Error>> {
let mut read_fds = FdSet::new();
for device in devices {
read_fds.insert(device.as_raw_fd());
@ -113,12 +132,7 @@ fn select_readable(devices: &Vec<Device>, watcher: &Option<Inotify>) -> Result<F
read_fds.insert(inotify.as_raw_fd());
}
select(None, &mut read_fds, None, None, None)?;
return Ok(read_fds);
}
fn usage(program: &str, opts: Options) -> String {
let brief = format!("Usage: {} CONFIG [options]", program);
opts.usage(&brief)
Ok(read_fds)
}
fn abort(message: &str) -> ! {

Loading…
Cancel
Save