refact: embed crate mouce, uinput (#8836)

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou 2024-07-28 17:27:09 +08:00 committed by GitHub
parent 541d9c6b86
commit c0e9445602
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 369 additions and 29 deletions

9
Cargo.lock generated
View File

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

View File

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

View File

@ -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<bool> {
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<Self> {
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<c_char> = "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::<InputEvent>();
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);
}
}
}
}