mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
linux_wayland_support: init merge, windows build
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
@@ -74,7 +74,7 @@ pub use macos::Enigo;
|
||||
#[cfg(target_os = "linux")]
|
||||
mod linux;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use crate::linux::Enigo;
|
||||
pub use crate::linux::{is_x11, Enigo};
|
||||
|
||||
/// DSL parser module
|
||||
pub mod dsl;
|
||||
@@ -249,7 +249,7 @@ pub trait MouseControllable {
|
||||
/// For alphabetical keys, use Key::Layout for a system independent key.
|
||||
/// If a key is missing, you can use the raw keycode with Key::Raw.
|
||||
#[cfg_attr(feature = "with_serde", derive(Serialize, Deserialize))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Key {
|
||||
/// alt key on Linux and Windows (option key on macOS)
|
||||
Alt,
|
||||
|
||||
37
libs/enigo/src/linux/mod.rs
Normal file
37
libs/enigo/src/linux/mod.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
mod nix_impl;
|
||||
mod pynput;
|
||||
mod xdo;
|
||||
|
||||
pub use self::nix_impl::Enigo;
|
||||
|
||||
/// Check if display manager is x11.
|
||||
pub fn is_x11() -> bool {
|
||||
let stdout =
|
||||
match std::process::Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg("loginctl show-session $(loginctl | awk '/tty/ {print $1}') -p Type | awk -F= '{print $2}'")
|
||||
.output() {
|
||||
Ok(output) => {
|
||||
output.stdout
|
||||
},
|
||||
Err(_) => {
|
||||
match std::process::Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg("echo $XDG_SESSION_TYPE")
|
||||
.output() {
|
||||
Ok(output) => {
|
||||
output.stdout
|
||||
},
|
||||
Err(_) => {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Ok(display_manager) = std::str::from_utf8(&stdout) {
|
||||
display_manager.trim() == "x11"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
178
libs/enigo/src/linux/nix_impl.rs
Normal file
178
libs/enigo/src/linux/nix_impl.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
use super::{pynput::EnigoPynput, xdo::EnigoXdo};
|
||||
use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
|
||||
|
||||
/// The main struct for handling the event emitting
|
||||
// #[derive(Default)]
|
||||
pub struct Enigo {
|
||||
xdo: EnigoXdo,
|
||||
pynput: EnigoPynput,
|
||||
is_x11: bool,
|
||||
uinput_keyboard: Option<Box<dyn KeyboardControllable + Send>>,
|
||||
uinput_mouse: Option<Box<dyn MouseControllable + Send>>,
|
||||
}
|
||||
|
||||
impl Enigo {
|
||||
/// Get delay of xdo implementation.
|
||||
pub fn delay(&self) -> u64 {
|
||||
self.xdo.delay()
|
||||
}
|
||||
/// Set delay of xdo implemetation.
|
||||
pub fn set_delay(&mut self, delay: u64) {
|
||||
self.xdo.set_delay(delay)
|
||||
}
|
||||
/// Reset pynput.
|
||||
pub fn reset(&mut self) {
|
||||
self.pynput.reset();
|
||||
}
|
||||
/// Set uinput keyboard.
|
||||
pub fn set_uinput_keyboard(
|
||||
&mut self,
|
||||
uinput_keyboard: Option<Box<dyn KeyboardControllable + Send>>,
|
||||
) {
|
||||
self.uinput_keyboard = uinput_keyboard
|
||||
}
|
||||
/// Set uinput mouse.
|
||||
pub fn set_uinput_mouse(&mut self, uinput_mouse: Option<Box<dyn MouseControllable + Send>>) {
|
||||
self.uinput_mouse = uinput_mouse
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Enigo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
is_x11: crate::linux::is_x11(),
|
||||
uinput_keyboard: None,
|
||||
uinput_mouse: None,
|
||||
xdo: EnigoXdo::default(),
|
||||
pynput: EnigoPynput::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MouseControllable for Enigo {
|
||||
fn mouse_move_to(&mut self, x: i32, y: i32) {
|
||||
if self.is_x11 {
|
||||
self.xdo.mouse_move_to(x, y);
|
||||
} else {
|
||||
if let Some(mouse) = &mut self.uinput_mouse {
|
||||
mouse.mouse_move_to(x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn mouse_move_relative(&mut self, x: i32, y: i32) {
|
||||
if self.is_x11 {
|
||||
self.xdo.mouse_move_relative(x, y);
|
||||
} else {
|
||||
if let Some(mouse) = &mut self.uinput_mouse {
|
||||
mouse.mouse_move_relative(x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn mouse_down(&mut self, button: MouseButton) -> crate::ResultType {
|
||||
if self.is_x11 {
|
||||
self.xdo.mouse_down(button)
|
||||
} else {
|
||||
if let Some(mouse) = &mut self.uinput_mouse {
|
||||
mouse.mouse_down(button)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
fn mouse_up(&mut self, button: MouseButton) {
|
||||
if self.is_x11 {
|
||||
self.xdo.mouse_up(button)
|
||||
} else {
|
||||
if let Some(mouse) = &mut self.uinput_mouse {
|
||||
mouse.mouse_up(button)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn mouse_click(&mut self, button: MouseButton) {
|
||||
if self.is_x11 {
|
||||
self.xdo.mouse_click(button)
|
||||
} else {
|
||||
if let Some(mouse) = &mut self.uinput_mouse {
|
||||
mouse.mouse_click(button)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn mouse_scroll_x(&mut self, length: i32) {
|
||||
if self.is_x11 {
|
||||
self.xdo.mouse_scroll_x(length)
|
||||
} else {
|
||||
if let Some(mouse) = &mut self.uinput_mouse {
|
||||
mouse.mouse_scroll_x(length)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn mouse_scroll_y(&mut self, length: i32) {
|
||||
if self.is_x11 {
|
||||
self.xdo.mouse_scroll_y(length)
|
||||
} else {
|
||||
if let Some(mouse) = &mut self.uinput_mouse {
|
||||
mouse.mouse_scroll_y(length)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardControllable for Enigo {
|
||||
fn get_key_state(&mut self, key: Key) -> bool {
|
||||
if self.is_x11 {
|
||||
self.xdo.get_key_state(key)
|
||||
} else {
|
||||
if let Some(keyboard) = &mut self.uinput_keyboard {
|
||||
keyboard.get_key_state(key)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_sequence(&mut self, sequence: &str) {
|
||||
if self.is_x11 {
|
||||
self.xdo.key_sequence(sequence)
|
||||
} else {
|
||||
if let Some(keyboard) = &mut self.uinput_keyboard {
|
||||
keyboard.key_sequence(sequence)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_down(&mut self, key: Key) -> crate::ResultType {
|
||||
if self.is_x11 {
|
||||
if self.pynput.send_pynput(&key, true) {
|
||||
return Ok(());
|
||||
}
|
||||
self.xdo.key_down(key)
|
||||
} else {
|
||||
if let Some(keyboard) = &mut self.uinput_keyboard {
|
||||
keyboard.key_down(key)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
fn key_up(&mut self, key: Key) {
|
||||
if self.is_x11 {
|
||||
if self.pynput.send_pynput(&key, false) {
|
||||
return;
|
||||
}
|
||||
self.xdo.key_up(key)
|
||||
} else {
|
||||
if let Some(keyboard) = &mut self.uinput_keyboard {
|
||||
keyboard.key_up(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn key_click(&mut self, key: Key) {
|
||||
if self.is_x11 {
|
||||
self.xdo.key_click(key)
|
||||
} else {
|
||||
if let Some(keyboard) = &mut self.uinput_keyboard {
|
||||
keyboard.key_click(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
280
libs/enigo/src/linux/pynput.rs
Normal file
280
libs/enigo/src/linux/pynput.rs
Normal file
@@ -0,0 +1,280 @@
|
||||
use crate::Key;
|
||||
use std::{io::prelude::*, sync::mpsc};
|
||||
|
||||
enum PyMsg {
|
||||
Char(char),
|
||||
Str(&'static str),
|
||||
}
|
||||
|
||||
/// The main struct for handling the event emitting
|
||||
pub(super) struct EnigoPynput {
|
||||
tx: mpsc::Sender<(PyMsg, bool)>,
|
||||
}
|
||||
|
||||
impl Default for EnigoPynput {
|
||||
fn default() -> Self {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
start_pynput_service(rx);
|
||||
Self { tx }
|
||||
}
|
||||
}
|
||||
impl EnigoPynput {
|
||||
pub(super) fn reset(&mut self) {
|
||||
self.tx.send((PyMsg::Char('\0'), true)).ok();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn send_pynput(&mut self, key: &Key, is_press: bool) -> bool {
|
||||
if unsafe { PYNPUT_EXIT || !PYNPUT_REDAY } {
|
||||
return false;
|
||||
}
|
||||
if let Key::Layout(c) = key {
|
||||
return self.tx.send((PyMsg::Char(*c), is_press)).is_ok();
|
||||
}
|
||||
if let Key::Raw(_) = key {
|
||||
return false;
|
||||
}
|
||||
#[allow(deprecated)]
|
||||
let s = match key {
|
||||
Key::Alt => "Alt_L",
|
||||
Key::Backspace => "BackSpace",
|
||||
Key::CapsLock => "Caps_Lock",
|
||||
Key::Control => "Control_L",
|
||||
Key::Delete => "Delete",
|
||||
Key::DownArrow => "Down",
|
||||
Key::End => "End",
|
||||
Key::Escape => "Escape",
|
||||
Key::F1 => "F1",
|
||||
Key::F10 => "F10",
|
||||
Key::F11 => "F11",
|
||||
Key::F12 => "F12",
|
||||
Key::F2 => "F2",
|
||||
Key::F3 => "F3",
|
||||
Key::F4 => "F4",
|
||||
Key::F5 => "F5",
|
||||
Key::F6 => "F6",
|
||||
Key::F7 => "F7",
|
||||
Key::F8 => "F8",
|
||||
Key::F9 => "F9",
|
||||
Key::Home => "Home",
|
||||
Key::LeftArrow => "Left",
|
||||
Key::Option => "Option",
|
||||
Key::PageDown => "Page_Down",
|
||||
Key::PageUp => "Page_Up",
|
||||
Key::Return => "Return",
|
||||
Key::RightArrow => "Right",
|
||||
Key::Shift => "Shift_L",
|
||||
Key::Space => "space",
|
||||
Key::Tab => "Tab",
|
||||
Key::UpArrow => "Up",
|
||||
Key::Numpad0 => "0",
|
||||
Key::Numpad1 => "1",
|
||||
Key::Numpad2 => "2",
|
||||
Key::Numpad3 => "3",
|
||||
Key::Numpad4 => "4",
|
||||
Key::Numpad5 => "5",
|
||||
Key::Numpad6 => "6",
|
||||
Key::Numpad7 => "7",
|
||||
Key::Numpad8 => "8",
|
||||
Key::Numpad9 => "9",
|
||||
Key::Decimal => "KP_Decimal",
|
||||
Key::Cancel => "Cancel",
|
||||
Key::Clear => "Clear",
|
||||
Key::Pause => "Pause",
|
||||
Key::Kana => "Kana",
|
||||
Key::Hangul => "Hangul",
|
||||
Key::Hanja => "Hanja",
|
||||
Key::Kanji => "Kanji",
|
||||
Key::Select => "Select",
|
||||
Key::Print => "Print",
|
||||
Key::Execute => "Execute",
|
||||
Key::Snapshot => "3270_PrintScreen",
|
||||
Key::Insert => "Insert",
|
||||
Key::Help => "Help",
|
||||
Key::Separator => "KP_Separator",
|
||||
Key::Scroll => "Scroll_Lock",
|
||||
Key::NumLock => "Num_Lock",
|
||||
Key::RWin => "Super_R",
|
||||
Key::Apps => "Menu",
|
||||
Key::Multiply => "KP_Multiply",
|
||||
Key::Add => "KP_Add",
|
||||
Key::Subtract => "KP_Subtract",
|
||||
Key::Divide => "KP_Divide",
|
||||
Key::Equals => "KP_Equal",
|
||||
Key::NumpadEnter => "KP_Enter",
|
||||
Key::RightShift => "Shift_R",
|
||||
Key::RightControl => "Control_R",
|
||||
Key::RightAlt => "Mode_switch",
|
||||
Key::Command | Key::Super | Key::Windows | Key::Meta => "Super_L",
|
||||
_ => {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
log::info!("send pynput: {:?}", &s);
|
||||
return self.tx.send((PyMsg::Str(s), is_press)).is_ok();
|
||||
}
|
||||
}
|
||||
|
||||
// impl MouseControllable for EnigoPynput {
|
||||
// fn mouse_move_to(&mut self, _x: i32, _y: i32) {
|
||||
// unimplemented!()
|
||||
// }
|
||||
// fn mouse_move_relative(&mut self, _x: i32, _y: i32) {
|
||||
// unimplemented!()
|
||||
// }
|
||||
// fn mouse_down(&mut self, _button: MouseButton) -> crate::ResultType {
|
||||
// unimplemented!()
|
||||
// }
|
||||
// fn mouse_up(&mut self, _button: MouseButton) {
|
||||
// unimplemented!()
|
||||
// }
|
||||
// fn mouse_click(&mut self, _button: MouseButton) {
|
||||
// unimplemented!()
|
||||
// }
|
||||
// fn mouse_scroll_x(&mut self, _length: i32) {
|
||||
// unimplemented!()
|
||||
// }
|
||||
// fn mouse_scroll_y(&mut self, _length: i32) {
|
||||
// unimplemented!()
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl KeyboardControllable for EnigoPynput {
|
||||
// fn get_key_state(&mut self, _key: Key) -> bool {
|
||||
// unimplemented!()
|
||||
// }
|
||||
|
||||
// fn key_sequence(&mut self, _sequence: &str) {
|
||||
// unimplemented!()
|
||||
// }
|
||||
// fn key_down(&mut self, key: Key) -> crate::ResultType {
|
||||
// let _ = self.send_pynput(&key, true);
|
||||
// Ok(())
|
||||
// }
|
||||
// fn key_up(&mut self, key: Key) {
|
||||
// let _ = self.send_pynput(&key, false);
|
||||
// }
|
||||
// fn key_click(&mut self, _key: Key) {
|
||||
// unimplemented!()
|
||||
// }
|
||||
// }
|
||||
|
||||
static mut PYNPUT_EXIT: bool = false;
|
||||
static mut PYNPUT_REDAY: bool = false;
|
||||
static IPC_FILE: &'static str = "/tmp/RustDesk/pynput_service";
|
||||
|
||||
fn start_pynput_service(rx: mpsc::Receiver<(PyMsg, bool)>) {
|
||||
let mut py = "./pynput_service.py".to_owned();
|
||||
if !std::path::Path::new(&py).exists() {
|
||||
py = "/usr/share/rustdesk/files/pynput_service.py".to_owned();
|
||||
if !std::path::Path::new(&py).exists() {
|
||||
py = "/usr/lib/rustdesk/pynput_service.py".to_owned();
|
||||
if !std::path::Path::new(&py).exists() {
|
||||
log::error!("{} not exits", py);
|
||||
}
|
||||
}
|
||||
}
|
||||
log::info!("pynput service: {}", py);
|
||||
std::thread::spawn(move || {
|
||||
let username = std::env::var("PYNPUT_USERNAME").unwrap_or("".to_owned());
|
||||
let userid = std::env::var("PYNPUT_USERID").unwrap_or("".to_owned());
|
||||
let status = if username.is_empty() {
|
||||
std::process::Command::new("python3")
|
||||
.arg(&py)
|
||||
.arg(IPC_FILE)
|
||||
.status()
|
||||
.map(|x| x.success())
|
||||
} else {
|
||||
let mut status = Ok(true);
|
||||
for i in 0..100 {
|
||||
if i % 10 == 0 {
|
||||
log::info!("#{} try to start pynput server", i);
|
||||
}
|
||||
status = std::process::Command::new("sudo")
|
||||
.args(vec![
|
||||
"-E",
|
||||
&format!("XDG_RUNTIME_DIR=/run/user/{}", userid) as &str,
|
||||
"-u",
|
||||
&username,
|
||||
"python3",
|
||||
&py,
|
||||
IPC_FILE,
|
||||
])
|
||||
.status()
|
||||
.map(|x| x.success());
|
||||
match status {
|
||||
Ok(true) => break,
|
||||
_ => {}
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
}
|
||||
status
|
||||
};
|
||||
log::info!(
|
||||
"pynput server exit with username/id {}/{}: {:?}",
|
||||
username,
|
||||
userid,
|
||||
status
|
||||
);
|
||||
unsafe {
|
||||
PYNPUT_EXIT = true;
|
||||
}
|
||||
});
|
||||
std::thread::spawn(move || {
|
||||
for i in 0..300 {
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
let mut conn = match std::os::unix::net::UnixStream::connect(IPC_FILE) {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => {
|
||||
if i % 15 == 0 {
|
||||
log::warn!("Failed to connect to {}: {}", IPC_FILE, err);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if let Err(err) = conn.set_nonblocking(true) {
|
||||
log::error!("Failed to set ipc nonblocking: {}", err);
|
||||
return;
|
||||
}
|
||||
log::info!("Conntected to pynput server");
|
||||
let d = std::time::Duration::from_millis(30);
|
||||
unsafe {
|
||||
PYNPUT_REDAY = true;
|
||||
}
|
||||
let mut buf = [0u8; 1024];
|
||||
loop {
|
||||
if unsafe { PYNPUT_EXIT } {
|
||||
break;
|
||||
}
|
||||
match rx.recv_timeout(d) {
|
||||
Ok((msg, is_press)) => {
|
||||
let msg = match msg {
|
||||
PyMsg::Char(chr) => {
|
||||
format!("{}{}", if is_press { 'p' } else { 'r' }, chr)
|
||||
}
|
||||
PyMsg::Str(s) => format!("{}{}", if is_press { 'p' } else { 'r' }, s),
|
||||
};
|
||||
let n = msg.len();
|
||||
buf[0] = n as _;
|
||||
buf[1..(n + 1)].copy_from_slice(msg.as_bytes());
|
||||
if let Err(err) = conn.write_all(&buf[..n + 1]) {
|
||||
log::error!("Failed to write to ipc: {}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(err) => match err {
|
||||
mpsc::RecvTimeoutError::Disconnected => {
|
||||
log::error!("pynput sender disconnecte");
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
PYNPUT_REDAY = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -3,7 +3,7 @@ use libc;
|
||||
use crate::{Key, KeyboardControllable, MouseButton, MouseControllable};
|
||||
|
||||
use self::libc::{c_char, c_int, c_void, useconds_t};
|
||||
use std::{borrow::Cow, ffi::CString, io::prelude::*, ptr, sync::mpsc};
|
||||
use std::{borrow::Cow, ffi::CString, ptr};
|
||||
|
||||
const CURRENT_WINDOW: c_int = 0;
|
||||
const DEFAULT_DELAY: u64 = 12000;
|
||||
@@ -60,34 +60,25 @@ fn mousebutton(button: MouseButton) -> c_int {
|
||||
}
|
||||
}
|
||||
|
||||
enum PyMsg {
|
||||
Char(char),
|
||||
Str(&'static str),
|
||||
}
|
||||
|
||||
/// The main struct for handling the event emitting
|
||||
pub struct Enigo {
|
||||
pub(super) struct EnigoXdo {
|
||||
xdo: Xdo,
|
||||
delay: u64,
|
||||
tx: mpsc::Sender<(PyMsg, bool)>,
|
||||
}
|
||||
// This is safe, we have a unique pointer.
|
||||
// TODO: use Unique<c_char> once stable.
|
||||
unsafe impl Send for Enigo {}
|
||||
unsafe impl Send for EnigoXdo {}
|
||||
|
||||
impl Default for Enigo {
|
||||
/// Create a new Enigo instance
|
||||
impl Default for EnigoXdo {
|
||||
/// Create a new EnigoXdo instance
|
||||
fn default() -> Self {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
start_pynput_service(rx);
|
||||
Self {
|
||||
xdo: unsafe { xdo_new(ptr::null()) },
|
||||
delay: DEFAULT_DELAY,
|
||||
tx,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Enigo {
|
||||
impl EnigoXdo {
|
||||
/// Get the delay per keypress.
|
||||
/// Default value is 12000.
|
||||
/// This is Linux-specific.
|
||||
@@ -99,101 +90,8 @@ impl Enigo {
|
||||
pub fn set_delay(&mut self, delay: u64) {
|
||||
self.delay = delay;
|
||||
}
|
||||
///
|
||||
pub fn reset(&mut self) {
|
||||
self.tx.send((PyMsg::Char('\0'), true)).ok();
|
||||
}
|
||||
#[inline]
|
||||
fn send_pynput(&mut self, key: &Key, is_press: bool) -> bool {
|
||||
if unsafe { PYNPUT_EXIT || !PYNPUT_REDAY } {
|
||||
return false;
|
||||
}
|
||||
if let Key::Layout(c) = key {
|
||||
return self.tx.send((PyMsg::Char(*c), is_press)).is_ok();
|
||||
}
|
||||
if let Key::Raw(_) = key {
|
||||
return false;
|
||||
}
|
||||
#[allow(deprecated)]
|
||||
let s = match key {
|
||||
Key::Alt => "Alt_L",
|
||||
Key::Backspace => "BackSpace",
|
||||
Key::CapsLock => "Caps_Lock",
|
||||
Key::Control => "Control_L",
|
||||
Key::Delete => "Delete",
|
||||
Key::DownArrow => "Down",
|
||||
Key::End => "End",
|
||||
Key::Escape => "Escape",
|
||||
Key::F1 => "F1",
|
||||
Key::F10 => "F10",
|
||||
Key::F11 => "F11",
|
||||
Key::F12 => "F12",
|
||||
Key::F2 => "F2",
|
||||
Key::F3 => "F3",
|
||||
Key::F4 => "F4",
|
||||
Key::F5 => "F5",
|
||||
Key::F6 => "F6",
|
||||
Key::F7 => "F7",
|
||||
Key::F8 => "F8",
|
||||
Key::F9 => "F9",
|
||||
Key::Home => "Home",
|
||||
Key::LeftArrow => "Left",
|
||||
Key::Option => "Option",
|
||||
Key::PageDown => "Page_Down",
|
||||
Key::PageUp => "Page_Up",
|
||||
Key::Return => "Return",
|
||||
Key::RightArrow => "Right",
|
||||
Key::Shift => "Shift_L",
|
||||
Key::Space => "space",
|
||||
Key::Tab => "Tab",
|
||||
Key::UpArrow => "Up",
|
||||
Key::Numpad0 => "0",
|
||||
Key::Numpad1 => "1",
|
||||
Key::Numpad2 => "2",
|
||||
Key::Numpad3 => "3",
|
||||
Key::Numpad4 => "4",
|
||||
Key::Numpad5 => "5",
|
||||
Key::Numpad6 => "6",
|
||||
Key::Numpad7 => "7",
|
||||
Key::Numpad8 => "8",
|
||||
Key::Numpad9 => "9",
|
||||
Key::Decimal => "KP_Decimal",
|
||||
Key::Cancel => "Cancel",
|
||||
Key::Clear => "Clear",
|
||||
Key::Pause => "Pause",
|
||||
Key::Kana => "Kana",
|
||||
Key::Hangul => "Hangul",
|
||||
Key::Hanja => "Hanja",
|
||||
Key::Kanji => "Kanji",
|
||||
Key::Select => "Select",
|
||||
Key::Print => "Print",
|
||||
Key::Execute => "Execute",
|
||||
Key::Snapshot => "3270_PrintScreen",
|
||||
Key::Insert => "Insert",
|
||||
Key::Help => "Help",
|
||||
Key::Separator => "KP_Separator",
|
||||
Key::Scroll => "Scroll_Lock",
|
||||
Key::NumLock => "Num_Lock",
|
||||
Key::RWin => "Super_R",
|
||||
Key::Apps => "Menu",
|
||||
Key::Multiply => "KP_Multiply",
|
||||
Key::Add => "KP_Add",
|
||||
Key::Subtract => "KP_Subtract",
|
||||
Key::Divide => "KP_Divide",
|
||||
Key::Equals => "KP_Equal",
|
||||
Key::NumpadEnter => "KP_Enter",
|
||||
Key::RightShift => "Shift_R",
|
||||
Key::RightControl => "Control_R",
|
||||
Key::RightAlt => "Mode_switch",
|
||||
Key::Command | Key::Super | Key::Windows | Key::Meta => "Super_L",
|
||||
_ => {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return self.tx.send((PyMsg::Str(s), is_press)).is_ok();
|
||||
}
|
||||
}
|
||||
impl Drop for Enigo {
|
||||
impl Drop for EnigoXdo {
|
||||
fn drop(&mut self) {
|
||||
if self.xdo.is_null() {
|
||||
return;
|
||||
@@ -203,7 +101,7 @@ impl Drop for Enigo {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl MouseControllable for Enigo {
|
||||
impl MouseControllable for EnigoXdo {
|
||||
fn mouse_move_to(&mut self, x: i32, y: i32) {
|
||||
if self.xdo.is_null() {
|
||||
return;
|
||||
@@ -378,7 +276,7 @@ fn keysequence<'a>(key: Key) -> Cow<'a, str> {
|
||||
_ => "",
|
||||
})
|
||||
}
|
||||
impl KeyboardControllable for Enigo {
|
||||
impl KeyboardControllable for EnigoXdo {
|
||||
fn get_key_state(&mut self, key: Key) -> bool {
|
||||
if self.xdo.is_null() {
|
||||
return false;
|
||||
@@ -431,9 +329,6 @@ impl KeyboardControllable for Enigo {
|
||||
if self.xdo.is_null() {
|
||||
return Ok(());
|
||||
}
|
||||
if self.send_pynput(&key, true) {
|
||||
return Ok(());
|
||||
}
|
||||
let string = CString::new(&*keysequence(key))?;
|
||||
unsafe {
|
||||
xdo_send_keysequence_window_down(
|
||||
@@ -449,9 +344,6 @@ impl KeyboardControllable for Enigo {
|
||||
if self.xdo.is_null() {
|
||||
return;
|
||||
}
|
||||
if self.send_pynput(&key, false) {
|
||||
return;
|
||||
}
|
||||
if let Ok(string) = CString::new(&*keysequence(key)) {
|
||||
unsafe {
|
||||
xdo_send_keysequence_window_up(
|
||||
@@ -479,127 +371,3 @@ impl KeyboardControllable for Enigo {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mut PYNPUT_EXIT: bool = false;
|
||||
static mut PYNPUT_REDAY: bool = false;
|
||||
static IPC_FILE: &'static str = "/tmp/RustDesk/pynput_service";
|
||||
|
||||
fn start_pynput_service(rx: mpsc::Receiver<(PyMsg, bool)>) {
|
||||
let mut py = "./pynput_service.py".to_owned();
|
||||
if !std::path::Path::new(&py).exists() {
|
||||
py = "/usr/share/rustdesk/files/pynput_service.py".to_owned();
|
||||
if !std::path::Path::new(&py).exists() {
|
||||
py = "/usr/lib/rustdesk/pynput_service.py".to_owned();
|
||||
if !std::path::Path::new(&py).exists() {
|
||||
// enigo libs, not rustdesk root project, so skip using appimage features
|
||||
py = std::env::var("APPDIR").unwrap_or("".to_string()) + "/usr/lib/rustdesk/pynput_service.py";
|
||||
if !std::path::Path::new(&py).exists() {
|
||||
log::error!("{} not exists", py);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log::info!("pynput service: {}", py);
|
||||
std::thread::spawn(move || {
|
||||
let username = std::env::var("PYNPUT_USERNAME").unwrap_or("".to_owned());
|
||||
let userid = std::env::var("PYNPUT_USERID").unwrap_or("".to_owned());
|
||||
let status = if username.is_empty() {
|
||||
std::process::Command::new("python3")
|
||||
.arg(&py)
|
||||
.arg(IPC_FILE)
|
||||
.status()
|
||||
.map(|x| x.success())
|
||||
} else {
|
||||
let mut status = Ok(true);
|
||||
for i in 0..100 {
|
||||
if i % 10 == 0 {
|
||||
log::info!("#{} try to start pynput server", i);
|
||||
}
|
||||
status = std::process::Command::new("sudo")
|
||||
.args(vec![
|
||||
"-E",
|
||||
&format!("XDG_RUNTIME_DIR=/run/user/{}", userid) as &str,
|
||||
"-u",
|
||||
&username,
|
||||
"python3",
|
||||
&py,
|
||||
IPC_FILE,
|
||||
])
|
||||
.status()
|
||||
.map(|x| x.success());
|
||||
match status {
|
||||
Ok(true) => break,
|
||||
_ => {}
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
}
|
||||
status
|
||||
};
|
||||
log::info!(
|
||||
"pynput server exit with username/id {}/{}: {:?}",
|
||||
username,
|
||||
userid,
|
||||
status
|
||||
);
|
||||
unsafe {
|
||||
PYNPUT_EXIT = true;
|
||||
}
|
||||
});
|
||||
std::thread::spawn(move || {
|
||||
for i in 0..300 {
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
let mut conn = match std::os::unix::net::UnixStream::connect(IPC_FILE) {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => {
|
||||
if i % 15 == 0 {
|
||||
log::warn!("Failed to connect to {}: {}", IPC_FILE, err);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if let Err(err) = conn.set_nonblocking(true) {
|
||||
log::error!("Failed to set ipc nonblocking: {}", err);
|
||||
return;
|
||||
}
|
||||
log::info!("Conntected to pynput server");
|
||||
let d = std::time::Duration::from_millis(30);
|
||||
unsafe {
|
||||
PYNPUT_REDAY = true;
|
||||
}
|
||||
let mut buf = [0u8; 1024];
|
||||
loop {
|
||||
if unsafe { PYNPUT_EXIT } {
|
||||
break;
|
||||
}
|
||||
match rx.recv_timeout(d) {
|
||||
Ok((msg, is_press)) => {
|
||||
let msg = match msg {
|
||||
PyMsg::Char(chr) => {
|
||||
format!("{}{}", if is_press { 'p' } else { 'r' }, chr)
|
||||
}
|
||||
PyMsg::Str(s) => format!("{}{}", if is_press { 'p' } else { 'r' }, s),
|
||||
};
|
||||
let n = msg.len();
|
||||
buf[0] = n as _;
|
||||
buf[1..(n + 1)].copy_from_slice(msg.as_bytes());
|
||||
if let Err(err) = conn.write_all(&buf[..n + 1]) {
|
||||
log::error!("Failed to write to ipc: {}", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(err) => match err {
|
||||
mpsc::RecvTimeoutError::Disconnected => {
|
||||
log::error!("pynput sender disconnecte");
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
PYNPUT_REDAY = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -21,6 +21,10 @@ impl Capturer {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_use_yuv(&mut self, use_yuv: bool) {
|
||||
self.inner.set_use_yuv(use_yuv);
|
||||
}
|
||||
|
||||
pub fn is_gdi(&self) -> bool {
|
||||
self.inner.is_gdi()
|
||||
}
|
||||
@@ -41,8 +45,8 @@ impl Capturer {
|
||||
self.height
|
||||
}
|
||||
|
||||
pub fn frame<'a>(&'a mut self, timeout_ms: Duration) -> io::Result<Frame<'a>> {
|
||||
match self.inner.frame(timeout_ms.as_millis() as _) {
|
||||
pub fn frame<'a>(&'a mut self, timeout: Duration) -> io::Result<Frame<'a>> {
|
||||
match self.inner.frame(timeout.as_millis() as _) {
|
||||
Ok(frame) => Ok(Frame(frame)),
|
||||
Err(ref error) if error.kind() == TimedOut => Err(WouldBlock.into()),
|
||||
Err(error) => Err(error),
|
||||
@@ -129,6 +133,11 @@ impl CapturerMag {
|
||||
data: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_use_yuv(&mut self, use_yuv: bool) {
|
||||
self.inner.set_use_yuv(use_yuv)
|
||||
}
|
||||
|
||||
pub fn exclude(&mut self, cls: &str, name: &str) -> io::Result<bool> {
|
||||
self.inner.exclude(cls, name)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,13 @@ impl Capturer {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_use_yuv(&mut self, use_yuv: bool) {
|
||||
match self {
|
||||
Capturer::X11(d) => d.set_use_yuv(use_yuv),
|
||||
Capturer::WAYLAND(d) => d.set_use_yuv(use_yuv),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn width(&self) -> usize {
|
||||
match self {
|
||||
Capturer::X11(d) => d.width(),
|
||||
@@ -31,10 +38,10 @@ impl Capturer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn frame<'a>(&'a mut self, timeout_ms: u32) -> io::Result<Frame<'a>> {
|
||||
pub fn frame<'a>(&'a mut self, timeout: Duration) -> io::Result<Frame<'a>> {
|
||||
match self {
|
||||
Capturer::X11(d) => d.frame(timeout_ms),
|
||||
Capturer::WAYLAND(d) => d.frame(timeout_ms),
|
||||
Capturer::X11(d) => d.frame(timeout),
|
||||
Capturer::WAYLAND(d) => d.frame(timeout),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +52,7 @@ pub enum Display {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_wayland() -> bool {
|
||||
pub fn is_wayland() -> bool {
|
||||
std::env::var("IS_WAYLAND").is_ok()
|
||||
|| std::env::var("XDG_SESSION_TYPE") == Ok("wayland".to_owned())
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ cfg_if! {
|
||||
mod wayland;
|
||||
mod x11;
|
||||
pub use self::linux::*;
|
||||
pub use self::x11::Frame;
|
||||
} else {
|
||||
mod x11;
|
||||
pub use self::x11::*;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::common::x11::Frame;
|
||||
use crate::wayland::{capturable::*, *};
|
||||
use std::io;
|
||||
use std::{io, time::Duration};
|
||||
|
||||
pub struct Capturer(Display, Box<dyn Recorder>, bool, Vec<u8>);
|
||||
|
||||
@@ -14,6 +14,10 @@ impl Capturer {
|
||||
Ok(Capturer(display, r, yuv, Default::default()))
|
||||
}
|
||||
|
||||
pub fn set_use_yuv(&mut self, use_yuv: bool) {
|
||||
self.2 = use_yuv;
|
||||
}
|
||||
|
||||
pub fn width(&self) -> usize {
|
||||
self.0.width()
|
||||
}
|
||||
@@ -22,8 +26,8 @@ impl Capturer {
|
||||
self.0.height()
|
||||
}
|
||||
|
||||
pub fn frame<'a>(&'a mut self, timeout_ms: u32) -> io::Result<Frame<'a>> {
|
||||
match self.1.capture(timeout_ms as _).map_err(map_err)? {
|
||||
pub fn frame<'a>(&'a mut self, timeout: Duration) -> io::Result<Frame<'a>> {
|
||||
match self.1.capture(timeout.as_millis() as _).map_err(map_err)? {
|
||||
PixelProvider::BGR0(w, h, x) => Ok(Frame(if self.2 {
|
||||
crate::common::bgra_to_i420(w as _, h as _, &x, &mut self.3);
|
||||
&self.3[..]
|
||||
|
||||
@@ -8,6 +8,10 @@ impl Capturer {
|
||||
x11::Capturer::new(display.0, yuv).map(Capturer)
|
||||
}
|
||||
|
||||
pub fn set_use_yuv(&mut self, use_yuv: bool) {
|
||||
self.0.set_use_yuv(use_yuv);
|
||||
}
|
||||
|
||||
pub fn width(&self) -> usize {
|
||||
self.0.display().rect().w as usize
|
||||
}
|
||||
|
||||
@@ -446,6 +446,10 @@ impl CapturerMag {
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
pub(crate) fn set_use_yuv(&mut self, use_yuv: bool) {
|
||||
self.use_yuv = use_yuv;
|
||||
}
|
||||
|
||||
pub(crate) fn exclude(&mut self, cls: &str, name: &str) -> Result<bool> {
|
||||
let name_c = CString::new(name).unwrap();
|
||||
unsafe {
|
||||
|
||||
@@ -156,6 +156,10 @@ impl Capturer {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_use_yuv(&mut self, use_yuv: bool) {
|
||||
self.use_yuv = use_yuv;
|
||||
}
|
||||
|
||||
pub fn is_gdi(&self) -> bool {
|
||||
self.gdi_capturer.is_some()
|
||||
}
|
||||
|
||||
@@ -74,6 +74,10 @@ impl Capturer {
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
pub fn set_use_yuv(&mut self, use_yuv: bool) {
|
||||
self.use_yuv = use_yuv;
|
||||
}
|
||||
|
||||
pub fn display(&self) -> &Display {
|
||||
&self.display
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user