mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-03-23 13:11:05 +03:00
fix(install): linux xdo (#14096)
Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -1,50 +1,22 @@
|
||||
//! XDO-based input emulation for Linux.
|
||||
//!
|
||||
//! This module uses libxdo-sys (patched to use dynamic loading stub) for input emulation.
|
||||
//! The stub handles dynamic loading of libxdo, so we just call the functions directly.
|
||||
//!
|
||||
//! If libxdo is not available at runtime, all operations become no-ops.
|
||||
|
||||
use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
|
||||
|
||||
use hbb_common::libc::{c_char, c_int, c_void, useconds_t};
|
||||
use std::{borrow::Cow, ffi::CString, ptr};
|
||||
use hbb_common::libc::c_int;
|
||||
use libxdo_sys::{self, xdo_t, CURRENTWINDOW};
|
||||
use std::{borrow::Cow, ffi::CString};
|
||||
|
||||
const CURRENT_WINDOW: c_int = 0;
|
||||
/// Default delay per keypress in microseconds.
|
||||
/// This value is passed to libxdo functions and must fit in `useconds_t` (u32).
|
||||
const DEFAULT_DELAY: u64 = 12000;
|
||||
type Window = c_int;
|
||||
type Xdo = *const c_void;
|
||||
|
||||
#[link(name = "xdo")]
|
||||
extern "C" {
|
||||
fn xdo_free(xdo: Xdo);
|
||||
fn xdo_new(display: *const c_char) -> Xdo;
|
||||
|
||||
fn xdo_click_window(xdo: Xdo, window: Window, button: c_int) -> c_int;
|
||||
fn xdo_mouse_down(xdo: Xdo, window: Window, button: c_int) -> c_int;
|
||||
fn xdo_mouse_up(xdo: Xdo, window: Window, button: c_int) -> c_int;
|
||||
fn xdo_move_mouse(xdo: Xdo, x: c_int, y: c_int, screen: c_int) -> c_int;
|
||||
fn xdo_move_mouse_relative(xdo: Xdo, x: c_int, y: c_int) -> c_int;
|
||||
|
||||
fn xdo_enter_text_window(
|
||||
xdo: Xdo,
|
||||
window: Window,
|
||||
string: *const c_char,
|
||||
delay: useconds_t,
|
||||
) -> c_int;
|
||||
fn xdo_send_keysequence_window(
|
||||
xdo: Xdo,
|
||||
window: Window,
|
||||
string: *const c_char,
|
||||
delay: useconds_t,
|
||||
) -> c_int;
|
||||
fn xdo_send_keysequence_window_down(
|
||||
xdo: Xdo,
|
||||
window: Window,
|
||||
string: *const c_char,
|
||||
delay: useconds_t,
|
||||
) -> c_int;
|
||||
fn xdo_send_keysequence_window_up(
|
||||
xdo: Xdo,
|
||||
window: Window,
|
||||
string: *const c_char,
|
||||
delay: useconds_t,
|
||||
) -> c_int;
|
||||
fn xdo_get_input_state(xdo: Xdo) -> u32;
|
||||
}
|
||||
/// Maximum allowed delay value (u32::MAX as u64).
|
||||
const MAX_DELAY: u64 = u32::MAX as u64;
|
||||
|
||||
fn mousebutton(button: MouseButton) -> c_int {
|
||||
match button {
|
||||
@@ -62,7 +34,7 @@ fn mousebutton(button: MouseButton) -> c_int {
|
||||
|
||||
/// The main struct for handling the event emitting
|
||||
pub(super) struct EnigoXdo {
|
||||
xdo: Xdo,
|
||||
xdo: *mut xdo_t,
|
||||
delay: u64,
|
||||
}
|
||||
// This is safe, we have a unique pointer.
|
||||
@@ -70,37 +42,61 @@ pub(super) struct EnigoXdo {
|
||||
unsafe impl Send for EnigoXdo {}
|
||||
|
||||
impl Default for EnigoXdo {
|
||||
/// Create a new EnigoXdo instance
|
||||
/// Create a new EnigoXdo instance.
|
||||
///
|
||||
/// If libxdo is not available, the xdo pointer will be null and all
|
||||
/// input operations will be no-ops.
|
||||
fn default() -> Self {
|
||||
let xdo = unsafe { libxdo_sys::xdo_new(std::ptr::null()) };
|
||||
if xdo.is_null() {
|
||||
log::warn!("Failed to create xdo context, xdo functions will be disabled");
|
||||
} else {
|
||||
log::info!("xdo context created successfully");
|
||||
}
|
||||
Self {
|
||||
xdo: unsafe { xdo_new(ptr::null()) },
|
||||
xdo,
|
||||
delay: DEFAULT_DELAY,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EnigoXdo {
|
||||
/// Get the delay per keypress.
|
||||
/// Default value is 12000.
|
||||
/// This is Linux-specific.
|
||||
/// Get the delay per keypress in microseconds.
|
||||
///
|
||||
/// Default value is 12000 (12ms). This is Linux-specific.
|
||||
pub fn delay(&self) -> u64 {
|
||||
self.delay
|
||||
}
|
||||
/// Set the delay per keypress.
|
||||
/// This is Linux-specific.
|
||||
|
||||
/// Set the delay per keypress in microseconds.
|
||||
///
|
||||
/// This is Linux-specific. The value is clamped to `u32::MAX` (approximately
|
||||
/// 4295 seconds) because libxdo uses `useconds_t` which is typically `u32`.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `delay` - Delay in microseconds. Values exceeding `u32::MAX` will be clamped.
|
||||
pub fn set_delay(&mut self, delay: u64) {
|
||||
self.delay = delay;
|
||||
self.delay = delay.min(MAX_DELAY);
|
||||
if delay > MAX_DELAY {
|
||||
log::warn!(
|
||||
"delay value {} exceeds maximum {}, clamped",
|
||||
delay,
|
||||
MAX_DELAY
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EnigoXdo {
|
||||
fn drop(&mut self) {
|
||||
if self.xdo.is_null() {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
xdo_free(self.xdo);
|
||||
if !self.xdo.is_null() {
|
||||
unsafe {
|
||||
libxdo_sys::xdo_free(self.xdo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MouseControllable for EnigoXdo {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
@@ -115,42 +111,47 @@ impl MouseControllable for EnigoXdo {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
xdo_move_mouse(self.xdo, x as c_int, y as c_int, 0);
|
||||
libxdo_sys::xdo_move_mouse(self.xdo as *const _, x, y, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_move_relative(&mut self, x: i32, y: i32) {
|
||||
if self.xdo.is_null() {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
xdo_move_mouse_relative(self.xdo, x as c_int, y as c_int);
|
||||
libxdo_sys::xdo_move_mouse_relative(self.xdo as *const _, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_down(&mut self, button: MouseButton) -> crate::ResultType {
|
||||
if self.xdo.is_null() {
|
||||
return Ok(());
|
||||
}
|
||||
unsafe {
|
||||
xdo_mouse_down(self.xdo, CURRENT_WINDOW, mousebutton(button));
|
||||
libxdo_sys::xdo_mouse_down(self.xdo as *const _, CURRENTWINDOW, mousebutton(button));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mouse_up(&mut self, button: MouseButton) {
|
||||
if self.xdo.is_null() {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
xdo_mouse_up(self.xdo, CURRENT_WINDOW, mousebutton(button));
|
||||
libxdo_sys::xdo_mouse_up(self.xdo as *const _, CURRENTWINDOW, mousebutton(button));
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_click(&mut self, button: MouseButton) {
|
||||
if self.xdo.is_null() {
|
||||
return;
|
||||
}
|
||||
unsafe {
|
||||
xdo_click_window(self.xdo, CURRENT_WINDOW, mousebutton(button));
|
||||
libxdo_sys::xdo_click_window(self.xdo as *const _, CURRENTWINDOW, mousebutton(button));
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_scroll_x(&mut self, length: i32) {
|
||||
let button;
|
||||
let mut length = length;
|
||||
@@ -169,6 +170,7 @@ impl MouseControllable for EnigoXdo {
|
||||
self.mouse_click(button);
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_scroll_y(&mut self, length: i32) {
|
||||
let button;
|
||||
let mut length = length;
|
||||
@@ -188,6 +190,7 @@ impl MouseControllable for EnigoXdo {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn keysequence<'a>(key: Key) -> Cow<'a, str> {
|
||||
if let Key::Layout(c) = key {
|
||||
return Cow::Owned(format!("U{:X}", c as u32));
|
||||
@@ -284,6 +287,7 @@ fn keysequence<'a>(key: Key) -> Cow<'a, str> {
|
||||
_ => "",
|
||||
})
|
||||
}
|
||||
|
||||
impl KeyboardControllable for EnigoXdo {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
@@ -314,7 +318,7 @@ impl KeyboardControllable for EnigoXdo {
|
||||
let mod_alt = 1 << 3;
|
||||
let mod_numlock = 1 << 4;
|
||||
let mod_meta = 1 << 6;
|
||||
let mask = unsafe { xdo_get_input_state(self.xdo) };
|
||||
let mask = unsafe { libxdo_sys::xdo_get_input_state(self.xdo as *const _) };
|
||||
match key {
|
||||
Key::Shift => mask & mod_shift != 0,
|
||||
Key::CapsLock => mask & mod_lock != 0,
|
||||
@@ -332,56 +336,59 @@ impl KeyboardControllable for EnigoXdo {
|
||||
}
|
||||
if let Ok(string) = CString::new(sequence) {
|
||||
unsafe {
|
||||
xdo_enter_text_window(
|
||||
self.xdo,
|
||||
CURRENT_WINDOW,
|
||||
libxdo_sys::xdo_enter_text_window(
|
||||
self.xdo as *const _,
|
||||
CURRENTWINDOW,
|
||||
string.as_ptr(),
|
||||
self.delay as useconds_t,
|
||||
self.delay as libxdo_sys::useconds_t,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_down(&mut self, key: Key) -> crate::ResultType {
|
||||
if self.xdo.is_null() {
|
||||
return Ok(());
|
||||
}
|
||||
let string = CString::new(&*keysequence(key))?;
|
||||
unsafe {
|
||||
xdo_send_keysequence_window_down(
|
||||
self.xdo,
|
||||
CURRENT_WINDOW,
|
||||
libxdo_sys::xdo_send_keysequence_window_down(
|
||||
self.xdo as *const _,
|
||||
CURRENTWINDOW,
|
||||
string.as_ptr(),
|
||||
self.delay as useconds_t,
|
||||
self.delay as libxdo_sys::useconds_t,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn key_up(&mut self, key: Key) {
|
||||
if self.xdo.is_null() {
|
||||
return;
|
||||
}
|
||||
if let Ok(string) = CString::new(&*keysequence(key)) {
|
||||
unsafe {
|
||||
xdo_send_keysequence_window_up(
|
||||
self.xdo,
|
||||
CURRENT_WINDOW,
|
||||
libxdo_sys::xdo_send_keysequence_window_up(
|
||||
self.xdo as *const _,
|
||||
CURRENTWINDOW,
|
||||
string.as_ptr(),
|
||||
self.delay as useconds_t,
|
||||
self.delay as libxdo_sys::useconds_t,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_click(&mut self, key: Key) {
|
||||
if self.xdo.is_null() {
|
||||
return;
|
||||
}
|
||||
if let Ok(string) = CString::new(&*keysequence(key)) {
|
||||
unsafe {
|
||||
xdo_send_keysequence_window(
|
||||
self.xdo,
|
||||
CURRENT_WINDOW,
|
||||
libxdo_sys::xdo_send_keysequence_window(
|
||||
self.xdo as *const _,
|
||||
CURRENTWINDOW,
|
||||
string.as_ptr(),
|
||||
self.delay as useconds_t,
|
||||
self.delay as libxdo_sys::useconds_t,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user