feat(inject): add wait mechanism on wayland injector
This commit is contained in:
parent
6a558f74c7
commit
31b93ebdb0
|
@ -26,16 +26,18 @@ mod uinput;
|
|||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
ffi::CString,
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use context::Context;
|
||||
use keymap::Keymap;
|
||||
use log::error;
|
||||
use std::iter::FromIterator;
|
||||
use log::{error, warn};
|
||||
use uinput::UInputDevice;
|
||||
|
||||
use crate::{linux::raw_keys::convert_to_sym_array, InjectorCreationOptions};
|
||||
use anyhow::Result;
|
||||
use crate::{
|
||||
linux::raw_keys::convert_to_sym_array, InjectorCreationOptions, KeyboardStateProvider,
|
||||
};
|
||||
use anyhow::{Result, bail};
|
||||
use itertools::Itertools;
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -75,6 +77,9 @@ const DEFAULT_MODIFIERS: [u32; 10] = [
|
|||
|
||||
const DEFAULT_MAX_MODIFIER_COMBINATION_LEN: i32 = 3;
|
||||
|
||||
// TODO: make the timeout a configurable option
|
||||
const DEFAULT_WAIT_KEY_RELEASE_TIMEOUT_MS: u64 = 4000;
|
||||
|
||||
pub type KeySym = u32;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -98,6 +103,9 @@ pub struct EVDEVInjector {
|
|||
// Ownership
|
||||
_context: Context,
|
||||
_keymap: Keymap,
|
||||
|
||||
// Keyboard state provider
|
||||
keyboard_state_provider: Option<Box<dyn KeyboardStateProvider>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::new_without_default)]
|
||||
|
@ -126,12 +134,17 @@ impl EVDEVInjector {
|
|||
// Create the uinput virtual device
|
||||
let device = UInputDevice::new()?;
|
||||
|
||||
if options.keyboard_state_provider.is_none() {
|
||||
warn!("EVDEVInjection has been initialized without a KeyboardStateProvider, which might result in partial injections.");
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
device,
|
||||
char_map,
|
||||
sym_map,
|
||||
_context: context,
|
||||
_keymap: keymap,
|
||||
keyboard_state_provider: options.keyboard_state_provider,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -211,6 +224,31 @@ impl EVDEVInjector {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_until_key_is_released(&self, code: u32) -> Result<()> {
|
||||
if let Some(key_provider) = &self.keyboard_state_provider {
|
||||
let key_provider_code = code + EVDEV_OFFSET;
|
||||
|
||||
if !key_provider.is_key_pressed(key_provider_code) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Key is pressed, wait until timeout
|
||||
let now = Instant::now();
|
||||
while now.elapsed() < std::time::Duration::from_millis(DEFAULT_WAIT_KEY_RELEASE_TIMEOUT_MS) {
|
||||
if !key_provider.is_key_pressed(key_provider_code) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
}
|
||||
|
||||
bail!("timed-out while waiting for key release: {}", code);
|
||||
} else {
|
||||
// Keyboard provider not available,
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Injector for EVDEVInjector {
|
||||
|
@ -235,7 +273,7 @@ impl Injector for EVDEVInjector {
|
|||
let mut current_modifiers: HashSet<u32> = HashSet::new();
|
||||
|
||||
for record in records? {
|
||||
let record_modifiers = HashSet::from_iter(record.modifiers.iter().cloned());
|
||||
let record_modifiers = record.modifiers.iter().cloned().collect::<HashSet<_>>();
|
||||
|
||||
// Release all the modifiers that are not needed anymore
|
||||
for expired_modifier in current_modifiers.difference(&record_modifiers) {
|
||||
|
@ -244,10 +282,12 @@ impl Injector for EVDEVInjector {
|
|||
|
||||
// Press all the new modifiers that are now needed
|
||||
for new_modifier in record_modifiers.difference(¤t_modifiers) {
|
||||
self.wait_until_key_is_released(record.code)?;
|
||||
self.send_key(*new_modifier, true, delay_us);
|
||||
}
|
||||
|
||||
// Send the char
|
||||
self.wait_until_key_is_released(record.code)?;
|
||||
self.send_key(record.code, true, delay_us);
|
||||
self.send_key(record.code, false, delay_us);
|
||||
|
||||
|
|
|
@ -98,6 +98,13 @@ pub struct InjectorCreationOptions {
|
|||
// Can be used to overwrite the keymap configuration
|
||||
// used by espanso to inject key presses.
|
||||
pub evdev_keyboard_rmlvo: Option<KeyboardConfig>,
|
||||
|
||||
// An optional provider that can be used by the injector
|
||||
// to determine which keys are pressed at the time of injection.
|
||||
// This is needed on Wayland to "wait" for key releases when
|
||||
// the injected string contains a key that it's currently pressed.
|
||||
// Otherwise, a key that is already pressed cannot be injected.
|
||||
pub keyboard_state_provider: Option<Box<dyn KeyboardStateProvider>>,
|
||||
}
|
||||
|
||||
// This struct identifies the keyboard layout that
|
||||
|
@ -111,6 +118,10 @@ pub struct KeyboardConfig {
|
|||
pub options: Option<String>,
|
||||
}
|
||||
|
||||
pub trait KeyboardStateProvider {
|
||||
fn is_key_pressed(&self, code: u32) -> bool;
|
||||
}
|
||||
|
||||
impl Default for InjectorCreationOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
@ -118,6 +129,7 @@ impl Default for InjectorCreationOptions {
|
|||
evdev_modifiers: None,
|
||||
evdev_max_modifier_combination_len: None,
|
||||
evdev_keyboard_rmlvo: None,
|
||||
keyboard_state_provider: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user