This commit is contained in:
Robin Appelman 2025-06-08 16:30:07 +02:00
commit 2d871e2e2f
5 changed files with 198 additions and 273 deletions

134
Cargo.lock generated
View file

@ -2,23 +2,11 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "bitflags"
version = "1.3.2"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "bitvec"
@ -32,12 +20,6 @@ dependencies = [
"wyz",
]
[[package]]
name = "cc"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549"
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -46,36 +28,35 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.1.1"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "ctrlc"
version = "3.4.4"
version = "3.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345"
checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73"
dependencies = [
"nix 0.28.0",
"nix 0.30.1",
"windows-sys",
]
[[package]]
name = "evdev"
version = "0.12.2"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab6055a93a963297befb0f4f6e18f314aec9767a4bbe88b151126df2433610a7"
checksum = "a3c10865aeab1a7399b3c2d6046e8dcc7f5227b656f235ed63ef5ee45a47b8f8"
dependencies = [
"bitvec",
"cfg-if",
"libc",
"nix 0.23.2",
"thiserror",
"nix 0.29.0",
]
[[package]]
name = "evtype"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"ctrlc",
"evdev",
@ -90,9 +71,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "libc"
version = "0.2.155"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "main_error"
@ -100,56 +81,28 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "155db5e86c6e45ee456bf32fad5a290ee1f7151c2faca27ea27097568da67d1a"
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]]
name = "nix"
version = "0.23.2"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags 1.3.2",
"cc",
"cfg-if",
"libc",
"memoffset",
]
[[package]]
name = "nix"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
"bitflags 2.6.0",
"bitflags",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]]
name = "proc-macro2"
version = "1.0.86"
name = "nix"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
"bitflags",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]]
@ -158,54 +111,17 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "syn"
version = "2.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "thiserror"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "windows-sys"
version = "0.52.0"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]

View file

@ -1,9 +1,10 @@
[package]
name = "evtype"
version = "0.2.0"
version = "0.2.1"
authors = ["Robin Appelman <robin@icewind.nl>"]
edition = "2018"
rust-version = "1.69.0"
repository = "https://codeberg.org/icewind/evtype"
[[bin]]
name = "evtype_daemon"
@ -14,6 +15,6 @@ name = "evtype"
path = "src/client.rs"
[dependencies]
evdev = "0.12.2"
evdev = "0.13.1"
main_error = "0.1.2"
ctrlc = "3.4.4"

View file

