fix(install): linux xdo (#14096)

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou
2026-01-21 20:43:15 +08:00
committed by GitHub
parent 21a7cef98a
commit be4bbd018d
12 changed files with 666 additions and 141 deletions

10
Cargo.lock generated
View File

@@ -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",

View File

@@ -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"

View File

@@ -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"

View File

@@ -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,
); );
} }
} }

View 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" }

View 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))
}

View File

@@ -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/

View File

@@ -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/

View File

@@ -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/

View File

@@ -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/

View File

@@ -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;