feat(inject): progress in the xdotool fallback implementation

This commit is contained in:
Federico Terzi 2022-04-12 21:24:40 +02:00
parent 4fec040551
commit bf35a80d4d
5 changed files with 224 additions and 21 deletions

View File

@ -6,7 +6,7 @@ use std::{
os::raw::{c_char, c_long, c_uint, c_ulong},
};
use libc::c_int;
use libc::{c_int, c_uchar};
pub enum Display {}
pub type Window = u64;
@ -123,4 +123,6 @@ extern "C" {
pub fn XFilterEvent(event: *mut XKeyEvent, window: c_ulong) -> c_int;
pub fn XCloseIM(input_method: XIM) -> c_int;
pub fn XFree(data: *mut c_void) -> c_int;
pub fn XKeycodeToKeysym(display: *mut Display, keycode: c_uchar, index: c_int) -> c_ulong;
pub fn XKeysymToString(keysym: c_ulong) -> *mut c_char;
}

View File

@ -39,6 +39,12 @@ extern "C" {
string: *const c_char,
delay: useconds_t,
);
pub fn xdo_send_keysequence_window(
xdo: *const xdo_t,
window: Window,
keysequence: *const c_char,
delay: useconds_t,
);
pub fn fast_send_event(xdo: *const xdo_t, window: Window, keycode: c_int, pressed: c_int);
pub fn fast_enter_text_window(
xdo: *const xdo_t,
@ -46,4 +52,10 @@ extern "C" {
string: *const c_char,
delay: useconds_t,
);
pub fn fast_send_keysequence_window(
xdo: *const xdo_t,
window: Window,
keysequence: *const c_char,
delay: useconds_t,
);
}

View File

