Initial implementation of espanso-inject on Windows
This commit is contained in:
parent
afb64df17c
commit
c2f497ef59
66
Cargo.lock
generated
66
Cargo.lock
generated
|
@ -1,5 +1,14 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.38"
|
||||
|
@ -133,6 +142,7 @@ name = "espanso"
|
|||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"espanso-detect",
|
||||
"espanso-inject",
|
||||
"espanso-ui",
|
||||
"maplit",
|
||||
"simplelog",
|
||||
|
@ -154,6 +164,23 @@ dependencies = [
|
|||
"widestring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "espanso-inject"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cc",
|
||||
"enum-as-inner",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"libc",
|
||||
"log",
|
||||
"regex",
|
||||
"scopeguard",
|
||||
"thiserror",
|
||||
"widestring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "espanso-ui"
|
||||
version = "0.1.0"
|
||||
|
@ -258,6 +285,12 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
|
||||
[[package]]
|
||||
name = "notify-rust"
|
||||
version = "4.2.2"
|
||||
|
@ -317,6 +350,12 @@ dependencies = [
|
|||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.19"
|
||||
|
@ -364,6 +403,24 @@ dependencies = [
|
|||
"rust-argon2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
|
||||
|
||||
[[package]]
|
||||
name = "rust-argon2"
|
||||
version = "0.8.3"
|
||||
|
@ -506,6 +563,15 @@ dependencies = [
|
|||
"syn 1.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
|
|
|
@ -4,4 +4,5 @@ members = [
|
|||
"espanso",
|
||||
"espanso-detect",
|
||||
"espanso-ui",
|
||||
"espanso-inject",
|
||||
]
|
27
espanso-inject/Cargo.toml
Normal file
27
espanso-inject/Cargo.toml
Normal file
|
@ -0,0 +1,27 @@
|
|||
[package]
|
||||
name = "espanso-inject"
|
||||
version = "0.1.0"
|
||||
authors = ["Federico Terzi <federico-terzi@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
build="build.rs"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.14"
|
||||
lazycell = "1.3.0"
|
||||
anyhow = "1.0.38"
|
||||
thiserror = "1.0.23"
|
||||
lazy_static = "1.4.0"
|
||||
regex = "1.4.3"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
widestring = "0.4.3"
|
||||
|
||||
[target.'cfg(target_os="linux")'.dependencies]
|
||||
libc = "0.2.85"
|
||||
scopeguard = "1.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0.66"
|
||||
|
||||
[dev-dependencies]
|
||||
enum-as-inner = "0.3.3"
|
76
espanso-inject/build.rs
Normal file
76
espanso-inject/build.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
*
|
||||
* espanso is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn cc_config() {
|
||||
println!("cargo:rerun-if-changed=src/win32/native.cpp");
|
||||
println!("cargo:rerun-if-changed=src/win32/native.h");
|
||||
cc::Build::new()
|
||||
.cpp(true)
|
||||
.include("src/win32/native.h")
|
||||
.file("src/win32/native.cpp")
|
||||
.compile("espansoinject");
|
||||
|
||||
println!("cargo:rustc-link-lib=static=espansoinject");
|
||||
println!("cargo:rustc-link-lib=dylib=user32");
|
||||
#[cfg(target_env = "gnu")]
|
||||
println!("cargo:rustc-link-lib=dylib=stdc++");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn cc_config() {
|
||||
println!("cargo:rerun-if-changed=src/x11/native.cpp");
|
||||
println!("cargo:rerun-if-changed=src/x11/native.h");
|
||||
println!("cargo:rerun-if-changed=src/evdev/native.cpp");
|
||||
println!("cargo:rerun-if-changed=src/evdev/native.h");
|
||||
cc::Build::new()
|
||||
.cpp(true)
|
||||
.include("src/x11/native.h")
|
||||
.file("src/x11/native.cpp")
|
||||
.compile("espansoinject");
|
||||
cc::Build::new()
|
||||
.cpp(true)
|
||||
.include("src/evdev/native.h")
|
||||
.file("src/evdev/native.cpp")
|
||||
.compile("espansoinjectevdev");
|
||||
println!("cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu/");
|
||||
println!("cargo:rustc-link-lib=static=espansoinject");
|
||||
println!("cargo:rustc-link-lib=static=espansoinjectevdev");
|
||||
println!("cargo:rustc-link-lib=dylib=X11");
|
||||
println!("cargo:rustc-link-lib=dylib=Xtst");
|
||||
println!("cargo:rustc-link-lib=dylib=xkbcommon");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn cc_config() {
|
||||
println!("cargo:rerun-if-changed=src/mac/native.mm");
|
||||
println!("cargo:rerun-if-changed=src/mac/native.h");
|
||||
cc::Build::new()
|
||||
.cpp(true)
|
||||
.include("src/mac/native.h")
|
||||
.file("src/mac/native.mm")
|
||||
.compile("espansoinject");
|
||||
println!("cargo:rustc-link-lib=dylib=c++");
|
||||
println!("cargo:rustc-link-lib=static=espansoinject");
|
||||
println!("cargo:rustc-link-lib=framework=Cocoa");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
cc_config();
|
||||
}
|
340
espanso-inject/src/keys.rs
Normal file
340
espanso-inject/src/keys.rs
Normal file
|
@ -0,0 +1,340 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
lazy_static! {
|
||||
static ref RAW_PARSER: Regex = Regex::new(r"^RAW\((\d+)\)$").unwrap();
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Key {
|
||||
// Modifiers
|
||||
Alt,
|
||||
CapsLock,
|
||||
Control,
|
||||
Meta,
|
||||
NumLock,
|
||||
Shift,
|
||||
|
||||
// Whitespace
|
||||
Enter,
|
||||
Tab,
|
||||
Space,
|
||||
|
||||
// Navigation
|
||||
ArrowDown,
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
ArrowUp,
|
||||
End,
|
||||
Home,
|
||||
PageDown,
|
||||
PageUp,
|
||||
|
||||
// UI
|
||||
Escape,
|
||||
|
||||
// Editing keys
|
||||
Backspace,
|
||||
Insert,
|
||||
Delete,
|
||||
|
||||
// Function keys
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
F11,
|
||||
F12,
|
||||
F13,
|
||||
F14,
|
||||
F15,
|
||||
F16,
|
||||
F17,
|
||||
F18,
|
||||
F19,
|
||||
F20,
|
||||
|
||||
// Alphabet
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
E,
|
||||
F,
|
||||
G,
|
||||
H,
|
||||
I,
|
||||
J,
|
||||
K,
|
||||
L,
|
||||
M,
|
||||
N,
|
||||
O,
|
||||
P,
|
||||
Q,
|
||||
R,
|
||||
S,
|
||||
T,
|
||||
U,
|
||||
V,
|
||||
W,
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
|
||||
// Numbers
|
||||
N0,
|
||||
N1,
|
||||
N2,
|
||||
N3,
|
||||
N4,
|
||||
N5,
|
||||
N6,
|
||||
N7,
|
||||
N8,
|
||||
N9,
|
||||
|
||||
// Numpad
|
||||
Numpad0,
|
||||
Numpad1,
|
||||
Numpad2,
|
||||
Numpad3,
|
||||
Numpad4,
|
||||
Numpad5,
|
||||
Numpad6,
|
||||
Numpad7,
|
||||
Numpad8,
|
||||
Numpad9,
|
||||
|
||||
// Specify the raw platform-specific virtual key code.
|
||||
Raw(i32),
|
||||
}
|
||||
|
||||
impl Display for Key {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match *self {
|
||||
Key::Alt => write!(f, "ALT"),
|
||||
Key::CapsLock => write!(f, "CAPSLOCK"),
|
||||
Key::Control => write!(f, "CTRL"),
|
||||
Key::Meta => write!(f, "META"),
|
||||
Key::NumLock => write!(f, "NUMLOCK"),
|
||||
Key::Shift => write!(f, "SHIFT"),
|
||||
Key::Enter => write!(f, "ENTER"),
|
||||
Key::Tab => write!(f, "TAB"),
|
||||
Key::Space => write!(f, "SPACE"),
|
||||
Key::ArrowDown => write!(f, "DOWN"),
|
||||
Key::ArrowLeft => write!(f, "LEFT"),
|
||||
Key::ArrowRight => write!(f, "RIGHT"),
|
||||
Key::ArrowUp => write!(f, "UP"),
|
||||
Key::End => write!(f, "END"),
|
||||
Key::Home => write!(f, "HOME"),
|
||||
Key::PageDown => write!(f, "PAGEDOWN"),
|
||||
Key::PageUp => write!(f, "PAGEUP"),
|
||||
Key::Escape => write!(f, "ESC"),
|
||||
Key::Backspace => write!(f, "BACKSPACE"),
|
||||
Key::Insert => write!(f, "INSERT"),
|
||||
Key::Delete => write!(f, "DELETE"),
|
||||
Key::F1 => write!(f, "F1"),
|
||||
Key::F2 => write!(f, "F2"),
|
||||
Key::F3 => write!(f, "F3"),
|
||||
Key::F4 => write!(f, "F4"),
|
||||
Key::F5 => write!(f, "F5"),
|
||||
Key::F6 => write!(f, "F6"),
|
||||
Key::F7 => write!(f, "F7"),
|
||||
Key::F8 => write!(f, "F8"),
|
||||
Key::F9 => write!(f, "F9"),
|
||||
Key::F10 => write!(f, "F10"),
|
||||
Key::F11 => write!(f, "F11"),
|
||||
Key::F12 => write!(f, "F12"),
|
||||
Key::F13 => write!(f, "F13"),
|
||||
Key::F14 => write!(f, "F14"),
|
||||
Key::F15 => write!(f, "F15"),
|
||||
Key::F16 => write!(f, "F16"),
|
||||
Key::F17 => write!(f, "F17"),
|
||||
Key::F18 => write!(f, "F18"),
|
||||
Key::F19 => write!(f, "F19"),
|
||||
Key::F20 => write!(f, "F20"),
|
||||
Key::A => write!(f, "A"),
|
||||
Key::B => write!(f, "B"),
|
||||
Key::C => write!(f, "C"),
|
||||
Key::D => write!(f, "D"),
|
||||
Key::E => write!(f, "E"),
|
||||
Key::F => write!(f, "F"),
|
||||
Key::G => write!(f, "G"),
|
||||
Key::H => write!(f, "H"),
|
||||
Key::I => write!(f, "I"),
|
||||
Key::J => write!(f, "J"),
|
||||
Key::K => write!(f, "K"),
|
||||
Key::L => write!(f, "L"),
|
||||
Key::M => write!(f, "M"),
|
||||
Key::N => write!(f, "N"),
|
||||
Key::O => write!(f, "O"),
|
||||
Key::P => write!(f, "P"),
|
||||
Key::Q => write!(f, "Q"),
|
||||
Key::R => write!(f, "R"),
|
||||
Key::S => write!(f, "S"),
|
||||
Key::T => write!(f, "T"),
|
||||
Key::U => write!(f, "U"),
|
||||
Key::V => write!(f, "V"),
|
||||
Key::W => write!(f, "W"),
|
||||
Key::X => write!(f, "X"),
|
||||
Key::Y => write!(f, "Y"),
|
||||
Key::Z => write!(f, "Z"),
|
||||
Key::N0 => write!(f, "0"),
|
||||
Key::N1 => write!(f, "1"),
|
||||
Key::N2 => write!(f, "2"),
|
||||
Key::N3 => write!(f, "3"),
|
||||
Key::N4 => write!(f, "4"),
|
||||
Key::N5 => write!(f, "5"),
|
||||
Key::N6 => write!(f, "6"),
|
||||
Key::N7 => write!(f, "7"),
|
||||
Key::N8 => write!(f, "8"),
|
||||
Key::N9 => write!(f, "9"),
|
||||
Key::Numpad0 => write!(f, "NUMPAD0"),
|
||||
Key::Numpad1 => write!(f, "NUMPAD1"),
|
||||
Key::Numpad2 => write!(f, "NUMPAD2"),
|
||||
Key::Numpad3 => write!(f, "NUMPAD3"),
|
||||
Key::Numpad4 => write!(f, "NUMPAD4"),
|
||||
Key::Numpad5 => write!(f, "NUMPAD5"),
|
||||
Key::Numpad6 => write!(f, "NUMPAD6"),
|
||||
Key::Numpad7 => write!(f, "NUMPAD7"),
|
||||
Key::Numpad8 => write!(f, "NUMPAD8"),
|
||||
Key::Numpad9 => write!(f, "NUMPAD9"),
|
||||
Key::Raw(code) => write!(f, "RAW({})", code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Key {
|
||||
pub fn parse(key: &str) -> Option<Key> {
|
||||
let parsed = match key {
|
||||
"ALT" => Some(Key::Alt),
|
||||
"CAPSLOCK" => Some(Key::CapsLock),
|
||||
"CTRL" => Some(Key::Control),
|
||||
"META" => Some(Key::Meta),
|
||||
"NUMLOCK" => Some(Key::NumLock),
|
||||
"SHIFT" => Some(Key::Shift),
|
||||
"ENTER" => Some(Key::Enter),
|
||||
"TAB" => Some(Key::Tab),
|
||||
"SPACE" => Some(Key::Space),
|
||||
"DOWN" => Some(Key::ArrowDown),
|
||||
"LEFT" => Some(Key::ArrowLeft),
|
||||
"RIGHT" => Some(Key::ArrowRight),
|
||||
"UP" => Some(Key::ArrowUp),
|
||||
"END" => Some(Key::End),
|
||||
"HOME" => Some(Key::Home),
|
||||
"PAGEDOWN" => Some(Key::PageDown),
|
||||
"PAGEUP" => Some(Key::PageUp),
|
||||
"ESC" => Some(Key::Escape),
|
||||
"BACKSPACE" => Some(Key::Backspace),
|
||||
"INSERT" => Some(Key::Insert),
|
||||
"DELETE" => Some(Key::Delete),
|
||||
"F1" => Some(Key::F1),
|
||||
"F2" => Some(Key::F2),
|
||||
"F3" => Some(Key::F3),
|
||||
"F4" => Some(Key::F4),
|
||||
"F5" => Some(Key::F5),
|
||||
"F6" => Some(Key::F6),
|
||||
"F7" => Some(Key::F7),
|
||||
"F8" => Some(Key::F8),
|
||||
"F9" => Some(Key::F9),
|
||||
"F10" => Some(Key::F10),
|
||||
"F11" => Some(Key::F11),
|
||||
"F12" => Some(Key::F12),
|
||||
"F13" => Some(Key::F13),
|
||||
"F14" => Some(Key::F14),
|
||||
"F15" => Some(Key::F15),
|
||||
"F16" => Some(Key::F16),
|
||||
"F17" => Some(Key::F17),
|
||||
"F18" => Some(Key::F18),
|
||||
"F19" => Some(Key::F19),
|
||||
"F20" => Some(Key::F20),
|
||||
"A" => Some(Key::A),
|
||||
"B" => Some(Key::B),
|
||||
"C" => Some(Key::C),
|
||||
"D" => Some(Key::D),
|
||||
"E" => Some(Key::E),
|
||||
"F" => Some(Key::F),
|
||||
"G" => Some(Key::G),
|
||||
"H" => Some(Key::H),
|
||||
"I" => Some(Key::I),
|
||||
"J" => Some(Key::J),
|
||||
"K" => Some(Key::K),
|
||||
"L" => Some(Key::L),
|
||||
"M" => Some(Key::M),
|
||||
"N" => Some(Key::N),
|
||||
"O" => Some(Key::O),
|
||||
"P" => Some(Key::P),
|
||||
"Q" => Some(Key::Q),
|
||||
"R" => Some(Key::R),
|
||||
"S" => Some(Key::S),
|
||||
"T" => Some(Key::T),
|
||||
"U" => Some(Key::U),
|
||||
"V" => Some(Key::V),
|
||||
"W" => Some(Key::W),
|
||||
"X" => Some(Key::X),
|
||||
"Y" => Some(Key::Y),
|
||||
"Z" => Some(Key::Z),
|
||||
"0" => Some(Key::N0),
|
||||
"1" => Some(Key::N1),
|
||||
"2" => Some(Key::N2),
|
||||
"3" => Some(Key::N3),
|
||||
"4" => Some(Key::N4),
|
||||
"5" => Some(Key::N5),
|
||||
"6" => Some(Key::N6),
|
||||
"7" => Some(Key::N7),
|
||||
"8" => Some(Key::N8),
|
||||
"9" => Some(Key::N9),
|
||||
"NUMPAD0" => Some(Key::Numpad0),
|
||||
"NUMPAD1" => Some(Key::Numpad1),
|
||||
"NUMPAD2" => Some(Key::Numpad2),
|
||||
"NUMPAD3" => Some(Key::Numpad3),
|
||||
"NUMPAD4" => Some(Key::Numpad4),
|
||||
"NUMPAD5" => Some(Key::Numpad5),
|
||||
"NUMPAD6" => Some(Key::Numpad6),
|
||||
"NUMPAD7" => Some(Key::Numpad7),
|
||||
"NUMPAD8" => Some(Key::Numpad8),
|
||||
"NUMPAD9" => Some(Key::Numpad9),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if parsed.is_none() {
|
||||
// Attempt to parse raw keys
|
||||
if RAW_PARSER.is_match(key) {
|
||||
if let Some(caps) = RAW_PARSER.captures(key) {
|
||||
let code_str = caps.get(1).map_or("", |m| m.as_str());
|
||||
let code = code_str.parse::<i32>();
|
||||
if let Ok(code) = code {
|
||||
return Some(Key::Raw(code));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parsed
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_works_correctly() {
|
||||
assert!(matches!(Key::parse("ALT").unwrap(), Key::Alt));
|
||||
assert!(matches!(Key::parse("RAW(1234)").unwrap(), Key::Raw(1234)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_invalid_keys() {
|
||||
assert!(Key::parse("INVALID").is_none());
|
||||
assert!(Key::parse("RAW(a)").is_none());
|
||||
}
|
||||
}
|
72
espanso-inject/src/lib.rs
Normal file
72
espanso-inject/src/lib.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
*
|
||||
* espanso is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
pub mod keys;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
mod win32;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod x11;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
mod evdev;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod mac;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
pub trait Injector {
|
||||
fn send_string(&self, string: &str) -> Result<()>;
|
||||
fn send_keys(&self, keys: &[keys::Key], delay: i32) -> Result<()>;
|
||||
fn send_key_combination(&self, keys: &[keys::Key], delay: i32) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct InjectorOptions {
|
||||
// Only relevant in Linux systems
|
||||
use_evdev: bool,
|
||||
}
|
||||
|
||||
impl Default for InjectorOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
use_evdev: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn get_injector(_options: InjectorOptions) -> impl Injector {
|
||||
win32::Win32Injector::new()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn get_injector(_options: InjectorOptions) -> impl Injector {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn get_injector(options: InjectorOptions) -> impl Injector {
|
||||
// TODO: differenciate based on the options
|
||||
}
|
||||
|
118
espanso-inject/src/win32/mod.rs
Normal file
118
espanso-inject/src/win32/mod.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
*
|
||||
* espanso is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
mod raw_keys;
|
||||
|
||||
use log::{error};
|
||||
use raw_keys::convert_key_to_vkey;
|
||||
|
||||
use anyhow::Result;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{keys, Injector};
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
#[link(name = "espansoinject", kind = "static")]
|
||||
extern "C" {
|
||||
pub fn inject_string(string: *const u16);
|
||||
pub fn inject_separate_vkeys(vkey_array: *const i32, vkey_count: i32);
|
||||
pub fn inject_vkeys_combination(vkey_array: *const i32, vkey_count: i32);
|
||||
pub fn inject_separate_vkeys_with_delay(vkey_array: *const i32, vkey_count: i32, delay: i32);
|
||||
pub fn inject_vkeys_combination_with_delay(vkey_array: *const i32, vkey_count: i32, delay: i32);
|
||||
}
|
||||
|
||||
pub struct Win32Injector {}
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
impl Win32Injector {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn convert_to_vk_array(keys: &[keys::Key]) -> Result<Vec<i32>> {
|
||||
let mut virtual_keys: Vec<i32> = Vec::new();
|
||||
for key in keys.iter() {
|
||||
let vk = convert_key_to_vkey(key);
|
||||
if let Some(vk) = vk {
|
||||
virtual_keys.push(vk)
|
||||
} else {
|
||||
return Err(Win32InjectorError::MappingFailure(key.clone()).into());
|
||||
}
|
||||
}
|
||||
Ok(virtual_keys)
|
||||
}
|
||||
}
|
||||
|
||||
impl Injector for Win32Injector {
|
||||
fn send_string(&self, string: &str) -> Result<()> {
|
||||
let wide_string = widestring::WideCString::from_str(string)?;
|
||||
unsafe {
|
||||
inject_string(wide_string.as_ptr());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_keys(&self, keys: &[keys::Key], delay: i32) -> Result<()> {
|
||||
let virtual_keys = Self::convert_to_vk_array(keys)?;
|
||||
|
||||
if delay == 0 {
|
||||
unsafe {
|
||||
inject_separate_vkeys(virtual_keys.as_ptr(), virtual_keys.len() as i32);
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
inject_separate_vkeys_with_delay(virtual_keys.as_ptr(), virtual_keys.len() as i32, delay);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_key_combination(&self, keys: &[keys::Key], delay: i32) -> Result<()> {
|
||||
let virtual_keys = Self::convert_to_vk_array(keys)?;
|
||||
|
||||
if delay == 0 {
|
||||
unsafe {
|
||||
inject_vkeys_combination(virtual_keys.as_ptr(), virtual_keys.len() as i32);
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
inject_vkeys_combination_with_delay(virtual_keys.as_ptr(), virtual_keys.len() as i32, delay);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Win32InjectorError {
|
||||
#[error("missing vkey mapping for key `{0}`")]
|
||||
MappingFailure(keys::Key),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn convert_raw_to_virtual_key_array() {
|
||||
assert_eq!(Win32Injector::convert_to_vk_array(&[keys::Key::Alt, keys::Key::V]).unwrap(), vec![0x12, 0x56]);
|
||||
}
|
||||
}
|
173
espanso-inject/src/win32/native.cpp
Normal file
173
espanso-inject/src/win32/native.cpp
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
*
|
||||
* espanso is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "native.h"
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <array>
|
||||
|
||||
#define UNICODE
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#ifndef WINVER
|
||||
#define WINVER 0x0606
|
||||
#endif
|
||||
#define STRSAFE_NO_DEPRECATE
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <winuser.h>
|
||||
#include <strsafe.h>
|
||||
#include <Windows.h>
|
||||
|
||||
void inject_string(wchar_t *string)
|
||||
{
|
||||
std::wstring msg(string);
|
||||
std::cout << msg.length() << std::endl;
|
||||
|
||||
std::vector<INPUT> vec;
|
||||
for (auto ch : msg)
|
||||
{
|
||||
INPUT input = {0};
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.dwFlags = KEYEVENTF_UNICODE;
|
||||
input.ki.wScan = ch;
|
||||
vec.push_back(input);
|
||||
|
||||
input.ki.dwFlags |= KEYEVENTF_KEYUP;
|
||||
vec.push_back(input);
|
||||
}
|
||||
|
||||
SendInput(vec.size(), vec.data(), sizeof(INPUT));
|
||||
}
|
||||
|
||||
void inject_separate_vkeys(int32_t *vkey_array, int32_t vkey_count)
|
||||
{
|
||||
std::vector<INPUT> vec;
|
||||
|
||||
for (int i = 0; i < vkey_count; i++)
|
||||
{
|
||||
INPUT input = {0};
|
||||
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wScan = 0;
|
||||
input.ki.time = 0;
|
||||
input.ki.dwExtraInfo = 0;
|
||||
input.ki.wVk = vkey_array[i];
|
||||
input.ki.dwFlags = 0; // 0 for key press
|
||||
vec.push_back(input);
|
||||
|
||||
input.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
vec.push_back(input);
|
||||
}
|
||||
|
||||
SendInput(vec.size(), vec.data(), sizeof(INPUT));
|
||||
}
|
||||
|
||||
void inject_vkeys_combination(int32_t *vkey_array, int32_t vkey_count)
|
||||
{
|
||||
std::vector<INPUT> vec;
|
||||
|
||||
// First send the presses
|
||||
for (int i = 0; i < vkey_count; i++)
|
||||
{
|
||||
INPUT input = {0};
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wScan = 0;
|
||||
input.ki.time = 0;
|
||||
input.ki.dwExtraInfo = 0;
|
||||
input.ki.wVk = vkey_array[i];
|
||||
input.ki.dwFlags = 0;
|
||||
vec.push_back(input);
|
||||
}
|
||||
|
||||
// Then the releases
|
||||
for (int i = (vkey_count - 1); i >= 0; i--)
|
||||
{
|
||||
INPUT input = {0};
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wScan = 0;
|
||||
input.ki.time = 0;
|
||||
input.ki.dwExtraInfo = 0;
|
||||
input.ki.wVk = vkey_array[i];
|
||||
input.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
vec.push_back(input);
|
||||
}
|
||||
|
||||
SendInput(vec.size(), vec.data(), sizeof(INPUT));
|
||||
}
|
||||
|
||||
void inject_separate_vkeys_with_delay(int32_t *vkey_array, int32_t vkey_count, int32_t delay)
|
||||
{
|
||||
for (int i = 0; i < vkey_count; i++)
|
||||
{
|
||||
INPUT input = {0};
|
||||
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wScan = 0;
|
||||
input.ki.time = 0;
|
||||
input.ki.dwExtraInfo = 0;
|
||||
input.ki.wVk = vkey_array[i];
|
||||
input.ki.dwFlags = 0; // 0 for key press
|
||||
SendInput(1, &input, sizeof(INPUT));
|
||||
|
||||
Sleep(delay);
|
||||
|
||||
input.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
|
||||
SendInput(1, &input, sizeof(INPUT));
|
||||
|
||||
Sleep(delay);
|
||||
}
|
||||
}
|
||||
|
||||
void inject_vkeys_combination_with_delay(int32_t *vkey_array, int32_t vkey_count, int32_t delay)
|
||||
{
|
||||
// First send the presses
|
||||
for (int i = 0; i < vkey_count; i++)
|
||||
{
|
||||
INPUT input = {0};
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wScan = 0;
|
||||
input.ki.time = 0;
|
||||
input.ki.dwExtraInfo = 0;
|
||||
input.ki.wVk = vkey_array[i];
|
||||
input.ki.dwFlags = 0;
|
||||
|
||||
SendInput(1, &input, sizeof(INPUT));
|
||||
Sleep(delay);
|
||||
}
|
||||
|
||||
// Then the releases
|
||||
for (int i = (vkey_count - 1); i >= 0; i--)
|
||||
{
|
||||
INPUT input = {0};
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wScan = 0;
|
||||
input.ki.time = 0;
|
||||
input.ki.dwExtraInfo = 0;
|
||||
input.ki.wVk = vkey_array[i];
|
||||
input.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
|
||||
SendInput(1, &input, sizeof(INPUT));
|
||||
Sleep(delay);
|
||||
}
|
||||
}
|
40
espanso-inject/src/win32/native.h
Normal file
40
espanso-inject/src/win32/native.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* This file is part of espanso.
|
||||
*
|
||||
* Copyright (C) 2019-2021 Federico Terzi
|
||||
*
|
||||
* espanso is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* espanso is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ESPANSO_INJECT_H
|
||||
#define ESPANSO_INJECT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Inject a complete string using the KEYEVENTF_UNICODE flag
|
||||
extern "C" void inject_string(wchar_t * string);
|
||||
|
||||
// Send a sequence of vkey presses and releases
|
||||
extern "C" void inject_separate_vkeys(int32_t *vkey_array, int32_t vkey_count);
|
||||
|
||||
// Send a combination of vkeys, first pressing all the vkeys and then releasing
|
||||
// This is needed for keyboard shortcuts, for example.
|
||||
extern "C" void inject_vkeys_combination(int32_t *vkey_array, int32_t vkey_count);
|
||||
|
||||
// These two variants introduce a delay between each event, which is sometimes needed when
|
||||
// dealing with slow applications
|
||||
extern "C" void inject_separate_vkeys_with_delay(int32_t *vkey_array, int32_t vkey_count, int32_t delay);
|
||||
extern "C" void inject_vkeys_combination_with_delay(int32_t *vkey_array, int32_t vkey_count, int32_t delay);
|
||||
|
||||
#endif //ESPANSO_INJECT_H
|
95
espanso-inject/src/win32/raw_keys.rs
Normal file
95
espanso-inject/src/win32/raw_keys.rs
Normal file
|
@ -0,0 +1,95 @@
|
|||
use crate::keys::Key;
|
||||
|
||||
pub fn convert_key_to_vkey(key: &Key) -> Option<i32> {
|
||||
let vkey = match key {
|
||||
Key::Alt => 0x12,
|
||||
Key::CapsLock => 0x14,
|
||||
Key::Control => 0x11,
|
||||
Key::Meta => 0x5B,
|
||||
Key::NumLock => 0x90,
|
||||
Key::Shift => 0xA0,
|
||||
Key::Enter => 0x0D,
|
||||
Key::Tab => 0x09,
|
||||
Key::Space => 0x20,
|
||||
Key::ArrowDown => 0x28,
|
||||
Key::ArrowLeft => 0x25,
|
||||
Key::ArrowRight => 0x27,
|
||||
Key::ArrowUp => 0x26,
|
||||
Key::End => 0x23,
|
||||
Key::Home => 0x24,
|
||||
Key::PageDown => 0x22,
|
||||
Key::PageUp => 0x21,
|
||||
Key::Escape => 0x1B,
|
||||
Key::Backspace => 0x08,
|
||||
Key::Insert => 0x2D,
|
||||
Key::Delete => 0x2E,
|
||||
Key::F1 => 0x70,
|
||||
Key::F2 => 0x71,
|
||||
Key::F3 => 0x72,
|
||||
Key::F4 => 0x73,
|
||||
Key::F5 => 0x74,
|
||||
Key::F6 => 0x75,
|
||||
Key::F7 => 0x76,
|
||||
Key::F8 => 0x77,
|
||||
Key::F9 => 0x78,
|
||||
Key::F10 => 0x79,
|
||||
Key::F11 => 0x7A,
|
||||
Key::F12 => 0x7B,
|
||||
Key::F13 => 0x7C,
|
||||
Key::F14 => 0x7D,
|
||||
Key::F15 => 0x7E,
|
||||
Key::F16 => 0x7F,
|
||||
Key::F17 => 0x80,
|
||||
Key::F18 => 0x81,
|
||||
Key::F19 => 0x82,
|
||||
Key::F20 => 0x83,
|
||||
Key::A => 0x41,
|
||||
Key::B => 0x42,
|
||||
Key::C => 0x43,
|
||||
Key::D => 0x44,
|
||||
Key::E => 0x45,
|
||||
Key::F => 0x46,
|
||||
Key::G => 0x47,
|
||||
Key::H => 0x48,
|
||||
Key::I => 0x49,
|
||||
Key::J => 0x4A,
|
||||
Key::K => 0x4B,
|
||||
Key::L => 0x4C,
|
||||
Key::M => 0x4D,
|
||||
Key::N => 0x4E,
|
||||
Key::O => 0x4F,
|
||||
Key::P => 0x50,
|
||||
Key::Q => 0x51,
|
||||
Key::R => 0x52,
|
||||
Key::S => 0x53,
|
||||
Key::T => 0x54,
|
||||
Key::U => 0x55,
|
||||
Key::V => 0x56,
|
||||
Key::W => 0x57,
|
||||
Key::X => 0x58,
|
||||
Key::Y => 0x59,
|
||||
Key::Z => 0x5A,
|
||||
Key::N0 => 0x30,
|
||||
Key::N1 => 0x31,
|
||||
Key::N2 => 0x32,
|
||||
Key::N3 => 0x33,
|
||||
Key::N4 => 0x34,
|
||||
Key::N5 => 0x35,
|
||||
Key::N6 => 0x36,
|
||||
Key::N7 => 0x37,
|
||||
Key::N8 => 0x38,
|
||||
Key::N9 => 0x39,
|
||||
Key::Numpad0 => 0x60,
|
||||
Key::Numpad1 => 0x61,
|
||||
Key::Numpad2 => 0x62,
|
||||
Key::Numpad3 => 0x63,
|
||||
Key::Numpad4 => 0x64,
|
||||
Key::Numpad5 => 0x65,
|
||||
Key::Numpad6 => 0x66,
|
||||
Key::Numpad7 => 0x67,
|
||||
Key::Numpad8 => 0x68,
|
||||
Key::Numpad9 => 0x69,
|
||||
Key::Raw(code) => *code,
|
||||
};
|
||||
Some(vkey)
|
||||
}
|
|
@ -11,5 +11,6 @@ edition = "2018"
|
|||
[dependencies]
|
||||
espanso-detect = { path = "../espanso-detect" }
|
||||
espanso-ui = { path = "../espanso-ui" }
|
||||
espanso-inject = { path = "../espanso-inject" }
|
||||
maplit = "1.0.2"
|
||||
simplelog = "0.9.0"
|
|
@ -1,5 +1,6 @@
|
|||
use espanso_detect::event::{InputEvent, Status};
|
||||
use espanso_ui::{event::UIEvent::*, icons::TrayIcon, mac::*, menu::*};
|
||||
use espanso_inject::{Injector, get_injector};
|
||||
use espanso_ui::{event::UIEvent::*, icons::TrayIcon, menu::*};
|
||||
use simplelog::{CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode};
|
||||
|
||||
fn main() {
|
||||
|
@ -35,25 +36,26 @@ fn main() {
|
|||
),
|
||||
];
|
||||
|
||||
// let (remote, mut eventloop) = espanso_ui::win32::create(espanso_ui::win32::Win32UIOptions {
|
||||
// show_icon: true,
|
||||
// icon_paths: &icon_paths,
|
||||
// notification_icon_path: r"C:\Users\Freddy\Insync\Development\Espanso\Images\icongreensmall.png"
|
||||
// .to_string(),
|
||||
// });
|
||||
let (remote, mut eventloop) = espanso_ui::mac::create(MacUIOptions {
|
||||
let (remote, mut eventloop) = espanso_ui::win32::create(espanso_ui::win32::Win32UIOptions {
|
||||
show_icon: true,
|
||||
icon_paths: &icon_paths,
|
||||
notification_icon_path: r"C:\Users\Freddy\Insync\Development\Espanso\Images\icongreensmall.png"
|
||||
.to_string(),
|
||||
});
|
||||
// let (remote, mut eventloop) = espanso_ui::mac::create(MacUIOptions {
|
||||
// show_icon: true,
|
||||
// icon_paths: &icon_paths,
|
||||
// });
|
||||
|
||||
eventloop.initialize();
|
||||
|
||||
let handle = std::thread::spawn(move || {
|
||||
//let mut source = espanso_detect::win32::Win32Source::new();
|
||||
let mut source = espanso_detect::win32::Win32Source::new();
|
||||
//let mut source = espanso_detect::x11::X11Source::new();
|
||||
let mut source = espanso_detect::mac::CocoaSource::new();
|
||||
//let mut source = espanso_detect::mac::CocoaSource::new();
|
||||
source.initialize();
|
||||
source.eventloop(Box::new(move |event: InputEvent| {
|
||||
let injector = get_injector(Default::default());
|
||||
println!("ev {:?}", event);
|
||||
match event {
|
||||
InputEvent::Mouse(_) => {}
|
||||
|
@ -61,6 +63,7 @@ fn main() {
|
|||
if evt.key == espanso_detect::event::Key::Shift && evt.status == Status::Pressed {
|
||||
//remote.update_tray_icon(espanso_ui::icons::TrayIcon::Disabled);
|
||||
//remote.show_notification("Espanso is running!");
|
||||
injector.send_string("hey guys");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user