linux_wayland_support: init merge, windows build

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou 2022-07-07 01:27:21 +08:00
parent 6533b30cac
commit aae6e2b16b
30 changed files with 1902 additions and 656 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
.vscode
.idea
.DS_Store
libsciter-gtk.so
src/ui/inline.rs
extractor
__pycache__

59
Cargo.lock generated
View File

@ -317,6 +317,18 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitvec"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1489fcb93a5bb47da0462ca93ad252ad6af2145cce58d10d46a83931ba9f016b"
dependencies = [
"funty",
"radium",
"tap",
"wyz",
]
[[package]]
name = "block"
version = "0.1.6"
@ -1323,6 +1335,16 @@ dependencies = [
"str-buf",
]
[[package]]
name = "evdev"
version = "0.11.5"
source = "git+https://github.com/fufesou/evdev#cec616e37790293d2cd2aa54a96601ed6b1b35a9"
dependencies = [
"bitvec",
"libc",
"nix 0.23.1",
]
[[package]]
name = "event-listener"
version = "2.5.2"
@ -1498,6 +1520,12 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "futures"
version = "0.3.21"
@ -2741,6 +2769,14 @@ dependencies = [
"windows-sys 0.28.0",
]
[[package]]
name = "mouce"
version = "0.2.1"
source = "git+https://github.com/fufesou/mouce.git#7da9d9b6597f4c4461881deb4ed49da2385e3cac"
dependencies = [
"glob",
]
[[package]]
name = "muldiv"
version = "0.2.1"
@ -3527,6 +3563,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]]
name = "rand"
version = "0.6.5"
@ -3933,6 +3975,7 @@ dependencies = [
"default-net",
"dispatch",
"enigo",
"evdev",
"flexi_logger",
"flutter_rust_bridge",
"flutter_rust_bridge_codegen",
@ -3947,6 +3990,7 @@ dependencies = [
"mac_address",
"machine-uid",
"magnum-opus",
"mouce",
"num_cpus",
"objc",
"parity-tokio-ipc",
@ -4587,6 +4631,12 @@ dependencies = [
"version-compare 0.1.0",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "target_build_utils"
version = "0.3.1"
@ -5542,6 +5592,15 @@ dependencies = [
"winapi-build",
]
[[package]]
name = "wyz"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e"
dependencies = [
"tap",
]
[[package]]
name = "x11"
version = "2.19.1"

View File

@ -31,7 +31,7 @@ default = ["use_dasp"]
[dependencies]
whoami = "1.2"
scrap = { path = "libs/scrap" }
scrap = { path = "libs/scrap", features = ["wayland"] }
hbb_common = { path = "libs/hbb_common" }
serde_derive = "1.0"
serde = "1.0"
@ -69,7 +69,7 @@ machine-uid = "0.2"
mac_address = "1.1"
sciter-rs = { git = "https://github.com/open-trade/rust-sciter", branch = "dyn" }
sys-locale = "0.2"
enigo = { path = "libs/enigo" }
enigo = { path = "libs/enigo", features = [ "with_serde" ] }
clipboard = { path = "libs/clipboard" }
rdev = { git = "https://github.com/open-trade/rdev" }
ctrlc = "3.2"
@ -99,6 +99,8 @@ psimple = { package = "libpulse-simple-binding", version = "2.25" }
pulse = { package = "libpulse-binding", version = "2.26" }
rust-pulsectl = { git = "https://github.com/open-trade/pulsectl" }
async-process = "1.3"
mouce = { git="https://github.com/fufesou/mouce.git" }
evdev = { git="https://github.com/fufesou/evdev" }
[target.'cfg(target_os = "android")'.dependencies]
android_logger = "0.11"

View File

@ -8,16 +8,20 @@ if [ "$1" = configure ]; then
if [ "systemd" == "$INITSYS" ]; then
if [ -e /etc/systemd/system/rustdesk.service ]; then
rm /etc/systemd/system/rustdesk.service
rm /etc/systemd/system/rustdesk.service /usr/lib/systemd/system/rustdesk.service /usr/lib/systemd/user/rustdesk.service >/dev/null 2>&1
fi
version=$(python3 -V 2>&1 | grep -Po '(?<=Python )(.+)')
parsedVersion=$(echo "${version//./}")
if [[ "$parsedVersion" -gt "360" ]]; then
sudo -H pip3 install pynput
fi
cp /usr/share/rustdesk/files/systemd/rustdesk.service /etc/systemd/system/rustdesk.service
cp /usr/share/rustdesk/files/systemd/rustdesk.service /usr/lib/systemd/system/rustdesk.service
systemctl daemon-reload
systemctl enable rustdesk
systemctl start rustdesk
cp /usr/share/rustdesk/files/systemd/rustdesk.service.user /usr/lib/systemd/user/rustdesk.service
curUser=$(who | awk '{print $1}' | head -1)
systemctl --machine=${curUser}@.host --user daemon-reload
fi
fi

View File

@ -7,6 +7,13 @@ case $1 in
INITSYS=$(ls -al /proc/1/exe | awk -F' ' '{print $NF}' | awk -F'/' '{print $NF}')
if [ "systemd" == "${INITSYS}" ]; then
service rustdesk stop || true
serverUser=$(ps -ef | grep -E 'rustdesk +--server' | awk '{print $1}' | head -1)
if [ "$serverUser" != "" ] && [ "$serverUser" != "root" ]
then
systemctl --machine=${serverUser}@.host --user stop rustdesk || true
fi
sleep 1
rm -rf /usr/bin/libsciter-gtk.so
fi

View File

@ -8,7 +8,14 @@ case $1 in
if [ "systemd" == "${INITSYS}" ]; then
systemctl stop rustdesk || true
systemctl disable rustdesk || true
rm /etc/systemd/system/rustdesk.service || true
serverUser=$(ps -ef | grep -E 'rustdesk +--server' | awk '{print $1}' | head -1)
if [ "$serverUser" != "" ] && [ "$serverUser" != "root" ]
then
systemctl --machine=${serverUser}@.host --user stop rustdesk || true
fi
rm /etc/systemd/system/rustdesk.service /usr/lib/systemd/system/rustdesk.service /usr/lib/systemd/user/rustdesk.service || true
fi
;;
esac

View File

@ -209,12 +209,15 @@ rcodesign notarize --api-issuer 69a6de7d-2907-47e3-e053-5b8c7c11a4d1 --api-key 9
os.system('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/')
os.system(
'cp rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
os.system(
'cp rustdesk.service.user tmpdeb/usr/share/rustdesk/files/systemd/')
os.system('cp pynput_service.py tmpdeb/usr/share/rustdesk/files/')
os.system('cp DEBIAN/* tmpdeb/DEBIAN/')
os.system('cp -a DEBIAN/* tmpdeb/DEBIAN/')
os.system('strip tmpdeb/usr/bin/rustdesk')
os.system('mkdir -p tmpdeb/usr/lib/rustdesk')
os.system('cp libsciter-gtk.so tmpdeb/usr/lib/rustdesk/')
md5_file('usr/share/rustdesk/files/systemd/rustdesk.service')
md5_file('usr/share/rustdesk/files/systemd/rustdesk.service.user')
md5_file('usr/share/rustdesk/files/pynput_service.py')
md5_file('usr/lib/rustdesk/libsciter-gtk.so')
os.system('dpkg-deb -b tmpdeb rustdesk.deb; /bin/rm -rf tmpdeb/')

View File

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

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

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

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

View File

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

View File

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

View File

@ -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())
}

View File

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

View File

@ -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[..]

View File

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

View File

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

View File

@ -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()
}

View File

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

View File

@ -1,236 +0,0 @@
from pynput.keyboard import Key, Controller
from pynput.keyboard._xorg import KeyCode
from pynput._util.xorg import display_manager
import Xlib
from pynput._util.xorg import *
import Xlib
import os
import sys
import socket
KeyCode._from_symbol("\0") # test
DEAD_KEYS = {
'`': 65104,
'´': 65105,
'^': 65106,
'~': 65107,
'¯': 65108,
'˘': 65109,
'˙': 65110,
'¨': 65111,
'˚': 65112,
'˝': 65113,
'ˇ': 65114,
'¸': 65115,
'˛': 65116,
'': 65117, # ?
'': 65118, # ?
'': 65119,
'ٜ': 65120,
'': 65121,
' ̛': 65122,
}
def my_keyboard_mapping(display):
"""Generates a mapping from *keysyms* to *key codes* and required
modifier shift states.
:param Xlib.display.Display display: The display for which to retrieve the
keyboard mapping.
:return: the keyboard mapping
"""
mapping = {}
shift_mask = 1 << 0
group_mask = alt_gr_mask(display)
# Iterate over all keysym lists in the keyboard mapping
min_keycode = display.display.info.min_keycode
keycode_count = display.display.info.max_keycode - min_keycode + 1
for index, keysyms in enumerate(display.get_keyboard_mapping(
min_keycode, keycode_count)):
key_code = index + min_keycode
# Normalise the keysym list to yield a tuple containing the two groups
normalized = keysym_normalize(keysyms)
if not normalized:
continue
# Iterate over the groups to extract the shift and modifier state
for groups, group in zip(normalized, (False, True)):
for keysym, shift in zip(groups, (False, True)):
if not keysym:
continue
shift_state = 0 \
| (shift_mask if shift else 0) \
| (group_mask if group else 0)
# !!!: Save all keycode combinations of keysym
if keysym in mapping:
mapping[keysym].append((key_code, shift_state))
else:
mapping[keysym] = [(key_code, shift_state)]
return mapping
class MyController(Controller):
def _update_keyboard_mapping(self):
"""Updates the keyboard mapping.
"""
with display_manager(self._display) as dm:
self._keyboard_mapping = my_keyboard_mapping(dm)
def send_event(self, event, keycode, shift_state):
with display_manager(self._display) as dm, self.modifiers as modifiers:
# Under certain cimcumstances, such as when running under Xephyr,
# the value returned by dm.get_input_focus is an int
window = dm.get_input_focus().focus
send_event = getattr(
window,
'send_event',
lambda event: dm.send_event(window, event))
send_event(event(
detail=keycode,
state=shift_state | self._shift_mask(modifiers),
time=0,
root=dm.screen().root,
window=window,
same_screen=0,
child=Xlib.X.NONE,
root_x=0, root_y=0, event_x=0, event_y=0))
def fake_input(self, keycode, is_press):
with display_manager(self._display) as dm:
Xlib.ext.xtest.fake_input(
dm,
Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease,
keycode)
def _handle(self, key, is_press):
"""Resolves a key identifier and sends a keyboard event.
:param event: The *X* keyboard event.
:param int keysym: The keysym to handle.
"""
event = Xlib.display.event.KeyPress if is_press \
else Xlib.display.event.KeyRelease
keysym = self._keysym(key)
if key.vk is not None:
keycode = self._display.keysym_to_keycode(key.vk)
self.fake_input(keycode, is_press)
# Otherwise use XSendEvent; we need to use this in the general case to
# work around problems with keyboard layouts
self._emit('_on_fake_event', key, is_press)
return
# Make sure to verify that the key was resolved
if keysym is None:
raise self.InvalidKeyException(key)
# There may be multiple keycodes for keysym in keyboard_mapping
keycode_flag = len(self.keyboard_mapping[keysym]) == 1
if keycode_flag:
keycode, shift_state = self.keyboard_mapping[keysym][0]
else:
keycode, shift_state = self._display.keysym_to_keycode(keysym), 0
keycode_set = set(map(lambda x: x[0], self.keyboard_mapping[keysym]))
# The keycode of the dead key is inconsistent, The keysym has multiple combinations of a keycode.
if keycode != self._display.keysym_to_keycode(keysym) \
or (keycode_flag == False and keycode == list(keycode_set)[0] and len(keycode_set) == 1):
deakkey_chr = str(key).replace("'", '')
keysym = DEAD_KEYS[deakkey_chr]
keycode, shift_state = self.keyboard_mapping[keysym][0]
# If the key has a virtual key code, use that immediately with
# fake_input; fake input,being an X server extension, has access to
# more internal state that we do
try:
with self.modifiers as modifiers:
alt_gr = Key.alt_gr in modifiers
# !!!: Send_event can't support lock screen, this condition cann't be modified
if alt_gr:
self.send_event(
event, keycode, shift_state)
else:
self.fake_input(keycode, is_press)
except KeyError:
with self._borrow_lock:
keycode, index, count = self._borrows[keysym]
self._send_key(
event,
keycode,
index_to_shift(self._display, index))
count += 1 if is_press else -1
self._borrows[keysym] = (keycode, index, count)
# Notify any running listeners
self._emit('_on_fake_event', key, is_press)
keyboard = MyController()
server_address = sys.argv[1]
if not os.path.exists(os.path.dirname(server_address)):
os.makedirs(os.path.dirname(server_address))
try:
os.unlink(server_address)
except OSError:
if os.path.exists(server_address):
raise
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
server.bind(server_address)
server.listen(1)
clientsocket, address = server.accept()
os.system('chmod a+rw %s' % server_address)
print("Got pynput connection")
def loop():
global keyboard
buf = []
while True:
data = clientsocket.recv(1024)
if not data:
print("Connection broken")
break
buf.extend(data)
while buf:
n = buf[0]
n = n + 1
if len(buf) < n:
break
msg = bytearray(buf[1:n]).decode("utf-8")
buf = buf[n:]
if len(msg) < 2:
continue
if msg[1] == "\0":
keyboard = MyController()
print("Keyboard reset")
continue
if len(msg) == 2:
name = msg[1]
else:
name = KeyCode._from_symbol(msg[1:])
if str(name) == "<0>":
continue
try:
if msg[0] == "p":
keyboard.press(name)
else:
keyboard.release(name)
except Exception as e:
print('[x] error key',e)
loop()
clientsocket.close()
server.close()

View File

@ -6,7 +6,7 @@ After=systemd-user-sessions.service
[Service]
Type=simple
ExecStart=/usr/bin/rustdesk --service
PIDFile=/var/run/rustdesk.pid
PIDFile=/run/rustdesk.pid
KillMode=mixed
TimeoutStopSec=30
User=root

13
rustdesk.service.user Normal file
View File

@ -0,0 +1,13 @@
[Unit]
Description=RustDesk
[Service]
Type=simple
ExecStart=/usr/bin/rustdesk --server
PIDFile=/run/rustdesk.user.pid
KillMode=mixed
TimeoutStopSec=30
LimitNOFILE=100000
[Install]
WantedBy=multi-user.target

View File

@ -96,6 +96,45 @@ pub enum FS {
},
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "t", content = "c")]
pub enum DataKeyboard {
Sequence(String),
KeyDown(enigo::Key),
KeyUp(enigo::Key),
KeyClick(enigo::Key),
GetKeyState(enigo::Key),
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "t", content = "c")]
pub enum DataKeyboardResponse {
GetKeyState(bool),
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "t", content = "c")]
pub enum DataMouse {
MoveTo(i32, i32),
MoveRelative(i32, i32),
Down(enigo::MouseButton),
Up(enigo::MouseButton),
Click(enigo::MouseButton),
ScrollX(i32),
ScrollY(i32),
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "t", content = "c")]
pub enum DataControl {
Resolution {
minx: i32,
maxx: i32,
miny: i32,
maxy: i32,
},
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(tag = "t", content = "c")]
pub enum Data {
@ -141,6 +180,11 @@ pub enum Data {
PrivacyModeState((i32, PrivacyModeState)),
TestRendezvousServer,
Bool((String, Option<bool>)),
Keyboard(DataKeyboard),
KeyboardResponse(DataKeyboardResponse),
Mouse(DataMouse),
Control(DataControl),
Empty,
}
#[tokio::main(flavor = "current_thread")]

View File

@ -108,6 +108,10 @@ fn main() {
args.len() > 1,
));
return;
} else if args[0] == "--extract" {
#[cfg(feature = "with_rc")]
hbb_common::allow_err!(crate::rc::extract_resources(&args[1]));
return;
}
}
if args[0] == "--remove" {

View File

@ -143,7 +143,75 @@ pub fn get_cursor_data(hcursor: u64) -> ResultType<CursorData> {
}
}
fn start_uinput_service() {
use crate::server::input_service::uinput::service;
std::thread::spawn(|| {
service::start_service_control();
});
std::thread::spawn(|| {
service::start_service_keyboard();
});
std::thread::spawn(|| {
service::start_service_mouse();
});
}
fn try_start_user_service(username: &str) {
if username == "" || username == "root" {
return;
}
if let Ok(mut cur_username) =
run_cmds("ps -ef | grep -E 'rustdesk +--server' | awk '{print $1}' | head -1".to_owned())
{
cur_username = cur_username.trim().to_owned();
if cur_username != "root" && cur_username != username {
let _ = run_cmds(format!(
"systemctl --machine={}@.host --user stop rustdesk",
&cur_username
));
} else if cur_username == username {
return;
}
}
let _ = run_cmds(format!(
"systemctl --machine={}@.host --user start rustdesk",
username
));
}
fn try_stop_user_service() {
if let Ok(mut username) =
run_cmds("ps -ef | grep -E 'rustdesk +--server' | awk '{print $1}' | head -1".to_owned())
{
username = username.trim().to_owned();
if username != "root" {
let _ = run_cmds(format!(
"systemctl --machine={}@.host --user stop rustdesk",
&username
));
}
}
}
fn stop_server(server: &mut Option<std::process::Child>) {
if let Some(mut ps) = server.take() {
allow_err!(ps.kill());
std::thread::sleep(std::time::Duration::from_millis(30));
match ps.try_wait() {
Ok(Some(_status)) => {}
Ok(None) => {
let _res = ps.wait();
}
Err(e) => log::error!("error attempting to wait: {e}"),
}
}
}
pub fn start_os_service() {
start_uinput_service();
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
let mut uid = "".to_owned();
@ -157,85 +225,106 @@ pub fn start_os_service() {
let mut cm0 = false;
let mut last_restart = std::time::Instant::now();
while running.load(Ordering::SeqCst) {
let cm = get_cm();
let tmp = get_active_userid();
let mut start_new = false;
if tmp != uid && !tmp.is_empty() {
uid = tmp;
log::info!("uid of seat0: {}", uid);
let gdm = format!("/run/user/{}/gdm/Xauthority", uid);
let mut auth = get_env_tries("XAUTHORITY", &uid, 10);
if auth.is_empty() {
auth = if std::path::Path::new(&gdm).exists() {
gdm
} else {
let username = get_active_username();
if username == "root" {
format!("/{}/.Xauthority", username)
let username = get_active_username();
let is_wayland = current_is_wayland();
if username == "root" || !is_wayland {
// try stop user service
try_stop_user_service();
// try start subprocess "--server"
let cm = get_cm();
let tmp = get_active_userid();
let mut start_new = false;
if tmp != uid && !tmp.is_empty() {
uid = tmp;
log::info!("uid of seat0: {}", uid);
let gdm = format!("/run/user/{}/gdm/Xauthority", uid);
let mut auth = get_env_tries("XAUTHORITY", &uid, 10);
if auth.is_empty() {
auth = if std::path::Path::new(&gdm).exists() {
gdm
} else {
let tmp = format!("/home/{}/.Xauthority", username);
if std::path::Path::new(&tmp).exists() {
tmp
let username = get_active_username();
if username == "root" {
format!("/{}/.Xauthority", username)
} else {
format!("/var/lib/{}/.Xauthority", username)
let tmp = format!("/home/{}/.Xauthority", username);
if std::path::Path::new(&tmp).exists() {
tmp
} else {
format!("/var/lib/{}/.Xauthority", username)
}
}
}
};
}
let mut d = get_env("DISPLAY", &uid);
if d.is_empty() {
d = get_display();
}
if d.is_empty() {
d = ":0".to_owned();
}
d = d.replace(&whoami::hostname(), "").replace("localhost", "");
log::info!("DISPLAY: {}", d);
log::info!("XAUTHORITY: {}", auth);
std::env::set_var("XAUTHORITY", auth);
std::env::set_var("DISPLAY", d);
if let Some(ps) = server.as_mut() {
allow_err!(ps.kill());
std::thread::sleep(std::time::Duration::from_millis(30));
last_restart = std::time::Instant::now();
}
} else if !cm
&& ((cm0 && last_restart.elapsed().as_secs() > 60)
|| last_restart.elapsed().as_secs() > 3600)
{
// restart server if new connections all closed, or every one hour,
// as a workaround to resolve "SpotUdp" (dns resolve)
// and x server get displays failure issue
if let Some(ps) = server.as_mut() {
allow_err!(ps.kill());
std::thread::sleep(std::time::Duration::from_millis(30));
last_restart = std::time::Instant::now();
log::info!("restart server");
}
}
if let Some(ps) = server.as_mut() {
match ps.try_wait() {
Ok(Some(_)) => {
server = None;
start_new = true;
};
}
_ => {}
let mut d = get_env("DISPLAY", &uid);
if d.is_empty() {
d = get_display();
}
if d.is_empty() {
d = ":0".to_owned();
}
d = d.replace(&whoami::hostname(), "").replace("localhost", "");
log::info!("DISPLAY: {}", d);
log::info!("XAUTHORITY: {}", auth);
std::env::set_var("XAUTHORITY", auth);
std::env::set_var("DISPLAY", d);
if let Some(ps) = server.as_mut() {
allow_err!(ps.kill());
std::thread::sleep(std::time::Duration::from_millis(30));
last_restart = std::time::Instant::now();
}
} else if !cm
&& ((cm0 && last_restart.elapsed().as_secs() > 60)
|| last_restart.elapsed().as_secs() > 3600)
{
// restart server if new connections all closed, or every one hour,
// as a workaround to resolve "SpotUdp" (dns resolve)
// and x server get displays failure issue
if let Some(ps) = server.as_mut() {
allow_err!(ps.kill());
std::thread::sleep(std::time::Duration::from_millis(30));
last_restart = std::time::Instant::now();
log::info!("restart server");
}
}
if let Some(ps) = server.as_mut() {
match ps.try_wait() {
Ok(Some(_)) => {
server = None;
start_new = true;
}
_ => {}
}
} else {
start_new = true;
}
if start_new {
match crate::run_me(vec!["--server"]) {
Ok(ps) => server = Some(ps),
Err(err) => {
log::error!("Failed to start server: {}", err);
}
}
}
cm0 = cm;
} else if username != "" {
if username != "gdm" {
// try kill subprocess "--server"
stop_server(&mut server);
// try start user service
try_start_user_service(&username);
}
} else {
start_new = true;
try_stop_user_service();
stop_server(&mut server);
}
if start_new {
match crate::run_me(vec!["--server"]) {
Ok(ps) => server = Some(ps),
Err(err) => {
log::error!("Failed to start server: {}", err);
}
}
}
cm0 = cm;
std::thread::sleep(std::time::Duration::from_millis(super::SERVICE_INTERVAL));
}
try_stop_user_service();
if let Some(ps) = server.take().as_mut() {
allow_err!(ps.kill());
}

View File

@ -279,6 +279,8 @@ impl Drop for Server {
for s in self.services.values() {
s.join();
}
#[cfg(target_os = "linux")]
video_service::wayland_support::clear();
}
}

View File

@ -237,7 +237,7 @@ impl Connection {
let mut msg_out = Message::new();
msg_out.set_misc(misc);
conn.send(msg_out).await;
conn.on_close("Close requested from connection manager", false);
conn.on_close("Close requested from connection manager", false).await;
SESSIONS.lock().unwrap().remove(&conn.lr.my_id);
break;
}
@ -327,7 +327,7 @@ impl Connection {
if let Some(res) = res {
match res {
Err(err) => {
conn.on_close(&err.to_string(), true);
conn.on_close(&err.to_string(), true).await;
break;
},
Ok(bytes) => {
@ -341,14 +341,14 @@ impl Connection {
}
}
} else {
conn.on_close("Reset by the peer", true);
conn.on_close("Reset by the peer", true).await;
break;
}
},
_ = conn.timer.tick() => {
if !conn.read_jobs.is_empty() {
if let Err(err) = fs::handle_read_jobs(&mut conn.read_jobs, &mut conn.stream).await {
conn.on_close(&err.to_string(), false);
conn.on_close(&err.to_string(), false).await;
break;
}
} else {
@ -361,7 +361,7 @@ impl Connection {
video_service::notify_video_frame_feched(id, Some(instant.into()));
}
if let Err(err) = conn.stream.send(&value as &Message).await {
conn.on_close(&err.to_string(), false);
conn.on_close(&err.to_string(), false).await;
break;
}
},
@ -379,13 +379,13 @@ impl Connection {
}
}
if let Err(err) = conn.stream.send(msg).await {
conn.on_close(&err.to_string(), false);
conn.on_close(&err.to_string(), false).await;
break;
}
},
_ = test_delay_timer.tick() => {
if last_recv_time.elapsed() >= SEC30 {
conn.on_close("Timeout", true);
conn.on_close("Timeout", true).await;
break;
}
let time = crate::get_time();
@ -417,7 +417,7 @@ impl Connection {
video_service::VIDEO_QOS.lock().unwrap().reset();
password::after_session(conn.authorized);
if let Err(err) = conn.try_port_forward_loop(&mut rx_from_cm).await {
conn.on_close(&err.to_string(), false);
conn.on_close(&err.to_string(), false).await;
}
conn.post_audit(json!({
@ -646,9 +646,9 @@ impl Connection {
#[cfg(target_os = "linux")]
if !self.file_transfer.is_some() && !self.port_forward_socket.is_some() {
let dtype = crate::platform::linux::get_display_server();
if dtype != "x11" {
if dtype != "x11" && dtype != "wayland" {
res.set_error(format!(
"Unsupported display server type {}, x11 expected",
"Unsupported display server type {}, x11 or wayland expected",
dtype
));
let mut msg_out = Message::new();
@ -684,7 +684,7 @@ impl Connection {
res.set_peer_info(pi);
} else {
try_activate_screen();
match video_service::get_displays() {
match super::video_service::get_displays().await {
Err(err) => {
res.set_error(format!("X11 error: {}", err));
}
@ -1175,7 +1175,7 @@ impl Connection {
},
Some(message::Union::Misc(misc)) => match misc.union {
Some(misc::Union::SwitchDisplay(s)) => {
video_service::switch_display(s.display);
video_service::switch_display(s.display).await;
}
Some(misc::Union::ChatMessage(c)) => {
self.send_to_cm(ipc::Data::ChatMessage { text: c.text });
@ -1185,7 +1185,7 @@ impl Connection {
}
Some(misc::Union::RefreshVideo(r)) => {
if r {
video_service::refresh();
super::video_service::refresh();
}
}
Some(misc::Union::VideoReceived(_)) => {
@ -1195,7 +1195,7 @@ impl Connection {
);
}
Some(misc::Union::CloseReason(_)) => {
self.on_close("Peer close", true);
self.on_close("Peer close", true).await;
SESSIONS.lock().unwrap().remove(&self.lr.my_id);
return false;
}
@ -1353,14 +1353,14 @@ impl Connection {
}
}
fn on_close(&mut self, reason: &str, lock: bool) {
async fn on_close(&mut self, reason: &str, lock: bool) {
if let Some(s) = self.server.upgrade() {
s.write().unwrap().remove_connection(&self.inner);
}
log::info!("#{} Connection closed: {}", self.inner.id(), reason);
if lock && self.lock_after_session_end && self.keyboard {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
lock_screen();
lock_screen().await;
}
self.tx_to_cm.send(ipc::Data::Close).ok();
self.port_forward_socket.take();

View File

@ -187,6 +187,26 @@ lazy_static::lazy_static! {
static ref IS_SERVER: bool = std::env::args().nth(1) == Some("--server".to_owned());
}
#[cfg(target_os = "linux")]
pub async fn set_uinput() -> ResultType<()> {
// Keyboard and mouse both open /dev/uinput
// TODO: Make sure there's no race
let keyboard = self::uinput::client::UInputKeyboard::new().await?;
log::info!("UInput keyboard created");
let mouse = self::uinput::client::UInputMouse::new().await?;
log::info!("UInput mouse created");
let mut en = ENIGO.lock().unwrap();
en.set_uinput_keyboard(Some(Box::new(keyboard)));
en.set_uinput_mouse(Some(Box::new(mouse)));
Ok(())
}
#[cfg(target_os = "linux")]
pub async fn set_uinput_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> {
self::uinput::client::set_resolution(minx, maxx, miny, maxy).await
}
pub fn is_left_up(evt: &MouseEvent) -> bool {
let buttons = evt.mask >> 3;
let evt_type = evt.mask & 0x7;
@ -439,7 +459,7 @@ pub fn is_enter(evt: &KeyEvent) -> bool {
return false;
}
pub fn lock_screen() {
pub async fn lock_screen() {
cfg_if::cfg_if! {
if #[cfg(target_os = "linux")] {
// xdg_screensaver lock not work on Linux from our service somehow
@ -469,7 +489,7 @@ pub fn lock_screen() {
crate::platform::lock_screen();
}
}
super::video_service::switch_to_primary();
super::video_service::switch_to_primary().await;
}
lazy_static::lazy_static! {
@ -548,7 +568,6 @@ lazy_static::lazy_static! {
(ControlKey::Equals, Key::Equals),
(ControlKey::NumpadEnter, Key::NumpadEnter),
(ControlKey::RAlt, Key::RightAlt),
(ControlKey::RWin, Key::RWin),
(ControlKey::RControl, Key::RightControl),
(ControlKey::RShift, Key::RightShift),
].iter().map(|(a, b)| (a.value(), b.clone())).collect();
@ -679,7 +698,7 @@ fn handle_key_(evt: &KeyEvent) {
allow_err!(send_sas());
});
} else if ck.value() == ControlKey::LockScreen.value() {
lock_screen();
lock_screen_2();
}
}
Some(key_event::Union::Chr(chr)) => {
@ -729,9 +748,669 @@ fn handle_key_(evt: &KeyEvent) {
}
}
#[tokio::main(flavor = "current_thread")]
async fn lock_screen_2() {
lock_screen().await;
}
#[tokio::main(flavor = "current_thread")]
async fn send_sas() -> ResultType<()> {
let mut stream = crate::ipc::connect(1000, crate::POSTFIX_SERVICE).await?;
timeout(1000, stream.send(&crate::ipc::Data::SAS)).await??;
Ok(())
}
#[cfg(target_os = "linux")]
pub mod uinput {
use crate::ipc::{self, new_listener, Connection, Data, DataKeyboard, DataMouse};
use enigo::{Key, KeyboardControllable, MouseButton, MouseControllable};
use evdev::{
uinput::{VirtualDevice, VirtualDeviceBuilder},
AttributeSet, EventType, InputEvent,
};
use hbb_common::{allow_err, bail, log, tokio, ResultType};
static IPC_CONN_TIMEOUT: u64 = 1000;
static IPC_REQUEST_TIMEOUT: u64 = 1000;
static IPC_POSTFIX_KEYBOARD: &str = "_uinput_keyboard";
static IPC_POSTFIX_MOUSE: &str = "_uinput_mouse";
static IPC_POSTFIX_CONTROL: &str = "_uinput_control";
pub mod client {
use super::*;
pub struct UInputKeyboard {
conn: Connection,
}
impl UInputKeyboard {
pub async fn new() -> ResultType<Self> {
let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_KEYBOARD).await?;
Ok(Self { conn })
}
#[tokio::main(flavor = "current_thread")]
async fn send(&mut self, data: Data) -> ResultType<()> {
self.conn.send(&data).await
}
#[tokio::main(flavor = "current_thread")]
async fn send_get_key_state(&mut self, data: Data) -> ResultType<bool> {
self.conn.send(&data).await?;
match self.conn.next_timeout(IPC_REQUEST_TIMEOUT).await {
Ok(Some(Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState(
state,
)))) => Ok(state),
Ok(Some(resp)) => {
// FATAL error!!!
bail!(
"FATAL error, wait keyboard result other response: {:?}",
&resp
);
}
Ok(None) => {
// FATAL error!!!
// Maybe wait later
bail!("FATAL error, wait keyboard result, receive None",);
}
Err(e) => {
// FATAL error!!!
bail!(
"FATAL error, wait keyboard result timeout {}, {}",
&e,
IPC_REQUEST_TIMEOUT
);
}
}
}
}
impl KeyboardControllable for UInputKeyboard {
fn get_key_state(&mut self, key: Key) -> bool {
match self.send_get_key_state(Data::Keyboard(DataKeyboard::GetKeyState(key))) {
Ok(state) => state,
Err(e) => {
// unreachable!()
log::error!("Failed to get key state {}", &e);
false
}
}
}
fn key_sequence(&mut self, sequence: &str) {
allow_err!(self.send(Data::Keyboard(DataKeyboard::Sequence(sequence.to_string()))));
}
// TODO: handle error???
fn key_down(&mut self, key: Key) -> enigo::ResultType {
allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyDown(key))));
Ok(())
}
fn key_up(&mut self, key: Key) {
allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyUp(key))));
}
fn key_click(&mut self, key: Key) {
allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyClick(key))));
}
}
pub struct UInputMouse {
conn: Connection,
}
impl UInputMouse {
pub async fn new() -> ResultType<Self> {
let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_MOUSE).await?;
Ok(Self { conn })
}
#[tokio::main(flavor = "current_thread")]
async fn send(&mut self, data: Data) -> ResultType<()> {
self.conn.send(&data).await
}
}
impl MouseControllable for UInputMouse {
fn mouse_move_to(&mut self, x: i32, y: i32) {
allow_err!(self.send(Data::Mouse(DataMouse::MoveTo(x, y))));
}
fn mouse_move_relative(&mut self, x: i32, y: i32) {
allow_err!(self.send(Data::Mouse(DataMouse::MoveRelative(x, y))));
}
// TODO: handle error???
fn mouse_down(&mut self, button: MouseButton) -> enigo::ResultType {
allow_err!(self.send(Data::Mouse(DataMouse::Down(button))));
Ok(())
}
fn mouse_up(&mut self, button: MouseButton) {
allow_err!(self.send(Data::Mouse(DataMouse::Up(button))));
}
fn mouse_click(&mut self, button: MouseButton) {
allow_err!(self.send(Data::Mouse(DataMouse::Click(button))));
}
fn mouse_scroll_x(&mut self, length: i32) {
allow_err!(self.send(Data::Mouse(DataMouse::ScrollX(length))));
}
fn mouse_scroll_y(&mut self, length: i32) {
allow_err!(self.send(Data::Mouse(DataMouse::ScrollY(length))));
}
}
pub async fn set_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> {
let mut conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_CONTROL).await?;
conn.send(&Data::Control(ipc::DataControl::Resolution {
minx,
maxx,
miny,
maxy,
}))
.await?;
let _ = conn.next().await?;
Ok(())
}
}
pub mod service {
use super::*;
use hbb_common::lazy_static;
use mouce::MouseActions;
use std::{collections::HashMap, sync::Mutex};
lazy_static::lazy_static! {
static ref KEY_MAP: HashMap<enigo::Key, evdev::Key> = HashMap::from(
[
(enigo::Key::Alt, evdev::Key::KEY_LEFTALT),
(enigo::Key::Backspace, evdev::Key::KEY_BACKSPACE),
(enigo::Key::CapsLock, evdev::Key::KEY_CAPSLOCK),
(enigo::Key::Control, evdev::Key::KEY_LEFTCTRL),
(enigo::Key::Delete, evdev::Key::KEY_DELETE),
(enigo::Key::DownArrow, evdev::Key::KEY_DOWN),
(enigo::Key::End, evdev::Key::KEY_END),
(enigo::Key::Escape, evdev::Key::KEY_ESC),
(enigo::Key::F1, evdev::Key::KEY_F1),
(enigo::Key::F10, evdev::Key::KEY_F10),
(enigo::Key::F11, evdev::Key::KEY_F11),
(enigo::Key::F12, evdev::Key::KEY_F12),
(enigo::Key::F2, evdev::Key::KEY_F2),
(enigo::Key::F3, evdev::Key::KEY_F3),
(enigo::Key::F4, evdev::Key::KEY_F4),
(enigo::Key::F5, evdev::Key::KEY_F5),
(enigo::Key::F6, evdev::Key::KEY_F6),
(enigo::Key::F7, evdev::Key::KEY_F7),
(enigo::Key::F8, evdev::Key::KEY_F8),
(enigo::Key::F9, evdev::Key::KEY_F9),
(enigo::Key::Home, evdev::Key::KEY_HOME),
(enigo::Key::LeftArrow, evdev::Key::KEY_LEFT),
(enigo::Key::Meta, evdev::Key::KEY_LEFTMETA),
(enigo::Key::Option, evdev::Key::KEY_OPTION),
(enigo::Key::PageDown, evdev::Key::KEY_PAGEDOWN),
(enigo::Key::PageUp, evdev::Key::KEY_PAGEUP),
(enigo::Key::Return, evdev::Key::KEY_ENTER),
(enigo::Key::RightArrow, evdev::Key::KEY_RIGHT),
(enigo::Key::Shift, evdev::Key::KEY_LEFTSHIFT),
(enigo::Key::Space, evdev::Key::KEY_SPACE),
(enigo::Key::Tab, evdev::Key::KEY_TAB),
(enigo::Key::UpArrow, evdev::Key::KEY_UP),
(enigo::Key::Numpad0, evdev::Key::KEY_KP0), // check if correct?
(enigo::Key::Numpad1, evdev::Key::KEY_KP1),
(enigo::Key::Numpad2, evdev::Key::KEY_KP2),
(enigo::Key::Numpad3, evdev::Key::KEY_KP3),
(enigo::Key::Numpad4, evdev::Key::KEY_KP4),
(enigo::Key::Numpad5, evdev::Key::KEY_KP5),
(enigo::Key::Numpad6, evdev::Key::KEY_KP6),
(enigo::Key::Numpad7, evdev::Key::KEY_KP7),
(enigo::Key::Numpad8, evdev::Key::KEY_KP8),
(enigo::Key::Numpad9, evdev::Key::KEY_KP9),
(enigo::Key::Cancel, evdev::Key::KEY_CANCEL),
(enigo::Key::Clear, evdev::Key::KEY_CLEAR),
(enigo::Key::Alt, evdev::Key::KEY_LEFTALT),
(enigo::Key::Pause, evdev::Key::KEY_PAUSE),
(enigo::Key::Kana, evdev::Key::KEY_KATAKANA), // check if correct?
(enigo::Key::Hangul, evdev::Key::KEY_HANGEUL), // check if correct?
// (enigo::Key::Junja, evdev::Key::KEY_JUNJA), // map?
// (enigo::Key::Final, evdev::Key::KEY_FINAL), // map?
(enigo::Key::Hanja, evdev::Key::KEY_HANJA),
// (enigo::Key::Kanji, evdev::Key::KEY_KANJI), // map?
// (enigo::Key::Convert, evdev::Key::KEY_CONVERT),
(enigo::Key::Select, evdev::Key::KEY_SELECT),
(enigo::Key::Print, evdev::Key::KEY_PRINT),
// (enigo::Key::Execute, evdev::Key::KEY_EXECUTE),
// (enigo::Key::Snapshot, evdev::Key::KEY_SNAPSHOT),
(enigo::Key::Insert, evdev::Key::KEY_INSERT),
(enigo::Key::Help, evdev::Key::KEY_HELP),
(enigo::Key::Sleep, evdev::Key::KEY_SLEEP),
// (enigo::Key::Separator, evdev::Key::KEY_SEPARATOR),
(enigo::Key::Scroll, evdev::Key::KEY_SCROLLLOCK),
(enigo::Key::NumLock, evdev::Key::KEY_NUMLOCK),
(enigo::Key::RWin, evdev::Key::KEY_RIGHTMETA),
(enigo::Key::Apps, evdev::Key::KEY_CONTEXT_MENU),
(enigo::Key::Multiply, evdev::Key::KEY_KPASTERISK),
(enigo::Key::Add, evdev::Key::KEY_KPPLUS),
(enigo::Key::Subtract, evdev::Key::KEY_KPMINUS),
(enigo::Key::Decimal, evdev::Key::KEY_KPCOMMA), // KEY_KPDOT and KEY_KPCOMMA are exchanged?
(enigo::Key::Divide, evdev::Key::KEY_KPSLASH),
(enigo::Key::Equals, evdev::Key::KEY_KPEQUAL),
(enigo::Key::NumpadEnter, evdev::Key::KEY_KPENTER),
(enigo::Key::RightAlt, evdev::Key::KEY_RIGHTALT),
(enigo::Key::RightControl, evdev::Key::KEY_RIGHTCTRL),
(enigo::Key::RightShift, evdev::Key::KEY_RIGHTSHIFT),
]);
static ref KEY_MAP_LAYOUT: HashMap<char, evdev::Key> = HashMap::from(
[
('a', evdev::Key::KEY_A),
('b', evdev::Key::KEY_B),
('c', evdev::Key::KEY_C),
('d', evdev::Key::KEY_D),
('e', evdev::Key::KEY_E),
('f', evdev::Key::KEY_F),
('g', evdev::Key::KEY_G),
('h', evdev::Key::KEY_H),
('i', evdev::Key::KEY_I),
('j', evdev::Key::KEY_J),
('k', evdev::Key::KEY_K),
('l', evdev::Key::KEY_L),
('m', evdev::Key::KEY_M),
('n', evdev::Key::KEY_N),
('o', evdev::Key::KEY_O),
('p', evdev::Key::KEY_P),
('q', evdev::Key::KEY_Q),
('r', evdev::Key::KEY_R),
('s', evdev::Key::KEY_S),
('t', evdev::Key::KEY_T),
('u', evdev::Key::KEY_U),
('v', evdev::Key::KEY_V),
('w', evdev::Key::KEY_W),
('x', evdev::Key::KEY_X),
('y', evdev::Key::KEY_Y),
('z', evdev::Key::KEY_Z),
('0', evdev::Key::KEY_0),
('1', evdev::Key::KEY_1),
('2', evdev::Key::KEY_2),
('3', evdev::Key::KEY_3),
('4', evdev::Key::KEY_4),
('5', evdev::Key::KEY_5),
('6', evdev::Key::KEY_6),
('7', evdev::Key::KEY_7),
('8', evdev::Key::KEY_8),
('9', evdev::Key::KEY_9),
('`', evdev::Key::KEY_GRAVE),
('-', evdev::Key::KEY_MINUS),
('=', evdev::Key::KEY_EQUAL),
('[', evdev::Key::KEY_LEFTBRACE),
(']', evdev::Key::KEY_RIGHTBRACE),
('\\', evdev::Key::KEY_BACKSLASH),
(',', evdev::Key::KEY_COMMA),
('.', evdev::Key::KEY_DOT),
('/', evdev::Key::KEY_SLASH),
(';', evdev::Key::KEY_SEMICOLON),
('\'', evdev::Key::KEY_APOSTROPHE),
]);
// ((minx, maxx), (miny, maxy))
static ref RESOLUTION: Mutex<((i32, i32), (i32, i32))> = Mutex::new(((0, 0), (0, 0)));
}
fn create_uinput_keyboard() -> ResultType<VirtualDevice> {
// TODO: ensure keys here
let mut keys = AttributeSet::<evdev::Key>::new();
for i in evdev::Key::KEY_ESC.code()..(evdev::Key::BTN_TRIGGER_HAPPY40.code() + 1) {
let key = evdev::Key::new(i);
if !format!("{:?}", &key).contains("unknown key") {
keys.insert(key);
}
}
let mut leds = AttributeSet::<evdev::LedType>::new();
leds.insert(evdev::LedType::LED_NUML);
leds.insert(evdev::LedType::LED_CAPSL);
leds.insert(evdev::LedType::LED_SCROLLL);
let mut miscs = AttributeSet::<evdev::MiscType>::new();
miscs.insert(evdev::MiscType::MSC_SCAN);
let keyboard = VirtualDeviceBuilder::new()?
.name("RustDesk UInput Keyboard")
.with_keys(&keys)?
.with_leds(&leds)?
.with_miscs(&miscs)?
.build()?;
Ok(keyboard)
}
fn map_key(key: &enigo::Key) -> ResultType<evdev::Key> {
if let Some(k) = KEY_MAP.get(&key) {
log::trace!("mapkey {:?}, get {:?}", &key, &k);
return Ok(k.clone());
} else {
match key {
enigo::Key::Layout(c) => {
if let Some(k) = KEY_MAP_LAYOUT.get(&c) {
log::trace!("mapkey {:?}, get {:?}", &key, k);
return Ok(k.clone());
}
}
// enigo::Key::Raw(c) => {
// let k = evdev::Key::new(c);
// if !format!("{:?}", &k).contains("unknown key") {
// return Ok(k.clone());
// }
// }
_ => {}
}
}
bail!("Failed to map key {:?}", &key);
}
async fn ipc_send_data(stream: &mut Connection, data: &Data) {
allow_err!(stream.send(data).await);
}
async fn handle_keyboard(
stream: &mut Connection,
keyboard: &mut VirtualDevice,
data: &DataKeyboard,
) {
log::trace!("handle_keyboard {:?}", &data);
match data {
DataKeyboard::Sequence(_seq) => {
// ignore
}
DataKeyboard::KeyDown(key) => {
if let Ok(k) = map_key(key) {
let down_event = InputEvent::new(EventType::KEY, k.code(), 1);
allow_err!(keyboard.emit(&[down_event]));
}
}
DataKeyboard::KeyUp(key) => {
if let Ok(k) = map_key(key) {
let up_event = InputEvent::new(EventType::KEY, k.code(), 0);
allow_err!(keyboard.emit(&[up_event]));
}
}
DataKeyboard::KeyClick(key) => {
if let Ok(k) = map_key(key) {
let down_event = InputEvent::new(EventType::KEY, k.code(), 1);
let up_event = InputEvent::new(EventType::KEY, k.code(), 0);
allow_err!(keyboard.emit(&[down_event, up_event]));
}
}
DataKeyboard::GetKeyState(key) => {
let key_state = if enigo::Key::CapsLock == *key {
match keyboard.get_led_state() {
Ok(leds) => leds.contains(evdev::LedType::LED_CAPSL),
Err(_e) => {
// log::debug!("Failed to get led state {}", &_e);
false
}
}
} else {
match keyboard.get_key_state() {
Ok(keys) => match key {
enigo::Key::Shift => {
keys.contains(evdev::Key::KEY_LEFTSHIFT)
|| keys.contains(evdev::Key::KEY_RIGHTSHIFT)
}
enigo::Key::Control => {
keys.contains(evdev::Key::KEY_LEFTCTRL)
|| keys.contains(evdev::Key::KEY_RIGHTCTRL)
}
enigo::Key::Alt => {
keys.contains(evdev::Key::KEY_LEFTALT)
|| keys.contains(evdev::Key::KEY_RIGHTALT)
}
enigo::Key::NumLock => keys.contains(evdev::Key::KEY_NUMLOCK),
enigo::Key::Meta => {
keys.contains(evdev::Key::KEY_LEFTMETA)
|| keys.contains(evdev::Key::KEY_RIGHTMETA)
}
_ => false,
},
Err(_e) => {
// log::debug!("Failed to get key state: {}", &_e);
false
}
}
};
ipc_send_data(
stream,
&Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState(key_state)),
)
.await;
}
}
}
fn handle_mouse(mouse: &mut mouce::nix::UInputMouseManager, data: &DataMouse) {
log::trace!("handle_mouse {:?}", &data);
match data {
DataMouse::MoveTo(x, y) => {
allow_err!(mouse.move_to(*x as _, *y as _))
}
DataMouse::MoveRelative(x, y) => {
allow_err!(mouse.move_relative(*x, *y))
}
DataMouse::Down(button) => {
let btn = match button {
enigo::MouseButton::Left => mouce::common::MouseButton::Left,
enigo::MouseButton::Middle => mouce::common::MouseButton::Middle,
enigo::MouseButton::Right => mouce::common::MouseButton::Right,
_ => {
return;
}
};
allow_err!(mouse.press_button(&btn))
}
DataMouse::Up(button) => {
let btn = match button {
enigo::MouseButton::Left => mouce::common::MouseButton::Left,
enigo::MouseButton::Middle => mouce::common::MouseButton::Middle,
enigo::MouseButton::Right => mouce::common::MouseButton::Right,
_ => {
return;
}
};
allow_err!(mouse.release_button(&btn))
}
DataMouse::Click(button) => {
let btn = match button {
enigo::MouseButton::Left => mouce::common::MouseButton::Left,
enigo::MouseButton::Middle => mouce::common::MouseButton::Middle,
enigo::MouseButton::Right => mouce::common::MouseButton::Right,
_ => {
return;
}
};
allow_err!(mouse.click_button(&btn))
}
DataMouse::ScrollX(_length) => {
// TODO: not supported for now
}
DataMouse::ScrollY(length) => {
let mut length = *length;
let scroll = if length < 0 {
mouce::common::ScrollDirection::Up
} else {
mouce::common::ScrollDirection::Down
};
if length < 0 {
length = -length;
}
for _ in 0..length {
allow_err!(mouse.scroll_wheel(&scroll))
}
}
}
}
fn spawn_keyboard_handler(mut stream: Connection) {
tokio::spawn(async move {
let mut keyboard = match create_uinput_keyboard() {
Ok(keyboard) => keyboard,
Err(e) => {
log::error!("Failed to create keyboard {}", e);
return;
}
};
loop {
tokio::select! {
res = stream.next() => {
match res {
Err(err) => {
log::info!("UInput keyboard ipc connection closed: {}", err);
break;
}
Ok(Some(data)) => {
match data {
Data::Keyboard(data) => {
handle_keyboard(&mut stream, &mut keyboard, &data).await;
}
_ => {
}
}
}
_ => {}
}
}
}
}
});
}
fn spawn_mouse_handler(mut stream: ipc::Connection) {
let resolution = RESOLUTION.lock().unwrap();
if resolution.0 .0 == resolution.0 .1 || resolution.1 .0 == resolution.1 .1 {
return;
}
let rng_x = resolution.0.clone();
let rng_y = resolution.1.clone();
tokio::spawn(async move {
log::info!(
"Create uinput mouce with rng_x: ({}, {}), rng_y: ({}, {})",
rng_x.0,
rng_x.1,
rng_y.0,
rng_y.1
);
let mut mouse = match mouce::Mouse::new_uinput(rng_x, rng_y) {
Ok(mouse) => mouse,
Err(e) => {
log::error!("Failed to create mouse, {}", e);
return;
}
};
loop {
tokio::select! {
res = stream.next() => {
match res {
Err(err) => {
log::info!("UInput mouse ipc connection closed: {}", err);
break;
}
Ok(Some(data)) => {
match data {
Data::Mouse(data) => {
handle_mouse(&mut mouse, &data);
}
_ => {
}
}
}
_ => {}
}
}
}
}
});
}
fn spawn_controller_handler(mut stream: ipc::Connection) {
tokio::spawn(async move {
loop {
tokio::select! {
res = stream.next() => {
match res {
Err(_err) => {
// log::info!("UInput controller ipc connection closed: {}", err);
break;
}
Ok(Some(data)) => {
match data {
Data::Control(data) => match data {
ipc::DataControl::Resolution{
minx,
maxx,
miny,
maxy,
} => {
*RESOLUTION.lock().unwrap() = ((minx, maxx), (miny, maxy));
allow_err!(stream.send(&Data::Empty).await);
}
}
_ => {
}
}
}
_ => {}
}
}
}
}
});
}
/// Start uinput service.
async fn start_service<F: FnOnce(ipc::Connection) + Copy>(postfix: &str, handler: F) {
match new_listener(postfix).await {
Ok(mut incoming) => {
while let Some(result) = incoming.next().await {
match result {
Ok(stream) => {
log::debug!("Got new connection of uinput ipc {}", postfix);
handler(Connection::new(stream));
}
Err(err) => {
log::error!("Couldn't get uinput mouse client: {:?}", err);
}
}
}
}
Err(err) => {
log::error!("Failed to start uinput mouse ipc service: {}", err);
}
}
}
/// Start uinput keyboard service.
#[tokio::main(flavor = "current_thread")]
pub async fn start_service_keyboard() {
log::info!("start uinput keyboard service");
start_service(IPC_POSTFIX_KEYBOARD, spawn_keyboard_handler).await;
}
/// Start uinput mouse service.
#[tokio::main(flavor = "current_thread")]
pub async fn start_service_mouse() {
log::info!("start uinput mouse service");
start_service(IPC_POSTFIX_MOUSE, spawn_mouse_handler).await;
}
/// Start uinput mouse service.
#[tokio::main(flavor = "current_thread")]
pub async fn start_service_control() {
log::info!("start uinput control service");
start_service(IPC_POSTFIX_CONTROL, spawn_controller_handler).await;
}
pub fn stop_service_keyboard() {
log::info!("stop uinput keyboard service");
}
pub fn stop_service_mouse() {
log::info!("stop uinput mouse service");
}
pub fn stop_service_control() {
log::info!("stop uinput control service");
}
}
}

View File

@ -130,6 +130,8 @@ impl VideoFrameController {
trait TraitCapturer {
fn frame<'a>(&'a mut self, timeout: Duration) -> Result<Frame<'a>>;
fn set_use_yuv(&mut self, use_yuv: bool);
#[cfg(windows)]
fn is_gdi(&self) -> bool;
#[cfg(windows)]
@ -141,6 +143,10 @@ impl TraitCapturer for Capturer {
self.frame(timeout)
}
fn set_use_yuv(&mut self, use_yuv: bool) {
self.set_use_yuv(use_yuv);
}
#[cfg(windows)]
fn is_gdi(&self) -> bool {
self.is_gdi()
@ -158,6 +164,10 @@ impl TraitCapturer for scrap::CapturerMag {
self.frame(_timeout_ms)
}
fn set_use_yuv(&mut self, use_yuv: bool) {
self.set_use_yuv(use_yuv);
}
fn is_gdi(&self) -> bool {
false
}
@ -179,6 +189,14 @@ fn check_display_changed(
last_width: usize,
last_hegiht: usize,
) -> bool {
#[cfg(target_os = "linux")]
{
// wayland do not support changing display for now
if scrap::is_wayland() {
return false;
}
}
let displays = match try_get_displays() {
Ok(d) => d,
_ => return false,
@ -293,6 +311,7 @@ fn ensure_close_virtual_device() -> ResultType<()> {
Ok(())
}
// This function works on privacy mode. Windows only for now.
pub fn test_create_capturer(privacy_mode_id: i32, timeout_millis: u64) -> bool {
let test_begin = Instant::now();
while test_begin.elapsed().as_millis() < timeout_millis as _ {
@ -321,9 +340,24 @@ fn check_uac_switch(privacy_mode_id: i32, captuerer_privacy_mode_id: i32) -> Res
Ok(())
}
fn run(sp: GenericService) -> ResultType<()> {
#[cfg(windows)]
ensure_close_virtual_device()?;
struct CapturerInfo {
origin: (i32, i32),
width: usize,
height: usize,
ndisplay: usize,
current: usize,
privacy_mode_id: i32,
_captuerer_privacy_mode_id: i32,
capturer: Box<dyn TraitCapturer>,
}
fn get_capturer(use_yuv: bool) -> ResultType<CapturerInfo> {
#[cfg(target_os = "linux")]
{
if scrap::is_wayland() {
return wayland_support::get_capturer();
}
}
let (ndisplay, current, display) = get_current_display()?;
let (origin, width, height) = (display.origin(), display.width(), display.height());
@ -338,38 +372,6 @@ fn run(sp: GenericService) -> ResultType<()> {
num_cpus::get(),
);
let mut video_qos = VIDEO_QOS.lock().unwrap();
video_qos.set_size(width as _, height as _);
let mut spf = video_qos.spf();
let bitrate = video_qos.generate_bitrate()?;
let abr = video_qos.check_abr_config();
drop(video_qos);
log::info!("init bitrate={}, abr enabled:{}", bitrate, abr);
let encoder_cfg = match Encoder::current_hw_encoder_name() {
Some(codec_name) => EncoderCfg::HW(HwEncoderConfig {
codec_name,
width,
height,
bitrate: bitrate as _,
}),
None => EncoderCfg::VPX(VpxEncoderConfig {
width: width as _,
height: height as _,
timebase: [1, 1000], // Output timestamp precision
bitrate,
codec: VpxVideoCodecId::VP9,
num_threads: (num_cpus::get() / 2) as _,
}),
};
let mut encoder;
match Encoder::new(encoder_cfg) {
Ok(x) => encoder = x,
Err(err) => bail!("Failed to create encoder: {}", err),
}
let privacy_mode_id = *PRIVACY_MODE_CONN_ID.lock().unwrap();
#[cfg(not(windows))]
let captuerer_privacy_mode_id = privacy_mode_id;
@ -389,17 +391,67 @@ fn run(sp: GenericService) -> ResultType<()> {
} else {
log::info!("In privacy mode, the peer side cannot watch the screen");
}
let mut c = create_capturer(captuerer_privacy_mode_id, display, encoder.use_yuv())?;
let capturer = create_capturer(captuerer_privacy_mode_id, display, use_yuv)?;
Ok(CapturerInfo {
origin,
width,
height,
ndisplay,
current,
privacy_mode_id,
_captuerer_privacy_mode_id: captuerer_privacy_mode_id,
capturer,
})
}
fn run(sp: GenericService) -> ResultType<()> {
#[cfg(windows)]
ensure_close_virtual_device()?;
let mut c = get_capturer(true)?;
let mut video_qos = VIDEO_QOS.lock().unwrap();
video_qos.set_size(c.width as _, c.height as _);
let mut spf = video_qos.spf();
let bitrate = video_qos.generate_bitrate()?;
let abr = video_qos.check_abr_config();
drop(video_qos);
log::info!("init bitrate={}, abr enabled:{}", bitrate, abr);
let encoder_cfg = match Encoder::current_hw_encoder_name() {
Some(codec_name) => EncoderCfg::HW(HwEncoderConfig {
codec_name,
width: c.width,
height: c.height,
bitrate: bitrate as _,
}),
None => EncoderCfg::VPX(VpxEncoderConfig {
width: c.width as _,
height: c.height as _,
timebase: [1, 1000], // Output timestamp precision
bitrate,
codec: VpxVideoCodecId::VP9,
num_threads: (num_cpus::get() / 2) as _,
}),
};
let mut encoder;
match Encoder::new(encoder_cfg) {
Ok(x) => encoder = x,
Err(err) => bail!("Failed to create encoder: {}", err),
}
c.capturer.set_use_yuv(encoder.use_yuv());
if *SWITCH.lock().unwrap() {
log::debug!("Broadcasting display switch");
let mut misc = Misc::new();
misc.set_switch_display(SwitchDisplay {
display: current as _,
x: origin.0 as _,
y: origin.1 as _,
width: width as _,
height: height as _,
display: c.current as _,
x: c.origin.0 as _,
y: c.origin.1 as _,
width: c.width as _,
height: c.height as _,
..Default::default()
});
let mut msg_out = Message::new();
@ -415,11 +467,11 @@ fn run(sp: GenericService) -> ResultType<()> {
#[cfg(windows)]
let mut try_gdi = 1;
#[cfg(windows)]
log::info!("gdi: {}", c.is_gdi());
log::info!("gdi: {}", c.capturer.is_gdi());
while sp.ok() {
#[cfg(windows)]
check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?;
check_uac_switch(c.privacy_mode_id, c._captuerer_privacy_mode_id)?;
{
let mut video_qos = VIDEO_QOS.lock().unwrap();
@ -437,11 +489,11 @@ fn run(sp: GenericService) -> ResultType<()> {
if *SWITCH.lock().unwrap() {
bail!("SWITCH");
}
if current != *CURRENT_DISPLAY.lock().unwrap() {
if c.current != *CURRENT_DISPLAY.lock().unwrap() {
*SWITCH.lock().unwrap() = true;
bail!("SWITCH");
}
check_privacy_mode_changed(&sp, privacy_mode_id)?;
check_privacy_mode_changed(&sp, c.privacy_mode_id)?;
#[cfg(windows)]
{
if crate::platform::windows::desktop_changed() {
@ -451,7 +503,7 @@ fn run(sp: GenericService) -> ResultType<()> {
let now = time::Instant::now();
if last_check_displays.elapsed().as_millis() > 1000 {
last_check_displays = now;
if ndisplay != get_display_num() {
if c.ndisplay != get_display_num() {
log::info!("Displays changed");
*SWITCH.lock().unwrap() = true;
bail!("SWITCH");
@ -463,7 +515,7 @@ fn run(sp: GenericService) -> ResultType<()> {
frame_controller.reset();
#[cfg(any(target_os = "android", target_os = "ios"))]
let res = match c.frame(spf) {
let res = match (*c.capturer).frame(spf) {
Ok(frame) => {
let time = now - start;
let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64;
@ -486,7 +538,7 @@ fn run(sp: GenericService) -> ResultType<()> {
};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
let res = match c.frame(spf) {
let res = match (*c.capturer).frame(spf) {
Ok(frame) => {
let time = now - start;
let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64;
@ -505,9 +557,9 @@ fn run(sp: GenericService) -> ResultType<()> {
Err(ref e) if e.kind() == WouldBlock =>
{
#[cfg(windows)]
if try_gdi > 0 && !c.is_gdi() {
if try_gdi > 0 && !c.capturer.is_gdi() {
if try_gdi > 3 {
c.set_gdi();
c.capturer.set_gdi();
try_gdi = 0;
log::info!("No image, fall back to gdi");
}
@ -515,15 +567,15 @@ fn run(sp: GenericService) -> ResultType<()> {
}
}
Err(err) => {
if check_display_changed(ndisplay, current, width, height) {
if check_display_changed(c.ndisplay, c.current, c.width, c.height) {
log::info!("Displays changed");
*SWITCH.lock().unwrap() = true;
bail!("SWITCH");
}
#[cfg(windows)]
if !c.is_gdi() {
c.set_gdi();
if !c.capturer.is_gdi() {
c.capturer.set_gdi();
log::info!("dxgi error, fall back to gdi: {:?}", err);
continue;
}
@ -537,9 +589,9 @@ fn run(sp: GenericService) -> ResultType<()> {
let timeout_millis = 3_000u64;
let wait_begin = Instant::now();
while wait_begin.elapsed().as_millis() < timeout_millis as _ {
check_privacy_mode_changed(&sp, privacy_mode_id)?;
check_privacy_mode_changed(&sp, c.privacy_mode_id)?;
#[cfg(windows)]
check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?;
check_uac_switch(c.privacy_mode_id, c._captuerer_privacy_mode_id)?;
frame_controller.try_wait_next(&mut fetched_conn_ids, 300);
// break if all connections have received current frame
if fetched_conn_ids.len() >= frame_controller.send_conn_ids.len() {
@ -633,6 +685,17 @@ pub fn handle_one_frame_encoded(
}
fn get_display_num() -> usize {
#[cfg(target_os = "linux")]
{
if scrap::is_wayland() {
return if let Ok(n) = wayland_support::get_display_num() {
n
} else {
0
};
}
}
if let Ok(d) = try_get_displays() {
d.len()
} else {
@ -640,14 +703,10 @@ fn get_display_num() -> usize {
}
}
pub fn get_displays() -> ResultType<(usize, Vec<DisplayInfo>)> {
// switch to primary display if long time (30 seconds) no users
if LAST_ACTIVE.lock().unwrap().elapsed().as_secs() >= 30 {
*CURRENT_DISPLAY.lock().unwrap() = usize::MAX;
}
fn get_displays_2(all: &Vec<Display>) -> (usize, Vec<DisplayInfo>) {
let mut displays = Vec::new();
let mut primary = 0;
for (i, d) in try_get_displays()?.iter().enumerate() {
for (i, d) in all.iter().enumerate() {
if d.is_primary() {
primary = i;
}
@ -665,12 +724,26 @@ pub fn get_displays() -> ResultType<(usize, Vec<DisplayInfo>)> {
if *lock >= displays.len() {
*lock = primary
}
Ok((*lock, displays))
(*lock, displays)
}
pub fn switch_display(i: i32) {
pub async fn get_displays() -> ResultType<(usize, Vec<DisplayInfo>)> {
#[cfg(target_os = "linux")]
{
if scrap::is_wayland() {
return wayland_support::get_displays().await;
}
}
// switch to primary display if long time (30 seconds) no users
if LAST_ACTIVE.lock().unwrap().elapsed().as_secs() >= 30 {
*CURRENT_DISPLAY.lock().unwrap() = usize::MAX;
}
Ok(get_displays_2(&try_get_displays()?))
}
pub async fn switch_display(i: i32) {
let i = i as usize;
if let Ok((_, displays)) = get_displays() {
if let Ok((_, displays)) = get_displays().await {
if i < displays.len() {
*CURRENT_DISPLAY.lock().unwrap() = i;
}
@ -684,6 +757,16 @@ pub fn refresh() {
}
fn get_primary() -> usize {
#[cfg(target_os = "linux")]
{
if scrap::is_wayland() {
return match wayland_support::get_primary() {
Ok(n) => n,
Err(_) => 0,
};
}
}
if let Ok(all) = try_get_displays() {
for (i, d) in all.iter().enumerate() {
if d.is_primary() {
@ -694,8 +777,8 @@ fn get_primary() -> usize {
0
}
pub fn switch_to_primary() {
switch_display(get_primary() as _);
pub async fn switch_to_primary() {
switch_display(get_primary() as _).await;
}
#[cfg(not(windows))]
@ -733,16 +816,15 @@ fn try_get_displays() -> ResultType<Vec<Display>> {
Ok(displays)
}
fn get_current_display() -> ResultType<(usize, usize, Display)> {
fn get_current_display_2(mut all: Vec<Display>) -> ResultType<(usize, usize, Display)> {
let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize;
let mut displays = try_get_displays()?;
if displays.len() == 0 {
if all.len() == 0 {
bail!("No displays");
}
let n = displays.len();
let n = all.len();
if current >= n {
current = 0;
for (i, d) in displays.iter().enumerate() {
for (i, d) in all.iter().enumerate() {
if d.is_primary() {
current = i;
break;
@ -750,5 +832,191 @@ fn get_current_display() -> ResultType<(usize, usize, Display)> {
}
*CURRENT_DISPLAY.lock().unwrap() = current;
}
return Ok((n, current, displays.remove(current)));
return Ok((n, current, all.remove(current)));
}
fn get_current_display() -> ResultType<(usize, usize, Display)> {
get_current_display_2(try_get_displays()?)
}
#[cfg(target_os = "linux")]
pub mod wayland_support {
use super::*;
use hbb_common::allow_err;
lazy_static::lazy_static! {
static ref CAP_DISPLAY_INFO: RwLock<u64> = RwLock::new(0);
}
struct CapDisplayInfo {
rects: Vec<((i32, i32), usize, usize)>,
displays: Vec<DisplayInfo>,
num: usize,
primary: usize,
current: usize,
capturer: *mut Capturer,
}
impl TraitCapturer for *mut Capturer {
fn frame<'a>(&'a mut self, timeout: Duration) -> Result<Frame<'a>> {
unsafe { (**self).frame(timeout) }
}
fn set_use_yuv(&mut self, use_yuv: bool) {
unsafe {
(**self).set_use_yuv(use_yuv);
}
}
}
async fn check_init() -> ResultType<()> {
if scrap::is_wayland() {
let mut minx = 0;
let mut maxx = 0;
let mut miny = 0;
let mut maxy = 0;
if *CAP_DISPLAY_INFO.read().unwrap() == 0 {
let mut lock = CAP_DISPLAY_INFO.write().unwrap();
if *lock == 0 {
let all = Display::all()?;
let num = all.len();
let (primary, displays) = get_displays_2(&all);
let mut rects: Vec<((i32, i32), usize, usize)> = Vec::new();
for d in &all {
rects.push((d.origin(), d.width(), d.height()));
}
let (ndisplay, current, display) = get_current_display_2(all)?;
let (origin, width, height) =
(display.origin(), display.width(), display.height());
log::debug!(
"#displays={}, current={}, origin: {:?}, width={}, height={}, cpus={}/{}",
ndisplay,
current,
&origin,
width,
height,
num_cpus::get_physical(),
num_cpus::get(),
);
minx = origin.0;
maxx = origin.0 + width as i32;
miny = origin.1;
maxy = origin.1 + height as i32;
let capturer = Box::into_raw(Box::new(
Capturer::new(display, true)
.with_context(|| "Failed to create capturer")?,
));
let cap_display_info = Box::into_raw(Box::new(CapDisplayInfo {
rects,
displays,
num,
primary,
current,
capturer,
}));
*lock = cap_display_info as _;
}
}
if minx != maxx && miny != maxy {
log::info!(
"send uinput resolution: ({}, {}), ({}, {})",
minx,
maxx,
miny,
maxy
);
allow_err!(input_service::set_uinput_resolution(minx, maxx, miny, maxy).await);
allow_err!(input_service::set_uinput().await);
}
}
Ok(())
}
pub fn clear() {
if !scrap::is_wayland() {
return;
}
let mut lock = CAP_DISPLAY_INFO.write().unwrap();
if *lock != 0 {
unsafe {
let cap_display_info = Box::from_raw(*lock as *mut CapDisplayInfo);
let _ = Box::from_raw(cap_display_info.capturer);
}
*lock = 0;
}
}
pub(super) async fn get_displays() -> ResultType<(usize, Vec<DisplayInfo>)> {
check_init().await?;
let addr = *CAP_DISPLAY_INFO.read().unwrap();
if addr != 0 {
let cap_display_info: *const CapDisplayInfo = addr as _;
unsafe {
let cap_display_info = &*cap_display_info;
let primary = cap_display_info.primary;
let displays = cap_display_info.displays.clone();
Ok((primary, displays))
}
} else {
bail!("Failed to get capturer display info");
}
}
pub(super) fn get_primary() -> ResultType<usize> {
let addr = *CAP_DISPLAY_INFO.read().unwrap();
if addr != 0 {
let cap_display_info: *const CapDisplayInfo = addr as _;
unsafe {
let cap_display_info = &*cap_display_info;
Ok(cap_display_info.primary)
}
} else {
bail!("Failed to get capturer display info");
}
}
pub(super) fn get_display_num() -> ResultType<usize> {
let addr = *CAP_DISPLAY_INFO.read().unwrap();
if addr != 0 {
let cap_display_info: *const CapDisplayInfo = addr as _;
unsafe {
let cap_display_info = &*cap_display_info;
Ok(cap_display_info.num)
}
} else {
bail!("Failed to get capturer display info");
}
}
pub(super) fn get_capturer() -> ResultType<CapturerInfo> {
if !scrap::is_wayland() {
bail!("Do not call this function if not wayland");
}
let addr = *CAP_DISPLAY_INFO.read().unwrap();
if addr != 0 {
let cap_display_info: *const CapDisplayInfo = addr as _;
unsafe {
let cap_display_info = &*cap_display_info;
let rect = cap_display_info.rects[cap_display_info.current];
Ok(CapturerInfo {
origin: rect.0,
width: rect.1,
height: rect.2,
ndisplay: cap_display_info.num,
current: cap_display_info.current,
privacy_mode_id: 0,
_captuerer_privacy_mode_id: 0,
capturer: Box::new(cap_display_info.capturer),
})
}
} else {
bail!("Failed to get capturer display info");
}
}
}