diff --git a/libs/enigo/src/linux.rs b/libs/enigo/src/linux.rs index 08e08912a..7e3ba6d51 100644 --- a/libs/enigo/src/linux.rs +++ b/libs/enigo/src/linux.rs @@ -60,11 +60,16 @@ fn mousebutton(button: MouseButton) -> c_int { } } +enum PyMsg { + Char(char), + Str(&'static str), +} + /// The main struct for handling the event emitting pub struct Enigo { xdo: Xdo, delay: u64, - tx: mpsc::Sender<(char, bool)>, + tx: mpsc::Sender<(PyMsg, bool)>, } // This is safe, we have a unique pointer. // TODO: use Unique once stable. @@ -74,7 +79,7 @@ impl Default for Enigo { /// Create a new Enigo instance fn default() -> Self { let (tx, rx) = mpsc::channel(); - // start_pynput_service(rx); + start_pynput_service(rx); Self { xdo: unsafe { xdo_new(ptr::null()) }, delay: DEFAULT_DELAY, @@ -94,15 +99,98 @@ impl Enigo { pub fn set_delay(&mut self, delay: u64) { self.delay = delay; } + /// + pub fn reset(&mut self) { + self.tx.send((PyMsg::Char('\0'), true)).ok(); + } #[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(); + return self.tx.send((PyMsg::Char(*c), is_press)).is_ok(); } - false + if let Key::Raw(_) = key { + return false; + } + #[allow(deprecated)] + let s = match key { + Key::Alt => "Alt_L", + Key::Backspace => "BackSpace", + Key::CapsLock => "Caps_Lock", + Key::Control => "Control_L", + Key::Delete => "Delete", + Key::DownArrow => "Down", + Key::End => "End", + Key::Escape => "Escape", + Key::F1 => "F1", + Key::F10 => "F10", + Key::F11 => "F11", + Key::F12 => "F12", + Key::F2 => "F2", + Key::F3 => "F3", + Key::F4 => "F4", + Key::F5 => "F5", + Key::F6 => "F6", + Key::F7 => "F7", + Key::F8 => "F8", + Key::F9 => "F9", + Key::Home => "Home", + Key::LeftArrow => "Left", + Key::Option => "Option", + Key::PageDown => "Page_Down", + Key::PageUp => "Page_Up", + Key::Return => "Return", + Key::RightArrow => "Right", + Key::Shift => "Shift_L", + Key::Space => "space", + Key::Tab => "Tab", + Key::UpArrow => "Up", + Key::Numpad0 => "0", + Key::Numpad1 => "1", + Key::Numpad2 => "2", + Key::Numpad3 => "3", + Key::Numpad4 => "4", + Key::Numpad5 => "5", + Key::Numpad6 => "6", + Key::Numpad7 => "7", + Key::Numpad8 => "8", + Key::Numpad9 => "9", + Key::Decimal => "KP_Decimal", + Key::Cancel => "Cancel", + Key::Clear => "Clear", + Key::Pause => "Pause", + Key::Kana => "Kana", + Key::Hangul => "Hangul", + Key::Hanja => "Hanja", + Key::Kanji => "Kanji", + Key::Select => "Select", + Key::Print => "Print", + Key::Execute => "Execute", + Key::Snapshot => "3270_PrintScreen", + Key::Insert => "Insert", + Key::Help => "Help", + Key::Separator => "KP_Separator", + Key::Scroll => "Scroll_Lock", + Key::NumLock => "Num_Lock", + Key::RWin => "Super_R", + Key::Apps => "Menu", + Key::Multiply => "KP_Multiply", + Key::Add => "KP_Add", + Key::Subtract => "KP_Subtract", + Key::Divide => "KP_Divide", + Key::Equals => "KP_Equal", + Key::NumpadEnter => "KP_Enter", + Key::RightShift => "Shift_R", + Key::RightControl => "Control_R", + Key::RightAlt => "Mode_switch", + Key::Command | Key::Super | Key::Windows | Key::Meta => "Super_L", + _ => { + return true; + } + }; + return self.tx.send((PyMsg::Str(s), is_press)).is_ok(); } } impl Drop for Enigo { @@ -385,7 +473,7 @@ 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)>) { +fn start_pynput_service(rx: mpsc::Receiver<(PyMsg, 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(); @@ -421,18 +509,23 @@ fn start_pynput_service(rx: mpsc::Receiver<(char, bool)>) { return; } let d = std::time::Duration::from_millis(30); - unsafe { PYNPUT_REDAY = true; } - let mut buf = [0u8; 10]; + unsafe { + PYNPUT_REDAY = true; + } + let mut buf = [0u8; 1024]; 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); + Ok((msg, is_press)) => { + let msg = match msg { + PyMsg::Char(chr) => format!("{}{}", if is_press { 'p' } else { 'r' }, chr), + PyMsg::Str(s) => format!("{}{}", if is_press { 'p' } else { 'r' }, s), + }; let n = msg.len(); buf[0] = n as _; - buf[1..(n+1)].copy_from_slice(msg.as_bytes()); + 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; diff --git a/pynput_service.py b/pynput_service.py index d09f44732..db79201f8 100644 --- a/pynput_service.py +++ b/pynput_service.py @@ -1,8 +1,11 @@ -from pynput.keyboard import Controller +from pynput.keyboard import Key, Controller +from pynput.keyboard._xorg import KeyCode import os import sys import socket +INVALID = KeyCode._from_symbol("\0") # test + keyboard = Controller() server_address = sys.argv[1] @@ -20,24 +23,44 @@ 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") + + +def loop(): + global keyboard + buf = [] + while True: + data = clientsocket.recv(1024) + if not data: + print("Connection broken") break - if msg[0] == "p": - keyboard.press(msg[1]) - else: - keyboard.release(msg[1]) - buf = buf[n:] + buf.extend(data) + while buf: + n = buf[0] + n = n + 1 + if len(buf) < n: + break + msg = bytearray(buf[1:n]).decode("utf-8") + buf = buf[n:] + if len(msg) < 2: + continue + if msg[1] == "\0": + keyboard = Controller() + print("Keyboard reset") + continue + print(msg) + if len(msg) == 2: + name = msg[1] + else: + name = KeyCode._from_symbol(msg[1:]) + print(name) + if name == INVALID: + continue + if msg[0] == "p": + keyboard.press(name) + else: + keyboard.release(name) + + +loop() clientsocket.close() server.close()