try out pynput
This commit is contained in:
parent
bcda7d3193
commit
1758aa0f1e
@ -3,7 +3,7 @@ use libc;
|
|||||||
use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
|
use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
|
||||||
|
|
||||||
use self::libc::{c_char, c_int, c_void, useconds_t};
|
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 CURRENT_WINDOW: c_int = 0;
|
||||||
const DEFAULT_DELAY: u64 = 12000;
|
const DEFAULT_DELAY: u64 = 12000;
|
||||||
@ -64,6 +64,7 @@ fn mousebutton(button: MouseButton) -> c_int {
|
|||||||
pub struct Enigo {
|
pub struct Enigo {
|
||||||
xdo: Xdo,
|
xdo: Xdo,
|
||||||
delay: u64,
|
delay: u64,
|
||||||
|
tx: mpsc::Sender<(char, bool)>,
|
||||||
}
|
}
|
||||||
// This is safe, we have a unique pointer.
|
// This is safe, we have a unique pointer.
|
||||||
// TODO: use Unique<c_char> once stable.
|
// TODO: use Unique<c_char> once stable.
|
||||||
@ -72,9 +73,12 @@ unsafe impl Send for Enigo {}
|
|||||||
impl Default for Enigo {
|
impl Default for Enigo {
|
||||||
/// Create a new Enigo instance
|
/// Create a new Enigo instance
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
// start_pynput_service(rx);
|
||||||
Self {
|
Self {
|
||||||
xdo: unsafe { xdo_new(ptr::null()) },
|
xdo: unsafe { xdo_new(ptr::null()) },
|
||||||
delay: DEFAULT_DELAY,
|
delay: DEFAULT_DELAY,
|
||||||
|
tx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,6 +94,16 @@ impl Enigo {
|
|||||||
pub fn set_delay(&mut self, delay: u64) {
|
pub fn set_delay(&mut self, delay: u64) {
|
||||||
self.delay = delay;
|
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 {
|
impl Drop for Enigo {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
@ -288,7 +302,6 @@ impl KeyboardControllable for Enigo {
|
|||||||
let mod_numlock = 1 << 4;
|
let mod_numlock = 1 << 4;
|
||||||
let mod_meta = 1 << 6;
|
let mod_meta = 1 << 6;
|
||||||
let mask = unsafe { xdo_get_input_state(self.xdo) };
|
let mask = unsafe { xdo_get_input_state(self.xdo) };
|
||||||
// println!("{:b}", mask);
|
|
||||||
match key {
|
match key {
|
||||||
Key::Shift => mask & mod_shift != 0,
|
Key::Shift => mask & mod_shift != 0,
|
||||||
Key::CapsLock => mask & mod_lock != 0,
|
Key::CapsLock => mask & mod_lock != 0,
|
||||||
@ -319,6 +332,9 @@ impl KeyboardControllable for Enigo {
|
|||||||
if self.xdo.is_null() {
|
if self.xdo.is_null() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
if self.send_pynput(&key, true) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
let string = CString::new(&*keysequence(key))?;
|
let string = CString::new(&*keysequence(key))?;
|
||||||
unsafe {
|
unsafe {
|
||||||
xdo_send_keysequence_window_down(
|
xdo_send_keysequence_window_down(
|
||||||
@ -334,6 +350,9 @@ impl KeyboardControllable for Enigo {
|
|||||||
if self.xdo.is_null() {
|
if self.xdo.is_null() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if self.send_pynput(&key, false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if let Ok(string) = CString::new(&*keysequence(key)) {
|
if let Ok(string) = CString::new(&*keysequence(key)) {
|
||||||
unsafe {
|
unsafe {
|
||||||
xdo_send_keysequence_window_up(
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
43
pynput_service.py
Normal file
43
pynput_service.py
Normal file
@ -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()
|
Loading…
x
Reference in New Issue
Block a user