From 634cb5ef1adeec2fc219b46a458a36eb00adb2d9 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 20 Jul 2022 09:50:08 -0700 Subject: [PATCH] linux_wayland_support: refactor Signed-off-by: fufesou --- src/platform/linux.rs | 2 +- src/server.rs | 8 +- src/server/input_service.rs | 661 +----------------------------------- src/server/uinput.rs | 651 +++++++++++++++++++++++++++++++++++ src/server/video_service.rs | 245 +++---------- src/server/wayland.rs | 179 ++++++++++ 6 files changed, 879 insertions(+), 867 deletions(-) create mode 100644 src/server/uinput.rs create mode 100644 src/server/wayland.rs diff --git a/src/platform/linux.rs b/src/platform/linux.rs index b08793d12..14d43e0e1 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -144,7 +144,7 @@ pub fn get_cursor_data(hcursor: u64) -> ResultType { } fn start_uinput_service() { - use crate::server::input_service::uinput::service; + use crate::server::uinput::service; std::thread::spawn(|| { service::start_service_control(); }); diff --git a/src/server.rs b/src/server.rs index f1171e61d..a71efaee2 100644 --- a/src/server.rs +++ b/src/server.rs @@ -7,7 +7,7 @@ use hbb_common::{ config::{Config, Config2, CONNECT_TIMEOUT, RELAY_PORT}, log, message_proto::*, - protobuf::{Message as _, Enum}, + protobuf::{Enum, Message as _}, rendezvous_proto::*, socket_client, sodiumoxide::crypto::{box_, secretbox, sign}, @@ -24,6 +24,10 @@ pub mod audio_service; cfg_if::cfg_if! { if #[cfg(not(any(target_os = "android", target_os = "ios")))] { mod clipboard_service; +#[cfg(target_os = "linux")] +mod wayland; +#[cfg(target_os = "linux")] +pub mod uinput; pub mod input_service; } else { mod clipboard_service { @@ -280,7 +284,7 @@ impl Drop for Server { s.join(); } #[cfg(target_os = "linux")] - video_service::wayland_support::clear(); + wayland::clear(); } } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 5d0ebe452..29f4207b3 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -191,9 +191,9 @@ lazy_static::lazy_static! { 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?; + let keyboard = super::uinput::client::UInputKeyboard::new().await?; log::info!("UInput keyboard created"); - let mouse = self::uinput::client::UInputMouse::new().await?; + let mouse = super::uinput::client::UInputMouse::new().await?; log::info!("UInput mouse created"); let mut en = ENIGO.lock().unwrap(); @@ -204,7 +204,7 @@ pub async fn set_uinput() -> ResultType<()> { #[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 + super::uinput::client::set_resolution(minx, maxx, miny, maxy).await } pub fn is_left_up(evt: &MouseEvent) -> bool { @@ -759,658 +759,3 @@ async fn send_sas() -> ResultType<()> { 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 { - 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 { - 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 { - 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 = 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 = 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 { - // TODO: ensure keys here - let mut keys = AttributeSet::::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::::new(); - leds.insert(evdev::LedType::LED_NUML); - leds.insert(evdev::LedType::LED_CAPSL); - leds.insert(evdev::LedType::LED_SCROLLL); - let mut miscs = AttributeSet::::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 { - 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(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"); - } - } -} diff --git a/src/server/uinput.rs b/src/server/uinput.rs new file mode 100644 index 000000000..7a6d47cff --- /dev/null +++ b/src/server/uinput.rs @@ -0,0 +1,651 @@ +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 { + 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 { + 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 { + 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 = 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 = 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 { + // TODO: ensure keys here + let mut keys = AttributeSet::::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::::new(); + leds.insert(evdev::LedType::LED_NUML); + leds.insert(evdev::LedType::LED_CAPSL); + leds.insert(evdev::LedType::LED_SCROLLL); + let mut miscs = AttributeSet::::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 { + 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(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"); + } +} diff --git a/src/server/video_service.rs b/src/server/video_service.rs index c8b628015..ede62efee 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -31,6 +31,7 @@ use scrap::{ use std::{ collections::HashSet, io::{ErrorKind::WouldBlock, Result}, + ops::{Deref, DerefMut}, time::{self, Duration, Instant}, }; #[cfg(windows)] @@ -127,7 +128,7 @@ impl VideoFrameController { } } -trait TraitCapturer { +pub(super) trait TraitCapturer { fn frame<'a>(&'a mut self, timeout: Duration) -> Result>; fn set_use_yuv(&mut self, use_yuv: bool); @@ -340,22 +341,36 @@ fn check_uac_switch(privacy_mode_id: i32, captuerer_privacy_mode_id: i32) -> Res Ok(()) } -struct CapturerInfo { - origin: (i32, i32), - width: usize, - height: usize, - ndisplay: usize, - current: usize, - privacy_mode_id: i32, - _captuerer_privacy_mode_id: i32, - capturer: Box, +pub(super) struct CapturerInfo { + pub origin: (i32, i32), + pub width: usize, + pub height: usize, + pub ndisplay: usize, + pub current: usize, + pub privacy_mode_id: i32, + pub _captuerer_privacy_mode_id: i32, + pub capturer: Box, +} + +impl Deref for CapturerInfo { + type Target = Box; + + fn deref(&self) -> &Self::Target { + &self.capturer + } +} + +impl DerefMut for CapturerInfo { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.capturer + } } fn get_capturer(use_yuv: bool) -> ResultType { #[cfg(target_os = "linux")] { if scrap::is_wayland() { - return wayland_support::get_capturer(); + return super::wayland::get_capturer(); } } @@ -441,7 +456,7 @@ fn run(sp: GenericService) -> ResultType<()> { Ok(x) => encoder = x, Err(err) => bail!("Failed to create encoder: {}", err), } - c.capturer.set_use_yuv(encoder.use_yuv()); + c.set_use_yuv(encoder.use_yuv()); if *SWITCH.lock().unwrap() { log::debug!("Broadcasting display switch"); @@ -467,7 +482,7 @@ fn run(sp: GenericService) -> ResultType<()> { #[cfg(windows)] let mut try_gdi = 1; #[cfg(windows)] - log::info!("gdi: {}", c.capturer.is_gdi()); + log::info!("gdi: {}", c.is_gdi()); while sp.ok() { #[cfg(windows)] @@ -515,7 +530,7 @@ fn run(sp: GenericService) -> ResultType<()> { frame_controller.reset(); #[cfg(any(target_os = "android", target_os = "ios"))] - let res = match (*c.capturer).frame(spf) { + let res = match c.frame(spf) { Ok(frame) => { let time = now - start; let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64; @@ -538,7 +553,7 @@ fn run(sp: GenericService) -> ResultType<()> { }; #[cfg(not(any(target_os = "android", target_os = "ios")))] - let res = match (*c.capturer).frame(spf) { + let res = match c.frame(spf) { Ok(frame) => { let time = now - start; let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64; @@ -557,9 +572,9 @@ fn run(sp: GenericService) -> ResultType<()> { Err(ref e) if e.kind() == WouldBlock => { #[cfg(windows)] - if try_gdi > 0 && !c.capturer.is_gdi() { + if try_gdi > 0 && !c.is_gdi() { if try_gdi > 3 { - c.capturer.set_gdi(); + c.set_gdi(); try_gdi = 0; log::info!("No image, fall back to gdi"); } @@ -574,8 +589,8 @@ fn run(sp: GenericService) -> ResultType<()> { } #[cfg(windows)] - if !c.capturer.is_gdi() { - c.capturer.set_gdi(); + if !c.is_gdi() { + c.set_gdi(); log::info!("dxgi error, fall back to gdi: {:?}", err); continue; } @@ -688,7 +703,7 @@ fn get_display_num() -> usize { #[cfg(target_os = "linux")] { if scrap::is_wayland() { - return if let Ok(n) = wayland_support::get_display_num() { + return if let Ok(n) = super::wayland::get_display_num() { n } else { 0 @@ -703,7 +718,7 @@ fn get_display_num() -> usize { } } -fn get_displays_2(all: &Vec) -> (usize, Vec) { +pub(super) fn get_displays_2(all: &Vec) -> (usize, Vec) { let mut displays = Vec::new(); let mut primary = 0; for (i, d) in all.iter().enumerate() { @@ -731,7 +746,7 @@ pub async fn get_displays() -> ResultType<(usize, Vec)> { #[cfg(target_os = "linux")] { if scrap::is_wayland() { - return wayland_support::get_displays().await; + return super::wayland::get_displays().await; } } // switch to primary display if long time (30 seconds) no users @@ -760,7 +775,7 @@ fn get_primary() -> usize { #[cfg(target_os = "linux")] { if scrap::is_wayland() { - return match wayland_support::get_primary() { + return match super::wayland::get_primary() { Ok(n) => n, Err(_) => 0, }; @@ -816,7 +831,7 @@ fn try_get_displays() -> ResultType> { Ok(displays) } -fn get_current_display_2(mut all: Vec) -> ResultType<(usize, usize, Display)> { +pub(super) fn get_current_display_2(mut all: Vec) -> ResultType<(usize, usize, Display)> { let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize; if all.len() == 0 { bail!("No displays"); @@ -838,185 +853,3 @@ fn get_current_display_2(mut all: Vec) -> ResultType<(usize, usize, Dis 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 = RwLock::new(0); - } - struct CapDisplayInfo { - rects: Vec<((i32, i32), usize, usize)>, - displays: Vec, - num: usize, - primary: usize, - current: usize, - capturer: *mut Capturer, - } - - impl TraitCapturer for *mut Capturer { - fn frame<'a>(&'a mut self, timeout: Duration) -> Result> { - 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)> { - 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 { - 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 { - 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 { - 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"); - } - } -} diff --git a/src/server/wayland.rs b/src/server/wayland.rs new file mode 100644 index 000000000..b81d378a6 --- /dev/null +++ b/src/server/wayland.rs @@ -0,0 +1,179 @@ +use super::*; +use hbb_common::allow_err; +use scrap::{Capturer, Display, Frame}; +use std::{io::Result, time::Duration}; + +lazy_static::lazy_static! { + static ref CAP_DISPLAY_INFO: RwLock = RwLock::new(0); +} +struct CapDisplayInfo { + rects: Vec<((i32, i32), usize, usize)>, + displays: Vec, + num: usize, + primary: usize, + current: usize, + capturer: *mut Capturer, +} + +impl super::video_service::TraitCapturer for *mut Capturer { + fn frame<'a>(&'a mut self, timeout: Duration) -> Result> { + 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) = super::video_service::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) = + super::video_service::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)> { + 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 { + 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 { + 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 { + 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(super::video_service::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"); + } +}