diff --git a/src/bin/commands/transmit.rs b/src/bin/commands/transmit.rs index 24679f0..d70a340 100644 --- a/src/bin/commands/transmit.rs +++ b/src/bin/commands/transmit.rs @@ -212,7 +212,10 @@ fn encode_args(transmit: &crate::Transmit) -> Message { fn encode_keymap(args: &crate::TransmitKeymap) -> Message { let remotes = match Keymap::parse(&args.keymap) { Ok(r) => r, - Err(_) => std::process::exit(2), + Err(e) => { + log::error!("{e}"); + std::process::exit(2); + } }; if !args.codes.is_empty() { diff --git a/src/keymap/encode.rs b/src/keymap/encode.rs index 5f4b070..1c9a44b 100644 --- a/src/keymap/encode.rs +++ b/src/keymap/encode.rs @@ -131,6 +131,8 @@ impl Keymap { } }; + log::debug!("using irp for encoding: {irp}"); + let irp = Irp::parse(irp)?; let mut vars = Vartable::new(); diff --git a/src/keymap/parse.rs b/src/keymap/parse.rs index 6dedd6f..c277cd9 100644 --- a/src/keymap/parse.rs +++ b/src/keymap/parse.rs @@ -71,12 +71,12 @@ fn string_to_scancode(s: &str) -> Result { } impl Keymap { pub fn parse(path: &Path) -> Result, String> { - let mut f = File::open(path).map_err(|e| format!("{e}"))?; + let mut f = File::open(path).map_err(|e| format!("{}: {e}", path.display()))?; let mut contents = String::new(); f.read_to_string(&mut contents) - .map_err(|e| format!("{e}"))?; + .map_err(|e| format!("{}: {e}", path.display()))?; Keymap::parse_text(&contents, path) } @@ -86,13 +86,16 @@ impl Keymap { if filename.extension() == Some(OsStr::new("toml")) { parse_toml(contents, filename) } else { - text_keymap::keymap(contents).map_err(|pos| format!("parse error at {pos}")) + text_keymap::keymap(contents) + .map_err(|pos| format!("{}: parse error at {pos}", filename.display())) } } } fn parse_toml(contents: &str, filename: &Path) -> Result, String> { - let top = contents.parse::().map_err(|e| e.to_string())?; + let top = contents + .parse::
() + .map_err(|e| format!("{}: {e}", filename.display()))?; let Some(Value::Array(protocols)) = top.get("protocols") else { return Err(format!( @@ -136,30 +139,36 @@ fn parse_toml(contents: &str, filename: &Path) -> Result, String> { if protocol == "raw" { // find raw entries let Some(Value::Array(e)) = entry.get("raw") else { - return Err("raw protocol is misssing raw entries".into()); + return Err(format!( + "{}: raw protocol is misssing raw entries", + filename.display() + )); }; for e in e { let Some(Value::String(keycode)) = e.get("keycode") else { - return Err("missing keycode".into()); + return Err(format!("{}: missing keycode", filename.display())); }; let raw = if let Some(Value::String(raw)) = e.get("raw") { - let raw = Message::parse(raw)?; + let raw = + Message::parse(raw).map_err(|e| format!("{}: {e}", filename.display()))?; Some(raw) } else { None }; let repeat = if let Some(Value::String(repeat)) = e.get("repeat") { - let repeat = Message::parse(repeat)?; + let repeat = Message::parse(repeat) + .map_err(|e| format!("{}: {e}", filename.display()))?; Some(repeat) } else { None }; let pronto = if let Some(Value::String(pronto)) = e.get("pronto") { - let pronto = Pronto::parse(pronto)?; + let pronto = Pronto::parse(pronto) + .map_err(|e| format!("{}: {e}", filename.display()))?; Some(pronto) } else { None @@ -167,13 +176,22 @@ fn parse_toml(contents: &str, filename: &Path) -> Result, String> { if pronto.is_some() { if raw.is_some() { - return Err("raw entry has both pronto hex code and raw".to_string()); + return Err(format!( + "{}: raw entry has both pronto hex code and raw", + filename.display() + )); } if repeat.is_some() { - return Err("raw entry has both pronto hex code and repeat".to_string()); + return Err(format!( + "{}: raw entry has both pronto hex code and repeat", + filename.display() + )); } } else if raw.is_none() { - return Err("raw entry has neither pronto hex code nor raw".to_string()); + return Err(format!( + "{}: raw entry has neither pronto hex code nor raw", + filename.display() + )); } raw_entries.push(Raw { @@ -185,7 +203,10 @@ fn parse_toml(contents: &str, filename: &Path) -> Result, String> { } } else { if entry.get("raw").is_some() { - return Err("raw entries for non-raw protocol".to_string()); + return Err(format!( + "{}: raw entries for non-raw protocol", + filename.display() + )); } if protocol == "irp" { @@ -193,15 +214,19 @@ fn parse_toml(contents: &str, filename: &Path) -> Result, String> { irp = Some(entry.to_owned()); } } else if entry.get("irp").is_some() { - return Err("set the protocol to irp when using irp".to_string()); + return Err(format!( + "{}: set the protocol to irp when using irp", + filename.display() + )); } else { irp = bpf_protocol_irp(protocol, entry.as_table().unwrap()); } if let Some(Value::Table(codes)) = entry.get("scancodes") { for (scancode, keycode) in codes { - let scancode = string_to_scancode(scancode) - .map_err(|_| format!("{scancode} is a not valid scancode"))?; + let scancode = string_to_scancode(scancode).map_err(|_| { + format!("{}: {scancode} is a not valid scancode", filename.display()) + })?; let Value::String(keycode) = keycode else { return Err(format!("{}: keycode should be string", filename.display())); }; @@ -441,7 +466,7 @@ fn parse_toml_test() { assert_eq!( Keymap::parse_text(s, Path::new("x.toml")), - Err("raw protocol is misssing raw entries".to_string()) + Err("x.toml: raw protocol is misssing raw entries".to_string()) ); let s = r#" @@ -454,7 +479,7 @@ fn parse_toml_test() { assert_eq!( Keymap::parse_text(s, Path::new("x.toml")), - Err("raw entry has neither pronto hex code nor raw".to_string()) + Err("x.toml: raw entry has neither pronto hex code nor raw".to_string()) ); let s = r#" @@ -468,7 +493,7 @@ fn parse_toml_test() { assert_eq!( Keymap::parse_text(s, Path::new("x.toml")), - Err("raw entry has neither pronto hex code nor raw".to_string()) + Err("x.toml: raw entry has neither pronto hex code nor raw".to_string()) ); } diff --git a/testdata/rc_keymaps/rc6_mce.toml b/testdata/rc_keymaps/rc6_mce.toml new file mode 100644 index 0000000..1ee9c72 --- /dev/null +++ b/testdata/rc_keymaps/rc6_mce.toml @@ -0,0 +1,70 @@ +# Generated with gen_keytables.pl from drivers/media/rc/keymaps/rc-rc6-mce.c +[[protocols]] +name = "rc6_mce" +protocol = "rc6" +variant = "rc6_mce" +[protocols.scancodes] +0x800f0400 = "KEY_NUMERIC_0" +0x800f0401 = "KEY_NUMERIC_1" +0x800f0402 = "KEY_NUMERIC_2" +0x800f0403 = "KEY_NUMERIC_3" +0x800f0404 = "KEY_NUMERIC_4" +0x800f0405 = "KEY_NUMERIC_5" +0x800f0406 = "KEY_NUMERIC_6" +0x800f0407 = "KEY_NUMERIC_7" +0x800f0408 = "KEY_NUMERIC_8" +0x800f0409 = "KEY_NUMERIC_9" +0x800f040a = "KEY_DELETE" +0x800f040b = "KEY_ENTER" +0x800f040c = "KEY_SLEEP" +0x800f040d = "KEY_MEDIA" +0x800f040e = "KEY_MUTE" +0x800f040f = "KEY_INFO" +0x800f0410 = "KEY_VOLUMEUP" +0x800f0411 = "KEY_VOLUMEDOWN" +0x800f0412 = "KEY_CHANNELUP" +0x800f0413 = "KEY_CHANNELDOWN" +0x800f0414 = "KEY_FASTFORWARD" +0x800f0415 = "KEY_REWIND" +0x800f0416 = "KEY_PLAY" +0x800f0417 = "KEY_RECORD" +0x800f0418 = "KEY_PAUSE" +0x800f0419 = "KEY_STOP" +0x800f041a = "KEY_NEXT" +0x800f041b = "KEY_PREVIOUS" +0x800f041c = "KEY_NUMERIC_POUND" +0x800f041d = "KEY_NUMERIC_STAR" +0x800f041e = "KEY_UP" +0x800f041f = "KEY_DOWN" +0x800f0420 = "KEY_LEFT" +0x800f0421 = "KEY_RIGHT" +0x800f0422 = "KEY_OK" +0x800f0423 = "KEY_EXIT" +0x800f0424 = "KEY_DVD" +0x800f0425 = "KEY_TUNER" +0x800f0426 = "KEY_EPG" +0x800f0427 = "KEY_ZOOM" +0x800f0432 = "KEY_MODE" +0x800f0433 = "KEY_PRESENTATION" +0x800f0434 = "KEY_EJECTCD" +0x800f043a = "KEY_BRIGHTNESSUP" +0x800f0446 = "KEY_TV" +0x800f0447 = "KEY_AUDIO" +0x800f0448 = "KEY_PVR" +0x800f0449 = "KEY_CAMERA" +0x800f044a = "KEY_VIDEO" +0x800f044c = "KEY_LANGUAGE" +0x800f044d = "KEY_TITLE" +0x800f044e = "KEY_PRINT" +0x800f0450 = "KEY_RADIO" +0x800f045a = "KEY_SUBTITLE" +0x800f045b = "KEY_RED" +0x800f045c = "KEY_GREEN" +0x800f045d = "KEY_YELLOW" +0x800f045e = "KEY_BLUE" +0x800f0465 = "KEY_POWER2" +0x800f0469 = "KEY_MESSENGER" +0x800f046e = "KEY_PLAYPAUSE" +0x800f046f = "KEY_PLAYER" +0x800f0480 = "KEY_BRIGHTNESSDOWN" +0x800f0481 = "KEY_PLAYPAUSE" diff --git a/tests/encode_tests.rs b/tests/encode_tests.rs index 2b45153..8c24fec 100644 --- a/tests/encode_tests.rs +++ b/tests/encode_tests.rs @@ -37,7 +37,7 @@ fn encode_lircd_raw_test() { .args([ "transmit", "--dry-run", - "lircd", + "keymap", "testdata/lircd_conf/pace/DC420N.lircd.conf", "1", ]) @@ -67,7 +67,7 @@ fn encode_lircd_aiwa_test() { .args([ "transmit", "--dry-run", - "lircd", + "keymap", "testdata/lircd_conf/aiwa/RC-5VP05.lircd.conf", "AUTO", ]) @@ -158,7 +158,7 @@ fn encode_lircd_grundig_test() { .args([ "transmit", "--dry-run", - "lircd", + "keymap", "testdata/lircd_conf/grundig/RP75_LCD.lircd.conf", "-m", "grundig_rp75", @@ -189,7 +189,7 @@ fn empty_lircd_conf() { .args([ "transmit", "--dry-run", - "lircd", + "keymap", "testdata/lircd_conf/empty", ]) .assert(); @@ -203,11 +203,144 @@ fn empty_lircd_conf() { assert_eq!( stderr, - r#"error: testdata/lircd_conf/empty: no remote definitions found + r#"error: testdata/lircd_conf/empty: parse error at error at 1:3: expected "table" "# ); } +#[test] +fn keymaps() { + let mut cmd = Command::cargo_bin("cir").unwrap(); + + let assert = cmd + .args([ + "transmit", + "--dry-run", + "-v", + "keymap", + "testdata/rc_keymaps/RM-687C.toml", + "KEY_0", + ]) + .assert(); + + let output = assert.get_output(); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + assert_eq!(stdout, ""); + + assert_eq!( + stderr, + r#"debug: using irp for encoding: {msb}<565,-637|1166,-637>(2369,-637,CODE:12,-40m) [CODE:0..4095] +info: carrier: 38000Hz +info: rawir: +2369 -637 +1166 -637 +565 -637 +565 -637 +1166 -637 +565 -637 +565 -637 +565 -637 +1166 -637 +565 -637 +565 -637 +565 -637 +565 -40637 +"# + ); + + let mut cmd = Command::cargo_bin("cir").unwrap(); + + let assert = cmd + .args([ + "transmit", + "--dry-run", + "keymap", + "testdata/rc_keymaps/RM-786.toml", + "KEY_CABLEFWD", + ]) + .assert(); + + let output = assert.get_output(); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + assert_eq!(stdout, ""); + + 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 +"# + ); + + let mut cmd = Command::cargo_bin("cir").unwrap(); + + let assert = cmd + .args([ + "transmit", + "--dry-run", + "keymap", + "foo.toml", + "KEY_CABLEFWD", + ]) + .assert(); + + let output = assert.get_output(); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + assert_eq!(stdout, ""); + + assert_eq!( + stderr, + r#"error: foo.toml: No such file or directory (os error 2) +"# + ); + + let mut cmd = Command::cargo_bin("cir").unwrap(); + + let assert = cmd + .args([ + "transmit", + "--dry-run", + "keymap", + "Cargo.toml", + "KEY_CABLEFWD", + ]) + .assert(); + + let output = assert.get_output(); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + assert_eq!(stdout, ""); + + assert_eq!( + stderr, + r#"error: Cargo.toml: missing top level protocols array +"# + ); + + let mut cmd = Command::cargo_bin("cir").unwrap(); + + let assert = cmd + .args([ + "transmit", + "--dry-run", + "keymap", + "testdata/rc_keymaps/rc6_mce.toml", + "KEY_ENTER", + "-v", + ]) + .assert(); + + let output = assert.get_output(); + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + assert_eq!(stdout, ""); + + assert_eq!( + stderr, + r#"debug: using irp for encoding: {36k,444,msb}<-1,1|1,-1>(6,-2,1:1,6:3,-2,2,CODE:16:16,T:1,CODE:15,MCE=(CODE>>16)==0x800f||(CODE>>16)==0x8034||(CODE>>16)==0x8046,^105m){MCE=1}[CODE:0..0xffffffff,T@:0..1=0] +info: carrier: 36000Hz +info: rawir: +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 +444 -444 +444 -444 +444 -444 +888 -888 +888 -444 +444 -68148 +"# + ); +} /// /// This tests needs a /dev/lirc0 rc-loopback device #[test]