use clap::{ArgAction, Parser};
use std::time::Duration;
#[derive(Parser, Debug, Clone)]
#[command(
author,
version,
about,
long_about = "An Interception Tools filter to eliminate keyboard chatter (switch bounce).\n\
Reads Linux input events from stdin, filters rapid duplicate key events, and writes the filtered events to stdout.\n\
Statistics are printed to stderr on exit.\n\
\n\
EXAMPLES:\n\
# Basic filtering (15ms window):\n\
sudo sh -c 'intercept -g /dev/input/by-id/your-keyboard-event-device | intercept-bounce --debounce-time 15ms | uinput -d /dev/input/by-id/your-keyboard-event-device'\n\
\n\
# Filtering with bounce logging:\n\
sudo sh -c 'intercept -g ... | intercept-bounce --debounce-time 20ms --log-bounces | uinput -d ...'\n\
\n\
# Debugging - log all events (no filtering):\n\
sudo sh -c 'intercept -g ... | intercept-bounce --debounce-time 0ms --log-all-events | uinput -d ...'\n\
\n\
# Periodic stats dump:\n\
sudo sh -c 'intercept -g ... | intercept-bounce --log-interval 60s | uinput -d ...'\n\
\n\
# udevmon integration (YAML):\n\
- JOB: \"intercept -g $DEVNODE | intercept-bounce | uinput -d $DEVNODE\"\n\
DEVICE:\n\
LINK: \"/dev/input/by-id/usb-Your_Keyboard_Name-event-kbd\" # Replace this!\n\
\n\
See README for more details and advanced usage."
)]
pub struct Args {
#[arg(short = 't', long, default_value = "25ms", value_parser = humantime::parse_duration)]
pub debounce_time: Duration,
#[arg(long, default_value = "100ms", value_parser = humantime::parse_duration)]
pub near_miss_threshold_time: Duration,
#[arg(long, default_value = "15m", value_parser = humantime::parse_duration)]
pub log_interval: Duration,
#[arg(long, action = clap::ArgAction::SetTrue)]
pub log_all_events: bool,
#[arg(long, action = clap::ArgAction::SetTrue)]
pub log_bounces: bool,
#[arg(long, action = clap::ArgAction::SetTrue)]
pub list_devices: bool,
#[arg(long, action = clap::ArgAction::SetTrue)]
pub stats_json: bool,
#[arg(long, action = clap::ArgAction::SetTrue)]
pub verbose: bool,
#[arg(long, default_value = "0")]
pub ring_buffer_size: usize,
#[arg(long = "debounce-key", value_name = "KEY", action = ArgAction::Append, value_parser = parse_key_identifier)]
pub debounce_keys: Vec<u16>,
#[arg(long = "ignore-key", value_name = "KEY", action = ArgAction::Append, value_parser = parse_key_identifier)]
pub ignore_keys: Vec<u16>,
#[arg(long)]
pub otel_endpoint: Option<String>,
}
pub fn parse_args() -> Args {
Args::parse()
}
fn parse_key_identifier(value: &str) -> Result<u16, String> {
crate::filter::keynames::resolve_key_code(value).ok_or_else(|| {
format!(
"Unknown key identifier '{value}'. Provide either a numeric code or a symbolic name like KEY_VOLUMEDOWN"
)
})
}