From f47d9e4be7902366f296739e0574ca66cad12c42 Mon Sep 17 00:00:00 2001 From: moparisthebest Date: Sun, 6 Oct 2019 13:51:01 -0400 Subject: [PATCH] keymapper.rs is now fully platform agnostic, successfully passed through generic hell --- src/keymapper.rs | 337 ++++++++++++++++++++---------------- src/lib.rs | 2 +- src/linux/device/builder.rs | 2 +- src/linux/mod.rs | 76 +++++++- 4 files changed, 262 insertions(+), 155 deletions(-) diff --git a/src/keymapper.rs b/src/keymapper.rs index 521eff7..40347df 100644 --- a/src/keymapper.rs +++ b/src/keymapper.rs @@ -1,57 +1,87 @@ -use crate::Device; -use libc::{c_int, input_event}; -use uinput_sys::{KEY_LEFTSHIFT, KEY_RIGHTSHIFT, KEY_CAPSLOCK}; - use std::fs::File; use std::io::Read; use std::collections::HashMap; +use std::hash::Hash; +use std::convert::TryFrom; use crate::{Error, Result}; -// 1 is down, 0 is up -const DOWN: i32 = 1; -const UP: i32 = 0; const INVERT_KEY_FLAG: char = '^'; const CAPS_MODIFY_KEY_FLAG: char = '*'; const HALF_KEY_SEPARATOR: char = ':'; -const LEFTSHIFT_INDEX: usize = KEY_LEFTSHIFT as usize; -const RIGHTSHIFT_INDEX: usize = KEY_RIGHTSHIFT as usize; -const CAPSLOCK_INDEX: usize = KEY_CAPSLOCK as usize; +// nightly only... +//pub trait KeyCode = Into + TryFrom + Copy + Clone + Eq + Hash + Default + 'static; -const KEY_LEFTSHIFT_U16: u16 = KEY_LEFTSHIFT as u16; -const KEY_RIGHTSHIFT_U16: u16 = KEY_RIGHTSHIFT as u16; -const KEY_CAPSLOCK_U16: u16 = KEY_CAPSLOCK as u16; - -trait KeyMapper { - fn send_event(&self, key_state: &[bool], event: &mut input_event, device: &Device) -> Result<()>; +#[derive(PartialEq)] +pub enum KeyState { + DOWN, + UP, + OTHER, } -pub struct KeyMaps { - keymaps: Vec>, - keymap_index_keys: HashMap, +pub trait KeyEvent + where + T: Into, +{ + fn code(&self) -> T; + fn value(&self) -> KeyState; +} + +pub trait Keyboard + where + T: Into, + E: KeyEvent, +{ + fn send(&self, event: &mut E) -> Result; + fn send_mod_code(&self, code: T, event: &mut E) -> Result; + fn send_mod_code_value(&self, code: T, up_not_down: bool, event: &mut E) -> Result; + fn synchronize(&self) -> Result; + fn left_shift_code(&self) -> T; + fn right_shift_code(&self) -> T; + fn caps_lock_code(&self) -> T; + fn block_key(&self) -> Result; +} + +pub trait KeyMapper + where + T: Into, + E: KeyEvent, + K: Keyboard, +{ + fn send_event(&self, key_state: &[bool], event: &mut E, device: &K) -> Result; +} + +pub struct KeyMaps + where + T: Into + Copy + Clone + Eq + Hash, + E: KeyEvent, + K: Keyboard, +{ + keymaps: Vec>>, + keymap_index_keys: HashMap, switch_layout_keys: Vec, key_state: [bool; KEY_MAX], - revert_default_key: u16, + revert_default_key: T, revert_keymap_index: usize, // above do not change, below does chosen_keymap_index: usize, current_keymap_index: usize, } -fn parse_key(key_map: &HashMap<&'static str, c_int>, key: &str) -> u16 { +fn parse_key(key_map: &HashMap<&'static str, T>, key: &str) -> T { match key_map.get(key.trim_matches(|c: char| c.is_whitespace() || c == INVERT_KEY_FLAG || c == CAPS_MODIFY_KEY_FLAG)) { - Some(key_code) => *key_code as u16, + Some(key_code) => *key_code, None => panic!("unknown key: {}", key.trim()) } } -fn parse_keymap_numeric(key_map: &HashMap<&'static str, c_int>, keymap: &str) -> Vec { +fn parse_keymap_numeric(key_map: &HashMap<&'static str, T>, keymap: &str) -> Vec { keymap.split(",").map(|k| parse_key(key_map, k)).collect() } -fn parse_key_half_inverted(key_map: &HashMap<&'static str, c_int>, key: &str) -> HalfInvertedKey { +fn parse_key_half_inverted(key_map: &HashMap<&'static str, T>, key: &str) -> HalfInvertedKey { HalfInvertedKey { code: parse_key(key_map, key), invert_shift: key.contains(INVERT_KEY_FLAG), @@ -60,14 +90,14 @@ fn parse_key_half_inverted(key_map: &HashMap<&'static str, c_int>, key: &str) -> } // maybe shortcut to this if not contains * or : -fn parse_keymap_u16(key_map: &HashMap<&'static str, c_int>, keymap: &str) -> Vec { +fn parse_keymap_u16(key_map: &HashMap<&'static str, T>, keymap: &str) -> Vec { keymap.split(",").map(|k| parse_key(key_map, k)).collect() } // todo: how do I return an iterator here instead of .collect to Vec? -fn parse_keymap(key_map: &HashMap<&'static str, c_int>, keymap: &str) -> Vec { +fn parse_keymap(key_map: &HashMap<&'static str, T>, keymap: &str) -> Vec> { keymap.split(",").map(|k| { - let ret: Key = if k.contains(HALF_KEY_SEPARATOR) { + let ret: Key = if k.contains(HALF_KEY_SEPARATOR) { let keys: Vec<&str> = k.split(HALF_KEY_SEPARATOR).collect(); if keys.len() != 2 { panic!("split key can only have 2 keys, 1 :, has {} keys", keys.len()); @@ -84,13 +114,18 @@ fn parse_keymap(key_map: &HashMap<&'static str, c_int>, keymap: &str) -> Vec>(key_map: &HashMap<&'static str, c_int>, path: P) -> KeyMaps { +impl KeyMaps + where + T: Into + TryFrom + Copy + Clone + Eq + Hash + Default + 'static, + E: KeyEvent, + K: Keyboard, +{ + pub fn from_cfg>(key_map: &HashMap<&'static str, T>, path: P) -> KeyMaps { let key_map_config = parse_cfg(path).expect("provided config cannot be found/parsed"); KeyMaps::new(key_map, key_map_config) } - pub fn new(key_map: &HashMap<&'static str, c_int>, config: KeymapConfig) -> KeyMaps { + pub fn new(key_map: &HashMap<&'static str, T>, config: KeymapConfig) -> KeyMaps { if config.keymaps.len() < 2 { panic!("must have at least 2 keymaps (original and mapped) but only have {},", config.keymaps.len()); } @@ -99,10 +134,10 @@ impl KeyMaps { } let base_keymap = parse_keymap_numeric(key_map, &config.keymaps[0]); //println!("base_keymap : {:?}", base_keymap); - let mut keymaps: Vec> = vec!(Box::new(Key::Noop)); // todo: can we share the box? - let mut keymap_index_keys: HashMap = HashMap::new(); + let mut keymaps: Vec>> = vec!(Box::new(Key::Noop)); // todo: can we share the box? + let mut keymap_index_keys: HashMap = HashMap::new(); for (x, v) in config.keymaps.iter().enumerate() { - keymap_index_keys.insert(*key_map.get(&*x.to_string()).unwrap() as u16, x); + keymap_index_keys.insert(*key_map.get(&*x.to_string()).unwrap(), x); if x == 0 { continue; } @@ -141,7 +176,7 @@ impl KeyMaps { KeyMaps { keymaps: keymaps, keymap_index_keys: keymap_index_keys, - switch_layout_keys: config.switch_layout_keys.iter().map(|k| parse_key(key_map, k) as usize).collect(), + switch_layout_keys: config.switch_layout_keys.iter().map(|k| parse_key(key_map, k).into()).collect(), key_state: [false; KEY_MAX], // todo: detect key state? at least CAPSLOCK... revert_default_key: parse_key(key_map, &config.revert_default_key), @@ -150,20 +185,21 @@ impl KeyMaps { current_keymap_index: config.default_keymap_index, } } -} +//} //impl KeyMapper for KeyMaps { -impl KeyMaps { - pub fn send_event(&mut self, mut event: &mut input_event, device: &Device) -> Result<()> { +//impl KeyMaps { +pub fn send_event(&mut self, mut event: &mut E, device: &K) -> Result { //println!("type: {} code: {} value: {}", event.type_, event.code, event.value); - if event.value != 2 { + let value = event.value(); + if value != KeyState::OTHER { // todo: index check here... - if event.code == KEY_CAPSLOCK_U16 { - if event.value == DOWN { - self.key_state[CAPSLOCK_INDEX] = !self.key_state[CAPSLOCK_INDEX]; + if event.code() == device.caps_lock_code() { + if value == KeyState::DOWN { + self.key_state[device.caps_lock_code().into()] = !self.key_state[device.caps_lock_code().into()]; } } else { - self.key_state[event.code as usize] = event.value == DOWN; + self.key_state[event.code().into()] = value == KeyState::DOWN; } let mut switch_layout_keys_pressed = true; for layout_switch_key in self.switch_layout_keys.iter_mut() { @@ -174,18 +210,18 @@ impl KeyMaps { } //println!("switch_layout_keys_pressed: {}", self.switch_layout_keys_pressed); if switch_layout_keys_pressed { - let new_index = self.keymap_index_keys.get(&event.code); + let new_index = self.keymap_index_keys.get(&event.code()); if new_index.is_some() { self.chosen_keymap_index = *new_index.unwrap(); self.current_keymap_index = self.chosen_keymap_index; // todo: what if revert_default_key is held? for now ignore - return Ok(()); // we don't want to also send this keypress, so bail + return device.block_key(); // we don't want to also send this keypress, so bail } } - if event.code == self.revert_default_key { - match event.value { + if event.code() == self.revert_default_key { + match value { // todo: ctrl+c will get c stuck because code c value 1 will be sent, but then we'll let go of ctrl, and code j value 0 is sent, so c is never released... fix that... - DOWN => self.current_keymap_index = self.revert_keymap_index, - UP => self.current_keymap_index = self.chosen_keymap_index, + KeyState::DOWN => self.current_keymap_index = self.revert_keymap_index, + KeyState::UP => self.current_keymap_index = self.chosen_keymap_index, _ => () // do nothing for 2 } } @@ -197,17 +233,17 @@ impl KeyMaps { // 249 is one more than KEY_MICMUTE which is max key in uinput-sys event.rs const KEY_MAX: usize = 249; -struct KeyMap { +struct KeyMap + Copy> { //keymap: Vec, - keymap: [Key; KEY_MAX], + keymap: [Key; KEY_MAX], } -impl KeyMap { +impl + Copy> KeyMap { pub fn new() -> Self { //let mut keymap = [0u16; KEY_MAX]; //let mut keymap : [Box; KEY_MAX] = [Box::new(NOOP); KEY_MAX]; //let mut keymap : [Box; KEY_MAX] = [Box::new(0u16); KEY_MAX]; - let keymap : [Key; KEY_MAX] = [Key::Noop; KEY_MAX]; + let keymap: [Key; KEY_MAX] = [Key::Noop; KEY_MAX]; /* let mut keymap: Vec = Vec::with_capacity(KEY_MAX); #[allow(unused_variables)] @@ -234,25 +270,30 @@ impl KeyMap { self.keymap[from as usize] = to; } */ - pub fn map(&mut self, from: u16, to: Key) { - self.keymap[from as usize] = to; + pub fn map(&mut self, from: T, to: Key) { + self.keymap[from.into()] = to; } } -impl KeyMapper for KeyMap { - fn send_event(&self, key_state: &[bool], event: &mut input_event, device: &Device) -> Result<()> { - self.keymap[event.code as usize].send_event(key_state, event, device) +impl KeyMapper for KeyMap + where + T: Into + Copy, + E: KeyEvent, + K: Keyboard, +{ + fn send_event(&self, key_state: &[bool], event: &mut E, device: &K) -> Result { + self.keymap[event.code().into()].send_event(key_state, event, device) } } -struct CodeKeyMap { +struct CodeKeyMap + TryFrom + Copy + Default> { //keymap: Vec, - keymap: [u16; KEY_MAX], + keymap: [T; KEY_MAX], } -impl CodeKeyMap { +impl + TryFrom + Copy + Default> CodeKeyMap { pub fn new() -> Self { - let mut keymap = [0u16; KEY_MAX]; + let mut keymap = [T::default(); KEY_MAX]; // which is rustier /* for x in 0..KEY_MAX { @@ -260,7 +301,7 @@ impl CodeKeyMap { } */ for (x, v) in keymap.iter_mut().enumerate() { - *v = x as u16; + *v = T::try_from(x).unwrap_or_else(|_| panic!("cannot convert from usize to T ????")); } //println!("keymap: {:?}", &keymap[..]); CodeKeyMap { @@ -268,29 +309,27 @@ impl CodeKeyMap { } } - pub fn map(&mut self, from: u16, to: u16) { - self.keymap[from as usize] = to; + pub fn map(&mut self, from: T, to: T) { + self.keymap[from.into()] = to; } } -impl KeyMapper for CodeKeyMap { - fn send_event(&self, key_state: &[bool], event: &mut input_event, device: &Device) -> Result<()> { - self.keymap[event.code as usize].send_event(key_state, event, device) - } -} - -#[allow(unused_variables, unused_mut)] -impl KeyMapper for u16 { - fn send_event(&self, key_state: &[bool], mut event: &mut input_event, device: &Device) -> Result<()> { - event.code = *self; - device.write_event(event) +impl KeyMapper for CodeKeyMap + where + T: Into + TryFrom + Copy + Default, + E: KeyEvent, + K: Keyboard, +{ + fn send_event(&self, _key_state: &[bool], event: &mut E, device: &K) -> Result { + device.send_mod_code(self.keymap[event.code().into()], event) + //self.keymap[event.code().into()].send_event(key_state, event, device) } } // todo:capslock_nomodify is like a whole-key thing, not a half-key thing, split code/invert_shift to own struct, send into send_key from *InvertedKey, maybe anyway, consider it, maybe 1 char for whole key and another for half? #[derive(Clone, Copy)] -struct HalfInvertedKey { - code: u16, +struct HalfInvertedKey { + code: T, // code this is describing invert_shift: bool, // true to invert shift for this code @@ -298,100 +337,102 @@ struct HalfInvertedKey { // true means capslock does not normally modify this, but you would like it to } -impl HalfInvertedKey { - fn send_key(&self, key_state: &[bool], event: &mut input_event, device: &Device, left_shift: bool, right_shift: bool, caps_lock: bool) -> Result<()> { - let code = self.code; - let value = event.value; - let mut invert_shift = self.invert_shift; - if value == DOWN { - if caps_lock && self.capslock_nomodify { - invert_shift = !invert_shift; - } - if invert_shift { - if left_shift { - event.code = KEY_LEFTSHIFT_U16; - event.value = UP; - } else if right_shift { - event.code = KEY_RIGHTSHIFT_U16; - event.value = UP; - } else { - event.code = KEY_LEFTSHIFT_U16; - event.value = DOWN; - } - //event.code.send_event(key_state, event, device); - device.write_event(event)?; - // SYN_REPORT after, then key, then key's SYN_REPORT - device.synchronize()?; - event.code = code; // not needed since u16 does it - event.value = value; - } +fn send_half_inverted_key(half_inverted_key: &HalfInvertedKey, event: &mut E, device: &K, left_shift: bool, right_shift: bool, caps_lock: bool) -> Result + where + T: Into + Clone + Copy, + E: KeyEvent, + K: Keyboard, +{ + let value = event.value(); + let mut invert_shift = half_inverted_key.invert_shift; + if value == KeyState::DOWN { + if caps_lock && half_inverted_key.capslock_nomodify { + invert_shift = !invert_shift; } - code.send_event(key_state, event, device)?; - if value == UP { - if caps_lock && self.capslock_nomodify { - invert_shift = !invert_shift; - } - if invert_shift { - if left_shift { - event.code = KEY_LEFTSHIFT_U16; - event.value = DOWN; - } else if right_shift { - event.code = KEY_RIGHTSHIFT_U16; - event.value = DOWN; - } else { - event.code = KEY_LEFTSHIFT_U16; - event.value = UP; - } - //event.code.send_event(key_state, event, device); - // SYN_REPORT first after key, then shift, then key's SYN_REPORT which will be used for shift's - device.synchronize()?; - device.write_event(event)?; - // neither of these are needed now... - event.code = code; // not needed since u16 does it - event.value = value; - } + if invert_shift { + let (shift_code, up_not_down) = if left_shift { + (device.left_shift_code(), true) + } else if right_shift { + (device.right_shift_code(), true) + } else { + (device.left_shift_code(), false) + }; + device.send_mod_code_value(shift_code, up_not_down, event)?; + // SYN_REPORT after, then key, then key's SYN_REPORT + device.synchronize()?; } - Ok(()) } + let ret = device.send_mod_code(half_inverted_key.code, event)?; + if value == KeyState::UP { + if caps_lock && half_inverted_key.capslock_nomodify { + invert_shift = !invert_shift; + } + if invert_shift { + let (shift_code, up_not_down) = if left_shift { + (device.left_shift_code(), false) + } else if right_shift { + (device.right_shift_code(), false) + } else { + (device.left_shift_code(), true) + }; + // SYN_REPORT first after key, then shift, then key's SYN_REPORT which will be used for shift's + device.synchronize()?; + device.send_mod_code_value(shift_code, up_not_down, event)?; + } + } + Ok(ret) } -impl KeyMapper for HalfInvertedKey { - fn send_event(&self, key_state: &[bool], event: &mut input_event, device: &Device) -> Result<()> { - let left_shift = key_state[LEFTSHIFT_INDEX]; - let right_shift = key_state[RIGHTSHIFT_INDEX]; - let caps_lock = key_state[CAPSLOCK_INDEX]; - self.send_key(key_state, event, device, left_shift, right_shift, caps_lock) +impl KeyMapper for HalfInvertedKey + where + T: Into + Clone + Copy, + E: KeyEvent, + K: Keyboard, +{ + fn send_event(&self, key_state: &[bool], event: &mut E, device: &K) -> Result { + let left_shift = key_state[device.left_shift_code().into()]; + let right_shift = key_state[device.right_shift_code().into()]; + let caps_lock = key_state[device.caps_lock_code().into()]; + send_half_inverted_key(self, event, device, left_shift, right_shift, caps_lock) } } #[derive(Clone, Copy)] -enum Key { +enum Key + where + T: Copy + Clone +{ Noop, - Direct(u16), - HalfKey(HalfInvertedKey), - FullKey(HalfInvertedKey, HalfInvertedKey), + Direct(T), + HalfKey(HalfInvertedKey), + FullKey(HalfInvertedKey, HalfInvertedKey), } -impl KeyMapper for Key { - fn send_event(&self, key_state: &[bool], event: &mut input_event, device: &Device) -> Result<()> { +impl KeyMapper for Key + where + T: Into + Copy, + E: KeyEvent, + K: Keyboard, +{ + fn send_event(&self, key_state: &[bool], event: &mut E, device: &K) -> Result { match *self { Key::Noop => { - device.write_event(event) + device.send(event) }, Key::Direct(code) => { - code.send_event(key_state, event, device) + device.send_mod_code(code, event) }, Key::HalfKey(ref key_half) => { key_half.send_event(key_state, event, device) }, Key::FullKey(ref noshift_half, ref shift_half) => { - let left_shift = key_state[LEFTSHIFT_INDEX]; - let right_shift = key_state[RIGHTSHIFT_INDEX]; - let caps_lock = key_state[CAPSLOCK_INDEX]; + let left_shift = key_state[device.left_shift_code().into()]; + let right_shift = key_state[device.right_shift_code().into()]; + let caps_lock = key_state[device.caps_lock_code().into()]; if caps_lock != (left_shift || right_shift) { - shift_half.send_key(key_state, event, device, left_shift, right_shift, caps_lock) + send_half_inverted_key(shift_half, event, device, left_shift, right_shift, caps_lock) } else { - noshift_half.send_key(key_state, event, device, left_shift, right_shift, caps_lock) + send_half_inverted_key(noshift_half, event, device, left_shift, right_shift, caps_lock) } }, } diff --git a/src/lib.rs b/src/lib.rs index e9e3647..6b2617c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ pub use error::Error; pub type Result = ::std::result::Result; pub mod keymapper; -pub use keymapper::KeyMaps; +pub use keymapper::*; #[cfg(target_os = "linux")] mod linux; diff --git a/src/linux/device/builder.rs b/src/linux/device/builder.rs index 77d86af..cd3f787 100644 --- a/src/linux/device/builder.rs +++ b/src/linux/device/builder.rs @@ -69,7 +69,7 @@ impl Builder { self } - pub fn event(mut self, key_codes: Values<&str, c_int>) -> Res { + pub fn event(mut self, key_codes: Values<&str, u16>) -> Res { self.abs = None; //let test_ev_key : c_int = EV_KEY as c_int; unsafe { diff --git a/src/linux/mod.rs b/src/linux/mod.rs index ecff424..2cbd661 100644 --- a/src/linux/mod.rs +++ b/src/linux/mod.rs @@ -22,6 +22,10 @@ use std::{env, thread}; use std::sync::mpsc; use std::sync::mpsc::Sender; +// 1 is down, 0 is up +const DOWN: i32 = 1; +const UP: i32 = 0; + use getopts::Options; use inotify::{ @@ -30,12 +34,74 @@ use inotify::{ WatchMask, }; use std::collections::HashMap; -use std::os::raw::c_int; const VERSION: &'static str = env!("CARGO_PKG_VERSION"); const EV_KEY_U16: u16 = EV_KEY as u16; +type LinuxKeyMaps = KeyMaps; + +impl KeyEvent for input_event { + fn code(&self) -> u16 { + self.code + } + + fn value(&self) -> KeyState { + match self.value { + UP => KeyState::UP, + DOWN => KeyState::DOWN, + _ => KeyState::OTHER, + } + } +} + +impl Keyboard for Device { + fn send(&self, event: &mut input_event) -> Result<()> { + self.write_event(event) + } + + fn send_mod_code(&self, code: u16, event: &mut input_event) -> Result<()> { + event.code = code; + Keyboard::send(self, event) + } + + fn send_mod_code_value(&self, code: u16, up_not_down: bool, event: &mut input_event) -> Result<()> { + event.code = code; + let value = event.value; + if up_not_down { + event.value = UP; + } else { + event.value = DOWN; + } + Keyboard::send(self, event)?; + // set it back + event.value = value; + Ok(()) + } + + fn synchronize(&self) -> Result<()> { + Device::synchronize(self) + } + + fn left_shift_code(&self) -> u16 { + KEY_LEFTSHIFT as u16 + } + + fn right_shift_code(&self) -> u16 { + KEY_RIGHTSHIFT as u16 + } + + fn caps_lock_code(&self) -> u16 { + KEY_CAPSLOCK as u16 + } + + fn block_key(&self) -> Result<()> { + Ok(()) // we don't actually use/need this here + } +} + + + #[derive(Debug)] struct Config { device_files: Vec, @@ -62,7 +128,7 @@ pub fn main_res() -> Result<()> { .event(key_map.values())? .create()?; - let mut key_map = KeyMaps::from_cfg(&key_map, &config.config_file); + let mut key_map = LinuxKeyMaps::from_cfg(&key_map, &config.config_file); //println!("keymaps: {:?}", keymaps); if config.device_files.len() == 1 { @@ -135,7 +201,7 @@ pub fn main_res() -> Result<()> { Ok(()) } -fn send_event(key_map: &mut KeyMaps, mut event: input_event, device: &Device) -> Result<()> { +fn send_event(key_map: &mut LinuxKeyMaps, mut event: input_event, device: &Device) -> Result<()> { if event.type_ == EV_KEY_U16 { key_map.send_event(&mut event, &device)? } else { @@ -233,7 +299,7 @@ fn get_keyboard_device_filenames() -> Vec { filenames } -pub fn key_map() -> HashMap<&'static str, c_int> { +pub fn key_map() -> HashMap<&'static str, u16> { [ // generated like: // grep -o 'KEY_[^ :;]*' ~/.cargo/registry/src/github.com-1ecc6299db9ec823/uinput-sys-0.1.3/src/events.rs | sed 's/^KEY_//' | awk '{print "(\""$1"\", KEY_"$1"),"}' @@ -541,5 +607,5 @@ pub fn key_map() -> HashMap<&'static str, c_int> { ("P0", KEY_KP0), ("PDOT", KEY_KPDOT), ("PENT", KEY_KPENTER), - ].iter().cloned().map(|(m, v)| (m, v)).collect() + ].iter().cloned().map(|(m, v)| (m, v as u16)).collect() } \ No newline at end of file