linux_wayland_support: refactor
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
613b1a8582
commit
634cb5ef1a
@ -144,7 +144,7 @@ pub fn get_cursor_data(hcursor: u64) -> ResultType<CursorData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn start_uinput_service() {
|
fn start_uinput_service() {
|
||||||
use crate::server::input_service::uinput::service;
|
use crate::server::uinput::service;
|
||||||
std::thread::spawn(|| {
|
std::thread::spawn(|| {
|
||||||
service::start_service_control();
|
service::start_service_control();
|
||||||
});
|
});
|
||||||
|
@ -7,7 +7,7 @@ use hbb_common::{
|
|||||||
config::{Config, Config2, CONNECT_TIMEOUT, RELAY_PORT},
|
config::{Config, Config2, CONNECT_TIMEOUT, RELAY_PORT},
|
||||||
log,
|
log,
|
||||||
message_proto::*,
|
message_proto::*,
|
||||||
protobuf::{Message as _, Enum},
|
protobuf::{Enum, Message as _},
|
||||||
rendezvous_proto::*,
|
rendezvous_proto::*,
|
||||||
socket_client,
|
socket_client,
|
||||||
sodiumoxide::crypto::{box_, secretbox, sign},
|
sodiumoxide::crypto::{box_, secretbox, sign},
|
||||||
@ -24,6 +24,10 @@ pub mod audio_service;
|
|||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(not(any(target_os = "android", target_os = "ios")))] {
|
if #[cfg(not(any(target_os = "android", target_os = "ios")))] {
|
||||||
mod clipboard_service;
|
mod clipboard_service;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod wayland;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub mod uinput;
|
||||||
pub mod input_service;
|
pub mod input_service;
|
||||||
} else {
|
} else {
|
||||||
mod clipboard_service {
|
mod clipboard_service {
|
||||||
@ -280,7 +284,7 @@ impl Drop for Server {
|
|||||||
s.join();
|
s.join();
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
video_service::wayland_support::clear();
|
wayland::clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,9 +191,9 @@ lazy_static::lazy_static! {
|
|||||||
pub async fn set_uinput() -> ResultType<()> {
|
pub async fn set_uinput() -> ResultType<()> {
|
||||||
// Keyboard and mouse both open /dev/uinput
|
// Keyboard and mouse both open /dev/uinput
|
||||||
// TODO: Make sure there's no race
|
// 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");
|
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");
|
log::info!("UInput mouse created");
|
||||||
|
|
||||||
let mut en = ENIGO.lock().unwrap();
|
let mut en = ENIGO.lock().unwrap();
|
||||||
@ -204,7 +204,7 @@ pub async fn set_uinput() -> ResultType<()> {
|
|||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub async fn set_uinput_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> {
|
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 {
|
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??;
|
timeout(1000, stream.send(&crate::ipc::Data::SAS)).await??;
|
||||||
Ok(())
|
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
651
src/server/uinput.rs
Normal file
651
src/server/uinput.rs
Normal file
@ -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<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");
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,7 @@ use scrap::{
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
io::{ErrorKind::WouldBlock, Result},
|
io::{ErrorKind::WouldBlock, Result},
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
time::{self, Duration, Instant},
|
time::{self, Duration, Instant},
|
||||||
};
|
};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@ -127,7 +128,7 @@ impl VideoFrameController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait TraitCapturer {
|
pub(super) trait TraitCapturer {
|
||||||
fn frame<'a>(&'a mut self, timeout: Duration) -> Result<Frame<'a>>;
|
fn frame<'a>(&'a mut self, timeout: Duration) -> Result<Frame<'a>>;
|
||||||
|
|
||||||
fn set_use_yuv(&mut self, use_yuv: bool);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CapturerInfo {
|
pub(super) struct CapturerInfo {
|
||||||
origin: (i32, i32),
|
pub origin: (i32, i32),
|
||||||
width: usize,
|
pub width: usize,
|
||||||
height: usize,
|
pub height: usize,
|
||||||
ndisplay: usize,
|
pub ndisplay: usize,
|
||||||
current: usize,
|
pub current: usize,
|
||||||
privacy_mode_id: i32,
|
pub privacy_mode_id: i32,
|
||||||
_captuerer_privacy_mode_id: i32,
|
pub _captuerer_privacy_mode_id: i32,
|
||||||
capturer: Box<dyn TraitCapturer>,
|
pub capturer: Box<dyn TraitCapturer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for CapturerInfo {
|
||||||
|
type Target = Box<dyn TraitCapturer>;
|
||||||
|
|
||||||
|
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<CapturerInfo> {
|
fn get_capturer(use_yuv: bool) -> ResultType<CapturerInfo> {
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
if scrap::is_wayland() {
|
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,
|
Ok(x) => encoder = x,
|
||||||
Err(err) => bail!("Failed to create encoder: {}", err),
|
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() {
|
if *SWITCH.lock().unwrap() {
|
||||||
log::debug!("Broadcasting display switch");
|
log::debug!("Broadcasting display switch");
|
||||||
@ -467,7 +482,7 @@ fn run(sp: GenericService) -> ResultType<()> {
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let mut try_gdi = 1;
|
let mut try_gdi = 1;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
log::info!("gdi: {}", c.capturer.is_gdi());
|
log::info!("gdi: {}", c.is_gdi());
|
||||||
|
|
||||||
while sp.ok() {
|
while sp.ok() {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@ -515,7 +530,7 @@ fn run(sp: GenericService) -> ResultType<()> {
|
|||||||
frame_controller.reset();
|
frame_controller.reset();
|
||||||
|
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
let res = match (*c.capturer).frame(spf) {
|
let res = match c.frame(spf) {
|
||||||
Ok(frame) => {
|
Ok(frame) => {
|
||||||
let time = now - start;
|
let time = now - start;
|
||||||
let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64;
|
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")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
let res = match (*c.capturer).frame(spf) {
|
let res = match c.frame(spf) {
|
||||||
Ok(frame) => {
|
Ok(frame) => {
|
||||||
let time = now - start;
|
let time = now - start;
|
||||||
let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64;
|
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 =>
|
Err(ref e) if e.kind() == WouldBlock =>
|
||||||
{
|
{
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
if try_gdi > 0 && !c.capturer.is_gdi() {
|
if try_gdi > 0 && !c.is_gdi() {
|
||||||
if try_gdi > 3 {
|
if try_gdi > 3 {
|
||||||
c.capturer.set_gdi();
|
c.set_gdi();
|
||||||
try_gdi = 0;
|
try_gdi = 0;
|
||||||
log::info!("No image, fall back to gdi");
|
log::info!("No image, fall back to gdi");
|
||||||
}
|
}
|
||||||
@ -574,8 +589,8 @@ fn run(sp: GenericService) -> ResultType<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
if !c.capturer.is_gdi() {
|
if !c.is_gdi() {
|
||||||
c.capturer.set_gdi();
|
c.set_gdi();
|
||||||
log::info!("dxgi error, fall back to gdi: {:?}", err);
|
log::info!("dxgi error, fall back to gdi: {:?}", err);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -688,7 +703,7 @@ fn get_display_num() -> usize {
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
if scrap::is_wayland() {
|
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
|
n
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
@ -703,7 +718,7 @@ fn get_display_num() -> usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_displays_2(all: &Vec<Display>) -> (usize, Vec<DisplayInfo>) {
|
pub(super) fn get_displays_2(all: &Vec<Display>) -> (usize, Vec<DisplayInfo>) {
|
||||||
let mut displays = Vec::new();
|
let mut displays = Vec::new();
|
||||||
let mut primary = 0;
|
let mut primary = 0;
|
||||||
for (i, d) in all.iter().enumerate() {
|
for (i, d) in all.iter().enumerate() {
|
||||||
@ -731,7 +746,7 @@ pub async fn get_displays() -> ResultType<(usize, Vec<DisplayInfo>)> {
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
if scrap::is_wayland() {
|
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
|
// switch to primary display if long time (30 seconds) no users
|
||||||
@ -760,7 +775,7 @@ fn get_primary() -> usize {
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
if scrap::is_wayland() {
|
if scrap::is_wayland() {
|
||||||
return match wayland_support::get_primary() {
|
return match super::wayland::get_primary() {
|
||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
Err(_) => 0,
|
Err(_) => 0,
|
||||||
};
|
};
|
||||||
@ -816,7 +831,7 @@ fn try_get_displays() -> ResultType<Vec<Display>> {
|
|||||||
Ok(displays)
|
Ok(displays)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_current_display_2(mut all: Vec<Display>) -> ResultType<(usize, usize, Display)> {
|
pub(super) fn get_current_display_2(mut all: Vec<Display>) -> ResultType<(usize, usize, Display)> {
|
||||||
let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize;
|
let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize;
|
||||||
if all.len() == 0 {
|
if all.len() == 0 {
|
||||||
bail!("No displays");
|
bail!("No displays");
|
||||||
@ -838,185 +853,3 @@ fn get_current_display_2(mut all: Vec<Display>) -> ResultType<(usize, usize, Dis
|
|||||||
fn get_current_display() -> ResultType<(usize, usize, Display)> {
|
fn get_current_display() -> ResultType<(usize, usize, Display)> {
|
||||||
get_current_display_2(try_get_displays()?)
|
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
179
src/server/wayland.rs
Normal file
179
src/server/wayland.rs
Normal file
@ -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<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 super::video_service::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) = 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<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<super::video_service::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(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");
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user