summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWez Furlong <wez@wezfurlong.org>2019-12-31 07:31:07 -0800
committerWez Furlong <wez@wezfurlong.org>2019-12-31 07:31:07 -0800
commit3219978c3fe2db287cd4dd9aa3a4c67811ae80f2 (patch)
tree62e908923269f8272cfb6c47175c9630827ed4ef
parentc9afa88699a558b22a2911ff0db856b8778664af (diff)
split into more modules
-rw-r--r--src/main.rs391
-rw-r--r--src/mapping.rs15
-rw-r--r--src/remapper.rs376
3 files changed, 397 insertions, 385 deletions
diff --git a/src/main.rs b/src/main.rs
index 78f9308..3473bf7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,12 +1,12 @@
+use crate::mapping::*;
+use crate::remapper::*;
use anyhow::*;
-use evdev_rs::enums::{EventCode, EV_KEY as KeyCode};
-use evdev_rs::{Device, GrabMode, InputEvent, ReadFlag, TimeVal, UInputDevice};
-use std::collections::{HashMap, HashSet};
-use std::path::Path;
use std::time::Duration;
use structopt::StructOpt;
mod deviceinfo;
+mod mapping;
+mod remapper;
#[derive(Debug, StructOpt)]
#[structopt(
@@ -22,368 +22,6 @@ struct Opt {
list_devices: bool,
}
-#[derive(Debug, Clone, Eq, PartialEq)]
-enum Mapping {
- DualRole {
- input: KeyCode,
- hold: Vec<KeyCode>,
- tap: Vec<KeyCode>,
- },
- Remap {
- input: HashSet<KeyCode>,
- output: HashSet<KeyCode>,
- },
-}
-
-#[derive(Clone, Copy, Debug)]
-enum KeyEventType {
- Release,
- Press,
- Repeat,
- Unknown(i32),
-}
-
-impl KeyEventType {
- fn from_value(value: i32) -> Self {
- match value {
- 0 => KeyEventType::Release,
- 1 => KeyEventType::Press,
- 2 => KeyEventType::Repeat,
- _ => KeyEventType::Unknown(value),
- }
- }
-
- fn value(&self) -> i32 {
- match self {
- Self::Release => 0,
- Self::Press => 1,
- Self::Repeat => 2,
- Self::Unknown(n) => *n,
- }
- }
-}
-
-fn timeval_diff(newer: &TimeVal, older: &TimeVal) -> Duration {
- const MICROS_PER_SECOND: i64 = 1000000;
- let secs = newer.tv_sec - older.tv_sec;
- let usecs = newer.tv_usec - older.tv_usec;
-
- let (secs, usecs) = if usecs < 0 {
- (secs - 1, usecs + MICROS_PER_SECOND)
- } else {
- (secs, usecs)
- };
-
- Duration::from_micros(((secs * MICROS_PER_SECOND) + usecs) as u64)
-}
-
-struct InputMapper {
- input: Device,
- output: UInputDevice,
- /// If present in this map, the key is down since the instant
- /// of its associated value
- input_state: HashMap<KeyCode, TimeVal>,
-
- mappings: Vec<Mapping>,
-
- /// The most recent candidate for a tap function is held here
- tapping: Option<KeyCode>,
-
- output_keys: HashSet<KeyCode>,
-}
-
-fn enable_key_code(input: &mut Device, key: KeyCode) -> Result<()> {
- input
- .enable(&EventCode::EV_KEY(key.clone()))
- .context(format!("enable key {:?}", key))?;
- Ok(())
-}
-
-impl InputMapper {
- pub fn create_mapper<P: AsRef<Path>>(path: P, mappings: Vec<Mapping>) -> Result<Self> {
- let path = path.as_ref();
- let f = std::fs::File::open(path).context(format!("opening {}", path.display()))?;
- let mut input = Device::new().ok_or_else(|| anyhow!("failed to make new Device"))?;
- input
- .set_fd(f)
- .context(format!("assigning fd for {} to Device", path.display()))?;
-
- input.set_name(&format!("evremap Virtual input for {}", path.display()));
-
- // Ensure that any remapped keys are supported by the generated output device
- for map in &mappings {
- match map {
- Mapping::DualRole { tap, hold, .. } => {
- for t in tap {
- enable_key_code(&mut input, t.clone())?;
- }
- for h in hold {
- enable_key_code(&mut input, h.clone())?;
- }
- }
- Mapping::Remap { output, .. } => {
- for o in output {
- enable_key_code(&mut input, o.clone())?;
- }
- }
- }
- }
-
- let output = UInputDevice::create_from_device(&input)
- .context(format!("creating UInputDevice from {}", path.display()))?;
-
- input
- .grab(GrabMode::Grab)
- .context(format!("grabbing exclusive access on {}", path.display()))?;
-
- Ok(Self {
- input,
- output,
- input_state: HashMap::new(),
- output_keys: HashSet::new(),
- tapping: None,
- mappings,
- })
- }
-
- /// Compute the effective set of keys that are pressed
- fn compute_keys(&self) -> HashSet<KeyCode> {
- // Start with the input keys
- let mut keys: HashSet<KeyCode> = self.input_state.keys().cloned().collect();
-
- // First phase is to apply any DualRole mappings as they are likely to
- // be used to produce modifiers when held.
- for map in &self.mappings {
- if let Mapping::DualRole { input, hold, .. } = map {
- if keys.contains(input) {
- keys.remove(input);
- for h in hold {
- keys.insert(h.clone());
- }
- }
- }
- }
-
- // Second pass to apply Remap items
- for map in &self.mappings {
- if let Mapping::Remap { input, output } = map {
- if input.is_subset(&keys) {
- for i in input {
- keys.remove(i);
- }
- for o in output {
- keys.insert(o.clone());
- }
- }
- }
- }
-
- keys
- }
-
- fn compute_and_apply_keys(&mut self, time: &TimeVal) -> Result<()> {
- let desired_keys = self.compute_keys();
- let to_release: Vec<KeyCode> = self
- .output_keys
- .difference(&desired_keys)
- .cloned()
- .collect();
-
- let to_press: Vec<KeyCode> = desired_keys
- .difference(&self.output_keys)
- .cloned()
- .collect();
-
- if !to_release.is_empty() {
- self.emit_keys(&to_release, time, KeyEventType::Release)?;
- }
- if !to_press.is_empty() {
- self.emit_keys(&to_press, time, KeyEventType::Press)?;
- }
- Ok(())
- }
-
- fn lookup_dual_role_mapping(&self, code: KeyCode) -> Option<Mapping> {
- for map in &self.mappings {
- if let Mapping::DualRole { input, .. } = map {
- if *input == code {
- // A DualRole mapping has the highest precedence
- // so we've found our match
- return Some(map.clone());
- }
- }
- }
- None
- }
-
- fn lookup_mapping(&self, code: KeyCode) -> Option<Mapping> {
- let mut candidates = vec![];
-
- for map in &self.mappings {
- match map {
- Mapping::DualRole { input, .. } => {
- if *input == code {
- // A DualRole mapping has the highest precedence
- // so we've found our match
- return Some(map.clone());
- }
- }
- Mapping::Remap { input, .. } => {
- // Look for a mapping that includes the current key.
- // If part of a chord, all of its component keys must
- // also be pressed.
- let mut code_matched = false;
- let mut all_matched = true;
- for i in input {
- if *i == code {
- code_matched = true;
- } else if !self.input_state.contains_key(i) {
- all_matched = false;
- break;
- }
- }
- if code_matched && all_matched {
- candidates.push(map);
- }
- }
- }
- }
-
- // Any matches must be Remap entries. We want the one
- // with the most active keys
- candidates.sort_by(|a, b| match (a, b) {
- (Mapping::Remap { input: input_a, .. }, Mapping::Remap { input: input_b, .. }) => {
- input_a.len().cmp(&input_b.len()).reverse()
- }
- _ => unreachable!(),
- });
-
- candidates.get(0).map(|&m| m.clone())
- }
-
- pub fn update_with_event(&mut self, event: &InputEvent, code: KeyCode) -> Result<()> {
- let event_type = KeyEventType::from_value(event.value);
- match event_type {
- KeyEventType::Release => {
- let pressed_at = match self.input_state.remove(&code) {
- None => {
- self.write_event_and_sync(event)?;
- return Ok(());
- }
- Some(p) => p,
- };
-
- self.compute_and_apply_keys(&event.time)?;
-
- if let Some(Mapping::DualRole { tap, .. }) =
- self.lookup_dual_role_mapping(code.clone())
- {
- // If released quickly enough, becomes a tap press.
- if let Some(tapping) = self.tapping.take() {
- if tapping == code
- && timeval_diff(&event.time, &pressed_at) <= Duration::from_millis(200)
- {
- self.emit_keys(&tap, &event.time, KeyEventType::Press)?;
- self.emit_keys(&tap, &event.time, KeyEventType::Release)?;
- }
- }
- }
- }
- KeyEventType::Press => {
- self.input_state.insert(code.clone(), event.time.clone());
-
- match self.lookup_mapping(code.clone()) {
- Some(_) => {
- self.compute_and_apply_keys(&event.time)?;
- self.tapping.replace(code);
- }
- None => {
- // Just pass it through
- self.cancel_pending_tap();
- self.compute_and_apply_keys(&event.time)?;
- }
- }
- }
- KeyEventType::Repeat => {
- match self.lookup_mapping(code.clone()) {
- Some(Mapping::DualRole { hold, .. }) => {
- self.emit_keys(&hold, &event.time, KeyEventType::Repeat)?;
- }
- Some(Mapping::Remap { output, .. }) => {
- let output: Vec<KeyCode> = output.iter().cloned().collect();
- self.emit_keys(&output, &event.time, KeyEventType::Repeat)?;
- }
- None => {
- // Just pass it through
- self.cancel_pending_tap();
- self.write_event_and_sync(event)?;
- }
- }
- }
- KeyEventType::Unknown(_) => {
- self.write_event_and_sync(event)?;
- }
- }
-
- Ok(())
- }
-
- fn cancel_pending_tap(&mut self) {
- self.tapping.take();
- }
-
- fn emit_keys(
- &mut self,
- key: &[KeyCode],
- time: &TimeVal,
- event_type: KeyEventType,
- ) -> Result<()> {
- for k in key {
- let event = make_event(k.clone(), time, event_type);
- self.write_event(&event)?;
- }
- self.generate_sync_event(time)?;
- Ok(())
- }
-
- fn write_event_and_sync(&mut self, event: &InputEvent) -> Result<()> {
- self.write_event(event)?;
- self.generate_sync_event(&event.time)?;
- Ok(())
- }
-
- fn write_event(&mut self, event: &InputEvent) -> Result<()> {
- log::trace!("OUT: {:?}", event);
- self.output.write_event(&event)?;
- if let EventCode::EV_KEY(ref key) = event.event_code {
- let event_type = KeyEventType::from_value(event.value);
- match event_type {
- KeyEventType::Press | KeyEventType::Repeat => {
- self.output_keys.insert(key.clone());
- }
- KeyEventType::Release => {
- self.output_keys.remove(key);
- }
- _ => {}
- }
- }
- Ok(())
- }
-
- fn generate_sync_event(&self, time: &TimeVal) -> Result<()> {
- self.output.write_event(&InputEvent::new(
- time,
- &EventCode::EV_SYN(evdev_rs::enums::EV_SYN::SYN_REPORT),
- 0,
- ))?;
- Ok(())
- }
-}
-
-fn make_event(key: KeyCode, time: &TimeVal, event_type: KeyEventType) -> InputEvent {
- InputEvent::new(time, &EventCode::EV_KEY(key), event_type.value())
-}
-
fn main() -> Result<()> {
pretty_env_logger::init();
let opt = Opt::from_args();
@@ -422,23 +60,6 @@ fn main() -> Result<()> {
let device_info = deviceinfo::DeviceInfo::with_name("AT Translated Set 2 keyboard")?;
let mut mapper = InputMapper::create_mapper(device_info.path, mappings)?;
-
- log::error!("Going into read loop");
- loop {
- let (status, event) = mapper
- .input
- .next_event(ReadFlag::NORMAL | ReadFlag::BLOCKING)?;
- match status {
- evdev_rs::ReadStatus::Success => {
- if let EventCode::EV_KEY(ref key) = event.event_code {
- log::trace!("IN {:?}", event);
- mapper.update_with_event(&event, key.clone())?;
- } else {
- log::trace!("PASSTHRU {:?}", event);
- mapper.output.write_event(&event)?;
- }
- }
- evdev_rs::ReadStatus::Sync => bail!("ReadStatus::Sync!"),
- }
- }
+ mapper.run_mapper()?;
+ Ok(())
}
diff --git a/src/mapping.rs b/src/mapping.rs
new file mode 100644
index 0000000..6fa655e
--- /dev/null
+++ b/src/mapping.rs
@@ -0,0 +1,15 @@
+pub use evdev_rs::enums::{EventCode, EV_KEY as KeyCode};
+use std::collections::HashSet;
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum Mapping {
+ DualRole {
+ input: KeyCode,
+ hold: Vec<KeyCode>,
+ tap: Vec<KeyCode>,
+ },
+ Remap {
+ input: HashSet<KeyCode>,
+ output: HashSet<KeyCode>,
+ },
+}
diff --git a/src/remapper.rs b/src/remapper.rs
new file mode 100644
index 0000000..b81f29b
--- /dev/null
+++ b/src/remapper.rs
@@ -0,0 +1,376 @@
+use crate::mapping::*;
+use anyhow::*;
+use evdev_rs::{Device, GrabMode, InputEvent, ReadFlag, TimeVal, UInputDevice};
+use std::collections::{HashMap, HashSet};
+use std::path::Path;
+use std::time::Duration;
+
+#[derive(Clone, Copy, Debug)]
+enum KeyEventType {
+ Release,
+ Press,
+ Repeat,
+ Unknown(i32),
+}
+
+impl KeyEventType {
+ fn from_value(value: i32) -> Self {
+ match value {
+ 0 => KeyEventType::Release,
+ 1 => KeyEventType::Press,
+ 2 => KeyEventType::Repeat,
+ _ => KeyEventType::Unknown(value),
+ }
+ }
+
+ fn value(&self) -> i32 {
+ match self {
+ Self::Release => 0,
+ Self::Press => 1,
+ Self::Repeat => 2,
+ Self::Unknown(n) => *n,
+ }
+ }
+}
+
+fn timeval_diff(newer: &TimeVal, older: &TimeVal) -> Duration {
+ const MICROS_PER_SECOND: i64 = 1000000;
+ let secs = newer.tv_sec - older.tv_sec;
+ let usecs = newer.tv_usec - older.tv_usec;
+
+ let (secs, usecs) = if usecs < 0 {
+ (secs - 1, usecs + MICROS_PER_SECOND)
+ } else {
+ (secs, usecs)
+ };
+
+ Duration::from_micros(((secs * MICROS_PER_SECOND) + usecs) as u64)
+}
+
+pub struct InputMapper {
+ input: Device,
+ output: UInputDevice,
+ /// If present in this map, the key is down since the instant
+ /// of its associated value
+ input_state: HashMap<KeyCode, TimeVal>,
+
+ mappings: Vec<Mapping>,
+
+ /// The most recent candidate for a tap function is held here
+ tapping: Option<KeyCode>,
+
+ output_keys: HashSet<KeyCode>,
+}
+
+fn enable_key_code(input: &mut Device, key: KeyCode) -> Result<()> {
+ input
+ .enable(&EventCode::EV_KEY(key.clone()))
+ .context(format!("enable key {:?}", key))?;
+ Ok(())
+}
+
+impl InputMapper {
+ pub fn create_mapper<P: AsRef<Path>>(path: P, mappings: Vec<Mapping>) -> Result<Self> {
+ let path = path.as_ref();
+ let f = std::fs::File::open(path).context(format!("opening {}", path.display()))?;
+ let mut input = Device::new().ok_or_else(|| anyhow!("failed to make new Device"))?;
+ input
+ .set_fd(f)
+ .context(format!("assigning fd for {} to Device", path.display()))?;
+
+ input.set_name(&format!("evremap Virtual input for {}", path.display()));
+
+ // Ensure that any remapped keys are supported by the generated output device
+ for map in &mappings {
+ match map {
+ Mapping::DualRole { tap, hold, .. } => {
+ for t in tap {
+ enable_key_code(&mut input, t.clone())?;
+ }
+ for h in hold {
+ enable_key_code(&mut input, h.clone())?;
+ }
+ }
+ Mapping::Remap { output, .. } => {
+ for o in output {
+ enable_key_code(&mut input, o.clone())?;
+ }
+ }
+ }
+ }
+
+ let output = UInputDevice::create_from_device(&input)
+ .context(format!("creating UInputDevice from {}", path.display()))?;
+
+ input
+ .grab(GrabMode::Grab)
+ .context(format!("grabbing exclusive access on {}", path.display()))?;
+
+ Ok(Self {
+ input,
+ output,
+ input_state: HashMap::new(),
+ output_keys: HashSet::new(),
+ tapping: None,
+ mappings,
+ })
+ }
+
+ pub fn run_mapper(&mut self) -> Result<()> {
+ log::error!("Going into read loop");
+ loop {
+ let (status, event) = self
+ .input
+ .next_event(ReadFlag::NORMAL | ReadFlag::BLOCKING)?;
+ match status {
+ evdev_rs::ReadStatus::Success => {
+ if let EventCode::EV_KEY(ref key) = event.event_code {
+ log::trace!("IN {:?}", event);
+ self.update_with_event(&event, key.clone())?;
+ } else {
+ log::trace!("PASSTHRU {:?}", event);
+ self.output.write_event(&event)?;
+ }
+ }
+ evdev_rs::ReadStatus::Sync => bail!("ReadStatus::Sync!"),
+ }
+ }
+ }
+
+ /// Compute the effective set of keys that are pressed
+ fn compute_keys(&self) -> HashSet<KeyCode> {
+ // Start with the input keys
+ let mut keys: HashSet<KeyCode> = self.input_state.keys().cloned().collect();
+
+ // First phase is to apply any DualRole mappings as they are likely to
+ // be used to produce modifiers when held.
+ for map in &self.mappings {
+ if let Mapping::DualRole { input, hold, .. } = map {
+ if keys.contains(input) {
+ keys.remove(input);
+ for h in hold {
+ keys.insert(h.clone());
+ }
+ }
+ }
+ }
+
+ // Second pass to apply Remap items
+ for map in &self.mappings {
+ if let Mapping::Remap { input, output } = map {
+ if input.is_subset(&keys) {
+ for i in input {
+ keys.remove(i);
+ }
+ for o in output {
+ keys.insert(o.clone());
+ }
+ }
+ }
+ }
+
+ keys
+ }
+
+ fn compute_and_apply_keys(&mut self, time: &TimeVal) -> Result<()> {
+ let desired_keys = self.compute_keys();
+ let to_release: Vec<KeyCode> = self
+ .output_keys
+ .difference(&desired_keys)
+ .cloned()
+ .collect();
+
+ let to_press: Vec<KeyCode> = desired_keys
+ .difference(&self.output_keys)
+ .cloned()
+ .collect();
+
+ if !to_release.is_empty() {
+ self.emit_keys(&to_release, time, KeyEventType::Release)?;
+ }
+ if !to_press.is_empty() {
+ self.emit_keys(&to_press, time, KeyEventType::Press)?;
+ }
+ Ok(())
+ }
+
+ fn lookup_dual_role_mapping(&self, code: KeyCode) -> Option<Mapping> {
+ for map in &self.mappings {
+ if let Mapping::DualRole { input, .. } = map {
+ if *input == code {
+ // A DualRole mapping has the highest precedence
+ // so we've found our match
+ return Some(map.clone());
+ }
+ }
+ }
+ None
+ }
+
+ fn lookup_mapping(&self, code: KeyCode) -> Option<Mapping> {
+ let mut candidates = vec![];
+
+ for map in &self.mappings {
+ match map {
+ Mapping::DualRole { input, .. } => {
+ if *input == code {
+ // A DualRole mapping has the highest precedence
+ // so we've found our match
+ return Some(map.clone());
+ }
+ }
+ Mapping::Remap { input, .. } => {
+ // Look for a mapping that includes the current key.
+ // If part of a chord, all of its component keys must
+ // also be pressed.
+ let mut code_matched = false;
+ let mut all_matched = true;
+ for i in input {
+ if *i == code {
+ code_matched = true;
+ } else if !self.input_state.contains_key(i) {
+ all_matched = false;
+ break;
+ }
+ }
+ if code_matched && all_matched {
+ candidates.push(map);
+ }
+ }
+ }
+ }
+
+ // Any matches must be Remap entries. We want the one
+ // with the most active keys
+ candidates.sort_by(|a, b| match (a, b) {
+ (Mapping::Remap { input: input_a, .. }, Mapping::Remap { input: input_b, .. }) => {
+ input_a.len().cmp(&input_b.len()).reverse()
+ }
+ _ => unreachable!(),
+ });
+
+ candidates.get(0).map(|&m| m.clone())
+ }
+
+ pub fn update_with_event(&mut self, event: &InputEvent, code: KeyCode) -> Result<()> {
+ let event_type = KeyEventType::from_value(event.value);
+ match event_type {
+ KeyEventType::Release => {
+ let pressed_at = match self.input_state.remove(&code) {
+ None => {
+ self.write_event_and_sync(event)?;
+ return Ok(());
+ }
+ Some(p) => p,
+ };
+
+ self.compute_and_apply_keys(&event.time)?;
+
+ if let Some(Mapping::DualRole { tap, .. }) =
+ self.lookup_dual_role_mapping(code.clone())
+ {
+ // If released quickly enough, becomes a tap press.
+ if let Some(tapping) = self.tapping.take() {
+ if tapping == code
+ && timeval_diff(&event.time, &pressed_at) <= Duration::from_millis(200)
+ {
+ self.emit_keys(&tap, &event.time, KeyEventType::Press)?;
+ self.emit_keys(&tap, &event.time, KeyEventType::Release)?;
+ }
+ }
+ }
+ }
+ KeyEventType::Press => {
+ self.input_state.insert(code.clone(), event.time.clone());
+
+ match self.lookup_mapping(code.clone()) {
+ Some(_) => {
+ self.compute_and_apply_keys(&event.time)?;
+ self.tapping.replace(code);
+ }
+ None => {
+ // Just pass it through
+ self.cancel_pending_tap();
+ self.compute_and_apply_keys(&event.time)?;
+ }
+ }
+ }
+ KeyEventType::Repeat => {
+ match self.lookup_mapping(code.clone()) {
+ Some(Mapping::DualRole { hold, .. }) => {
+ self.emit_keys(&hold, &event.time, KeyEventType::Repeat)?;
+ }
+ Some(Mapping::Remap { output, .. }) => {
+ let output: Vec<KeyCode> = output.iter().cloned().collect();
+ self.emit_keys(&output, &event.time, KeyEventType::Repeat)?;
+ }
+ None => {
+ // Just pass it through
+ self.cancel_pending_tap();
+ self.write_event_and_sync(event)?;
+ }
+ }
+ }
+ KeyEventType::Unknown(_) => {
+ self.write_event_and_sync(event)?;
+ }
+ }
+
+ Ok(())
+ }
+
+ fn cancel_pending_tap(&mut self) {
+ self.tapping.take();
+ }
+
+ fn emit_keys(
+ &mut self,
+ key: &[KeyCode],
+ time: &TimeVal,
+ event_type: KeyEventType,
+ ) -> Result<()> {
+ for k in key {
+ let event = make_event(k.clone(), time, event_type);
+ self.write_event(&event)?;
+ }
+ self.generate_sync_event(time)?;
+ Ok(())
+ }
+
+ fn write_event_and_sync(&mut self, event: &InputEvent) -> Result<()> {
+ self.write_event(event)?;
+ self.generate_sync_event(&event.time)?;
+ Ok(())
+ }
+
+ fn write_event(&mut self, event: &InputEvent) -> Result<()> {
+ log::trace!("OUT: {:?}", event);
+ self.output.write_event(&event)?;
+ if let EventCode::EV_KEY(ref key) = event.event_code {
+ let event_type = KeyEventType::from_value(event.value);
+ match event_type {
+ KeyEventType::Press | KeyEventType::Repeat => {
+ self.output_keys.insert(key.clone());
+ }
+ KeyEventType::Release => {
+ self.output_keys.remove(key);
+ }
+ _ => {}
+ }
+ }
+ Ok(())
+ }
+
+ fn generate_sync_event(&self, time: &TimeVal) -> Result<()> {
+ self.output.write_event(&InputEvent::new(
+ time,
+ &EventCode::EV_SYN(evdev_rs::enums::EV_SYN::SYN_REPORT),
+ 0,
+ ))?;
+ Ok(())
+ }
+}
+
+fn make_event(key: KeyCode, time: &TimeVal, event_type: KeyEventType) -> InputEvent {
+ InputEvent::new(time, &EventCode::EV_KEY(key), event_type.value())
+}