espanso/espanso-inject/src/evdev/mod.rs
2021-02-14 21:02:50 +01:00

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(&current_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),
}