From b10d0b170397fe2bd1b0af73f66a9dbc5ee62d79 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sat, 27 Apr 2024 12:52:26 +0100 Subject: [PATCH] Add decode using keymap Signed-off-by: Sean Young --- TODO | 4 +- irp/src/decoder.rs | 2 +- irp/src/lib.rs | 2 +- irp/src/message.rs | 8 +- src/bin/cir.rs | 18 ++- src/bin/commands/decode.rs | 226 ++++++++++++++++++++++++++++++- src/keymap/decode.rs | 108 +++++++++++++++ src/keymap/encode.rs | 34 +++-- src/keymap/mod.rs | 1 + src/keymap/parse.rs | 11 +- src/keymap/protocol.rs | 30 ++++ testdata/rc_keymaps/sony-12.toml | 14 ++ testdata/rc_keymaps/sony.toml | 13 ++ tests/decode_tests.rs | 104 +++++++++++++- tests/encode_tests.rs | 2 +- 15 files changed, 533 insertions(+), 44 deletions(-) create mode 100644 src/keymap/decode.rs create mode 100644 testdata/rc_keymaps/sony-12.toml create mode 100644 testdata/rc_keymaps/sony.toml diff --git a/TODO b/TODO index 086ff21..314767d 100644 --- a/TODO +++ b/TODO @@ -11,11 +11,9 @@ - DFA edges can be overlapping - removing overlapping parts - rc_mapping() kfunc - cir decode irp should read IrpProtocols.xml -- cir decode keymap should support keymap.toml - access to io_error in aya -- parse of keymap.toml should ensure that raw codes have trailing spaces - -> remove Message.extend() hack - cir transmit rawir -S sony15:12 -r 2 ?? +- keymap needs aeps/eps/ignore_mask/gap? irp language oddities diff --git a/irp/src/decoder.rs b/irp/src/decoder.rs index 3361203..c114a85 100644 --- a/irp/src/decoder.rs +++ b/irp/src/decoder.rs @@ -8,7 +8,7 @@ use log::trace; use std::{collections::HashMap, fmt, fmt::Write}; /// NFA Decoder state -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Decoder<'a> { pos: Vec<(usize, Vartable<'a>)>, options: Options<'a>, diff --git a/irp/src/lib.rs b/irp/src/lib.rs index 3e05018..391e1f9 100644 --- a/irp/src/lib.rs +++ b/irp/src/lib.rs @@ -222,7 +222,7 @@ impl fmt::Display for Event { } /// Options for the decoder -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone)] pub struct Options<'a> { /// Name of the decoder pub name: &'a str, diff --git a/irp/src/message.rs b/irp/src/message.rs index 587a0c1..5f94055 100644 --- a/irp/src/message.rs +++ b/irp/src/message.rs @@ -17,8 +17,10 @@ impl Message { } } - /// Concatenate to packets + /// Concatenate two messages. Self must have a trailing gap. pub fn extend(&mut self, other: &Message) { + assert!(self.raw.is_empty() || self.has_trailing_gap()); + if self.carrier.is_none() { self.carrier = other.carrier; } @@ -27,10 +29,6 @@ impl Message { self.duty_cycle = other.duty_cycle; } - if !self.raw.is_empty() && !self.has_trailing_gap() { - self.raw.push(20000); - } - self.raw.extend_from_slice(&other.raw); } diff --git a/src/bin/cir.rs b/src/bin/cir.rs index d096317..f53e306 100644 --- a/src/bin/cir.rs +++ b/src/bin/cir.rs @@ -125,8 +125,8 @@ enum DecodeCommands { #[command(about = "Decode using IRP Notation")] Irp(DecodeIrp), - #[command(about = "Decode using lircd.conf file")] - Lircd(DecodeLircd), + #[command(about = "Decode using keymap or lircd.conf file")] + Keymap(DecodeKeymap), } #[derive(Args)] @@ -136,9 +136,9 @@ struct DecodeIrp { } #[derive(Args)] -struct DecodeLircd { - /// lircd.conf file - lircdconf: OsString, +struct DecodeKeymap { + /// Keymap or lircd.conf file + keymap: PathBuf, } #[cfg(target_os = "linux")] @@ -541,8 +541,12 @@ fn main() { DecodeCommands::Irp(irp) => { commands::decode::decode_irp(decode, &irp.irp); } - DecodeCommands::Lircd(lircd) => { - commands::decode::decode_lircd(decode, &lircd.lircdconf); + DecodeCommands::Keymap(keymap) => { + if keymap.keymap.to_string_lossy().ends_with(".lircd.conf") { + commands::decode::decode_lircd(decode, &keymap.keymap); + } else { + commands::decode::decode_keymap(decode, &keymap.keymap); + } } }, Commands::Transmit(transmit) => commands::transmit::transmit(transmit), diff --git a/src/bin/commands/decode.rs b/src/bin/commands/decode.rs index c701fdd..26e03be 100644 --- a/src/bin/commands/decode.rs +++ b/src/bin/commands/decode.rs @@ -2,13 +2,13 @@ use super::config::{find_devices, Purpose}; #[cfg(target_os = "linux")] use cir::lirc; -use cir::lircd_conf::parse; +use cir::{keymap::Keymap, lircd_conf::parse}; use irp::{Decoder, InfraredData, Irp, Message, Options}; use itertools::Itertools; use log::{error, info}; #[cfg(target_os = "linux")] use std::path::PathBuf; -use std::{ffi::OsString, fs, path::Path}; +use std::{fs, path::Path}; pub fn decode_irp(decode: &crate::Decode, irp_str: &String) { #[allow(unused_mut)] @@ -232,7 +232,227 @@ pub fn decode_irp(decode: &crate::Decode, irp_str: &String) { } } -pub fn decode_lircd(decode: &crate::Decode, conf: &OsString) { +pub fn decode_keymap(decode: &crate::Decode, path: &Path) { + #[allow(unused_mut)] + let mut abs_tolerance = decode.options.aeps; + let rel_tolerance = decode.options.eps; + #[allow(unused_mut)] + let mut max_gap = 100000; + + let keymaps = match Keymap::parse(path) { + Ok(r) => r, + Err(e) => { + log::error!("{e}"); + std::process::exit(2); + } + }; + + let input_on_cli = !decode.file.is_empty() || !decode.rawir.is_empty(); + #[cfg(not(target_os = "linux"))] + if !input_on_cli { + eprintln!("no infrared input provided"); + std::process::exit(2); + } + + #[cfg(target_os = "linux")] + let lircdev = if !input_on_cli { + // open lirc + let rcdev = find_devices(&decode.device, Purpose::Receive); + + if let Some(lircdev) = rcdev.lircdev { + let lircpath = PathBuf::from(lircdev); + + log::trace!("opening lirc device: {}", lircpath.display()); + + let mut lircdev = match lirc::open(&lircpath) { + Ok(l) => l, + Err(s) => { + eprintln!("error: {}: {}", lircpath.display(), s); + std::process::exit(1); + } + }; + + if decode.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); + } + } + + if lircdev.can_receive_raw() { + if let Ok(resolution) = lircdev.receiver_resolution() { + if resolution > abs_tolerance { + info!( + "{} resolution is {}, using absolute tolerance {} rather than {}", + lircdev, resolution, resolution, abs_tolerance + ); + + abs_tolerance = resolution; + } + } + + if let Ok(timeout) = lircdev.get_timeout() { + let dev_max_gap = (timeout * 9) / 10; + + log::trace!( + "device reports timeout of {}, using 90% of that as {} max_gap", + timeout, + dev_max_gap + ); + + max_gap = dev_max_gap; + } + + Some(lircdev) + } else { + error!("{}: device cannot receive raw", lircdev); + std::process::exit(1); + } + } else { + error!("{}: no lirc device found", rcdev.name); + std::process::exit(1); + } + } else { + None + }; + + let mut decoders = keymaps + .iter() + .map(|keymap| { + let mut options = Options { + name: &keymap.name, + max_gap, + aeps: abs_tolerance, + eps: rel_tolerance, + ..Default::default() + }; + + options.nfa = decode.options.save_nfa; + options.dfa = decode.options.save_dfa; + + match keymap.decoder(options) { + Ok(decoder) => decoder, + Err(e) => { + log::error!("{}: {e}", path.display()); + std::process::exit(2); + } + } + }) + .collect::>(); + + let mut feed_decoder = |raw: &[InfraredData]| { + for ir in raw { + for decoder in &mut decoders { + decoder.input(*ir, |name, _| { + println!("decoded: keymap:{} code:{}", decoder.remote.name, name); + }); + } + } + }; + + for filename in &decode.file { + let input = match fs::read_to_string(filename) { + Ok(s) => s, + Err(s) => { + error!("{}: {}", Path::new(filename).display(), s); + std::process::exit(2); + } + }; + + info!("parsing ‘{}’ as rawir", filename.to_string_lossy()); + + match Message::parse(&input) { + Ok(raw) => { + info!("decoding: {}", raw.print_rawir()); + feed_decoder(&InfraredData::from_u32_slice(&raw.raw)); + } + Err(msg) => { + info!("parsing ‘{}’ as mode2", filename.to_string_lossy()); + + match Message::parse_mode2(&input) { + Ok(m) => { + info!("decoding: {}", m.print_rawir()); + feed_decoder(&InfraredData::from_u32_slice(&m.raw)); + } + Err((line_no, error)) => { + error!("{}: parse as rawir: {}", Path::new(filename).display(), msg); + error!( + "{}:{}: parse as mode2: {}", + Path::new(filename).display(), + line_no, + error + ); + std::process::exit(2); + } + } + } + } + } + + for rawir in &decode.rawir { + match Message::parse(rawir) { + Ok(raw) => { + info!("decoding: {}", raw.print_rawir()); + feed_decoder(&InfraredData::from_u32_slice(&raw.raw)); + } + Err(msg) => { + error!("parsing ‘{}’: {}", rawir, msg); + std::process::exit(2); + } + } + } + + #[cfg(target_os = "linux")] + if let Some(mut lircdev) = lircdev { + let mut rawbuf = Vec::with_capacity(1024); + + loop { + if let Err(err) = lircdev.receive_raw(&mut rawbuf) { + eprintln!("error: {err}"); + std::process::exit(1); + } + + let raw: Vec<_> = rawbuf + .iter() + .filter_map(|raw| { + if raw.is_pulse() { + Some(InfraredData::Flash(raw.value())) + } else if raw.is_space() || raw.is_timeout() { + Some(InfraredData::Gap(raw.value())) + } else if raw.is_overflow() { + Some(InfraredData::Reset) + } else { + None + } + }) + .collect(); + + log::trace!("decoding: {}", raw.iter().join(" ")); + + feed_decoder(&raw); + } + } +} + +pub fn decode_lircd(decode: &crate::Decode, conf: &PathBuf) { #[allow(unused_mut)] let mut abs_tolerance = decode.options.aeps; let rel_tolerance = decode.options.eps; diff --git a/src/keymap/decode.rs b/src/keymap/decode.rs new file mode 100644 index 0000000..7f28680 --- /dev/null +++ b/src/keymap/decode.rs @@ -0,0 +1,108 @@ +use crate::keymap::LinuxProtocol; + +use super::Keymap; +use irp::{Decoder, InfraredData, Irp, Options, DFA, NFA}; +use log::debug; + +pub struct KeymapDecoder<'a> { + pub remote: &'a Keymap, + pub dfa: Vec, + pub decoder: Vec>, +} + +impl Keymap { + /// Create DFAs for this remote + pub fn build_dfa<'b>(&'b self, options: &Options<'b>) -> Result, String> { + let nfa = if self.raw.is_empty() { + let mut irps = Vec::new(); + if let Some(irp) = &self.irp { + irps.push(irp.as_str()); + } else { + if self.variant.is_none() { + if let Some(protocols) = LinuxProtocol::find_decoder(&self.protocol) { + // TODO: ideally the decoder tells us which protocol was decoded + irps = protocols.iter().filter_map(|p| p.irp).collect(); + } + } + + let protocol = self.variant.as_ref().unwrap_or(&self.protocol); + + if irps.is_empty() { + if let Some(linux_protocol) = LinuxProtocol::find_like(protocol) { + if let Some(irp) = linux_protocol.irp { + irps.push(irp); + } else { + return Err(format!("unable to decode protocol {protocol}")); + } + } else { + return Err(format!("unknown protocol {protocol}")); + } + } + }; + + irps.iter() + .map(|irp| { + debug!("decoding irp {irp} for keymap {}", self.name); + + let irp = Irp::parse(irp).unwrap(); + + irp.build_nfa().unwrap() + }) + .collect() + } else { + let mut nfa = NFA::default(); + + for (i, raw) in self.raw.iter().enumerate() { + let message = self.encode_raw(raw, 0); + nfa.add_raw(&message.raw, irp::Event::Down, u32::MAX as i64 + i as i64); + } + + vec![nfa] + }; + + // TODO: merge NFAs so we end up with on DFA + Ok(nfa.iter().map(|nfa| nfa.build_dfa(options)).collect()) + } + + /// Create a decoder for this remote + pub fn decoder<'b>(&'b self, options: Options<'b>) -> Result, String> { + let dfa = self.build_dfa(&options)?; + + let decoder = vec![Decoder::new(options); dfa.len()]; + + Ok(KeymapDecoder { + remote: self, + dfa, + decoder, + }) + } +} + +impl<'a> KeymapDecoder<'a> { + pub fn input(&mut self, ir: InfraredData, mut callback: F) + where + F: FnMut(&'a str, u64), + { + for i in 0..self.dfa.len() { + self.decoder[i].dfa_input(ir, &self.dfa[i], |_, vars| { + // FIXME: vars do not have to be called CODE + if let Some(decoded) = vars.get("CODE") { + let decoded = *decoded as u64; + if self.remote.raw.is_empty() { + if let Some(key_code) = self.remote.scancodes.get(&decoded) { + callback(key_code, decoded); + } + } else { + let decoded: usize = decoded as usize - u32::MAX as usize; + + callback(&self.remote.raw[decoded].keycode, decoded as u64); + } + } + }) + } + } + + pub fn reset(&mut self) { + self.decoder.iter_mut().for_each(|d| d.reset()); + } +} diff --git a/src/keymap/encode.rs b/src/keymap/encode.rs index 065fb16..71f6389 100644 --- a/src/keymap/encode.rs +++ b/src/keymap/encode.rs @@ -1,4 +1,4 @@ -use super::{Keymap, LinuxProtocol}; +use super::{Keymap, LinuxProtocol, Raw}; use irp::{Irp, Message, Vartable}; use itertools::Itertools; @@ -83,7 +83,7 @@ pub fn encode( for raw_code in &remote.raw { if raw_code.keycode == *encode_code { - let encoded = remote.encode_raw(encode_code, repeats)?; + let encoded = remote.encode_raw(raw_code, repeats); message.extend(&encoded); @@ -109,8 +109,10 @@ impl Keymap { pub fn encode(&self, code: &str, repeats: u64) -> Result { if let Some((scancode, _)) = self.scancodes.iter().find(|(_, v)| *v == code) { self.encode_scancode(*scancode, repeats) + } else if let Some(raw) = self.raw.iter().find(|e| e.keycode == code) { + Ok(self.encode_raw(raw, repeats)) } else { - self.encode_raw(code, repeats) + Err(format!("{code} not found")) } } @@ -161,28 +163,24 @@ impl Keymap { irp.encode_raw(vars, repeats) } - pub fn encode_raw(&self, code: &str, repeats: u64) -> Result { - if let Some(raw) = self.raw.iter().find(|e| e.keycode == code) { - if let Some(pronto) = &raw.pronto { - return Ok(pronto.encode(repeats as usize)); - } + pub fn encode_raw(&self, raw: &Raw, repeats: u64) -> Message { + if let Some(pronto) = &raw.pronto { + return pronto.encode(repeats as usize); + } - let e = raw.raw.as_ref().unwrap(); + let e = raw.raw.as_ref().unwrap(); - let mut m = e.clone(); + let mut m = e.clone(); - if repeats > 0 { - let rep = raw.repeat.as_ref().unwrap_or(e); + if repeats > 0 { + let rep = raw.repeat.as_ref().unwrap_or(e); - for _ in 0..repeats { - m.extend(rep); - } + for _ in 0..repeats { + m.extend(rep); } - - return Ok(m); } - Err(format!("{code} not found")) + m } } diff --git a/src/keymap/mod.rs b/src/keymap/mod.rs index 53ed1f1..f3749bf 100644 --- a/src/keymap/mod.rs +++ b/src/keymap/mod.rs @@ -1,6 +1,7 @@ use irp::{Message, Pronto}; use std::collections::HashMap; +mod decode; mod encode; mod parse; mod protocol; diff --git a/src/keymap/parse.rs b/src/keymap/parse.rs index c277cd9..df7a541 100644 --- a/src/keymap/parse.rs +++ b/src/keymap/parse.rs @@ -151,16 +151,23 @@ fn parse_toml(contents: &str, filename: &Path) -> Result, String> { }; let raw = if let Some(Value::String(raw)) = e.get("raw") { - let raw = + let mut raw = Message::parse(raw).map_err(|e| format!("{}: {e}", filename.display()))?; + + if !raw.has_trailing_gap() { + raw.raw.push(20000); + } Some(raw) } else { None }; let repeat = if let Some(Value::String(repeat)) = e.get("repeat") { - let repeat = Message::parse(repeat) + let mut repeat = Message::parse(repeat) .map_err(|e| format!("{}: {e}", filename.display()))?; + if !repeat.has_trailing_gap() { + repeat.raw.push(20000); + } Some(repeat) } else { None diff --git a/src/keymap/protocol.rs b/src/keymap/protocol.rs index 369b234..9cd19f7 100644 --- a/src/keymap/protocol.rs +++ b/src/keymap/protocol.rs @@ -1,6 +1,7 @@ use super::LinuxProtocol; impl LinuxProtocol { + /// Find the protocol that matches the name exactly pub fn find(name: &str) -> Option<&'static LinuxProtocol> { LINUX_PROTOCOLS.iter().find(|e| e.name == name) } @@ -24,6 +25,26 @@ impl LinuxProtocol { LINUX_PROTOCOLS.iter().find(|e| str_like(e.name) == name) } + + /// Find list of protocols supported by this decoder. Some linux kernel decoders + /// can decode multiple (closely related) IR protocols. + pub fn find_decoder(name: &str) -> Option<&'static [LinuxProtocol]> { + if let Some(start) = LINUX_PROTOCOLS.iter().position(|p| p.decoder == name) { + let mut end = start; + + while LINUX_PROTOCOLS + .get(end + 1) + .map(|p| p.decoder == name) + .unwrap_or_default() + { + end += 1; + } + + Some(&LINUX_PROTOCOLS[start..=end]) + } else { + None + } + } } const LINUX_PROTOCOLS: &[LinuxProtocol] = &[ @@ -227,4 +248,13 @@ fn find_like() { let p = LinuxProtocol::find_like("sony-12").unwrap(); assert_eq!(p.name, "sony12"); + + let Some(p) = LinuxProtocol::find_decoder("sony") else { + panic!(); + }; + + assert_eq!(p.len(), 3); + assert_eq!(p[0].name, "sony12"); + assert_eq!(p[1].name, "sony15"); + assert_eq!(p[2].name, "sony20"); } diff --git a/testdata/rc_keymaps/sony-12.toml b/testdata/rc_keymaps/sony-12.toml new file mode 100644 index 0000000..7519e32 --- /dev/null +++ b/testdata/rc_keymaps/sony-12.toml @@ -0,0 +1,14 @@ +[[protocols]] +name = 'Sony-RM-U305C' +protocol = 'sony' +variant = 'sony-12' +[protocols.scancodes] +0x100060 = 'KEY_SONY-AV-SLEEP' +0x10015 = 'KEY_SONY-AV-AV-I/O' +0x100022 = 'KEY_SONY-AV-VIDEO' +0x10007d = 'KEY_SONY-AV-DVD/LD' +0x10006a = 'KEY_SONY-AV-TV/SAT' +0x10001d = 'KEY_SONY-AV-AUX' +0x100025 = 'KEY_SONY-AV-CD/SACD' +0x100021 = 'KEY_SONY-AV-TUNER' +0x100015 = 'KEY_SONY-AV-1' diff --git a/testdata/rc_keymaps/sony.toml b/testdata/rc_keymaps/sony.toml new file mode 100644 index 0000000..4daf8cb --- /dev/null +++ b/testdata/rc_keymaps/sony.toml @@ -0,0 +1,13 @@ +[[protocols]] +name = 'Sony-RM-U305C' +protocol = 'sony' +[protocols.scancodes] +0x100060 = 'KEY_SONY-AV-SLEEP' +0x10015 = 'KEY_SONY-AV-AV-I/O' +0x100022 = 'KEY_SONY-AV-VIDEO' +0x10007d = 'KEY_SONY-AV-DVD/LD' +0x10006a = 'KEY_SONY-AV-TV/SAT' +0x10001d = 'KEY_SONY-AV-AUX' +0x100025 = 'KEY_SONY-AV-CD/SACD' +0x100021 = 'KEY_SONY-AV-TUNER' +0x100015 = 'KEY_SONY-AV-1' diff --git a/tests/decode_tests.rs b/tests/decode_tests.rs index 5953f6a..fb3f9c5 100644 --- a/tests/decode_tests.rs +++ b/tests/decode_tests.rs @@ -6,7 +6,7 @@ fn toggle_bit_mask() { let assert = cmd .args([ - "decode", "lircd", "testdata/lircd_conf/d-link/DSM-10.lircd.conf", "-q", "-r", + "decode", "keymap", "testdata/lircd_conf/d-link/DSM-10.lircd.conf", "-q", "-r", "+9132 -4396 +664 -460 +664 -460 +664 -460 +664 -1592 +664 -460 +664 -460 +664 -460 +664 -460 +664 -460 +664 -1592 +664 -1592 +664 -460 +664 -460 +664 -1592 +664 -1592 +664 -1592 +664 -460 +664 -460 +664 -1592 +664 -460 +664 -1592 +664 -460 +664 -460 +664 -460 +664 -1592 +664 -1592 +664 -460 +664 -1592 +664 -460 +664 -1592 +664 -1592 +664 -1592 +671 -42232 +9128 -2143 +671 -96305" ]) .assert(); @@ -33,7 +33,7 @@ fn ignore_mask() { let assert = cmd .args([ - "decode", "lircd", "testdata/lircd_conf/apple/A1156.lircd.conf", "-q", "-r", + "decode", "keymap", "testdata/lircd_conf/apple/A1156.lircd.conf", "-q", "-r", "+9065 -4484 +574 -547 +574 -1668 +574 -1668 +574 -1668 +574 -547 +574 -1668 +574 -1668 +574 -1668 +574 -1668 +574 -1668 +574 -1668 +574 -547 +574 -547 +574 -547 +574 -547 +574 -1668 +574 -547 +574 -1668 +574 -1668 +574 -547 +574 -547 +574 -547 +574 -547 +574 -547 +574 -1668 +574 -1668 +574 -547 +574 -547 +574 -547 +574 -1668 +574 -547 +574 -1668 +567 -37600 +9031 -2242 +567 -37600" ]) .assert(); @@ -56,7 +56,7 @@ decoded: remote:Apple_A1156 code:KEY_PLAY let assert = cmd .args([ - "decode", "lircd", "testdata/lircd_conf/apple/A1156.lircd.conf", "-q", "-r", + "decode", "keymap", "testdata/lircd_conf/apple/A1156.lircd.conf", "-q", "-r", "+9065 -4484 +574 -547 +574 -1668 +574 -1668 +574 -1668 +574 -547 +574 -1668 +574 -1668 +574 -1668 +574 -1668 +574 -1668 +574 -1668 +574 -547 +574 -547 +574 -547 +574 -547 +574 -1668 +574 -547 +574 -1668 +574 -1668 +574 -547 +574 -547 +574 -547 +574 -547 +574 -547 +574 -547 +574 -547 +574 -1668 +574 -1668 +574 -1668 +574 -547 +574 -1668 +574 -547 +567 -37600 +9031 -2242 +567 -37600" ]) .assert(); @@ -75,3 +75,101 @@ decoded: remote:Apple_A1156 code:KEY_PLAY "# ); } + +#[test] +fn keymap() { + let mut cmd = Command::cargo_bin("cir").unwrap(); + + let assert = cmd + .args([ + "decode", "keymap", "testdata/rc_keymaps/sony.toml", "-v", "-r", + "+2400 -600 +600 -600 +600 -600 +600 -600 +600 -600 +600 -600 +1200 -600 +1200 -600 +600 -600 +600 -600 +600 -600 +600 -600 +1200 -26400" + ]) + .assert(); + + let output = assert.get_output(); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + assert_eq!( + stderr, + r#"debug: decoding irp {40k,600}<1,-1|2,-1>(4,-1,CODE:7,CODE:5:16,^45m) [CODE:0..0x1fffff] for keymap Sony-RM-U305C +debug: decoding irp {40k,600}<1,-1|2,-1>(4,-1,CODE:7,CODE:8:16,^45m) [CODE:0..0xffffff] for keymap Sony-RM-U305C +debug: decoding irp {40k,600}<1,-1|2,-1>(4,-1,CODE:7,CODE:5:16,CODE:8:8,^45m) [CODE:0..0x1fffff] for keymap Sony-RM-U305C +info: decoding: +2400 -600 +600 -600 +600 -600 +600 -600 +600 -600 +600 -600 +1200 -600 +1200 -600 +600 -600 +600 -600 +600 -600 +600 -600 +1200 -26400 +"# + ); + + assert_eq!( + stdout, + r#"decoded: keymap:Sony-RM-U305C code:KEY_SONY-AV-SLEEP +"# + ); + + let mut cmd = Command::cargo_bin("cir").unwrap(); + + let assert = cmd + .args([ + "decode", "keymap", "testdata/rc_keymaps/dish_network.toml", "-q", "-r", + "+525 -6045 +440 -2780 +440 -2780 +440 -2780 +440 -2780 +440 -1645 +440 -2780 +440 -2780 +440 -2780 +440 -2780 +440 -2780 +440 -2780 +440 -2780 +440 -2780 +440 -2780 +440 -2780 +440 -2780 +450 -40000" + ]) + .assert(); + + let output = assert.get_output(); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + assert_eq!(stderr, ""); + + assert_eq!( + stdout, + r#"decoded: keymap:Dish Network code:KEY_POWER +"# + ); + + let mut cmd = Command::cargo_bin("cir").unwrap(); + + let assert = cmd + .args([ + "decode", "keymap", "testdata/rc_keymaps/rc6_mce.toml", "-q", "-r", + "+2664 -888 +444 -444 +444 -444 +444 -888 +444 -888 +1332 -888 +444 -444 +444 -444 +444 -444 +444 -444 +444 -444 +444 -444 +444 -444 +444 -444 +444 -444 +444 -444 +888 -444 +444 -444 +444 -444 +444 -888 +444 -444 +444 -444 +444 -444 +444 -444 +888 -888 +444 -444 +444 -444 +888 -888 +888 -444 +444 -444 +444 -888 +444 -444 +444 -67704" + ]) + .assert(); + + let output = assert.get_output(); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + assert_eq!(stderr, ""); + + assert_eq!( + stdout, + r#"decoded: keymap:rc6_mce code:KEY_GREEN +"# + ); + + let mut cmd = Command::cargo_bin("cir").unwrap(); + + let assert = cmd + .args([ + "decode", "keymap", "testdata/rc_keymaps/RM-786.toml", "-r", + "+2465 -569 +620 -582 +618 -584 +1242 -581 +618 -585 +620 -583 +620 -585 +1242 -607 +622 -575 +1243 -584 +1243 -578 +621 -579 +619 -20000" + ]) + .assert(); + + let output = assert.get_output(); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + assert_eq!(stderr, "info: decoding: +2465 -569 +620 -582 +618 -584 +1242 -581 +618 -585 +620 -583 +620 -585 +1242 -607 +622 -575 +1243 -584 +1243 -578 +621 -579 +619 -20000\n"); + + assert_eq!( + stdout, + r#"decoded: keymap:HSVP code:KEY_AUXMUTE +"# + ); +} diff --git a/tests/encode_tests.rs b/tests/encode_tests.rs index 8c24fec..4b6be88 100644 --- a/tests/encode_tests.rs +++ b/tests/encode_tests.rs @@ -259,7 +259,7 @@ info: rawir: +2369 -637 +1166 -637 +565 -637 +565 -637 +1166 -637 +565 -637 +565 assert_eq!( stderr, - r#"info: rawir: +2437 -553 +618 -569 +619 -576 +1239 -573 +618 -572 +1239 -578 +1238 -580 +616 -597 +619 -570 +618 -564 +618 -577 +618 -573 +1242 + r#"info: rawir: +2437 -553 +618 -569 +619 -576 +1239 -573 +618 -572 +1239 -578 +1238 -580 +616 -597 +619 -570 +618 -564 +618 -577 +618 -573 +1242 -20000 "# );