diff --git a/Cargo.toml b/Cargo.toml index 4a58146..4b5e961 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,9 @@ name = "qmk-host" path = "src/main.rs" [dependencies] -hidapi = "2" # Cross-platform Raw HID access -anyhow = "1" # Ergonomic error handling -log = "0.4" # Logging facade -env_logger = "0.11" # Logger implementation (RUST_LOG env var) -ctrlc = "3" # Ctrl+C / SIGINT handler +hidapi = "2" # Cross-platform Raw HID access +anyhow = "1" # Ergonomic error handling +log = "0.4" # Logging facade +env_logger = "0.11" # Logger implementation (RUST_LOG env var) +ctrlc = "3" # Ctrl+C / SIGINT handler +clap = { version = "4", features = ["derive"] } # CLI argument parsing diff --git a/src/keyboard.rs b/src/keyboard.rs index 724fe11..7681dd3 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -15,7 +15,7 @@ use std::time::Duration; use anyhow::{Context, Result}; use hidapi::HidApi; -use log::{debug, error, info, warn}; +use log::{debug, error, info, trace, warn}; use crate::protocol::{Packet, PACKET_SIZE, USAGE_ID, USAGE_PAGE}; @@ -122,10 +122,14 @@ fn io_loop( // --- Drain the outgoing write queue (non-blocking) ----------------- while let Ok(pkt) = rx.try_recv() { write_buf[1..].copy_from_slice(&pkt.0); + trace!("[kbd {:02x}] TX {:02x?}", device_id, &write_buf[1..]); if let Err(e) = device.write(&write_buf) { error!("[kbd {:02x}] write error: {}", device_id, e); } else { - debug!("[kbd {:02x}] sent cmd=0x{:02x}", device_id, pkt.cmd()); + debug!( + "[kbd {:02x}] TX cmd=0x{:02x} src=0x{:02x} flags=0x{:02x}", + device_id, pkt.cmd(), pkt.src(), pkt.flags() + ); } } @@ -136,11 +140,13 @@ fn io_loop( } Ok(_n) => { let pkt = Packet(read_buf); + trace!("[kbd {:02x}] RX {:02x?}", device_id, &read_buf[..]); if pkt.is_custom() { debug!( - "[kbd {:02x}] received cmd=0x{:02x} flags=0x{:02x}", + "[kbd {:02x}] RX cmd=0x{:02x} src=0x{:02x} flags=0x{:02x}", device_id, pkt.cmd(), + pkt.src(), pkt.flags() ); if to_broker.send((device_id, pkt)).is_err() { diff --git a/src/main.rs b/src/main.rs index 76a8374..40a0ea2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,11 @@ //! # Usage //! //! ``` -//! RUST_LOG=debug qmk-host +//! qmk-host # info-level output (connections, value changes) +//! qmk-host -v # debug-level: log every packet cmd/src/flags +//! qmk-host -vv # trace-level: full 32-byte hex dump of every packet +//! qmk-host -q # quiet: errors only +//! RUST_LOG=debug qmk-host # env var still works as an override //! ``` //! //! # Permissions @@ -36,6 +40,7 @@ use std::sync::mpsc; use std::thread; use anyhow::{Context, Result}; +use clap::Parser; use log::{error, info, warn}; mod app_monitor; @@ -47,9 +52,59 @@ mod volume; use protocol::{DEV_HOST, Packet}; +// --------------------------------------------------------------------------- +// CLI +// --------------------------------------------------------------------------- + +/// Raw HID bridge for QMK keyboards. +/// +/// Syncs layer state between keyboards and delivers host data (volume, +/// brightness) via the QMK raw-HID interface. +#[derive(Parser)] +#[command(name = "qmk-host", version)] +struct Args { + /// Increase log verbosity. + /// + /// Pass once (-v) for debug output (every packet's command, source and + /// flags). Pass twice (-vv) for trace output (full 32-byte hex dump of + /// every packet sent and received). The RUST_LOG environment variable + /// still overrides this flag when set. + #[arg(short, long, action = clap::ArgAction::Count)] + verbose: u8, + + /// Suppress all output except errors. + #[arg(short, long)] + quiet: bool, +} + +impl Args { + /// Resolve the effective log-level filter string from the CLI flags. + /// The caller passes this to `env_logger` as the default; `RUST_LOG` + /// will still override it if set. + fn log_level(&self) -> &'static str { + if self.quiet { + "error" + } else { + match self.verbose { + 0 => "info", + 1 => "debug", + _ => "trace", + } + } + } +} + +// --------------------------------------------------------------------------- +// Entry point +// --------------------------------------------------------------------------- + fn main() -> Result<()> { - // Initialise logging. Set RUST_LOG=debug for verbose output. - env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); + let args = Args::parse(); + + env_logger::Builder::from_env( + env_logger::Env::default().default_filter_or(args.log_level()), + ) + .init(); info!("qmk-host starting");