mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-02-17 14:07:28 +08:00
fix(install): linux xdo (#14096)
Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -2517,6 +2517,7 @@ version = "0.0.14"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"core-graphics 0.22.3",
|
"core-graphics 0.22.3",
|
||||||
"hbb_common",
|
"hbb_common",
|
||||||
|
"libxdo-sys",
|
||||||
"log",
|
"log",
|
||||||
"objc",
|
"objc",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
@@ -3720,6 +3721,7 @@ dependencies = [
|
|||||||
"httparse",
|
"httparse",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libc",
|
"libc",
|
||||||
|
"libloading 0.8.4",
|
||||||
"log",
|
"log",
|
||||||
"mac_address",
|
"mac_address",
|
||||||
"machine-uid",
|
"machine-uid",
|
||||||
@@ -3755,6 +3757,7 @@ dependencies = [
|
|||||||
"webrtc",
|
"webrtc",
|
||||||
"whoami",
|
"whoami",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
|
"x11 2.21.0",
|
||||||
"zstd 0.13.1",
|
"zstd 0.13.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4546,11 +4549,8 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "libxdo-sys"
|
name = "libxdo-sys"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"hbb_common",
|
||||||
"x11 2.21.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -7181,9 +7181,9 @@ dependencies = [
|
|||||||
"kcp-sys",
|
"kcp-sys",
|
||||||
"keepawake",
|
"keepawake",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libloading 0.8.4",
|
|
||||||
"libpulse-binding",
|
"libpulse-binding",
|
||||||
"libpulse-simple-binding",
|
"libpulse-simple-binding",
|
||||||
|
"libxdo-sys",
|
||||||
"mac_address",
|
"mac_address",
|
||||||
"magnum-opus",
|
"magnum-opus",
|
||||||
"nix 0.29.0",
|
"nix 0.29.0",
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ crossbeam-queue = "0.3"
|
|||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
cidr-utils = "0.5"
|
cidr-utils = "0.5"
|
||||||
libloading = "0.8"
|
|
||||||
fon = "0.6"
|
fon = "0.6"
|
||||||
zip = "0.6"
|
zip = "0.6"
|
||||||
shutdown_hooks = "0.1"
|
shutdown_hooks = "0.1"
|
||||||
@@ -177,6 +176,7 @@ bytemuck = "1.23"
|
|||||||
ttf-parser = "0.25"
|
ttf-parser = "0.25"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
libxdo-sys = "0.11"
|
||||||
psimple = { package = "libpulse-simple-binding", version = "2.27" }
|
psimple = { package = "libpulse-simple-binding", version = "2.27" }
|
||||||
pulse = { package = "libpulse-binding", version = "2.27" }
|
pulse = { package = "libpulse-binding", version = "2.27" }
|
||||||
rust-pulsectl = { git = "https://github.com/rustdesk-org/pulsectl" }
|
rust-pulsectl = { git = "https://github.com/rustdesk-org/pulsectl" }
|
||||||
@@ -207,6 +207,11 @@ android-wakelock = { git = "https://github.com/rustdesk-org/android-wakelock" }
|
|||||||
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/portable", "libs/remote_printer"]
|
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/portable", "libs/remote_printer"]
|
||||||
exclude = ["vdi/host", "examples/custom_plugin"]
|
exclude = ["vdi/host", "examples/custom_plugin"]
|
||||||
|
|
||||||
|
# Patch libxdo-sys to use a stub implementation that doesn't require libxdo
|
||||||
|
# This allows building and running on systems without libxdo installed (e.g., Wayland-only)
|
||||||
|
[patch.crates-io]
|
||||||
|
libxdo-sys = { path = "libs/libxdo-sys-stub" }
|
||||||
|
|
||||||
[package.metadata.winres]
|
[package.metadata.winres]
|
||||||
LegalCopyright = "Copyright © 2025 Purslane Ltd. All rights reserved."
|
LegalCopyright = "Copyright © 2025 Purslane Ltd. All rights reserved."
|
||||||
ProductName = "RustDesk"
|
ProductName = "RustDesk"
|
||||||
|
|||||||
@@ -37,5 +37,8 @@ core-graphics = "0.22"
|
|||||||
objc = "0.2"
|
objc = "0.2"
|
||||||
unicode-segmentation = "1.10"
|
unicode-segmentation = "1.10"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
libxdo-sys = "0.11"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
pkg-config = "0.3"
|
pkg-config = "0.3"
|
||||||
|
|||||||
@@ -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 crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
|
||||||
|
|
||||||
use hbb_common::libc::{c_char, c_int, c_void, useconds_t};
|
use hbb_common::libc::c_int;
|
||||||
use std::{borrow::Cow, ffi::CString, ptr};
|
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;
|
const DEFAULT_DELAY: u64 = 12000;
|
||||||
type Window = c_int;
|
|
||||||
type Xdo = *const c_void;
|
|
||||||
|
|
||||||
#[link(name = "xdo")]
|
/// Maximum allowed delay value (u32::MAX as u64).
|
||||||
extern "C" {
|
const MAX_DELAY: u64 = u32::MAX as u64;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mousebutton(button: MouseButton) -> c_int {
|
fn mousebutton(button: MouseButton) -> c_int {
|
||||||
match button {
|
match button {
|
||||||
@@ -62,7 +34,7 @@ fn mousebutton(button: MouseButton) -> c_int {
|
|||||||
|
|
||||||
/// The main struct for handling the event emitting
|
/// The main struct for handling the event emitting
|
||||||
pub(super) struct EnigoXdo {
|
pub(super) struct EnigoXdo {
|
||||||
xdo: Xdo,
|
xdo: *mut xdo_t,
|
||||||
delay: u64,
|
delay: u64,
|
||||||
}
|
}
|
||||||
// This is safe, we have a unique pointer.
|
// This is safe, we have a unique pointer.
|
||||||
@@ -70,37 +42,61 @@ pub(super) struct EnigoXdo {
|
|||||||
unsafe impl Send for EnigoXdo {}
|
unsafe impl Send for EnigoXdo {}
|
||||||
|
|
||||||
impl Default 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 {
|
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 {
|
Self {
|
||||||
xdo: unsafe { xdo_new(ptr::null()) },
|
xdo,
|
||||||
delay: DEFAULT_DELAY,
|
delay: DEFAULT_DELAY,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnigoXdo {
|
impl EnigoXdo {
|
||||||
/// Get the delay per keypress.
|
/// Get the delay per keypress in microseconds.
|
||||||
/// Default value is 12000.
|
///
|
||||||
/// This is Linux-specific.
|
/// Default value is 12000 (12ms). This is Linux-specific.
|
||||||
pub fn delay(&self) -> u64 {
|
pub fn delay(&self) -> u64 {
|
||||||
self.delay
|
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) {
|
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 {
|
impl Drop for EnigoXdo {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.xdo.is_null() {
|
if !self.xdo.is_null() {
|
||||||
return;
|
|
||||||
}
|
|
||||||
unsafe {
|
unsafe {
|
||||||
xdo_free(self.xdo);
|
libxdo_sys::xdo_free(self.xdo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MouseControllable for EnigoXdo {
|
impl MouseControllable for EnigoXdo {
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
@@ -115,42 +111,47 @@ impl MouseControllable for EnigoXdo {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unsafe {
|
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) {
|
fn mouse_move_relative(&mut self, x: i32, y: i32) {
|
||||||
if self.xdo.is_null() {
|
if self.xdo.is_null() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unsafe {
|
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 {
|
fn mouse_down(&mut self, button: MouseButton) -> crate::ResultType {
|
||||||
if self.xdo.is_null() {
|
if self.xdo.is_null() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
xdo_mouse_down(self.xdo, CURRENT_WINDOW, mousebutton(button));
|
libxdo_sys::xdo_mouse_down(self.xdo as *const _, CURRENTWINDOW, mousebutton(button));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_up(&mut self, button: MouseButton) {
|
fn mouse_up(&mut self, button: MouseButton) {
|
||||||
if self.xdo.is_null() {
|
if self.xdo.is_null() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unsafe {
|
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) {
|
fn mouse_click(&mut self, button: MouseButton) {
|
||||||
if self.xdo.is_null() {
|
if self.xdo.is_null() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unsafe {
|
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) {
|
fn mouse_scroll_x(&mut self, length: i32) {
|
||||||
let button;
|
let button;
|
||||||
let mut length = length;
|
let mut length = length;
|
||||||
@@ -169,6 +170,7 @@ impl MouseControllable for EnigoXdo {
|
|||||||
self.mouse_click(button);
|
self.mouse_click(button);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_scroll_y(&mut self, length: i32) {
|
fn mouse_scroll_y(&mut self, length: i32) {
|
||||||
let button;
|
let button;
|
||||||
let mut length = length;
|
let mut length = length;
|
||||||
@@ -188,6 +190,7 @@ impl MouseControllable for EnigoXdo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keysequence<'a>(key: Key) -> Cow<'a, str> {
|
fn keysequence<'a>(key: Key) -> Cow<'a, str> {
|
||||||
if let Key::Layout(c) = key {
|
if let Key::Layout(c) = key {
|
||||||
return Cow::Owned(format!("U{:X}", c as u32));
|
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 {
|
impl KeyboardControllable for EnigoXdo {
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
@@ -314,7 +318,7 @@ impl KeyboardControllable for EnigoXdo {
|
|||||||
let mod_alt = 1 << 3;
|
let mod_alt = 1 << 3;
|
||||||
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 { libxdo_sys::xdo_get_input_state(self.xdo as *const _) };
|
||||||
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,
|
||||||
@@ -332,56 +336,59 @@ impl KeyboardControllable for EnigoXdo {
|
|||||||
}
|
}
|
||||||
if let Ok(string) = CString::new(sequence) {
|
if let Ok(string) = CString::new(sequence) {
|
||||||
unsafe {
|
unsafe {
|
||||||
xdo_enter_text_window(
|
libxdo_sys::xdo_enter_text_window(
|
||||||
self.xdo,
|
self.xdo as *const _,
|
||||||
CURRENT_WINDOW,
|
CURRENTWINDOW,
|
||||||
string.as_ptr(),
|
string.as_ptr(),
|
||||||
self.delay as useconds_t,
|
self.delay as libxdo_sys::useconds_t,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_down(&mut self, key: Key) -> crate::ResultType {
|
fn key_down(&mut self, key: Key) -> crate::ResultType {
|
||||||
if self.xdo.is_null() {
|
if self.xdo.is_null() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let string = CString::new(&*keysequence(key))?;
|
let string = CString::new(&*keysequence(key))?;
|
||||||
unsafe {
|
unsafe {
|
||||||
xdo_send_keysequence_window_down(
|
libxdo_sys::xdo_send_keysequence_window_down(
|
||||||
self.xdo,
|
self.xdo as *const _,
|
||||||
CURRENT_WINDOW,
|
CURRENTWINDOW,
|
||||||
string.as_ptr(),
|
string.as_ptr(),
|
||||||
self.delay as useconds_t,
|
self.delay as libxdo_sys::useconds_t,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_up(&mut self, key: Key) {
|
fn key_up(&mut self, key: Key) {
|
||||||
if self.xdo.is_null() {
|
if self.xdo.is_null() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Ok(string) = CString::new(&*keysequence(key)) {
|
if let Ok(string) = CString::new(&*keysequence(key)) {
|
||||||
unsafe {
|
unsafe {
|
||||||
xdo_send_keysequence_window_up(
|
libxdo_sys::xdo_send_keysequence_window_up(
|
||||||
self.xdo,
|
self.xdo as *const _,
|
||||||
CURRENT_WINDOW,
|
CURRENTWINDOW,
|
||||||
string.as_ptr(),
|
string.as_ptr(),
|
||||||
self.delay as useconds_t,
|
self.delay as libxdo_sys::useconds_t,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_click(&mut self, key: Key) {
|
fn key_click(&mut self, key: Key) {
|
||||||
if self.xdo.is_null() {
|
if self.xdo.is_null() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Ok(string) = CString::new(&*keysequence(key)) {
|
if let Ok(string) = CString::new(&*keysequence(key)) {
|
||||||
unsafe {
|
unsafe {
|
||||||
xdo_send_keysequence_window(
|
libxdo_sys::xdo_send_keysequence_window(
|
||||||
self.xdo,
|
self.xdo as *const _,
|
||||||
CURRENT_WINDOW,
|
CURRENTWINDOW,
|
||||||
string.as_ptr(),
|
string.as_ptr(),
|
||||||
self.delay as useconds_t,
|
self.delay as libxdo_sys::useconds_t,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Submodule libs/hbb_common updated: 7d93d5af48...900077a2c2
9
libs/libxdo-sys-stub/Cargo.toml
Normal file
9
libs/libxdo-sys-stub/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "libxdo-sys"
|
||||||
|
version = "0.11.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
description = "Dynamic loading wrapper for libxdo-sys that doesn't require libxdo at compile/link time"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
hbb_common = { path = "../hbb_common" }
|
||||||
505
libs/libxdo-sys-stub/src/lib.rs
Normal file
505
libs/libxdo-sys-stub/src/lib.rs
Normal file
@@ -0,0 +1,505 @@
|
|||||||
|
//! Dynamic loading wrapper for libxdo.
|
||||||
|
//!
|
||||||
|
//! Provides the same API as libxdo-sys but loads libxdo at runtime,
|
||||||
|
//! allowing the program to run on systems without libxdo installed
|
||||||
|
//! (e.g., Wayland-only environments).
|
||||||
|
|
||||||
|
use hbb_common::{
|
||||||
|
libc::{c_char, c_int, c_uint},
|
||||||
|
libloading::{Library, Symbol},
|
||||||
|
log,
|
||||||
|
};
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
pub use hbb_common::x11::xlib::{Display, Screen, Window};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct xdo_t {
|
||||||
|
_private: [u8; 0],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct charcodemap_t {
|
||||||
|
_private: [u8; 0],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct xdo_search_t {
|
||||||
|
_private: [u8; 0],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type useconds_t = c_uint;
|
||||||
|
|
||||||
|
pub const CURRENTWINDOW: Window = 0;
|
||||||
|
|
||||||
|
type FnXdoNew = unsafe extern "C" fn(*const c_char) -> *mut xdo_t;
|
||||||
|
type FnXdoNewWithOpenedDisplay =
|
||||||
|
unsafe extern "C" fn(*mut Display, *const c_char, c_int) -> *mut xdo_t;
|
||||||
|
type FnXdoFree = unsafe extern "C" fn(*mut xdo_t);
|
||||||
|
type FnXdoSendKeysequenceWindow =
|
||||||
|
unsafe extern "C" fn(*const xdo_t, Window, *const c_char, useconds_t) -> c_int;
|
||||||
|
type FnXdoSendKeysequenceWindowDown =
|
||||||
|
unsafe extern "C" fn(*const xdo_t, Window, *const c_char, useconds_t) -> c_int;
|
||||||
|
type FnXdoSendKeysequenceWindowUp =
|
||||||
|
unsafe extern "C" fn(*const xdo_t, Window, *const c_char, useconds_t) -> c_int;
|
||||||
|
type FnXdoEnterTextWindow =
|
||||||
|
unsafe extern "C" fn(*const xdo_t, Window, *const c_char, useconds_t) -> c_int;
|
||||||
|
type FnXdoClickWindow = unsafe extern "C" fn(*const xdo_t, Window, c_int) -> c_int;
|
||||||
|
type FnXdoMouseDown = unsafe extern "C" fn(*const xdo_t, Window, c_int) -> c_int;
|
||||||
|
type FnXdoMouseUp = unsafe extern "C" fn(*const xdo_t, Window, c_int) -> c_int;
|
||||||
|
type FnXdoMoveMouse = unsafe extern "C" fn(*const xdo_t, c_int, c_int, c_int) -> c_int;
|
||||||
|
type FnXdoMoveMouseRelative = unsafe extern "C" fn(*const xdo_t, c_int, c_int) -> c_int;
|
||||||
|
type FnXdoMoveMouseRelativeToWindow =
|
||||||
|
unsafe extern "C" fn(*const xdo_t, Window, c_int, c_int) -> c_int;
|
||||||
|
type FnXdoGetMouseLocation =
|
||||||
|
unsafe extern "C" fn(*const xdo_t, *mut c_int, *mut c_int, *mut c_int) -> c_int;
|
||||||
|
type FnXdoGetMouseLocation2 =
|
||||||
|
unsafe extern "C" fn(*const xdo_t, *mut c_int, *mut c_int, *mut c_int, *mut Window) -> c_int;
|
||||||
|
type FnXdoGetActiveWindow = unsafe extern "C" fn(*const xdo_t, *mut Window) -> c_int;
|
||||||
|
type FnXdoGetFocusedWindow = unsafe extern "C" fn(*const xdo_t, *mut Window) -> c_int;
|
||||||
|
type FnXdoGetFocusedWindowSane = unsafe extern "C" fn(*const xdo_t, *mut Window) -> c_int;
|
||||||
|
type FnXdoGetWindowLocation =
|
||||||
|
unsafe extern "C" fn(*const xdo_t, Window, *mut c_int, *mut c_int, *mut *mut Screen) -> c_int;
|
||||||
|
type FnXdoGetWindowSize =
|
||||||
|
unsafe extern "C" fn(*const xdo_t, Window, *mut c_uint, *mut c_uint) -> c_int;
|
||||||
|
type FnXdoGetInputState = unsafe extern "C" fn(*const xdo_t) -> c_uint;
|
||||||
|
type FnXdoActivateWindow = unsafe extern "C" fn(*const xdo_t, Window) -> c_int;
|
||||||
|
type FnXdoWaitForMouseMoveFrom = unsafe extern "C" fn(*const xdo_t, c_int, c_int) -> c_int;
|
||||||
|
type FnXdoWaitForMouseMoveTo = unsafe extern "C" fn(*const xdo_t, c_int, c_int) -> c_int;
|
||||||
|
type FnXdoSetWindowClass =
|
||||||
|
unsafe extern "C" fn(*const xdo_t, Window, *const c_char, *const c_char) -> c_int;
|
||||||
|
type FnXdoSearchWindows =
|
||||||
|
unsafe extern "C" fn(*const xdo_t, *const xdo_search_t, *mut *mut Window, *mut c_uint) -> c_int;
|
||||||
|
|
||||||
|
struct XdoLib {
|
||||||
|
_lib: Library,
|
||||||
|
xdo_new: FnXdoNew,
|
||||||
|
xdo_new_with_opened_display: Option<FnXdoNewWithOpenedDisplay>,
|
||||||
|
xdo_free: FnXdoFree,
|
||||||
|
xdo_send_keysequence_window: FnXdoSendKeysequenceWindow,
|
||||||
|
xdo_send_keysequence_window_down: Option<FnXdoSendKeysequenceWindowDown>,
|
||||||
|
xdo_send_keysequence_window_up: Option<FnXdoSendKeysequenceWindowUp>,
|
||||||
|
xdo_enter_text_window: Option<FnXdoEnterTextWindow>,
|
||||||
|
xdo_click_window: Option<FnXdoClickWindow>,
|
||||||
|
xdo_mouse_down: Option<FnXdoMouseDown>,
|
||||||
|
xdo_mouse_up: Option<FnXdoMouseUp>,
|
||||||
|
xdo_move_mouse: Option<FnXdoMoveMouse>,
|
||||||
|
xdo_move_mouse_relative: Option<FnXdoMoveMouseRelative>,
|
||||||
|
xdo_move_mouse_relative_to_window: Option<FnXdoMoveMouseRelativeToWindow>,
|
||||||
|
xdo_get_mouse_location: Option<FnXdoGetMouseLocation>,
|
||||||
|
xdo_get_mouse_location2: Option<FnXdoGetMouseLocation2>,
|
||||||
|
xdo_get_active_window: Option<FnXdoGetActiveWindow>,
|
||||||
|
xdo_get_focused_window: Option<FnXdoGetFocusedWindow>,
|
||||||
|
xdo_get_focused_window_sane: Option<FnXdoGetFocusedWindowSane>,
|
||||||
|
xdo_get_window_location: Option<FnXdoGetWindowLocation>,
|
||||||
|
xdo_get_window_size: Option<FnXdoGetWindowSize>,
|
||||||
|
xdo_get_input_state: Option<FnXdoGetInputState>,
|
||||||
|
xdo_activate_window: Option<FnXdoActivateWindow>,
|
||||||
|
xdo_wait_for_mouse_move_from: Option<FnXdoWaitForMouseMoveFrom>,
|
||||||
|
xdo_wait_for_mouse_move_to: Option<FnXdoWaitForMouseMoveTo>,
|
||||||
|
xdo_set_window_class: Option<FnXdoSetWindowClass>,
|
||||||
|
xdo_search_windows: Option<FnXdoSearchWindows>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl XdoLib {
|
||||||
|
fn load() -> Option<Self> {
|
||||||
|
// https://github.com/rustdesk/rustdesk/issues/13711
|
||||||
|
const LIB_NAMES: [&str; 3] = ["libxdo.so.4", "libxdo.so.3", "libxdo.so"];
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let (lib, lib_name) = LIB_NAMES
|
||||||
|
.iter()
|
||||||
|
.find_map(|name| Library::new(name).ok().map(|lib| (lib, *name)))?;
|
||||||
|
|
||||||
|
log::info!("libxdo-sys Loaded {}", lib_name);
|
||||||
|
|
||||||
|
let xdo_new: FnXdoNew = *lib.get(b"xdo_new").ok()?;
|
||||||
|
let xdo_free: FnXdoFree = *lib.get(b"xdo_free").ok()?;
|
||||||
|
let xdo_send_keysequence_window: FnXdoSendKeysequenceWindow =
|
||||||
|
*lib.get(b"xdo_send_keysequence_window").ok()?;
|
||||||
|
|
||||||
|
let xdo_new_with_opened_display = lib
|
||||||
|
.get(b"xdo_new_with_opened_display")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoNewWithOpenedDisplay>| *s);
|
||||||
|
let xdo_send_keysequence_window_down = lib
|
||||||
|
.get(b"xdo_send_keysequence_window_down")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoSendKeysequenceWindowDown>| *s);
|
||||||
|
let xdo_send_keysequence_window_up = lib
|
||||||
|
.get(b"xdo_send_keysequence_window_up")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoSendKeysequenceWindowUp>| *s);
|
||||||
|
let xdo_enter_text_window = lib
|
||||||
|
.get(b"xdo_enter_text_window")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoEnterTextWindow>| *s);
|
||||||
|
let xdo_click_window = lib
|
||||||
|
.get(b"xdo_click_window")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoClickWindow>| *s);
|
||||||
|
let xdo_mouse_down = lib
|
||||||
|
.get(b"xdo_mouse_down")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoMouseDown>| *s);
|
||||||
|
let xdo_mouse_up = lib
|
||||||
|
.get(b"xdo_mouse_up")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoMouseUp>| *s);
|
||||||
|
let xdo_move_mouse = lib
|
||||||
|
.get(b"xdo_move_mouse")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoMoveMouse>| *s);
|
||||||
|
let xdo_move_mouse_relative = lib
|
||||||
|
.get(b"xdo_move_mouse_relative")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoMoveMouseRelative>| *s);
|
||||||
|
let xdo_move_mouse_relative_to_window = lib
|
||||||
|
.get(b"xdo_move_mouse_relative_to_window")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoMoveMouseRelativeToWindow>| *s);
|
||||||
|
let xdo_get_mouse_location = lib
|
||||||
|
.get(b"xdo_get_mouse_location")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoGetMouseLocation>| *s);
|
||||||
|
let xdo_get_mouse_location2 = lib
|
||||||
|
.get(b"xdo_get_mouse_location2")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoGetMouseLocation2>| *s);
|
||||||
|
let xdo_get_active_window = lib
|
||||||
|
.get(b"xdo_get_active_window")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoGetActiveWindow>| *s);
|
||||||
|
let xdo_get_focused_window = lib
|
||||||
|
.get(b"xdo_get_focused_window")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoGetFocusedWindow>| *s);
|
||||||
|
let xdo_get_focused_window_sane = lib
|
||||||
|
.get(b"xdo_get_focused_window_sane")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoGetFocusedWindowSane>| *s);
|
||||||
|
let xdo_get_window_location = lib
|
||||||
|
.get(b"xdo_get_window_location")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoGetWindowLocation>| *s);
|
||||||
|
let xdo_get_window_size = lib
|
||||||
|
.get(b"xdo_get_window_size")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoGetWindowSize>| *s);
|
||||||
|
let xdo_get_input_state = lib
|
||||||
|
.get(b"xdo_get_input_state")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoGetInputState>| *s);
|
||||||
|
let xdo_activate_window = lib
|
||||||
|
.get(b"xdo_activate_window")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoActivateWindow>| *s);
|
||||||
|
let xdo_wait_for_mouse_move_from = lib
|
||||||
|
.get(b"xdo_wait_for_mouse_move_from")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoWaitForMouseMoveFrom>| *s);
|
||||||
|
let xdo_wait_for_mouse_move_to = lib
|
||||||
|
.get(b"xdo_wait_for_mouse_move_to")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoWaitForMouseMoveTo>| *s);
|
||||||
|
let xdo_set_window_class = lib
|
||||||
|
.get(b"xdo_set_window_class")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoSetWindowClass>| *s);
|
||||||
|
let xdo_search_windows = lib
|
||||||
|
.get(b"xdo_search_windows")
|
||||||
|
.ok()
|
||||||
|
.map(|s: Symbol<FnXdoSearchWindows>| *s);
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
_lib: lib,
|
||||||
|
xdo_new,
|
||||||
|
xdo_new_with_opened_display,
|
||||||
|
xdo_free,
|
||||||
|
xdo_send_keysequence_window,
|
||||||
|
xdo_send_keysequence_window_down,
|
||||||
|
xdo_send_keysequence_window_up,
|
||||||
|
xdo_enter_text_window,
|
||||||
|
xdo_click_window,
|
||||||
|
xdo_mouse_down,
|
||||||
|
xdo_mouse_up,
|
||||||
|
xdo_move_mouse,
|
||||||
|
xdo_move_mouse_relative,
|
||||||
|
xdo_move_mouse_relative_to_window,
|
||||||
|
xdo_get_mouse_location,
|
||||||
|
xdo_get_mouse_location2,
|
||||||
|
xdo_get_active_window,
|
||||||
|
xdo_get_focused_window,
|
||||||
|
xdo_get_focused_window_sane,
|
||||||
|
xdo_get_window_location,
|
||||||
|
xdo_get_window_size,
|
||||||
|
xdo_get_input_state,
|
||||||
|
xdo_activate_window,
|
||||||
|
xdo_wait_for_mouse_move_from,
|
||||||
|
xdo_wait_for_mouse_move_to,
|
||||||
|
xdo_set_window_class,
|
||||||
|
xdo_search_windows,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static XDO_LIB: OnceLock<Option<XdoLib>> = OnceLock::new();
|
||||||
|
|
||||||
|
fn get_lib() -> Option<&'static XdoLib> {
|
||||||
|
XDO_LIB
|
||||||
|
.get_or_init(|| {
|
||||||
|
let lib = XdoLib::load();
|
||||||
|
if lib.is_none() {
|
||||||
|
log::info!("libxdo-sys libxdo not found, xdo functions will be disabled");
|
||||||
|
}
|
||||||
|
lib
|
||||||
|
})
|
||||||
|
.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_new(display: *const c_char) -> *mut xdo_t {
|
||||||
|
get_lib().map_or(std::ptr::null_mut(), |lib| (lib.xdo_new)(display))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_new_with_opened_display(
|
||||||
|
xdpy: *mut Display,
|
||||||
|
display: *const c_char,
|
||||||
|
close_display_when_freed: c_int,
|
||||||
|
) -> *mut xdo_t {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_new_with_opened_display)
|
||||||
|
.map_or(std::ptr::null_mut(), |f| {
|
||||||
|
f(xdpy, display, close_display_when_freed)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_free(xdo: *mut xdo_t) {
|
||||||
|
if xdo.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(lib) = get_lib() {
|
||||||
|
(lib.xdo_free)(xdo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_send_keysequence_window(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
window: Window,
|
||||||
|
keysequence: *const c_char,
|
||||||
|
delay: useconds_t,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib().map_or(1, |lib| {
|
||||||
|
(lib.xdo_send_keysequence_window)(xdo, window, keysequence, delay)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_send_keysequence_window_down(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
window: Window,
|
||||||
|
keysequence: *const c_char,
|
||||||
|
delay: useconds_t,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_send_keysequence_window_down)
|
||||||
|
.map_or(1, |f| f(xdo, window, keysequence, delay))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_send_keysequence_window_up(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
window: Window,
|
||||||
|
keysequence: *const c_char,
|
||||||
|
delay: useconds_t,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_send_keysequence_window_up)
|
||||||
|
.map_or(1, |f| f(xdo, window, keysequence, delay))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_enter_text_window(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
window: Window,
|
||||||
|
string: *const c_char,
|
||||||
|
delay: useconds_t,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_enter_text_window)
|
||||||
|
.map_or(1, |f| f(xdo, window, string, delay))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_click_window(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
window: Window,
|
||||||
|
button: c_int,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_click_window)
|
||||||
|
.map_or(1, |f| f(xdo, window, button))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_mouse_down(xdo: *const xdo_t, window: Window, button: c_int) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_mouse_down)
|
||||||
|
.map_or(1, |f| f(xdo, window, button))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_mouse_up(xdo: *const xdo_t, window: Window, button: c_int) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_mouse_up)
|
||||||
|
.map_or(1, |f| f(xdo, window, button))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_move_mouse(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
x: c_int,
|
||||||
|
y: c_int,
|
||||||
|
screen: c_int,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_move_mouse)
|
||||||
|
.map_or(1, |f| f(xdo, x, y, screen))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_move_mouse_relative(xdo: *const xdo_t, x: c_int, y: c_int) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_move_mouse_relative)
|
||||||
|
.map_or(1, |f| f(xdo, x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_move_mouse_relative_to_window(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
window: Window,
|
||||||
|
x: c_int,
|
||||||
|
y: c_int,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_move_mouse_relative_to_window)
|
||||||
|
.map_or(1, |f| f(xdo, window, x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_get_mouse_location(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
x: *mut c_int,
|
||||||
|
y: *mut c_int,
|
||||||
|
screen_num: *mut c_int,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_get_mouse_location)
|
||||||
|
.map_or(1, |f| f(xdo, x, y, screen_num))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_get_mouse_location2(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
x: *mut c_int,
|
||||||
|
y: *mut c_int,
|
||||||
|
screen_num: *mut c_int,
|
||||||
|
window: *mut Window,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_get_mouse_location2)
|
||||||
|
.map_or(1, |f| f(xdo, x, y, screen_num, window))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_get_active_window(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
window_ret: *mut Window,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_get_active_window)
|
||||||
|
.map_or(1, |f| f(xdo, window_ret))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_get_focused_window(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
window_ret: *mut Window,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_get_focused_window)
|
||||||
|
.map_or(1, |f| f(xdo, window_ret))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_get_focused_window_sane(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
window_ret: *mut Window,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_get_focused_window_sane)
|
||||||
|
.map_or(1, |f| f(xdo, window_ret))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_get_window_location(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
window: Window,
|
||||||
|
x: *mut c_int,
|
||||||
|
y: *mut c_int,
|
||||||
|
screen_ret: *mut *mut Screen,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_get_window_location)
|
||||||
|
.map_or(1, |f| f(xdo, window, x, y, screen_ret))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_get_window_size(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
window: Window,
|
||||||
|
width: *mut c_uint,
|
||||||
|
height: *mut c_uint,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_get_window_size)
|
||||||
|
.map_or(1, |f| f(xdo, window, width, height))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_get_input_state(xdo: *const xdo_t) -> c_uint {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_get_input_state)
|
||||||
|
.map_or(0, |f| f(xdo))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_activate_window(xdo: *const xdo_t, wid: Window) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_activate_window)
|
||||||
|
.map_or(1, |f| f(xdo, wid))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_wait_for_mouse_move_from(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
origin_x: c_int,
|
||||||
|
origin_y: c_int,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_wait_for_mouse_move_from)
|
||||||
|
.map_or(1, |f| f(xdo, origin_x, origin_y))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_wait_for_mouse_move_to(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
dest_x: c_int,
|
||||||
|
dest_y: c_int,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_wait_for_mouse_move_to)
|
||||||
|
.map_or(1, |f| f(xdo, dest_x, dest_y))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_set_window_class(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
wid: Window,
|
||||||
|
name: *const c_char,
|
||||||
|
class: *const c_char,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_set_window_class)
|
||||||
|
.map_or(1, |f| f(xdo, wid, name, class))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn xdo_search_windows(
|
||||||
|
xdo: *const xdo_t,
|
||||||
|
search: *const xdo_search_t,
|
||||||
|
windowlist_ret: *mut *mut Window,
|
||||||
|
nwindows_ret: *mut c_uint,
|
||||||
|
) -> c_int {
|
||||||
|
get_lib()
|
||||||
|
.and_then(|lib| lib.xdo_search_windows)
|
||||||
|
.map_or(1, |f| f(xdo, search, windowlist_ret, nwindows_ret))
|
||||||
|
}
|
||||||
@@ -5,8 +5,8 @@ Summary: RPM package
|
|||||||
License: GPL-3.0
|
License: GPL-3.0
|
||||||
URL: https://rustdesk.com
|
URL: https://rustdesk.com
|
||||||
Vendor: rustdesk <info@rustdesk.com>
|
Vendor: rustdesk <info@rustdesk.com>
|
||||||
Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils libXtst6 libva2 pam gstreamer-plugins-base gstreamer-plugin-pipewire
|
Requires: gtk3 libxcb1 libXfixes3 alsa-utils libXtst6 libva2 pam gstreamer-plugins-base gstreamer-plugin-pipewire
|
||||||
Recommends: libayatana-appindicator3-1
|
Recommends: libayatana-appindicator3-1 xdotool
|
||||||
Provides: libdesktop_drop_plugin.so()(64bit), libdesktop_multi_window_plugin.so()(64bit), libfile_selector_linux_plugin.so()(64bit), libflutter_custom_cursor_plugin.so()(64bit), libflutter_linux_gtk.so()(64bit), libscreen_retriever_plugin.so()(64bit), libtray_manager_plugin.so()(64bit), liburl_launcher_linux_plugin.so()(64bit), libwindow_manager_plugin.so()(64bit), libwindow_size_plugin.so()(64bit), libtexture_rgba_renderer_plugin.so()(64bit)
|
Provides: libdesktop_drop_plugin.so()(64bit), libdesktop_multi_window_plugin.so()(64bit), libfile_selector_linux_plugin.so()(64bit), libflutter_custom_cursor_plugin.so()(64bit), libflutter_linux_gtk.so()(64bit), libscreen_retriever_plugin.so()(64bit), libtray_manager_plugin.so()(64bit), liburl_launcher_linux_plugin.so()(64bit), libwindow_manager_plugin.so()(64bit), libwindow_size_plugin.so()(64bit), libtexture_rgba_renderer_plugin.so()(64bit)
|
||||||
|
|
||||||
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/
|
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ Summary: RPM package
|
|||||||
License: GPL-3.0
|
License: GPL-3.0
|
||||||
URL: https://rustdesk.com
|
URL: https://rustdesk.com
|
||||||
Vendor: rustdesk <info@rustdesk.com>
|
Vendor: rustdesk <info@rustdesk.com>
|
||||||
Requires: gtk3 libxcb libxdo libXfixes alsa-lib libva pam gstreamer1-plugins-base
|
Requires: gtk3 libxcb libXfixes alsa-lib libva pam gstreamer1-plugins-base
|
||||||
Recommends: libayatana-appindicator-gtk3
|
Recommends: libayatana-appindicator-gtk3 libxdo
|
||||||
Provides: libdesktop_drop_plugin.so()(64bit), libdesktop_multi_window_plugin.so()(64bit), libfile_selector_linux_plugin.so()(64bit), libflutter_custom_cursor_plugin.so()(64bit), libflutter_linux_gtk.so()(64bit), libscreen_retriever_plugin.so()(64bit), libtray_manager_plugin.so()(64bit), liburl_launcher_linux_plugin.so()(64bit), libwindow_manager_plugin.so()(64bit), libwindow_size_plugin.so()(64bit), libtexture_rgba_renderer_plugin.so()(64bit)
|
Provides: libdesktop_drop_plugin.so()(64bit), libdesktop_multi_window_plugin.so()(64bit), libfile_selector_linux_plugin.so()(64bit), libflutter_custom_cursor_plugin.so()(64bit), libflutter_linux_gtk.so()(64bit), libscreen_retriever_plugin.so()(64bit), libtray_manager_plugin.so()(64bit), liburl_launcher_linux_plugin.so()(64bit), libwindow_manager_plugin.so()(64bit), libwindow_size_plugin.so()(64bit), libtexture_rgba_renderer_plugin.so()(64bit)
|
||||||
|
|
||||||
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/
|
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ Version: 1.1.9
|
|||||||
Release: 0
|
Release: 0
|
||||||
Summary: RPM package
|
Summary: RPM package
|
||||||
License: GPL-3.0
|
License: GPL-3.0
|
||||||
Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils libXtst6 libva2 pam gstreamer-plugins-base gstreamer-plugin-pipewire
|
Requires: gtk3 libxcb1 libXfixes3 alsa-utils libXtst6 libva2 pam gstreamer-plugins-base gstreamer-plugin-pipewire
|
||||||
Recommends: libayatana-appindicator3-1
|
Recommends: libayatana-appindicator3-1 xdotool
|
||||||
|
|
||||||
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/
|
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ Summary: RPM package
|
|||||||
License: GPL-3.0
|
License: GPL-3.0
|
||||||
URL: https://rustdesk.com
|
URL: https://rustdesk.com
|
||||||
Vendor: rustdesk <info@rustdesk.com>
|
Vendor: rustdesk <info@rustdesk.com>
|
||||||
Requires: gtk3 libxcb libxdo libXfixes alsa-lib libva2 pam gstreamer1-plugins-base
|
Requires: gtk3 libxcb libXfixes alsa-lib libva2 pam gstreamer1-plugins-base
|
||||||
Recommends: libayatana-appindicator-gtk3
|
Recommends: libayatana-appindicator-gtk3 libxdo
|
||||||
|
|
||||||
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/
|
# https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/
|
||||||
|
|
||||||
|
|||||||
@@ -6,29 +6,26 @@ use hbb_common::{
|
|||||||
anyhow::anyhow,
|
anyhow::anyhow,
|
||||||
bail,
|
bail,
|
||||||
config::{keys::OPTION_ALLOW_LINUX_HEADLESS, Config},
|
config::{keys::OPTION_ALLOW_LINUX_HEADLESS, Config},
|
||||||
libc::{c_char, c_int, c_long, c_void},
|
libc::{c_char, c_int, c_long, c_uint, c_void},
|
||||||
log,
|
log,
|
||||||
message_proto::{DisplayInfo, Resolution},
|
message_proto::{DisplayInfo, Resolution},
|
||||||
regex::{Captures, Regex},
|
regex::{Captures, Regex},
|
||||||
users::{get_user_by_name, os::unix::UserExt},
|
users::{get_user_by_name, os::unix::UserExt},
|
||||||
};
|
};
|
||||||
|
use libxdo_sys::{self, xdo_t, Window};
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
ffi::{OsStr, OsString},
|
ffi::{OsStr, OsString},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::{Child, Command},
|
process::{Child, Command},
|
||||||
string::String,
|
string::String,
|
||||||
sync::{
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
atomic::{AtomicBool, Ordering},
|
sync::Arc,
|
||||||
Arc,
|
|
||||||
},
|
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use terminfo::{capability as cap, Database};
|
use terminfo::{capability as cap, Database};
|
||||||
use wallpaper;
|
use wallpaper;
|
||||||
|
|
||||||
type Xdo = *const c_void;
|
|
||||||
|
|
||||||
pub const PA_SAMPLE_RATE: u32 = 48000;
|
pub const PA_SAMPLE_RATE: u32 = 48000;
|
||||||
static mut UNMODIFIED: bool = true;
|
static mut UNMODIFIED: bool = true;
|
||||||
|
|
||||||
@@ -86,35 +83,20 @@ lazy_static::lazy_static! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static XDO: RefCell<Xdo> = RefCell::new(unsafe { xdo_new(std::ptr::null()) });
|
// XDO context - created via libxdo-sys (which uses dynamic loading stub).
|
||||||
|
// If libxdo is not available, xdo will be null and xdo-based functions become no-ops.
|
||||||
|
static XDO: RefCell<*mut xdo_t> = RefCell::new({
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
xdo
|
||||||
|
});
|
||||||
static DISPLAY: RefCell<*mut c_void> = RefCell::new(unsafe { XOpenDisplay(std::ptr::null())});
|
static DISPLAY: RefCell<*mut c_void> = RefCell::new(unsafe { XOpenDisplay(std::ptr::null())});
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
fn xdo_get_mouse_location(
|
|
||||||
xdo: Xdo,
|
|
||||||
x: *mut c_int,
|
|
||||||
y: *mut c_int,
|
|
||||||
screen_num: *mut c_int,
|
|
||||||
) -> c_int;
|
|
||||||
fn xdo_move_mouse(xdo: Xdo, x: c_int, y: c_int, screen: c_int) -> c_int;
|
|
||||||
fn xdo_new(display: *const c_char) -> Xdo;
|
|
||||||
fn xdo_get_active_window(xdo: Xdo, window: *mut *mut c_void) -> c_int;
|
|
||||||
fn xdo_get_window_location(
|
|
||||||
xdo: Xdo,
|
|
||||||
window: *mut c_void,
|
|
||||||
x: *mut c_int,
|
|
||||||
y: *mut c_int,
|
|
||||||
screen_num: *mut c_int,
|
|
||||||
) -> c_int;
|
|
||||||
fn xdo_get_window_size(
|
|
||||||
xdo: Xdo,
|
|
||||||
window: *mut c_void,
|
|
||||||
width: *mut c_int,
|
|
||||||
height: *mut c_int,
|
|
||||||
) -> c_int;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[link(name = "X11")]
|
#[link(name = "X11")]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn XOpenDisplay(display_name: *const c_char) -> *mut c_void;
|
fn XOpenDisplay(display_name: *const c_char) -> *mut c_void;
|
||||||
@@ -160,14 +142,19 @@ fn sleep_millis(millis: u64) {
|
|||||||
pub fn get_cursor_pos() -> Option<(i32, i32)> {
|
pub fn get_cursor_pos() -> Option<(i32, i32)> {
|
||||||
let mut res = None;
|
let mut res = None;
|
||||||
XDO.with(|xdo| {
|
XDO.with(|xdo| {
|
||||||
if let Ok(xdo) = xdo.try_borrow_mut() {
|
if let Ok(xdo) = xdo.try_borrow() {
|
||||||
if xdo.is_null() {
|
if xdo.is_null() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut x: c_int = 0;
|
let mut x: c_int = 0;
|
||||||
let mut y: c_int = 0;
|
let mut y: c_int = 0;
|
||||||
unsafe {
|
unsafe {
|
||||||
xdo_get_mouse_location(*xdo, &mut x as _, &mut y as _, std::ptr::null_mut());
|
libxdo_sys::xdo_get_mouse_location(
|
||||||
|
*xdo as *const _,
|
||||||
|
&mut x as _,
|
||||||
|
&mut y as _,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
res = Some((x, y));
|
res = Some((x, y));
|
||||||
}
|
}
|
||||||
@@ -178,14 +165,14 @@ pub fn get_cursor_pos() -> Option<(i32, i32)> {
|
|||||||
pub fn set_cursor_pos(x: i32, y: i32) -> bool {
|
pub fn set_cursor_pos(x: i32, y: i32) -> bool {
|
||||||
let mut res = false;
|
let mut res = false;
|
||||||
XDO.with(|xdo| {
|
XDO.with(|xdo| {
|
||||||
match xdo.try_borrow_mut() {
|
match xdo.try_borrow() {
|
||||||
Ok(xdo) => {
|
Ok(xdo) => {
|
||||||
if xdo.is_null() {
|
if xdo.is_null() {
|
||||||
log::debug!("set_cursor_pos: xdo is null");
|
log::debug!("set_cursor_pos: xdo is null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
let ret = xdo_move_mouse(*xdo, x, y, 0);
|
let ret = libxdo_sys::xdo_move_mouse(*xdo as *const _, x, y, 0);
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"set_cursor_pos: xdo_move_mouse failed with code {} for coordinates ({}, {})",
|
"set_cursor_pos: xdo_move_mouse failed with code {} for coordinates ({}, {})",
|
||||||
@@ -230,22 +217,22 @@ pub fn reset_input_cache() {}
|
|||||||
pub fn get_focused_display(displays: Vec<DisplayInfo>) -> Option<usize> {
|
pub fn get_focused_display(displays: Vec<DisplayInfo>) -> Option<usize> {
|
||||||
let mut res = None;
|
let mut res = None;
|
||||||
XDO.with(|xdo| {
|
XDO.with(|xdo| {
|
||||||
if let Ok(xdo) = xdo.try_borrow_mut() {
|
if let Ok(xdo) = xdo.try_borrow() {
|
||||||
if xdo.is_null() {
|
if xdo.is_null() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut x: c_int = 0;
|
let mut x: c_int = 0;
|
||||||
let mut y: c_int = 0;
|
let mut y: c_int = 0;
|
||||||
let mut width: c_int = 0;
|
let mut width: c_uint = 0;
|
||||||
let mut height: c_int = 0;
|
let mut height: c_uint = 0;
|
||||||
let mut window: *mut c_void = std::ptr::null_mut();
|
let mut window: Window = 0;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if xdo_get_active_window(*xdo, &mut window) != 0 {
|
if libxdo_sys::xdo_get_active_window(*xdo as *const _, &mut window) != 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if xdo_get_window_location(
|
if libxdo_sys::xdo_get_window_location(
|
||||||
*xdo,
|
*xdo as *const _,
|
||||||
window,
|
window,
|
||||||
&mut x as _,
|
&mut x as _,
|
||||||
&mut y as _,
|
&mut y as _,
|
||||||
@@ -254,11 +241,17 @@ pub fn get_focused_display(displays: Vec<DisplayInfo>) -> Option<usize> {
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if xdo_get_window_size(*xdo, window, &mut width as _, &mut height as _) != 0 {
|
if libxdo_sys::xdo_get_window_size(
|
||||||
|
*xdo as *const _,
|
||||||
|
window,
|
||||||
|
&mut width,
|
||||||
|
&mut height,
|
||||||
|
) != 0
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let center_x = x + width / 2;
|
let center_x = x + (width / 2) as c_int;
|
||||||
let center_y = y + height / 2;
|
let center_y = y + (height / 2) as c_int;
|
||||||
res = displays.iter().position(|d| {
|
res = displays.iter().position(|d| {
|
||||||
center_x >= d.x
|
center_x >= d.x
|
||||||
&& center_x < d.x + d.width
|
&& center_x < d.x + d.width
|
||||||
@@ -497,7 +490,10 @@ fn get_all_term_values(uid: &str) -> Vec<String> {
|
|||||||
let Ok(cmdline) = std::fs::read(&cmdline_path) else {
|
let Ok(cmdline) = std::fs::read(&cmdline_path) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let exe_end = cmdline.iter().position(|&b| b == 0).unwrap_or(cmdline.len());
|
let exe_end = cmdline
|
||||||
|
.iter()
|
||||||
|
.position(|&b| b == 0)
|
||||||
|
.unwrap_or(cmdline.len());
|
||||||
let exe_str = String::from_utf8_lossy(&cmdline[..exe_end]);
|
let exe_str = String::from_utf8_lossy(&cmdline[..exe_end]);
|
||||||
if !re.is_match(&exe_str) {
|
if !re.is_match(&exe_str) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user