diff --git a/libs/enigo/src/linux.rs b/libs/enigo/src/linux.rs index 24628c1f9..08e08912a 100644 --- a/libs/enigo/src/linux.rs +++ b/libs/enigo/src/linux.rs @@ -3,7 +3,7 @@ use libc; use crate::{Key, KeyboardControllable, MouseButton, MouseControllable}; use self::libc::{c_char, c_int, c_void, useconds_t}; -use std::{borrow::Cow, ffi::CString, ptr}; +use std::{borrow::Cow, ffi::CString, io::prelude::*, ptr, sync::mpsc}; const CURRENT_WINDOW: c_int = 0; const DEFAULT_DELAY: u64 = 12000; @@ -64,6 +64,7 @@ fn mousebutton(button: MouseButton) -> c_int { pub struct Enigo { xdo: Xdo, delay: u64, + tx: mpsc::Sender<(char, bool)>, } // This is safe, we have a unique pointer. // TODO: use Unique once stable. @@ -72,9 +73,12 @@ unsafe impl Send for Enigo {} impl Default for Enigo { /// Create a new Enigo instance fn default() -> Self { + let (tx, rx) = mpsc::channel(); + // start_pynput_service(rx); Self { xdo: unsafe { xdo_new(ptr::null()) }, delay: DEFAULT_DELAY, + tx, } } } @@ -90,6 +94,16 @@ impl Enigo { pub fn set_delay(&mut self, delay: u64) { self.delay = delay; } + #[inline] + fn send_pynput(&mut self, key: &Key, is_press: bool) -> bool { + if unsafe { PYNPUT_EXIT || !PYNPUT_REDAY } { + return false; + } + if let Key::Layout(c) = key { + return self.tx.send((*c, is_press)).is_ok(); + } + false + } } impl Drop for Enigo { fn drop(&mut self) { @@ -288,7 +302,6 @@ impl KeyboardControllable for Enigo { let mod_numlock = 1 << 4; let mod_meta = 1 << 6; let mask = unsafe { xdo_get_input_state(self.xdo) }; - // println!("{:b}", mask); match key { Key::Shift => mask & mod_shift != 0, Key::CapsLock => mask & mod_lock != 0, @@ -319,6 +332,9 @@ impl KeyboardControllable for Enigo { if self.xdo.is_null() { return Ok(()); } + if self.send_pynput(&key, true) { + return Ok(()); + } let string = CString::new(&*keysequence(key))?; unsafe { xdo_send_keysequence_window_down( @@ -334,6 +350,9 @@ impl KeyboardControllable for Enigo { if self.xdo.is_null() { return; } + if self.send_pynput(&key, false) { + return; + } if let Ok(string) = CString::new(&*keysequence(key)) { unsafe { xdo_send_keysequence_window_up( @@ -361,3 +380,75 @@ impl KeyboardControllable for Enigo { } } } + +static mut PYNPUT_EXIT: bool = false; +static mut PYNPUT_REDAY: bool = false; +static IPC_FILE: &'static str = "/tmp/RustDesk/pynput_service"; + +fn start_pynput_service(rx: mpsc::Receiver<(char, bool)>) { + let mut py = "./pynput_service.py".to_owned(); + if !std::path::Path::new(&py).exists() { + py = "/usr/share/rustdesk/files/pynput_service.py".to_owned(); + if !std::path::Path::new(&py).exists() { + log::error!("{} not exits", py); + } + } + log::info!("pynput service: {}", py); + std::thread::spawn(move || { + log::error!( + "pynput server exit: {:?}", + std::process::Command::new("python3") + .arg("./pynput_service.py") + .arg(IPC_FILE) + .status() + ); + unsafe { + PYNPUT_EXIT = true; + } + }); + std::thread::spawn(move || { + // wait for pynput_server.py + std::thread::sleep(std::time::Duration::from_millis(1000)); + let mut conn = match std::os::unix::net::UnixStream::connect(IPC_FILE) { + Ok(conn) => conn, + Err(err) => { + log::error!("Failed to connect to {}: {}", IPC_FILE, err); + return; + } + }; + if let Err(err) = conn.set_nonblocking(true) { + log::error!("Failed to set ipc nonblocking: {}", err); + return; + } + let d = std::time::Duration::from_millis(30); + unsafe { PYNPUT_REDAY = true; } + let mut buf = [0u8; 10]; + loop { + if unsafe { PYNPUT_EXIT } { + break; + } + match rx.recv_timeout(d) { + Ok((chr, is_press)) => { + let msg = format!("{}{}", if is_press { 'p' } else { 'r' }, chr); + let n = msg.len(); + buf[0] = n as _; + buf[1..(n+1)].copy_from_slice(msg.as_bytes()); + if let Err(err) = conn.write_all(&buf[..n + 1]) { + log::error!("Failed to write to ipc: {}", err); + break; + } + } + Err(err) => match err { + mpsc::RecvTimeoutError::Disconnected => { + log::error!("pynput sender disconnecte"); + break; + } + _ => {} + }, + } + } + unsafe { + PYNPUT_REDAY = false; + } + }); +} diff --git a/pynput_service.py b/pynput_service.py new file mode 100644 index 000000000..d09f44732 --- /dev/null +++ b/pynput_service.py @@ -0,0 +1,43 @@ +from pynput.keyboard import Controller +import os +import sys +import socket + +keyboard = Controller() + +server_address = sys.argv[1] +if not os.path.exists(os.path.dirname(server_address)): + os.makedirs(os.path.dirname(server_address)) + +try: + os.unlink(server_address) +except OSError: + if os.path.exists(server_address): + raise + +server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) +server.bind(server_address) +server.listen(1) +clientsocket, address = server.accept() +print("Got pynput connection") +buf = [] +while True: + data = clientsocket.recv(1024) + if not data: + print("Connection broken") + break + buf.extend(data) + n = buf[0] + n = n + 1 + if len(buf) >= n: + msg = bytearray(buf[1:n]).decode("utf-8") + if len(msg) != 2: + print("Wrong message") + break + if msg[0] == "p": + keyboard.press(msg[1]) + else: + keyboard.release(msg[1]) + buf = buf[n:] +clientsocket.close() +server.close()