diff --git a/Cargo.lock b/Cargo.lock index 43b8ed497..d994c404d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3777,14 +3777,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "mouce" -version = "0.2.1" -source = "git+https://github.com/rustdesk-org/mouce.git#177625a395cd8fa73964714d0039535cb9b47893" -dependencies = [ - "glob", -] - [[package]] name = "muda" version = "0.13.5" @@ -5514,7 +5506,6 @@ dependencies = [ "libpulse-simple-binding", "mac_address", "magnum-opus", - "mouce", "num_cpus", "objc", "objc_id", diff --git a/Cargo.toml b/Cargo.toml index c1a841358..2879ba91f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -152,7 +152,6 @@ psimple = { package = "libpulse-simple-binding", version = "2.27" } pulse = { package = "libpulse-binding", version = "2.27" } rust-pulsectl = { git = "https://github.com/open-trade/pulsectl" } async-process = "1.7" -mouce = { git="https://github.com/rustdesk-org/mouce.git" } evdev = { git="https://github.com/rustdesk-org/evdev" } dbus = "0.9" dbus-crossroads = "0.5" diff --git a/src/server/uinput.rs b/src/server/uinput.rs index f36ad0336..942f3753a 100644 --- a/src/server/uinput.rs +++ b/src/server/uinput.rs @@ -4,7 +4,11 @@ use evdev::{ uinput::{VirtualDevice, VirtualDeviceBuilder}, AttributeSet, EventType, InputEvent, }; -use hbb_common::{allow_err, bail, log, tokio::{self, runtime::Runtime}, ResultType}; +use hbb_common::{ + allow_err, bail, log, + tokio::{self, runtime::Runtime}, + ResultType, +}; static IPC_CONN_TIMEOUT: u64 = 1000; static IPC_REQUEST_TIMEOUT: u64 = 1000; @@ -34,7 +38,10 @@ pub mod client { fn send_get_key_state(&mut self, data: Data) -> ResultType { self.rt.block_on(self.conn.send(&data))?; - match self.rt.block_on(self.conn.next_timeout(IPC_REQUEST_TIMEOUT)) { + match self + .rt + .block_on(self.conn.next_timeout(IPC_REQUEST_TIMEOUT)) + { Ok(Some(Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState(state)))) => { Ok(state) } @@ -171,7 +178,6 @@ pub mod client { pub mod service { use super::*; use hbb_common::lazy_static; - use mouce::MouseActions; use std::{collections::HashMap, sync::Mutex}; lazy_static::lazy_static! { @@ -389,7 +395,7 @@ pub mod service { } else { match key { enigo::Key::Layout(c) => { - if let Some((k,is_shift)) = KEY_MAP_LAYOUT.get(&c) { + if let Some((k, is_shift)) = KEY_MAP_LAYOUT.get(&c) { log::trace!("mapkey {:?}, get {:?}", &key, k); return Ok((k.clone(), is_shift.clone())); } @@ -431,7 +437,8 @@ pub mod service { DataKeyboard::KeyDown(key) => { if let Ok((k, is_shift)) = map_key(key) { if is_shift { - let down_event = InputEvent::new(EventType::KEY, evdev::Key::KEY_LEFTSHIFT.code(), 1); + let down_event = + InputEvent::new(EventType::KEY, evdev::Key::KEY_LEFTSHIFT.code(), 1); allow_err!(keyboard.emit(&[down_event])); } let down_event = InputEvent::new(EventType::KEY, k.code(), 1); @@ -504,7 +511,7 @@ pub mod service { } } - fn handle_mouse(mouse: &mut mouce::nix::UInputMouseManager, data: &DataMouse) { + fn handle_mouse(mouse: &mut mouce::UInputMouseManager, data: &DataMouse) { log::trace!("handle_mouse {:?}", &data); match data { DataMouse::MoveTo(x, y) => { @@ -515,9 +522,9 @@ pub mod service { } 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, + enigo::MouseButton::Left => mouce::MouseButton::Left, + enigo::MouseButton::Middle => mouce::MouseButton::Middle, + enigo::MouseButton::Right => mouce::MouseButton::Right, _ => { return; } @@ -526,9 +533,9 @@ pub mod service { } 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, + enigo::MouseButton::Left => mouce::MouseButton::Left, + enigo::MouseButton::Middle => mouce::MouseButton::Middle, + enigo::MouseButton::Right => mouce::MouseButton::Right, _ => { return; } @@ -537,9 +544,9 @@ pub mod service { } 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, + enigo::MouseButton::Left => mouce::MouseButton::Left, + enigo::MouseButton::Middle => mouce::MouseButton::Middle, + enigo::MouseButton::Right => mouce::MouseButton::Right, _ => { return; } @@ -553,9 +560,9 @@ pub mod service { let mut length = *length; let scroll = if length < 0 { - mouce::common::ScrollDirection::Up + mouce::ScrollDirection::Up } else { - mouce::common::ScrollDirection::Down + mouce::ScrollDirection::Down }; if length < 0 { @@ -621,7 +628,7 @@ pub mod service { rng_y.0, rng_y.1 ); - let mut mouse = match mouce::Mouse::new_uinput(rng_x, rng_y) { + let mut mouse = match mouce::UInputMouseManager::new(rng_x, rng_y) { Ok(mouse) => mouse, Err(e) => { log::error!("Failed to create mouse, {}", e); @@ -650,7 +657,7 @@ pub mod service { rng_y.0, rng_y.1 ); - mouse = match mouce::Mouse::new_uinput(rng_x, rng_y) { + mouse = match mouce::UInputMouseManager::new(rng_x, rng_y) { Ok(mouse) => mouse, Err(e) => { log::error!("Failed to create mouse, {}", e); @@ -761,3 +768,346 @@ pub mod service { log::info!("stop uinput control service"); } } + +// https://github.com/emrebicer/mouce +mod mouce { + use std::{ + fs::File, + io::{Error, ErrorKind, Result}, + mem::size_of, + os::{ + raw::{c_char, c_int, c_long, c_uint, c_ulong, c_ushort}, + unix::{fs::OpenOptionsExt, io::AsRawFd}, + }, + thread, + time::Duration, + }; + + pub const O_NONBLOCK: c_int = 2048; + + /// ioctl and uinput definitions + const UI_ABS_SETUP: c_ulong = 1075598596; + const UI_SET_EVBIT: c_ulong = 1074025828; + const UI_SET_KEYBIT: c_ulong = 1074025829; + const UI_SET_RELBIT: c_ulong = 1074025830; + const UI_SET_ABSBIT: c_ulong = 1074025831; + const UI_DEV_SETUP: c_ulong = 1079792899; + const UI_DEV_CREATE: c_ulong = 21761; + const UI_DEV_DESTROY: c_uint = 21762; + + pub const EV_KEY: c_int = 0x01; + pub const EV_REL: c_int = 0x02; + pub const EV_ABS: c_int = 0x03; + pub const REL_X: c_uint = 0x00; + pub const REL_Y: c_uint = 0x01; + pub const ABS_X: c_uint = 0x00; + pub const ABS_Y: c_uint = 0x01; + pub const REL_WHEEL: c_uint = 0x08; + pub const REL_HWHEEL: c_uint = 0x06; + pub const BTN_LEFT: c_int = 0x110; + pub const BTN_RIGHT: c_int = 0x111; + pub const BTN_MIDDLE: c_int = 0x112; + pub const BTN_SIDE: c_int = 0x113; + pub const BTN_EXTRA: c_int = 0x114; + pub const BTN_FORWARD: c_int = 0x115; + pub const BTN_BACK: c_int = 0x116; + pub const BTN_TASK: c_int = 0x117; + const SYN_REPORT: c_int = 0x00; + const EV_SYN: c_int = 0x00; + const BUS_USB: c_ushort = 0x03; + + /// uinput types + #[repr(C)] + struct UInputSetup { + id: InputId, + name: [c_char; UINPUT_MAX_NAME_SIZE], + ff_effects_max: c_ulong, + } + + #[repr(C)] + struct InputId { + bustype: c_ushort, + vendor: c_ushort, + product: c_ushort, + version: c_ushort, + } + + #[repr(C)] + pub struct InputEvent { + pub time: TimeVal, + pub r#type: c_ushort, + pub code: c_ushort, + pub value: c_int, + } + + #[repr(C)] + pub struct TimeVal { + pub tv_sec: c_ulong, + pub tv_usec: c_ulong, + } + + #[repr(C)] + pub struct UinputAbsSetup { + pub code: c_ushort, + pub absinfo: InputAbsinfo, + } + + #[repr(C)] + pub struct InputAbsinfo { + pub value: c_int, + pub minimum: c_int, + pub maximum: c_int, + pub fuzz: c_int, + pub flat: c_int, + pub resolution: c_int, + } + + extern "C" { + fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; + fn write(fd: c_int, buf: *mut InputEvent, count: usize) -> c_long; + } + + #[derive(Debug, Copy, Clone)] + pub enum MouseButton { + Left, + Middle, + Side, + Extra, + Right, + Back, + Forward, + Task, + } + + #[derive(Debug, Copy, Clone)] + pub enum ScrollDirection { + Up, + Down, + Right, + Left, + } + + const UINPUT_MAX_NAME_SIZE: usize = 80; + + pub struct UInputMouseManager { + uinput_file: File, + } + + impl UInputMouseManager { + pub fn new(rng_x: (i32, i32), rng_y: (i32, i32)) -> Result { + let manager = UInputMouseManager { + uinput_file: File::options() + .write(true) + .custom_flags(O_NONBLOCK) + .open("/dev/uinput")?, + }; + let fd = manager.uinput_file.as_raw_fd(); + unsafe { + // For press events (also needed for mouse movement) + ioctl(fd, UI_SET_EVBIT, EV_KEY); + ioctl(fd, UI_SET_KEYBIT, BTN_LEFT); + ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT); + ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE); + + // For mouse movement + ioctl(fd, UI_SET_EVBIT, EV_ABS); + ioctl(fd, UI_SET_ABSBIT, ABS_X); + ioctl( + fd, + UI_ABS_SETUP, + &UinputAbsSetup { + code: ABS_X as _, + absinfo: InputAbsinfo { + value: 0, + minimum: rng_x.0, + maximum: rng_x.1, + fuzz: 0, + flat: 0, + resolution: 0, + }, + }, + ); + ioctl(fd, UI_SET_ABSBIT, ABS_Y); + ioctl( + fd, + UI_ABS_SETUP, + &UinputAbsSetup { + code: ABS_Y as _, + absinfo: InputAbsinfo { + value: 0, + minimum: rng_y.0, + maximum: rng_y.1, + fuzz: 0, + flat: 0, + resolution: 0, + }, + }, + ); + + ioctl(fd, UI_SET_EVBIT, EV_REL); + ioctl(fd, UI_SET_RELBIT, REL_X); + ioctl(fd, UI_SET_RELBIT, REL_Y); + ioctl(fd, UI_SET_RELBIT, REL_WHEEL); + ioctl(fd, UI_SET_RELBIT, REL_HWHEEL); + } + + let mut usetup = UInputSetup { + id: InputId { + bustype: BUS_USB, + // Random vendor and product + vendor: 0x2222, + product: 0x3333, + version: 0, + }, + name: [0; UINPUT_MAX_NAME_SIZE], + ff_effects_max: 0, + }; + + let mut device_bytes: Vec = "mouce-library-fake-mouse" + .chars() + .map(|ch| ch as c_char) + .collect(); + + // Fill the rest of the name buffer with empty chars + for _ in 0..UINPUT_MAX_NAME_SIZE - device_bytes.len() { + device_bytes.push('\0' as c_char); + } + + usetup.name.copy_from_slice(&device_bytes); + + unsafe { + ioctl(fd, UI_DEV_SETUP, &usetup); + ioctl(fd, UI_DEV_CREATE); + } + + // On UI_DEV_CREATE the kernel will create the device node for this + // device. We are inserting a pause here so that userspace has time + // to detect, initialize the new device, and can start listening to + // the event, otherwise it will not notice the event we are about to send. + thread::sleep(Duration::from_millis(300)); + + Ok(manager) + } + + /// Write the given event to the uinput file + fn emit(&self, r#type: c_int, code: c_int, value: c_int) -> Result<()> { + let mut event = InputEvent { + time: TimeVal { + tv_sec: 0, + tv_usec: 0, + }, + r#type: r#type as c_ushort, + code: code as c_ushort, + value, + }; + let fd = self.uinput_file.as_raw_fd(); + + unsafe { + let count = size_of::(); + let written_bytes = write(fd, &mut event, count); + if written_bytes == -1 || written_bytes != count as c_long { + return Err(Error::new( + ErrorKind::Other, + format!("failed while trying to write to a file"), + )); + } + } + + Ok(()) + } + + /// Syncronize the device + fn syncronize(&self) -> Result<()> { + self.emit(EV_SYN, SYN_REPORT, 0)?; + // Give uinput some time to update the mouse location, + // otherwise it fails to move the mouse on release mode + // A delay of 1 milliseconds seems to be enough for it + thread::sleep(Duration::from_millis(1)); + Ok(()) + } + + /// Move the mouse relative to the current position + fn move_relative_(&self, x: i32, y: i32) -> Result<()> { + // uinput does not move the mouse in pixels but uses `units`. I couldn't + // find information regarding to this uinput `unit`, but according to + // my findings 1 unit corresponds to exactly 2 pixels. + // + // To achieve the expected behavior; divide the parameters by 2 + // + // This seems like there is a bug in this crate, but the + // behavior is the same on other projects that make use of + // uinput. e.g. `ydotool`. When you try to move your mouse, + // it will move 2x further pixels + self.emit(EV_REL, REL_X as c_int, (x as f32 / 2.).ceil() as c_int)?; + self.emit(EV_REL, REL_Y as c_int, (y as f32 / 2.).ceil() as c_int)?; + self.syncronize() + } + + fn map_btn(button: &MouseButton) -> c_int { + match button { + MouseButton::Left => BTN_LEFT, + MouseButton::Right => BTN_RIGHT, + MouseButton::Middle => BTN_MIDDLE, + MouseButton::Side => BTN_SIDE, + MouseButton::Extra => BTN_EXTRA, + MouseButton::Forward => BTN_FORWARD, + MouseButton::Back => BTN_BACK, + MouseButton::Task => BTN_TASK, + } + } + + pub fn move_to(&self, x: usize, y: usize) -> Result<()> { + // // For some reason, absolute mouse move events are not working on uinput + // // (as I understand those events are intended for touch events) + // // + // // As a work around solution; first set the mouse to top left, then + // // call relative move function to simulate an absolute move event + //self.move_relative(i32::MIN, i32::MIN)?; + //self.move_relative(x as i32, y as i32) + + self.emit(EV_ABS, ABS_X as c_int, x as c_int)?; + self.emit(EV_ABS, ABS_Y as c_int, y as c_int)?; + self.syncronize() + } + + pub fn move_relative(&self, x_offset: i32, y_offset: i32) -> Result<()> { + self.move_relative_(x_offset, y_offset) + } + + pub fn press_button(&self, button: &MouseButton) -> Result<()> { + self.emit(EV_KEY, Self::map_btn(button), 1)?; + self.syncronize() + } + + pub fn release_button(&self, button: &MouseButton) -> Result<()> { + self.emit(EV_KEY, Self::map_btn(button), 0)?; + self.syncronize() + } + + pub fn click_button(&self, button: &MouseButton) -> Result<()> { + self.press_button(button)?; + self.release_button(button) + } + + pub fn scroll_wheel(&self, direction: &ScrollDirection) -> Result<()> { + let (code, scroll_value) = match direction { + ScrollDirection::Up => (REL_WHEEL, 1), + ScrollDirection::Down => (REL_WHEEL, -1), + ScrollDirection::Left => (REL_HWHEEL, -1), + ScrollDirection::Right => (REL_HWHEEL, 1), + }; + self.emit(EV_REL, code as c_int, scroll_value)?; + self.syncronize() + } + } + + impl Drop for UInputMouseManager { + fn drop(&mut self) { + let fd = self.uinput_file.as_raw_fd(); + unsafe { + // Destroy the device, the file is closed automatically by the File module + ioctl(fd, UI_DEV_DESTROY as c_ulong); + } + } + } +}