Skip to content

Commit

Permalink
Add encode_raw() function to keymap
Browse files Browse the repository at this point in the history
Signed-off-by: Sean Young <[email protected]>
  • Loading branch information
seanyoung committed Apr 27, 2024
1 parent 316666d commit a70ac2c
Show file tree
Hide file tree
Showing 6 changed files with 521 additions and 36 deletions.
2 changes: 1 addition & 1 deletion irp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub use protocols::Protocol;
use num_rational::Rational64;
use std::{collections::HashMap, fmt, rc::Rc};

#[derive(Debug, PartialEq, Default, Eq)]
#[derive(Debug, PartialEq, Default, Clone, Eq)]
/// An encoded raw infrared message
pub struct Message {
/// The carrier for the message. None means unknown, Some(0) means unmodulated
Expand Down
8 changes: 0 additions & 8 deletions src/bin/commands/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,6 @@ fn load_keymap(inputdev: &Device, keymap_filename: &Path) {
}
};

let scancode = match u64::from_str_radix(scancode.trim_start_matches("0x"), 16) {
Ok(scancode) => scancode,
Err(_) => {
eprintln!("error: ‘{scancode}’ is not a valid scancode");
continue;
}
};

// Kernels from before v5.7 want the scancode in 4 bytes; try this if possible
let scancode = if let Ok(scancode) = u32::try_from(scancode) {
scancode.to_ne_bytes().to_vec()
Expand Down
40 changes: 40 additions & 0 deletions src/keymap/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ use super::{Keymap, LinuxProtocol};
use irp::{Irp, Message, Vartable};

impl Keymap {
pub fn encode(&self, code: &str, repeats: u64) -> Result<Message, String> {
if let Some(scancodes) = &self.scancodes {
if let Some((scancode, _)) = scancodes.iter().find(|(_, v)| *v == code) {
return self.encode_scancode(*scancode, repeats);
}
}

self.encode_raw(code, repeats)
}

pub fn encode_scancode(&self, scancode: u64, repeats: u64) -> Result<Message, String> {
let irp = if let Some(i) = &self.irp {
i.as_str()
Expand Down Expand Up @@ -42,6 +52,36 @@ impl Keymap {

irp.encode_raw(vars, repeats)
}

pub fn encode_raw(&self, code: &str, repeats: u64) -> Result<Message, String> {
if let Some(raw) = &self.raw {
if let Some(raw) = raw.iter().find(|e| e.keycode == code) {
if let Some(pronto) = &raw.pronto {
return Ok(pronto.encode(repeats as usize));
}

let e = raw.raw.as_ref().unwrap();

let mut m = e.clone();

if repeats > 0 && m.has_trailing_gap() {
let rep = raw.repeat.as_ref().unwrap_or(e);

for _ in 0..repeats {
m.extend(rep);

if rep.has_trailing_gap() {
break;
}
}
}

return Ok(m);
}
}

Err(format!("{code} not found"))
}
}

fn gen_mask(v: u32) -> u64 {
Expand Down
13 changes: 7 additions & 6 deletions src/keymap/mod.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
use irp::{Message, Pronto};
use std::collections::HashMap;

mod encode;
mod parse;
mod protocol;

/// A Linux keymap, either toml or text format used by ir-keytable
#[derive(PartialEq, Eq, Debug, Default)]
#[derive(PartialEq, Debug, Default)]
pub struct Keymap {
pub name: String,
pub protocol: String,
pub variant: Option<String>,
pub irp: Option<String>,
pub rc_protocol: Option<u16>,
pub raw: Option<Vec<Raw>>,
pub scancodes: Option<HashMap<String, String>>,
pub scancodes: Option<HashMap<u64, String>>,
}

#[derive(PartialEq, Eq, Debug)]
#[derive(PartialEq, Debug)]
pub struct Raw {
pub keycode: String,
pub raw: Option<String>,
pub repeat: Option<String>,
pub pronto: Option<String>,
pub raw: Option<Message>,
pub repeat: Option<Message>,
pub pronto: Option<Pronto>,
}

pub struct LinuxProtocol {
Expand Down
56 changes: 35 additions & 21 deletions src/keymap/parse.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Parse linux rc keymaps
use super::{Keymap, Raw};
use irp::{Message, Pronto};
use std::{collections::HashMap, ffi::OsStr, fmt::Write, path::Path};
use toml::{Table, Value};

Expand All @@ -12,7 +13,8 @@ peg::parser! {
let mut scancodes = HashMap::new();

for (code, name) in lines.into_iter().flatten() {
scancodes.insert(code.to_owned(), name.to_owned());
let code = string_to_scancode(code).unwrap();
scancodes.insert(code, name.to_owned());
}

let mut protocol = vec![Keymap {
Expand Down Expand Up @@ -60,6 +62,13 @@ peg::parser! {
}
}

fn string_to_scancode(s: &str) -> Result<u64, std::num::ParseIntError> {
if let Some(hex) = s.strip_prefix("0x") {
u64::from_str_radix(hex, 16)
} else {
str::parse(s)
}
}
impl Keymap {
/// Parse a rc keymap file, either toml or old text format. No validation is done of key codes or protocol names
pub fn parse(contents: &str, filename: &Path) -> Result<Vec<Keymap>, String> {
Expand Down Expand Up @@ -127,19 +136,22 @@ fn parse_toml(contents: &str, filename: &Path) -> Result<Vec<Keymap>, String> {
};

let raw = if let Some(Value::String(raw)) = e.get("raw") {
Some(raw.to_owned())
let raw = Message::parse(raw)?;
Some(raw)
} else {
None
};

let repeat = if let Some(Value::String(repeat)) = e.get("repeat") {
Some(repeat.to_owned())
let repeat = Message::parse(repeat)?;
Some(repeat)
} else {
None
};

let pronto = if let Some(Value::String(pronto)) = e.get("pronto") {
Some(pronto.to_owned())
let pronto = Pronto::parse(pronto)?;
Some(pronto)
} else {
None
};
Expand Down Expand Up @@ -182,12 +194,14 @@ fn parse_toml(contents: &str, filename: &Path) -> Result<Vec<Keymap>, String> {
if let Some(Value::Table(codes)) = entry.get("scancodes") {
let mut res = HashMap::new();

for (key, value) in codes {
let Value::String(value) = value else {
return Err(format!("{}: scancode should be string", filename.display()));
for (scancode, keycode) in codes {
let scancode = string_to_scancode(scancode)
.map_err(|_| format!("{scancode} is a not valid scancode"))?;
let Value::String(keycode) = keycode else {
return Err(format!("{}: keycode should be string", filename.display()));
};

res.insert(key.to_owned(), value.to_owned());
res.insert(scancode, keycode.to_owned());
}

scancodes = Some(res);
Expand Down Expand Up @@ -407,8 +421,8 @@ fn parse_toml_test() {
assert_eq!(k[0].variant, Some(String::from("rc5")));
if let Some(scancodes) = &k[0].scancodes {
for s in scancodes {
match (s.0.as_str(), s.1.as_str()) {
("0x1e3b", "KEY_SELECT") | ("0x1e3d", "KEY_POWER2") | ("0x1e1c", "KEY_TV") => {}
match (s.0, s.1.as_str()) {
(0x1e3b, "KEY_SELECT") | (0x1e3d, "KEY_POWER2") | (0x1e1c, "KEY_TV") => {}
_ => panic!("{s:?} not expected"),
}
}
Expand Down Expand Up @@ -473,8 +487,8 @@ fn parse_text_test() {
assert_eq!(k[0].variant, None);
if let Some(scancodes) = &k[0].scancodes {
for s in scancodes {
match (s.0.as_str(), s.1.as_str()) {
("0x1e3b", "KEY_SELECT") | ("0x1e3d", "KEY_POWER2") | ("0x1e1c", "KEY_TV") => {}
match (s.0, s.1.as_str()) {
(0x1e3b, "KEY_SELECT") | (0x1e3d, "KEY_POWER2") | (0x1e1c, "KEY_TV") => {}
_ => panic!("{s:?} not expected"),
}
}
Expand All @@ -498,11 +512,11 @@ fn parse_text_test() {
assert_eq!(k[0].variant, None);
if let Some(scancodes) = &k[0].scancodes {
for s in scancodes {
match (s.0.as_str(), s.1.as_str()) {
("0x800f0400", "KEY_NUMERIC_0")
| ("0x800f0401", "KEY_NUMERIC_1")
| ("0x800f0402", "KEY_NUMERIC_2")
| ("0x800f0403", "KEY_NUMERIC_3") => {}
match (s.0, s.1.as_str()) {
(0x800f0400, "KEY_NUMERIC_0")
| (0x800f0401, "KEY_NUMERIC_1")
| (0x800f0402, "KEY_NUMERIC_2")
| (0x800f0403, "KEY_NUMERIC_3") => {}
_ => panic!("{s:?} not expected"),
}
}
Expand All @@ -522,10 +536,10 @@ fn parse_text_test() {
assert_eq!(k[0].variant, None);
if let Some(scancodes) = &k[0].scancodes {
for s in scancodes {
match (s.0.as_str(), s.1.as_str()) {
("0x28c0", "KEY_NUMERIC_0")
| ("0x28c1", "KEY_NUMERIC_1")
| ("0x28c2", "KEY_NUMERIC_2") => {}
match (s.0, s.1.as_str()) {
(0x28c0, "KEY_NUMERIC_0")
| (0x28c1, "KEY_NUMERIC_1")
| (0x28c2, "KEY_NUMERIC_2") => {}
_ => panic!("{s:?} not expected"),
}
}
Expand Down
Loading

0 comments on commit a70ac2c

Please sign in to comment.