diff --git a/Cargo.toml b/Cargo.toml index ae7e280..b835e27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ toml = "0.8" itertools = "0.12" evdev = { git = "https://github.com/emberian/evdev" } nix = { version = "0.28", features = [ "fs", "ioctl", "poll" ] } -humantime = "2.1" bitflags = "2.5" num-integer = "0.1" terminal_size = "0.3" diff --git a/TODO b/TODO index b67cb21..b994019 100644 --- a/TODO +++ b/TODO @@ -3,7 +3,6 @@ Necessary: - calculate maximum length for timeout - calculate minimum $extent for avoiding runtime calcs - lead in gap (or whatever irptransmogrifier calls it) -- Formatting of ir receive is poor/broken - DFA edges can be overlapping - removing overlapping parts - keymap needs aeps/eps/ignore_mask/gap? - lircd decoding toggle_bit_mask with more than 1 bit not supported diff --git a/src/bin/cir.rs b/src/bin/cir.rs index 9a3163e..1663d90 100644 --- a/src/bin/cir.rs +++ b/src/bin/cir.rs @@ -310,6 +310,10 @@ struct Test { /// Stop receiving after first timeout message #[arg(long = "one-shot", short = '1')] one_shot: bool, + + /// Only print raw IR + #[arg(long = "raw", short = 'r')] + raw: bool, } #[cfg(target_os = "linux")] diff --git a/src/bin/commands/test.rs b/src/bin/commands/test.rs index 4c6dfb9..e5ecee3 100644 --- a/src/bin/commands/test.rs +++ b/src/bin/commands/test.rs @@ -1,8 +1,11 @@ -use cir::lirc::{Lirc, LIRC_SCANCODE_FLAG_REPEAT, LIRC_SCANCODE_FLAG_TOGGLE}; +use cir::{ + lirc::{Lirc, LIRC_SCANCODE_FLAG_REPEAT, LIRC_SCANCODE_FLAG_TOGGLE}, + rcdev::Rcdev, +}; use evdev::Device; use nix::{ errno::Errno, - fcntl::{FcntlArg, OFlag}, + fcntl::{fcntl, FcntlArg, OFlag}, poll::{poll, PollFd, PollFlags, PollTimeout}, }; use std::os::{fd::AsFd, unix::io::AsRawFd}; @@ -16,86 +19,20 @@ use super::config::{find_devices, open_lirc, Purpose}; pub fn test(test: &crate::Test) { let rcdev = find_devices(&test.device, Purpose::Receive); + if test.raw { + test_raw(rcdev, test); + } else { + test_all(rcdev, test); + } +} + +fn test_all(rcdev: Rcdev, test: &crate::Test) { let mut scandev = None; let mut rawdev = None; let mut eventdev = None; if let Some(lircdev) = rcdev.lircdev { - let lircpath = PathBuf::from(lircdev); - - let mut lircdev = match Lirc::open(&lircpath) { - Ok(l) => l, - Err(s) => { - eprintln!("error: {}: {}", lircpath.display(), s); - std::process::exit(1); - } - }; - - if test.learning { - let mut learning_mode = false; - - if lircdev.can_measure_carrier() { - if let Err(err) = lircdev.set_measure_carrier(true) { - eprintln!("error: {lircdev}: failed to enable measure carrier: {err}"); - std::process::exit(1); - } - learning_mode = true; - } - - if lircdev.can_use_wideband_receiver() { - if let Err(err) = lircdev.set_wideband_receiver(true) { - eprintln!("error: {lircdev}: failed to enable wideband receiver: {err}"); - std::process::exit(1); - } - learning_mode = true; - } - - if !learning_mode { - eprintln!("error: {lircdev}: lirc device does not support learning mode"); - std::process::exit(1); - } - } else { - if lircdev.can_measure_carrier() { - if let Err(err) = lircdev.set_measure_carrier(false) { - eprintln!("error: {lircdev}: failed to disable measure carrier: {err}"); - std::process::exit(1); - } - } - - if lircdev.can_use_wideband_receiver() { - if let Err(err) = lircdev.set_wideband_receiver(false) { - eprintln!("error: {lircdev}: failed to disable wideband receiver: {err}"); - std::process::exit(1); - } - } - } - - if let Some(timeout) = test.timeout { - if lircdev.can_set_timeout() { - match lircdev.get_min_max_timeout() { - Ok(range) if range.contains(&timeout) => { - if let Err(err) = lircdev.set_timeout(timeout) { - eprintln!("error: {lircdev}: {err}"); - std::process::exit(1); - } - } - Ok(range) => { - eprintln!( - "error: {} not in the supported range {}-{}", - timeout, range.start, range.end - ); - std::process::exit(1); - } - Err(err) => { - eprintln!("error: {lircdev}: {err}"); - std::process::exit(1); - } - } - } else { - eprintln!("error: {lircdev}: changing timeout not supported"); - std::process::exit(1); - } - } + let lircdev = open(lircdev, test); if lircdev.can_receive_raw() { if lircdev.can_receive_scancodes() { @@ -105,27 +42,29 @@ pub fn test(test: &crate::Test) { .scancode_mode() .expect("should be able to switch to scancode mode"); - let raw_fd = lircdev.as_raw_fd(); - - nix::fcntl::fcntl(raw_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)) + fcntl(lircdev.as_raw_fd(), FcntlArg::F_SETFL(OFlag::O_NONBLOCK)) .expect("should be able to set non-blocking"); scandev = Some(lircdev); } + fcntl(lircdev.as_raw_fd(), FcntlArg::F_SETFL(OFlag::O_NONBLOCK)) + .expect("should be able to set non-blocking"); + rawdev = Some(lircdev); } else if lircdev.can_receive_scancodes() { - let raw_fd = lircdev.as_raw_fd(); - - nix::fcntl::fcntl(raw_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)) + fcntl(lircdev.as_raw_fd(), FcntlArg::F_SETFL(OFlag::O_NONBLOCK)) .expect("should be able to set non-blocking"); scandev = Some(lircdev); + } else { + eprintln!("error: {lircdev}: device does not support receiving"); + std::process::exit(1); } } - if let Some(inputdev) = rcdev.inputdev { - let inputdev = match Device::open(&inputdev) { + if let Some(inputdev) = &rcdev.inputdev { + let inputdev = match Device::open(inputdev) { Ok(l) => l, Err(s) => { eprintln!("error: {inputdev}: {s}"); @@ -133,9 +72,7 @@ pub fn test(test: &crate::Test) { } }; - let raw_fd = inputdev.as_raw_fd(); - - nix::fcntl::fcntl(raw_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)) + fcntl(inputdev.as_raw_fd(), FcntlArg::F_SETFL(OFlag::O_NONBLOCK)) .expect("should be able to set non-blocking"); eventdev = Some(inputdev); @@ -145,8 +82,8 @@ pub fn test(test: &crate::Test) { let mut carrier = None; let mut leading_space = true; let mut scanbuf = Vec::with_capacity(1024); - let mut last_event_time = None; - let mut last_lirc_time = None; + + println!("Testing events. Press Ctrl+C to abort"); 'outer: loop { if let Some(lircdev) = &mut rawdev { @@ -157,6 +94,8 @@ pub fn test(test: &crate::Test) { } } + print!("{lircdev}: raw: "); + for entry in &rawbuf { if entry.is_space() { if !leading_space { @@ -165,7 +104,6 @@ pub fn test(test: &crate::Test) { } else if entry.is_pulse() { if leading_space { leading_space = false; - print!("raw ir: ") } print!("+{} ", entry.value()); } else if entry.is_frequency() { @@ -194,6 +132,8 @@ pub fn test(test: &crate::Test) { leading_space = true; } } + + println!(); } if let Some(lircdev) = &mut scandev { @@ -212,28 +152,8 @@ pub fn test(test: &crate::Test) { (entry.timestamp % 1_000_000_000) as u32, ); - if let Some(last) = last_lirc_time { - if timestamp > last { - print!( - "lirc: later: {}, ", - humantime::format_duration(timestamp - last) - ); - } else if timestamp < last { - print!( - "lirc: earlier: {}, ", - humantime::format_duration(last - timestamp) - ); - } else { - print!("lirc: same time, "); - } - } else { - print!("lirc: "); - }; - - last_lirc_time = Some(timestamp); - println!( - "scancode={:x} keycode={:?}{}{}", + "{lircdev}: scancode: timestamp={timestamp:?} scancode={:x} keycode={:?}{}{}", entry.scancode, keycode, if (entry.flags & LIRC_SCANCODE_FLAG_REPEAT) != 0 { @@ -251,6 +171,8 @@ pub fn test(test: &crate::Test) { } if let Some(eventdev) = &mut eventdev { + let display_name = rcdev.inputdev.as_ref().unwrap(); + match eventdev.fetch_events() { Ok(iterator) => { for ev in iterator { @@ -259,27 +181,13 @@ pub fn test(test: &crate::Test) { .elapsed() .expect("input time should never exceed system time"); - if let Some(last) = last_event_time { - if timestamp > last { - print!( - "event: later: {}, type: ", - humantime::format_duration(timestamp - last) - ); - } else if timestamp < last { - print!( - "event: earlier: {}, type: ", - humantime::format_duration(last - timestamp) - ); - } else { - print!("event: same time, type: "); - } - } else { - print!("event: type: "); - }; - - last_event_time = Some(timestamp); - - println!("{ev:?}"); + let ty = ev.event_type(); + + let summary = ev.destructure(); + + println!( + "{display_name}: event: timestamp={timestamp:?} {ty:?} {summary:?}" + ); } } Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => (), @@ -290,7 +198,7 @@ pub fn test(test: &crate::Test) { } } - let mut polls: Vec = Vec::new(); + let mut polls = Vec::with_capacity(3); if let Some(dev) = &scandev { polls.push(PollFd::new(dev.as_fd(), PollFlags::POLLIN)); @@ -312,3 +220,145 @@ pub fn test(test: &crate::Test) { } } } + +fn test_raw(rcdev: Rcdev, test: &crate::Test) { + if let Some(lircdev) = rcdev.lircdev { + let mut lircdev = open(lircdev, test); + + if !lircdev.can_receive_raw() { + eprintln!("error: {lircdev} does not support raw mode"); + } + + let mut rawbuf = Vec::with_capacity(1024); + let mut leading_space = true; + let mut carrier = None; + + 'outer: loop { + if let Err(err) = lircdev.receive_raw(&mut rawbuf) { + if err.kind() != std::io::ErrorKind::WouldBlock { + eprintln!("error: {err}"); + std::process::exit(1); + } + } + + for entry in &rawbuf { + if entry.is_space() { + if !leading_space { + print!("-{} ", entry.value()); + } + } else if entry.is_pulse() { + if leading_space { + leading_space = false; + } + print!("+{} ", entry.value()); + } else if entry.is_frequency() { + carrier = Some(entry.value()); + } else if entry.is_overflow() { + if let Some(freq) = carrier { + println!(" # receiver overflow, carrier {freq}Hz"); + carrier = None; + } else { + println!(" # receiver overflow"); + } + if test.one_shot { + break 'outer; + } + leading_space = true; + } else if entry.is_timeout() { + if let Some(freq) = carrier { + println!(" # timeout {}, carrier {}Hz", entry.value(), freq); + carrier = None; + } else { + println!(" # timeout {}", entry.value()); + } + if test.one_shot { + break 'outer; + } + leading_space = true; + } + } + } + } else { + eprintln!("error: {}: has no lirc device", rcdev.name); + std::process::exit(1); + } +} + +fn open(lircdev: String, test: &crate::Test) -> Lirc { + let lircpath = PathBuf::from(lircdev); + + let mut lircdev = match Lirc::open(&lircpath) { + Ok(l) => l, + Err(s) => { + eprintln!("error: {}: {}", lircpath.display(), s); + std::process::exit(1); + } + }; + + if test.learning { + let mut learning_mode = false; + + if lircdev.can_measure_carrier() { + if let Err(err) = lircdev.set_measure_carrier(true) { + eprintln!("error: {lircdev}: failed to enable measure carrier: {err}"); + std::process::exit(1); + } + learning_mode = true; + } + + if lircdev.can_use_wideband_receiver() { + if let Err(err) = lircdev.set_wideband_receiver(true) { + eprintln!("error: {lircdev}: failed to enable wideband receiver: {err}"); + std::process::exit(1); + } + learning_mode = true; + } + + if !learning_mode { + eprintln!("error: {lircdev}: lirc device does not support learning mode"); + std::process::exit(1); + } + } else { + if lircdev.can_measure_carrier() { + if let Err(err) = lircdev.set_measure_carrier(false) { + eprintln!("error: {lircdev}: failed to disable measure carrier: {err}"); + std::process::exit(1); + } + } + + if lircdev.can_use_wideband_receiver() { + if let Err(err) = lircdev.set_wideband_receiver(false) { + eprintln!("error: {lircdev}: failed to disable wideband receiver: {err}"); + std::process::exit(1); + } + } + } + + if let Some(timeout) = test.timeout { + if lircdev.can_set_timeout() { + match lircdev.get_min_max_timeout() { + Ok(range) if range.contains(&timeout) => { + if let Err(err) = lircdev.set_timeout(timeout) { + eprintln!("error: {lircdev}: {err}"); + std::process::exit(1); + } + } + Ok(range) => { + eprintln!( + "error: {} not in the supported range {}-{}", + timeout, range.start, range.end + ); + std::process::exit(1); + } + Err(err) => { + eprintln!("error: {lircdev}: {err}"); + std::process::exit(1); + } + } + } else { + eprintln!("error: {lircdev}: changing timeout not supported"); + std::process::exit(1); + } + } + lircdev +}