333 lines
9.1 KiB
Rust
333 lines
9.1 KiB
Rust
/*
|
|
* 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 context;
|
|
mod ffi;
|
|
mod keymap;
|
|
mod state;
|
|
mod uinput;
|
|
|
|
use std::{
|
|
collections::{HashMap, HashSet},
|
|
ffi::{CString},
|
|
};
|
|
|
|
use context::Context;
|
|
use keymap::Keymap;
|
|
use log::error;
|
|
use std::iter::FromIterator;
|
|
use uinput::UInputDevice;
|
|
|
|
use crate::{
|
|
linux::raw_keys::{convert_to_sym_array},
|
|
InjectorCreationOptions,
|
|
};
|
|
use anyhow::Result;
|
|
use itertools::Itertools;
|
|
use thiserror::Error;
|
|
|
|
use crate::{keys, InjectionOptions, Injector};
|
|
|
|
use self::state::State;
|
|
|
|
// Offset between evdev keycodes (where KEY_ESCAPE is 1), and the evdev XKB
|
|
// keycode set (where ESC is 9).
|
|
const EVDEV_OFFSET: u32 = 8;
|
|
|
|
// List of modifier keycodes, as defined in the "input-event-codes.h" header
|
|
// These can be overridden by changing the "evdev_modifier" option during initialization
|
|
const KEY_LEFTCTRL: u32 = 29;
|
|
const KEY_LEFTSHIFT: u32 = 42;
|
|
const KEY_RIGHTSHIFT: u32 = 54;
|
|
const KEY_LEFTALT: u32 = 56;
|
|
const KEY_LEFTMETA: u32 = 125;
|
|
const KEY_RIGHTMETA: u32 = 126;
|
|
const KEY_RIGHTCTRL: u32 = 97;
|
|
const KEY_RIGHTALT: u32 = 100;
|
|
const KEY_CAPSLOCK: u32 = 58;
|
|
const KEY_NUMLOCK: u32 = 69;
|
|
|
|
const DEFAULT_MODIFIERS: [u32; 10] = [
|
|
KEY_LEFTCTRL,
|
|
KEY_LEFTSHIFT,
|
|
KEY_RIGHTSHIFT,
|
|
KEY_LEFTALT,
|
|
KEY_LEFTMETA,
|
|
KEY_RIGHTMETA,
|
|
KEY_RIGHTCTRL,
|
|
KEY_RIGHTALT,
|
|
KEY_CAPSLOCK,
|
|
KEY_NUMLOCK,
|
|
];
|
|
|
|
const DEFAULT_MAX_MODIFIER_COMBINATION_LEN: i32 = 3;
|
|
|
|
pub type KeySym = u32;
|
|
|
|
#[derive(Clone, Debug)]
|
|
struct KeyRecord {
|
|
// Keycode
|
|
code: u32,
|
|
// List of modifiers that must be pressed
|
|
modifiers: Vec<u32>,
|
|
}
|
|
|
|
type CharMap = HashMap<String, KeyRecord>;
|
|
type SymMap = HashMap<KeySym, KeyRecord>;
|
|
|
|
pub struct EVDEVInjector {
|
|
device: UInputDevice,
|
|
|
|
// Lookup maps
|
|
char_map: CharMap,
|
|
sym_map: SymMap,
|
|
|
|
// Ownership
|
|
_context: Context,
|
|
_keymap: Keymap,
|
|
}
|
|
|
|
#[allow(clippy::new_without_default)]
|
|
impl EVDEVInjector {
|
|
pub fn new(options: InjectorCreationOptions) -> Result<Self> {
|
|
let modifiers = options
|
|
.evdev_modifiers
|
|
.unwrap_or_else(|| DEFAULT_MODIFIERS.to_vec());
|
|
let max_modifier_combination_len = options
|
|
.evdev_max_modifier_combination_len
|
|
.unwrap_or(DEFAULT_MAX_MODIFIER_COMBINATION_LEN);
|
|
|
|
// Necessary to properly handle non-ascii chars
|
|
let empty_string = CString::new("")?;
|
|
unsafe {
|
|
libc::setlocale(libc::LC_ALL, empty_string.as_ptr());
|
|
}
|
|
|
|
let context = Context::new().expect("unable to obtain xkb context");
|
|
let keymap = Keymap::new(&context, options.evdev_keyboard_rmlvo).expect("unable to create xkb keymap");
|
|
|
|
let (char_map, sym_map) =
|
|
Self::generate_maps(&modifiers, max_modifier_combination_len, &keymap)?;
|
|
|
|
// Create the uinput virtual device
|
|
let device = UInputDevice::new()?;
|
|
|
|
Ok(Self {
|
|
device,
|
|
char_map,
|
|
sym_map,
|
|
_context: context,
|
|
_keymap: keymap,
|
|
})
|
|
}
|
|
|
|
fn generate_maps(
|
|
modifiers: &[u32],
|
|
max_modifier_sequence_len: i32,
|
|
keymap: &Keymap,
|
|
) -> Result<(CharMap, SymMap)> {
|
|
let mut char_map = HashMap::new();
|
|
let mut sym_map = HashMap::new();
|
|
|
|
let modifier_combinations = Self::generate_combinations(modifiers, max_modifier_sequence_len);
|
|
|
|
// Cycle through all code/modifiers combinations to populate the reverse lookup tables
|
|
for key_code in 8..256u32 {
|
|
for modifier_combination in modifier_combinations.iter() {
|
|
let state = State::new(keymap)?;
|
|
|
|
// Apply the modifiers
|
|
for modifier in modifier_combination.iter() {
|
|
// We need to add the EVDEV offset for xkbcommon to recognize it correctly
|
|
state.update_key(*modifier + EVDEV_OFFSET, true);
|
|
}
|
|
|
|
let key_record = KeyRecord {
|
|
code: key_code - EVDEV_OFFSET,
|
|
modifiers: modifier_combination.clone(),
|
|
};
|
|
|
|
// Keysym was found
|
|
if let Some(sym) = state.get_sym(key_code) {
|
|
sym_map.entry(sym).or_insert_with(|| key_record.clone());
|
|
}
|
|
|
|
// Char was found
|
|
if let Some(string) = state.get_string(key_code) {
|
|
char_map.entry(string).or_insert(key_record);
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok((char_map, sym_map))
|
|
}
|
|
|
|
fn generate_combinations(modifiers: &[u32], max_modifier_sequence_len: i32) -> Vec<Vec<u32>> {
|
|
let mut combinations = vec![vec![]]; // Initial empty combination
|
|
|
|
for sequence_len in 1..=max_modifier_sequence_len {
|
|
let current_combinations = modifiers
|
|
.iter()
|
|
.cloned()
|
|
.combinations(sequence_len as usize);
|
|
combinations.extend(current_combinations);
|
|
}
|
|
|
|
combinations
|
|
}
|
|
|
|
fn convert_to_record_array(&self, syms: &[u64]) -> Result<Vec<KeyRecord>> {
|
|
syms
|
|
.iter()
|
|
.map(|sym| {
|
|
self
|
|
.sym_map
|
|
.get(&(*sym as u32))
|
|
.cloned()
|
|
.ok_or_else(|| EVDEVInjectorError::SymMappingFailure(*sym as u32).into())
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
fn send_key(&self, code: u32, pressed: bool, delay_us: u32) {
|
|
self.device.emit(code, pressed);
|
|
if delay_us != 0 {
|
|
unsafe {
|
|
libc::usleep(delay_us);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Injector for EVDEVInjector {
|
|
fn send_string(&self, string: &str, options: InjectionOptions) -> Result<()> {
|
|
// Compute all the key record sequence first to make sure a mapping is available
|
|
let records: Result<Vec<KeyRecord>> = string
|
|
.chars()
|
|
.map(|c| c.to_string())
|
|
.map(|char| {
|
|
self
|
|
.char_map
|
|
.get(&char)
|
|
.cloned()
|
|
.ok_or_else(|| EVDEVInjectorError::CharMappingFailure(char).into())
|
|
})
|
|
.collect();
|
|
|
|
let delay_us = options.delay as u32 * 1000; // Convert to micro seconds
|
|
|
|
// We need to keep track of the modifiers currently pressed to
|
|
// press or release them accordingly
|
|
let mut current_modifiers: HashSet<u32> = HashSet::new();
|
|
|
|
for record in records? {
|
|
let record_modifiers = HashSet::from_iter(record.modifiers.iter().cloned());
|
|
|
|
// Release all the modifiers that are not needed anymore
|
|
for expired_modifier in current_modifiers.difference(&record_modifiers) {
|
|
self.send_key(*expired_modifier, false, delay_us);
|
|
}
|
|
|
|
// Press all the new modifiers that are now needed
|
|
for new_modifier in record_modifiers.difference(¤t_modifiers) {
|
|
self.send_key(*new_modifier, true, delay_us);
|
|
}
|
|
|
|
// Send the char
|
|
self.send_key(record.code, true, delay_us);
|
|
self.send_key(record.code, false, delay_us);
|
|
|
|
current_modifiers = record_modifiers;
|
|
}
|
|
|
|
// Release all the remaining modifiers
|
|
for expired_modifier in current_modifiers {
|
|
self.send_key(expired_modifier, false, delay_us);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn send_keys(&self, keys: &[keys::Key], options: InjectionOptions) -> Result<()> {
|
|
// Compute all the key record sequence first to make sure a mapping is available
|
|
let syms = convert_to_sym_array(keys)?;
|
|
let records = self.convert_to_record_array(&syms)?;
|
|
|
|
let delay_us = options.delay as u32 * 1000; // Convert to micro seconds
|
|
|
|
for record in records {
|
|
// Press the modifiers
|
|
for modifier in record.modifiers.iter() {
|
|
self.send_key(*modifier, true, delay_us);
|
|
}
|
|
|
|
// Send the key
|
|
self.send_key(record.code, true, delay_us);
|
|
self.send_key(record.code, false, delay_us);
|
|
|
|
// Release the modifiers
|
|
for modifier in record.modifiers.iter() {
|
|
self.send_key(*modifier, false, delay_us);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn send_key_combination(&self, keys: &[keys::Key], options: InjectionOptions) -> Result<()> {
|
|
// Compute all the key record sequence first to make sure a mapping is available
|
|
let syms = convert_to_sym_array(keys)?;
|
|
let records = self.convert_to_record_array(&syms)?;
|
|
|
|
let delay_us = options.delay as u32 * 1000; // Convert to micro seconds
|
|
|
|
// First press the keys
|
|
for record in records.iter() {
|
|
// Press the modifiers
|
|
for modifier in record.modifiers.iter() {
|
|
self.send_key(*modifier, true, delay_us);
|
|
}
|
|
|
|
// Send the key
|
|
self.send_key(record.code, true, delay_us);
|
|
}
|
|
|
|
// Then release them
|
|
for record in records.iter().rev() {
|
|
self.send_key(record.code, false, delay_us);
|
|
|
|
// Release the modifiers
|
|
for modifier in record.modifiers.iter() {
|
|
self.send_key(*modifier, false, delay_us);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Error, Debug)]
|
|
pub enum EVDEVInjectorError {
|
|
#[error("missing vkey mapping for char `{0}`")]
|
|
CharMappingFailure(String),
|
|
|
|
#[error("missing record mapping for sym `{0}`")]
|
|
SymMappingFailure(u32),
|
|
}
|