@ -17,17 +17,22 @@
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
*/
use std::{convert::TryInto, ffi::CString};
use std::{
convert::TryInto,
ffi::{CStr, CString},
};
use crate::Injector;
use anyhow::{bail, Context, Result};
use libc::c_int;
use log::debug;
mod ffi;
use self::ffi::{xdo_t, CURRENTWINDOW};
use self::ffi::{fast_send_keysequence_window, xdo_send_keysequence_window, xdo_t, CURRENTWINDOW};
use super::ffi::{Window, XGetInputFocus, XQueryKeymap, XTestFakeKeyEvent};
use super::ffi::{
Display, Window, XGetInputFocus, XKeycodeToKeysym, XKeysymToString, XQueryKeymap,
XTestFakeKeyEvent,
};
pub struct X11XDOToolInjector {
xdo: *const xdo_t,
@ -67,6 +72,15 @@ impl X11XDOToolInjector {
}
}
fn get_focused_window(&self) -> Window {
let mut focused_window: Window = 0;
let mut revert_to = 0;
unsafe {
XGetInputFocus((*self.xdo).xdpy, &mut focused_window, &mut revert_to);
}
focused_window
}
fn xfake_send_string(
&self,
string: &str,
@ -95,17 +109,13 @@ impl X11XDOToolInjector {
}
fn fast_release_all_keys(&self) {
let mut focused: Window = 0;
let mut revert_to: c_int = 0;
unsafe {
XGetInputFocus((*self.xdo).xdpy, &mut focused, &mut revert_to);
}
let mut keys: [u8; 32] = [0; 32];
unsafe {
XQueryKeymap((*self.xdo).xdpy, keys.as_mut_ptr());
}
let focused_window = self.get_focused_window();
#[allow(clippy::needless_range_loop)]
for i in 0..32 {
// Only those that are pressed should be changed
@ -114,7 +124,7 @@ impl X11XDOToolInjector {
if (keys[i] & (1 << k)) != 0 {
let key_code = i * 8 + k;
unsafe {
ffi::fast_send_event(self.xdo, focused, key_code.try_into().unwrap(), 0);
ffi::fast_send_event(self.xdo, focused_window, key_code.try_into().unwrap(), 0);
}
}
}
@ -130,19 +140,13 @@ impl X11XDOToolInjector {
// and inject a key_release event so that they can be further registered.
self.fast_release_all_keys();
let mut focused: Window = 0;
let mut revert_to: c_int = 0;
unsafe {
XGetInputFocus((*self.xdo).xdpy, &mut focused, &mut revert_to);
}
let c_string = CString::new(string).context("unable to create CString")?;
let delay = options.delay * 1000;
unsafe {
ffi::fast_enter_text_window(
self.xdo,
focused,
self.get_focused_window(),
c_string.as_ptr(),
delay.try_into().unwrap(),
);
@ -166,7 +170,37 @@ impl Injector for X11XDOToolInjector {
keys: &[crate::keys::Key],
options: crate::InjectionOptions,
) -> anyhow::Result<()> {
//todo!()
let key_syms: Vec<String> = keys
.iter()
.filter_map(|key| unsafe { convert_key_to_keysym((*self.xdo).xdpy, key) })
.collect();
let delay = options.delay * 1000;
for key in key_syms {
let c_str = CString::new(key).context("unable to generate CString")?;
if options.disable_fast_inject {
unsafe {
xdo_send_keysequence_window(
self.xdo,
CURRENTWINDOW,
c_str.as_ptr(),
delay.try_into().unwrap(),
);
}
} else {
unsafe {
fast_send_keysequence_window(
self.xdo,
self.get_focused_window(),
c_str.as_ptr(),
delay.try_into().unwrap(),
);
}
}
}
Ok(())
}
@ -175,7 +209,36 @@ impl Injector for X11XDOToolInjector {
keys: &[crate::keys::Key],
options: crate::InjectionOptions,
) -> anyhow::Result<()> {
//todo!()
let key_syms: Vec<String> = keys
.iter()
.filter_map(|key| unsafe { convert_key_to_keysym((*self.xdo).xdpy, key) })
.collect();
let key_combination = key_syms.join("+");
let delay = options.delay * 1000;
let c_key_combination = CString::new(key_combination).context("unable to generate CString")?;
if options.disable_fast_inject {
unsafe {
xdo_send_keysequence_window(
self.xdo,
CURRENTWINDOW,
c_key_combination.as_ptr(),
delay.try_into().unwrap(),
);
}
} else {
unsafe {
fast_send_keysequence_window(
self.xdo,
self.get_focused_window(),
c_key_combination.as_ptr(),
delay.try_into().unwrap(),
);
}
}
Ok(())
}
}
@ -185,3 +248,101 @@ impl Drop for X11XDOToolInjector {
unsafe { ffi::xdo_free(self.xdo) }
}
}
fn convert_key_to_keysym(display: *mut Display, key: &crate::keys::Key) -> Option<String> {
match key {
crate::keys::Key::Alt => Some("Alt_L".to_string()),
crate::keys::Key::CapsLock => Some("Caps_Lock".to_string()),
crate::keys::Key::Control => Some("Control_L".to_string()),
crate::keys::Key::Meta => Some("Meta_L".to_string()),
crate::keys::Key::NumLock => Some("Num_Lock".to_string()),
crate::keys::Key::Shift => Some("Shift_L".to_string()),
crate::keys::Key::Enter => Some("Return".to_string()),
crate::keys::Key::Tab => Some("Tab".to_string()),
crate::keys::Key::Space => Some("space".to_string()),
crate::keys::Key::ArrowDown => Some("downarrow".to_string()),
crate::keys::Key::ArrowLeft => Some("leftarrow".to_string()),
crate::keys::Key::ArrowRight => Some("rightarrow".to_string()),
crate::keys::Key::ArrowUp => Some("uparrow".to_string()),
crate::keys::Key::End => Some("End".to_string()),
crate::keys::Key::Home => Some("Home".to_string()),
crate::keys::Key::PageDown => Some("Page_Down".to_string()),
crate::keys::Key::PageUp => Some("Page_Up".to_string()),
crate::keys::Key::Escape => Some("Escape".to_string()),
crate::keys::Key::Backspace => Some("BackSpace".to_string()),
crate::keys::Key::Insert => Some("Insert".to_string()),
crate::keys::Key::Delete => Some("Delete".to_string()),
crate::keys::Key::F1 => Some("F1".to_string()),
crate::keys::Key::F2 => Some("F2".to_string()),
crate::keys::Key::F3 => Some("F3".to_string()),
crate::keys::Key::F4 => Some("F4".to_string()),
crate::keys::Key::F5 => Some("F5".to_string()),
crate::keys::Key::F6 => Some("F6".to_string()),
crate::keys::Key::F7 => Some("F7".to_string()),
crate::keys::Key::F8 => Some("F8".to_string()),
crate::keys::Key::F9 => Some("F9".to_string()),
crate::keys::Key::F10 => Some("F10".to_string()),
crate::keys::Key::F11 => Some("F11".to_string()),
crate::keys::Key::F12 => Some("F12".to_string()),
crate::keys::Key::F13 => Some("F13".to_string()),
crate::keys::Key::F14 => Some("F14".to_string()),
crate::keys::Key::F15 => Some("F15".to_string()),
crate::keys::Key::F16 => Some("F16".to_string()),
crate::keys::Key::F17 => Some("F17".to_string()),
crate::keys::Key::F18 => Some("F18".to_string()),
crate::keys::Key::F19 => Some("F19".to_string()),
crate::keys::Key::F20 => Some("F20".to_string()),
crate::keys::Key::A => Some("a".to_string()),
crate::keys::Key::B => Some("b".to_string()),
crate::keys::Key::C => Some("c".to_string()),
crate::keys::Key::D => Some("d".to_string()),
crate::keys::Key::E => Some("e".to_string()),
crate::keys::Key::F => Some("f".to_string()),
crate::keys::Key::G => Some("g".to_string()),
crate::keys::Key::H => Some("h".to_string()),
crate::keys::Key::I => Some("i".to_string()),
crate::keys::Key::J => Some("j".to_string()),
crate::keys::Key::K => Some("k".to_string()),
crate::keys::Key::L => Some("l".to_string()),
crate::keys::Key::M => Some("m".to_string()),
crate::keys::Key::N => Some("n".to_string()),
crate::keys::Key::O => Some("o".to_string()),
crate::keys::Key::P => Some("p".to_string()),
crate::keys::Key::Q => Some("q".to_string()),
crate::keys::Key::R => Some("r".to_string()),
crate::keys::Key::S => Some("s".to_string()),
crate::keys::Key::T => Some("t".to_string()),
crate::keys::Key::U => Some("u".to_string()),
crate::keys::Key::V => Some("v".to_string()),
crate::keys::Key::W => Some("w".to_string()),
crate::keys::Key::X => Some("x".to_string()),
crate::keys::Key::Y => Some("y".to_string()),
crate::keys::Key::Z => Some("z".to_string()),
crate::keys::Key::N0 => Some("0".to_string()),
crate::keys::Key::N1 => Some("1".to_string()),
crate::keys::Key::N2 => Some("2".to_string()),
crate::keys::Key::N3 => Some("3".to_string()),
crate::keys::Key::N4 => Some("4".to_string()),
crate::keys::Key::N5 => Some("5".to_string()),
crate::keys::Key::N6 => Some("6".to_string()),
crate::keys::Key::N7 => Some("7".to_string()),
crate::keys::Key::N8 => Some("8".to_string()),
crate::keys::Key::N9 => Some("9".to_string()),
crate::keys::Key::Numpad0 => Some("KP_0".to_string()),
crate::keys::Key::Numpad1 => Some("KP_1".to_string()),
crate::keys::Key::Numpad2 => Some("KP_2".to_string()),
crate::keys::Key::Numpad3 => Some("KP_3".to_string()),
crate::keys::Key::Numpad4 => Some("KP_4".to_string()),
crate::keys::Key::Numpad5 => Some("KP_5".to_string()),
crate::keys::Key::Numpad6 => Some("KP_6".to_string()),
crate::keys::Key::Numpad7 => Some("KP_7".to_string()),
crate::keys::Key::Numpad8 => Some("KP_8".to_string()),
crate::keys::Key::Numpad9 => Some("KP_9".to_string()),
crate::keys::Key::Raw(key_code) => unsafe {
let key_sym = XKeycodeToKeysym(display, (*key_code).try_into().unwrap(), 0);
let string = XKeysymToString(key_sym);
let c_str = CStr::from_ptr(string);
Some(c_str.to_string_lossy().to_string())
},
}
}

View File

@ -2237,4 +2237,30 @@ void fast_send_event(const xdo_t *xdo, Window window, int keycode, int pressed)
event.xkey =xk;
XSendEvent(xdo->xdpy, window, True, 0, &event);
}
int fast_send_keysequence_window_do(const xdo_t *xdo, Window window, const char *keyseq,
int pressed, int *modifier, useconds_t delay) {
int ret = 0;
charcodemap_t *keys = NULL;
int nkeys = 0;
if (_xdo_send_keysequence_window_to_keycode_list(xdo, keyseq, &keys, &nkeys) == False) {
fprintf(stderr, "Failure converting key sequence '%s' to keycodes\n", keyseq);
return 1;
}
ret = fast_send_keysequence_window_list_do(xdo, window, keys, nkeys, pressed, modifier, delay);
free(keys);
return ret;
}
int fast_send_keysequence_window(const xdo_t *xdo, Window window, const char *keyseq,
useconds_t delay) {
int ret = 0;
int modifier = 0;
ret += fast_send_keysequence_window_do(xdo, window, keyseq, True, &modifier, delay / 2);
ret += fast_send_keysequence_window_do(xdo, window, keyseq, False, &modifier, delay / 2);
return ret;
}

View File

@ -927,6 +927,8 @@ void fast_send_key(const xdo_t *xdo, Window window, charcodemap_t *key,
int modstate, int is_press, useconds_t delay);
int fast_enter_text_window(const xdo_t *xdo, Window window, const char *string, useconds_t delay);
void fast_send_event(const xdo_t *xdo, Window window, int keycode, int pressed);
int fast_send_keysequence_window(const xdo_t *xdo, Window window,
const char *keysequence, useconds_t delay);
#ifdef __cplusplus
} /* extern "C" */