@ -1,14 +1,24 @@
use main_error::MainError;
use std::io::Write;
use std::os::unix::net::UnixStream;
use std::process::ExitCode;
fn main() -> Result<(), MainError> {
fn main() -> ExitCode {
let path = "/var/run/evtype/evtype.sock";
let text = std::env::args().nth(1).unwrap_or_default();
let mut stream = UnixStream::connect(path)?;
stream.write_all(text.as_bytes())?;
let mut stream = match UnixStream::connect(path) {
Ok(stream) => stream,
Err(e) => {
eprintln!("Failed to open evtype socket: {e:#}");
return ExitCode::FAILURE;
}
};
Ok(())
if let Err(e) = stream.write_all(text.as_bytes()) {
eprintln!("Failed to write to evtype socket: {e:#}");
return ExitCode::FAILURE;
}
ExitCode::SUCCESS
}

View file

@ -1,6 +1,6 @@
use crate::keyboard::{char_to_key, create_device};
use evdev::uinput::VirtualDevice;
use evdev::{EventType, InputEvent, Key, Synchronization};
use evdev::{KeyCode, KeyEvent, SynchronizationCode, SynchronizationEvent};
use main_error::MainError;
use std::fs;
use std::fs::{create_dir_all, Permissions};
@ -20,22 +20,20 @@ fn type_string(dev: &mut VirtualDevice, text: &str) -> Result<(), MainError> {
for c in text.chars() {
let (key, shift) = char_to_key(c)?;
if shift {
dev.emit(&[InputEvent::new(EventType::KEY, Key::KEY_LEFTSHIFT.0, 1)])?;
dev.emit(&[*KeyEvent::new(KeyCode::KEY_LEFTSHIFT, 1)])?;
}
dev.emit(&[InputEvent::new(EventType::KEY, key.0, 1)])?;
dev.emit(&[InputEvent::new(
EventType::SYNCHRONIZATION,
Synchronization::SYN_REPORT.0,
dev.emit(&[*KeyEvent::new(key, 1)])?;
dev.emit(&[*SynchronizationEvent::new(
SynchronizationCode::SYN_REPORT,
1,
)])?;
sleep(TYPE_DELAY);
dev.emit(&[InputEvent::new(EventType::KEY, key.0, 0)])?;
dev.emit(&[*KeyEvent::new(key, 0)])?;
if shift {
dev.emit(&[InputEvent::new(EventType::KEY, Key::KEY_LEFTSHIFT.0, 0)])?;
dev.emit(&[*KeyEvent::new(KeyCode::KEY_LEFTSHIFT, 0)])?;
}
dev.emit(&[InputEvent::new(
EventType::SYNCHRONIZATION,
Synchronization::SYN_REPORT.0,
dev.emit(&[*SynchronizationEvent::new(
SynchronizationCode::SYN_REPORT,
0,
)])?;
sleep(TYPE_DELAY);

View file

@ -1,165 +1,165 @@
use evdev::uinput::{VirtualDevice, VirtualDeviceBuilder};
use evdev::{AttributeSet, Key};
use evdev::uinput::VirtualDevice;
use evdev::{AttributeSet, KeyCode};
use main_error::MainError;
pub fn char_to_key(c: char) -> Result<(Key, bool), MainError> {
pub fn char_to_key(c: char) -> Result<(KeyCode, bool), MainError> {
match c {
'0' => Ok((Key::KEY_0, false)),
'1' => Ok((Key::KEY_1, false)),
'2' => Ok((Key::KEY_2, false)),
'3' => Ok((Key::KEY_3, false)),
'4' => Ok((Key::KEY_4, false)),
'5' => Ok((Key::KEY_5, false)),
'6' => Ok((Key::KEY_6, false)),
'7' => Ok((Key::KEY_7, false)),
'8' => Ok((Key::KEY_8, false)),
'9' => Ok((Key::KEY_9, false)),
')' => Ok((Key::KEY_0, true)),
'!' => Ok((Key::KEY_1, true)),
'@' => Ok((Key::KEY_2, true)),
'#' => Ok((Key::KEY_3, true)),
'$' => Ok((Key::KEY_4, true)),
'%' => Ok((Key::KEY_5, true)),
'^' => Ok((Key::KEY_6, true)),
'&' => Ok((Key::KEY_7, true)),
'*' => Ok((Key::KEY_8, true)),
'(' => Ok((Key::KEY_9, true)),
'A' => Ok((Key::KEY_A, true)),
'B' => Ok((Key::KEY_B, true)),
'C' => Ok((Key::KEY_C, true)),
'D' => Ok((Key::KEY_D, true)),
'E' => Ok((Key::KEY_E, true)),
'F' => Ok((Key::KEY_F, true)),
'G' => Ok((Key::KEY_G, true)),
'H' => Ok((Key::KEY_H, true)),
'I' => Ok((Key::KEY_I, true)),
'J' => Ok((Key::KEY_J, true)),
'K' => Ok((Key::KEY_K, true)),
'L' => Ok((Key::KEY_L, true)),
'M' => Ok((Key::KEY_M, true)),
'N' => Ok((Key::KEY_N, true)),
'O' => Ok((Key::KEY_O, true)),
'P' => Ok((Key::KEY_P, true)),
'Q' => Ok((Key::KEY_Q, true)),
'R' => Ok((Key::KEY_R, true)),
'S' => Ok((Key::KEY_S, true)),
'T' => Ok((Key::KEY_T, true)),
'U' => Ok((Key::KEY_U, true)),
'V' => Ok((Key::KEY_V, true)),
'W' => Ok((Key::KEY_W, true)),
'X' => Ok((Key::KEY_X, true)),
'Y' => Ok((Key::KEY_Y, true)),
'Z' => Ok((Key::KEY_Z, true)),
'a' => Ok((Key::KEY_A, false)),
'b' => Ok((Key::KEY_B, false)),
'c' => Ok((Key::KEY_C, false)),
'd' => Ok((Key::KEY_D, false)),
'e' => Ok((Key::KEY_E, false)),
'f' => Ok((Key::KEY_F, false)),
'g' => Ok((Key::KEY_G, false)),
'h' => Ok((Key::KEY_H, false)),
'i' => Ok((Key::KEY_I, false)),
'j' => Ok((Key::KEY_J, false)),
'k' => Ok((Key::KEY_K, false)),
'l' => Ok((Key::KEY_L, false)),
'm' => Ok((Key::KEY_M, false)),
'n' => Ok((Key::KEY_N, false)),
'o' => Ok((Key::KEY_O, false)),
'p' => Ok((Key::KEY_P, false)),
'q' => Ok((Key::KEY_Q, false)),
'r' => Ok((Key::KEY_R, false)),
's' => Ok((Key::KEY_S, false)),
't' => Ok((Key::KEY_T, false)),
'u' => Ok((Key::KEY_U, false)),
'v' => Ok((Key::KEY_V, false)),
'w' => Ok((Key::KEY_W, false)),
'x' => Ok((Key::KEY_X, false)),
'y' => Ok((Key::KEY_Y, false)),
'z' => Ok((Key::KEY_Z, false)),
'-' => Ok((Key::KEY_MINUS, false)),
'_' => Ok((Key::KEY_MINUS, true)),
'=' => Ok((Key::KEY_EQUAL, false)),
'+' => Ok((Key::KEY_EQUAL, true)),
',' => Ok((Key::KEY_COMMA, false)),
'<' => Ok((Key::KEY_COMMA, true)),
'.' => Ok((Key::KEY_DOT, false)),
'>' => Ok((Key::KEY_DOT, true)),
'[' => Ok((Key::KEY_LEFTBRACE, false)),
'{' => Ok((Key::KEY_LEFTBRACE, true)),
']' => Ok((Key::KEY_RIGHTBRACE, false)),
'}' => Ok((Key::KEY_RIGHTBRACE, true)),
';' => Ok((Key::KEY_SEMICOLON, false)),
':' => Ok((Key::KEY_SEMICOLON, true)),
'\'' => Ok((Key::KEY_APOSTROPHE, false)),
'"' => Ok((Key::KEY_APOSTROPHE, true)),
'`' => Ok((Key::KEY_GRAVE, false)),
'~' => Ok((Key::KEY_GRAVE, true)),
'\\' => Ok((Key::KEY_BACKSLASH, false)),
'|' => Ok((Key::KEY_BACKSLASH, true)),
'/' => Ok((Key::KEY_SLASH, false)),
'?' => Ok((Key::KEY_SLASH, true)),
' ' => Ok((Key::KEY_SPACE, false)),
'\n' => Ok((Key::KEY_ENTER, false)),
'0' => Ok((KeyCode::KEY_0, false)),
'1' => Ok((KeyCode::KEY_1, false)),
'2' => Ok((KeyCode::KEY_2, false)),
'3' => Ok((KeyCode::KEY_3, false)),
'4' => Ok((KeyCode::KEY_4, false)),
'5' => Ok((KeyCode::KEY_5, false)),
'6' => Ok((KeyCode::KEY_6, false)),
'7' => Ok((KeyCode::KEY_7, false)),
'8' => Ok((KeyCode::KEY_8, false)),
'9' => Ok((KeyCode::KEY_9, false)),
')' => Ok((KeyCode::KEY_0, true)),
'!' => Ok((KeyCode::KEY_1, true)),
'@' => Ok((KeyCode::KEY_2, true)),
'#' => Ok((KeyCode::KEY_3, true)),
'$' => Ok((KeyCode::KEY_4, true)),
'%' => Ok((KeyCode::KEY_5, true)),
'^' => Ok((KeyCode::KEY_6, true)),
'&' => Ok((KeyCode::KEY_7, true)),
'*' => Ok((KeyCode::KEY_8, true)),
'(' => Ok((KeyCode::KEY_9, true)),
'A' => Ok((KeyCode::KEY_A, true)),
'B' => Ok((KeyCode::KEY_B, true)),
'C' => Ok((KeyCode::KEY_C, true)),
'D' => Ok((KeyCode::KEY_D, true)),
'E' => Ok((KeyCode::KEY_E, true)),
'F' => Ok((KeyCode::KEY_F, true)),
'G' => Ok((KeyCode::KEY_G, true)),
'H' => Ok((KeyCode::KEY_H, true)),
'I' => Ok((KeyCode::KEY_I, true)),
'J' => Ok((KeyCode::KEY_J, true)),
'K' => Ok((KeyCode::KEY_K, true)),
'L' => Ok((KeyCode::KEY_L, true)),
'M' => Ok((KeyCode::KEY_M, true)),
'N' => Ok((KeyCode::KEY_N, true)),
'O' => Ok((KeyCode::KEY_O, true)),
'P' => Ok((KeyCode::KEY_P, true)),
'Q' => Ok((KeyCode::KEY_Q, true)),
'R' => Ok((KeyCode::KEY_R, true)),
'S' => Ok((KeyCode::KEY_S, true)),
'T' => Ok((KeyCode::KEY_T, true)),
'U' => Ok((KeyCode::KEY_U, true)),
'V' => Ok((KeyCode::KEY_V, true)),
'W' => Ok((KeyCode::KEY_W, true)),
'X' => Ok((KeyCode::KEY_X, true)),
'Y' => Ok((KeyCode::KEY_Y, true)),
'Z' => Ok((KeyCode::KEY_Z, true)),
'a' => Ok((KeyCode::KEY_A, false)),
'b' => Ok((KeyCode::KEY_B, false)),
'c' => Ok((KeyCode::KEY_C, false)),
'd' => Ok((KeyCode::KEY_D, false)),
'e' => Ok((KeyCode::KEY_E, false)),
'f' => Ok((KeyCode::KEY_F, false)),
'g' => Ok((KeyCode::KEY_G, false)),
'h' => Ok((KeyCode::KEY_H, false)),
'i' => Ok((KeyCode::KEY_I, false)),
'j' => Ok((KeyCode::KEY_J, false)),
'k' => Ok((KeyCode::KEY_K, false)),
'l' => Ok((KeyCode::KEY_L, false)),
'm' => Ok((KeyCode::KEY_M, false)),
'n' => Ok((KeyCode::KEY_N, false)),
'o' => Ok((KeyCode::KEY_O, false)),
'p' => Ok((KeyCode::KEY_P, false)),
'q' => Ok((KeyCode::KEY_Q, false)),
'r' => Ok((KeyCode::KEY_R, false)),
's' => Ok((KeyCode::KEY_S, false)),
't' => Ok((KeyCode::KEY_T, false)),
'u' => Ok((KeyCode::KEY_U, false)),
'v' => Ok((KeyCode::KEY_V, false)),
'w' => Ok((KeyCode::KEY_W, false)),
'x' => Ok((KeyCode::KEY_X, false)),
'y' => Ok((KeyCode::KEY_Y, false)),
'z' => Ok((KeyCode::KEY_Z, false)),
'-' => Ok((KeyCode::KEY_MINUS, false)),
'_' => Ok((KeyCode::KEY_MINUS, true)),
'=' => Ok((KeyCode::KEY_EQUAL, false)),
'+' => Ok((KeyCode::KEY_EQUAL, true)),
',' => Ok((KeyCode::KEY_COMMA, false)),
'<' => Ok((KeyCode::KEY_COMMA, true)),
'.' => Ok((KeyCode::KEY_DOT, false)),
'>' => Ok((KeyCode::KEY_DOT, true)),
'[' => Ok((KeyCode::KEY_LEFTBRACE, false)),
'{' => Ok((KeyCode::KEY_LEFTBRACE, true)),
']' => Ok((KeyCode::KEY_RIGHTBRACE, false)),
'}' => Ok((KeyCode::KEY_RIGHTBRACE, true)),
';' => Ok((KeyCode::KEY_SEMICOLON, false)),
':' => Ok((KeyCode::KEY_SEMICOLON, true)),
'\'' => Ok((KeyCode::KEY_APOSTROPHE, false)),
'"' => Ok((KeyCode::KEY_APOSTROPHE, true)),
'`' => Ok((KeyCode::KEY_GRAVE, false)),
'~' => Ok((KeyCode::KEY_GRAVE, true)),
'\\' => Ok((KeyCode::KEY_BACKSLASH, false)),
'|' => Ok((KeyCode::KEY_BACKSLASH, true)),
'/' => Ok((KeyCode::KEY_SLASH, false)),
'?' => Ok((KeyCode::KEY_SLASH, true)),
' ' => Ok((KeyCode::KEY_SPACE, false)),
'\n' => Ok((KeyCode::KEY_ENTER, false)),
_ => Err("Invalid character".to_string().into()),
}
}
pub fn create_device() -> Result<VirtualDevice, MainError> {
let keys = &[
Key::KEY_0,
Key::KEY_1,
Key::KEY_2,
Key::KEY_3,
Key::KEY_4,
Key::KEY_5,
Key::KEY_6,
Key::KEY_7,
Key::KEY_8,
Key::KEY_9,
Key::KEY_A,
Key::KEY_B,
Key::KEY_C,
Key::KEY_D,
Key::KEY_E,
Key::KEY_F,
Key::KEY_G,
Key::KEY_H,
Key::KEY_I,
Key::KEY_J,
Key::KEY_K,
Key::KEY_L,
Key::KEY_M,
Key::KEY_N,
Key::KEY_O,
Key::KEY_P,
Key::KEY_Q,
Key::KEY_R,
Key::KEY_S,
Key::KEY_T,
Key::KEY_U,
Key::KEY_V,
Key::KEY_W,
Key::KEY_X,
Key::KEY_Y,
Key::KEY_Z,
Key::KEY_MINUS,
Key::KEY_EQUAL,
Key::KEY_COMMA,
Key::KEY_DOT,
Key::KEY_LEFTBRACE,
Key::KEY_RIGHTBRACE,
Key::KEY_SEMICOLON,
Key::KEY_APOSTROPHE,
Key::KEY_GRAVE,
Key::KEY_BACKSLASH,
Key::KEY_SLASH,
Key::KEY_SPACE,
Key::KEY_ENTER,
KeyCode::KEY_0,
KeyCode::KEY_1,
KeyCode::KEY_2,
KeyCode::KEY_3,
KeyCode::KEY_4,
KeyCode::KEY_5,
KeyCode::KEY_6,
KeyCode::KEY_7,
KeyCode::KEY_8,
KeyCode::KEY_9,
KeyCode::KEY_A,
KeyCode::KEY_B,
KeyCode::KEY_C,
KeyCode::KEY_D,
KeyCode::KEY_E,
KeyCode::KEY_F,
KeyCode::KEY_G,
KeyCode::KEY_H,
KeyCode::KEY_I,
KeyCode::KEY_J,
KeyCode::KEY_K,
KeyCode::KEY_L,
KeyCode::KEY_M,
KeyCode::KEY_N,
KeyCode::KEY_O,
KeyCode::KEY_P,
KeyCode::KEY_Q,
KeyCode::KEY_R,
KeyCode::KEY_S,
KeyCode::KEY_T,
KeyCode::KEY_U,
KeyCode::KEY_V,
KeyCode::KEY_W,
KeyCode::KEY_X,
KeyCode::KEY_Y,
KeyCode::KEY_Z,
KeyCode::KEY_MINUS,
KeyCode::KEY_EQUAL,
KeyCode::KEY_COMMA,
KeyCode::KEY_DOT,
KeyCode::KEY_LEFTBRACE,
KeyCode::KEY_RIGHTBRACE,
KeyCode::KEY_SEMICOLON,
KeyCode::KEY_APOSTROPHE,
KeyCode::KEY_GRAVE,
KeyCode::KEY_BACKSLASH,
KeyCode::KEY_SLASH,
KeyCode::KEY_SPACE,
KeyCode::KEY_ENTER,
]
.iter()
.collect::<AttributeSet<_>>();
Ok(VirtualDeviceBuilder::new()?
Ok(VirtualDevice::builder()?
.name("EvType virtual keyboard")
.with_keys(keys)?
.build()?)