mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-02-02 02:51:53 +08:00
feat: RustDesk 扩展支持相对鼠标模式,上报设备标识修改为 Windows
This commit is contained in:
@@ -395,6 +395,8 @@ pub struct Ch9329Backend {
|
||||
last_abs_x: AtomicU16,
|
||||
/// Last absolute mouse Y position (CH9329 coordinate: 0-4095)
|
||||
last_abs_y: AtomicU16,
|
||||
/// Whether relative mouse mode is active (set by incoming events)
|
||||
relative_mouse_active: AtomicBool,
|
||||
/// Consecutive error count
|
||||
error_count: AtomicU32,
|
||||
/// Whether a reset is in progress
|
||||
@@ -426,6 +428,7 @@ impl Ch9329Backend {
|
||||
address: DEFAULT_ADDR,
|
||||
last_abs_x: AtomicU16::new(0),
|
||||
last_abs_y: AtomicU16::new(0),
|
||||
relative_mouse_active: AtomicBool::new(false),
|
||||
error_count: AtomicU32::new(0),
|
||||
reset_in_progress: AtomicBool::new(false),
|
||||
last_success: Mutex::new(None),
|
||||
@@ -1014,12 +1017,14 @@ impl HidBackend for Ch9329Backend {
|
||||
match event.event_type {
|
||||
MouseEventType::Move => {
|
||||
// Relative movement - send delta directly without inversion
|
||||
self.relative_mouse_active.store(true, Ordering::Relaxed);
|
||||
let dx = event.x.clamp(-127, 127) as i8;
|
||||
let dy = event.y.clamp(-127, 127) as i8;
|
||||
self.send_mouse_relative(buttons, dx, dy, 0)?;
|
||||
}
|
||||
MouseEventType::MoveAbs => {
|
||||
// Absolute movement
|
||||
self.relative_mouse_active.store(false, Ordering::Relaxed);
|
||||
// Frontend sends 0-32767 (HID standard), CH9329 expects 0-4095
|
||||
let x = ((event.x.clamp(0, 32767) as u32) * CH9329_MOUSE_RESOLUTION / 32768) as u16;
|
||||
let y = ((event.y.clamp(0, 32767) as u32) * CH9329_MOUSE_RESOLUTION / 32768) as u16;
|
||||
@@ -1031,28 +1036,40 @@ impl HidBackend for Ch9329Backend {
|
||||
MouseEventType::Down => {
|
||||
if let Some(button) = event.button {
|
||||
let bit = button.to_hid_bit();
|
||||
let x = self.last_abs_x.load(Ordering::Relaxed);
|
||||
let y = self.last_abs_y.load(Ordering::Relaxed);
|
||||
let new_buttons = self.mouse_buttons.fetch_or(bit, Ordering::Relaxed) | bit;
|
||||
trace!("Mouse down: {:?} buttons=0x{:02X}", button, new_buttons);
|
||||
self.send_mouse_absolute(new_buttons, x, y, 0)?;
|
||||
if self.relative_mouse_active.load(Ordering::Relaxed) {
|
||||
self.send_mouse_relative(new_buttons, 0, 0, 0)?;
|
||||
} else {
|
||||
let x = self.last_abs_x.load(Ordering::Relaxed);
|
||||
let y = self.last_abs_y.load(Ordering::Relaxed);
|
||||
self.send_mouse_absolute(new_buttons, x, y, 0)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
MouseEventType::Up => {
|
||||
if let Some(button) = event.button {
|
||||
let bit = button.to_hid_bit();
|
||||
let x = self.last_abs_x.load(Ordering::Relaxed);
|
||||
let y = self.last_abs_y.load(Ordering::Relaxed);
|
||||
let new_buttons = self.mouse_buttons.fetch_and(!bit, Ordering::Relaxed) & !bit;
|
||||
trace!("Mouse up: {:?} buttons=0x{:02X}", button, new_buttons);
|
||||
self.send_mouse_absolute(new_buttons, x, y, 0)?;
|
||||
if self.relative_mouse_active.load(Ordering::Relaxed) {
|
||||
self.send_mouse_relative(new_buttons, 0, 0, 0)?;
|
||||
} else {
|
||||
let x = self.last_abs_x.load(Ordering::Relaxed);
|
||||
let y = self.last_abs_y.load(Ordering::Relaxed);
|
||||
self.send_mouse_absolute(new_buttons, x, y, 0)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
MouseEventType::Scroll => {
|
||||
// Use absolute mouse for scroll with last position
|
||||
let x = self.last_abs_x.load(Ordering::Relaxed);
|
||||
let y = self.last_abs_y.load(Ordering::Relaxed);
|
||||
self.send_mouse_absolute(buttons, x, y, event.scroll)?;
|
||||
if self.relative_mouse_active.load(Ordering::Relaxed) {
|
||||
self.send_mouse_relative(buttons, 0, 0, event.scroll)?;
|
||||
} else {
|
||||
// Use absolute mouse for scroll with last position
|
||||
let x = self.last_abs_x.load(Ordering::Relaxed);
|
||||
let y = self.last_abs_y.load(Ordering::Relaxed);
|
||||
self.send_mouse_absolute(buttons, x, y, event.scroll)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1073,6 +1090,7 @@ impl HidBackend for Ch9329Backend {
|
||||
self.mouse_buttons.store(0, Ordering::Relaxed);
|
||||
self.last_abs_x.store(0, Ordering::Relaxed);
|
||||
self.last_abs_y.store(0, Ordering::Relaxed);
|
||||
self.relative_mouse_active.store(false, Ordering::Relaxed);
|
||||
self.send_mouse_absolute(0, 0, 0, 0)?;
|
||||
|
||||
// Reset media keys
|
||||
|
||||
@@ -47,6 +47,11 @@ const DEFAULT_SCREEN_HEIGHT: u32 = 1080;
|
||||
/// Default mouse event throttle interval (16ms ≈ 60Hz)
|
||||
const DEFAULT_MOUSE_THROTTLE_MS: u64 = 16;
|
||||
|
||||
/// Advertised RustDesk version for client compatibility.
|
||||
const RUSTDESK_COMPAT_VERSION: &str = "1.4.5";
|
||||
// Advertised platform for RustDesk clients. This affects which UI options are shown.
|
||||
const RUSTDESK_COMPAT_PLATFORM: &str = "Windows";
|
||||
|
||||
/// Input event throttler
|
||||
///
|
||||
/// Limits the rate of input events sent to HID devices to prevent EAGAIN errors.
|
||||
@@ -164,6 +169,8 @@ pub struct Connection {
|
||||
last_test_delay_sent: Option<Instant>,
|
||||
/// Last known CapsLock state from RustDesk modifiers (for detecting toggle)
|
||||
last_caps_lock: bool,
|
||||
/// Whether relative mouse mode is currently active for this connection
|
||||
relative_mouse_active: bool,
|
||||
}
|
||||
|
||||
/// Messages sent to connection handler
|
||||
@@ -241,6 +248,7 @@ impl Connection {
|
||||
last_delay: 0,
|
||||
last_test_delay_sent: None,
|
||||
last_caps_lock: false,
|
||||
relative_mouse_active: false,
|
||||
};
|
||||
|
||||
(conn, rx)
|
||||
@@ -1113,11 +1121,11 @@ impl Connection {
|
||||
let mut peer_info = PeerInfo::new();
|
||||
peer_info.username = "one-kvm".to_string();
|
||||
peer_info.hostname = get_hostname();
|
||||
peer_info.platform = "Linux".to_string();
|
||||
peer_info.platform = RUSTDESK_COMPAT_PLATFORM.to_string();
|
||||
peer_info.displays.push(display_info);
|
||||
peer_info.current_display = 0;
|
||||
peer_info.sas_enabled = false;
|
||||
peer_info.version = env!("CARGO_PKG_VERSION").to_string();
|
||||
peer_info.version = RUSTDESK_COMPAT_VERSION.to_string();
|
||||
peer_info.encoding = protobuf::MessageField::some(encoding);
|
||||
|
||||
let mut login_response = LoginResponse::new();
|
||||
@@ -1324,9 +1332,16 @@ impl Connection {
|
||||
async fn handle_mouse_event(&mut self, me: &MouseEvent) -> anyhow::Result<()> {
|
||||
// Parse RustDesk mask format: (button << 3) | event_type
|
||||
let event_type = me.mask & 0x07;
|
||||
let is_relative_move = event_type == mouse_type::MOVE_RELATIVE;
|
||||
|
||||
if is_relative_move {
|
||||
self.relative_mouse_active = true;
|
||||
} else if event_type == mouse_type::MOVE {
|
||||
self.relative_mouse_active = false;
|
||||
}
|
||||
|
||||
// Check if this is a pure move event (no button/scroll)
|
||||
let is_pure_move = event_type == mouse_type::MOVE;
|
||||
let is_pure_move = event_type == mouse_type::MOVE || is_relative_move;
|
||||
|
||||
// For pure move events, apply throttling
|
||||
if is_pure_move && !self.input_throttler.should_send_mouse_move() {
|
||||
@@ -1337,7 +1352,8 @@ impl Connection {
|
||||
debug!("Mouse event: x={}, y={}, mask={}", me.x, me.y, me.mask);
|
||||
|
||||
// Convert RustDesk mouse event to One-KVM mouse events
|
||||
let mouse_events = convert_mouse_event(me, self.screen_width, self.screen_height);
|
||||
let mouse_events =
|
||||
convert_mouse_event(me, self.screen_width, self.screen_height, self.relative_mouse_active);
|
||||
|
||||
// Send to HID controller if available
|
||||
if let Some(ref hid) = self.hid {
|
||||
|
||||
@@ -18,6 +18,7 @@ pub mod mouse_type {
|
||||
pub const UP: i32 = 2;
|
||||
pub const WHEEL: i32 = 3;
|
||||
pub const TRACKPAD: i32 = 4;
|
||||
pub const MOVE_RELATIVE: i32 = 5;
|
||||
}
|
||||
|
||||
/// Mouse button IDs from RustDesk protocol (before left shift by 3)
|
||||
@@ -36,23 +37,25 @@ pub fn convert_mouse_event(
|
||||
event: &MouseEvent,
|
||||
screen_width: u32,
|
||||
screen_height: u32,
|
||||
relative_mode: bool,
|
||||
) -> Vec<OneKvmMouseEvent> {
|
||||
let mut events = Vec::new();
|
||||
|
||||
// RustDesk uses absolute coordinates
|
||||
let x = event.x.max(0) as u32;
|
||||
let y = event.y.max(0) as u32;
|
||||
|
||||
// Normalize to 0-32767 range for absolute mouse (USB HID standard)
|
||||
let abs_x = ((x as u64 * 32767) / screen_width.max(1) as u64) as i32;
|
||||
let abs_y = ((y as u64 * 32767) / screen_height.max(1) as u64) as i32;
|
||||
|
||||
// Parse RustDesk mask format: (button << 3) | event_type
|
||||
let event_type = event.mask & 0x07;
|
||||
let button_id = event.mask >> 3;
|
||||
let include_abs_move = !relative_mode;
|
||||
|
||||
match event_type {
|
||||
mouse_type::MOVE => {
|
||||
// RustDesk uses absolute coordinates
|
||||
let x = event.x.max(0) as u32;
|
||||
let y = event.y.max(0) as u32;
|
||||
|
||||
// Normalize to 0-32767 range for absolute mouse (USB HID standard)
|
||||
let abs_x = ((x as u64 * 32767) / screen_width.max(1) as u64) as i32;
|
||||
let abs_y = ((y as u64 * 32767) / screen_height.max(1) as u64) as i32;
|
||||
|
||||
// Move event - may have button held down (button_id > 0 means dragging)
|
||||
// Just send move, button state is tracked separately by HID backend
|
||||
events.push(OneKvmMouseEvent {
|
||||
@@ -63,55 +66,83 @@ pub fn convert_mouse_event(
|
||||
scroll: 0,
|
||||
});
|
||||
}
|
||||
mouse_type::DOWN => {
|
||||
// Button down - first move, then press
|
||||
mouse_type::MOVE_RELATIVE => {
|
||||
// Relative movement uses delta values directly (dx, dy).
|
||||
events.push(OneKvmMouseEvent {
|
||||
event_type: MouseEventType::MoveAbs,
|
||||
x: abs_x,
|
||||
y: abs_y,
|
||||
event_type: MouseEventType::Move,
|
||||
x: event.x,
|
||||
y: event.y,
|
||||
button: None,
|
||||
scroll: 0,
|
||||
});
|
||||
}
|
||||
mouse_type::DOWN => {
|
||||
if include_abs_move {
|
||||
// Button down - first move, then press
|
||||
let x = event.x.max(0) as u32;
|
||||
let y = event.y.max(0) as u32;
|
||||
let abs_x = ((x as u64 * 32767) / screen_width.max(1) as u64) as i32;
|
||||
let abs_y = ((y as u64 * 32767) / screen_height.max(1) as u64) as i32;
|
||||
events.push(OneKvmMouseEvent {
|
||||
event_type: MouseEventType::MoveAbs,
|
||||
x: abs_x,
|
||||
y: abs_y,
|
||||
button: None,
|
||||
scroll: 0,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(button) = button_id_to_button(button_id) {
|
||||
events.push(OneKvmMouseEvent {
|
||||
event_type: MouseEventType::Down,
|
||||
x: abs_x,
|
||||
y: abs_y,
|
||||
x: 0,
|
||||
y: 0,
|
||||
button: Some(button),
|
||||
scroll: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
mouse_type::UP => {
|
||||
// Button up - first move, then release
|
||||
events.push(OneKvmMouseEvent {
|
||||
event_type: MouseEventType::MoveAbs,
|
||||
x: abs_x,
|
||||
y: abs_y,
|
||||
button: None,
|
||||
scroll: 0,
|
||||
});
|
||||
if include_abs_move {
|
||||
// Button up - first move, then release
|
||||
let x = event.x.max(0) as u32;
|
||||
let y = event.y.max(0) as u32;
|
||||
let abs_x = ((x as u64 * 32767) / screen_width.max(1) as u64) as i32;
|
||||
let abs_y = ((y as u64 * 32767) / screen_height.max(1) as u64) as i32;
|
||||
events.push(OneKvmMouseEvent {
|
||||
event_type: MouseEventType::MoveAbs,
|
||||
x: abs_x,
|
||||
y: abs_y,
|
||||
button: None,
|
||||
scroll: 0,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(button) = button_id_to_button(button_id) {
|
||||
events.push(OneKvmMouseEvent {
|
||||
event_type: MouseEventType::Up,
|
||||
x: abs_x,
|
||||
y: abs_y,
|
||||
x: 0,
|
||||
y: 0,
|
||||
button: Some(button),
|
||||
scroll: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
mouse_type::WHEEL => {
|
||||
// Scroll event - move first, then scroll
|
||||
events.push(OneKvmMouseEvent {
|
||||
event_type: MouseEventType::MoveAbs,
|
||||
x: abs_x,
|
||||
y: abs_y,
|
||||
button: None,
|
||||
scroll: 0,
|
||||
});
|
||||
if include_abs_move {
|
||||
// Scroll event - move first, then scroll
|
||||
let x = event.x.max(0) as u32;
|
||||
let y = event.y.max(0) as u32;
|
||||
let abs_x = ((x as u64 * 32767) / screen_width.max(1) as u64) as i32;
|
||||
let abs_y = ((y as u64 * 32767) / screen_height.max(1) as u64) as i32;
|
||||
events.push(OneKvmMouseEvent {
|
||||
event_type: MouseEventType::MoveAbs,
|
||||
x: abs_x,
|
||||
y: abs_y,
|
||||
button: None,
|
||||
scroll: 0,
|
||||
});
|
||||
}
|
||||
|
||||
// RustDesk encodes scroll direction in the y coordinate
|
||||
// Positive y = scroll up, Negative y = scroll down
|
||||
@@ -119,21 +150,27 @@ pub fn convert_mouse_event(
|
||||
let scroll = if event.y > 0 { 1i8 } else { -1i8 };
|
||||
events.push(OneKvmMouseEvent {
|
||||
event_type: MouseEventType::Scroll,
|
||||
x: abs_x,
|
||||
y: abs_y,
|
||||
x: 0,
|
||||
y: 0,
|
||||
button: None,
|
||||
scroll,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
// Unknown event type, just move
|
||||
events.push(OneKvmMouseEvent {
|
||||
event_type: MouseEventType::MoveAbs,
|
||||
x: abs_x,
|
||||
y: abs_y,
|
||||
button: None,
|
||||
scroll: 0,
|
||||
});
|
||||
if include_abs_move {
|
||||
// Unknown event type, just move
|
||||
let x = event.x.max(0) as u32;
|
||||
let y = event.y.max(0) as u32;
|
||||
let abs_x = ((x as u64 * 32767) / screen_width.max(1) as u64) as i32;
|
||||
let abs_y = ((y as u64 * 32767) / screen_height.max(1) as u64) as i32;
|
||||
events.push(OneKvmMouseEvent {
|
||||
event_type: MouseEventType::MoveAbs,
|
||||
x: abs_x,
|
||||
y: abs_y,
|
||||
button: None,
|
||||
scroll: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,7 +559,7 @@ mod tests {
|
||||
event.y = 300;
|
||||
event.mask = mouse_type::MOVE; // Pure move event
|
||||
|
||||
let events = convert_mouse_event(&event, 1920, 1080);
|
||||
let events = convert_mouse_event(&event, 1920, 1080, false);
|
||||
assert!(!events.is_empty());
|
||||
assert_eq!(events[0].event_type, MouseEventType::MoveAbs);
|
||||
}
|
||||
@@ -534,7 +571,7 @@ mod tests {
|
||||
event.y = 300;
|
||||
event.mask = (mouse_button::LEFT << 3) | mouse_type::DOWN;
|
||||
|
||||
let events = convert_mouse_event(&event, 1920, 1080);
|
||||
let events = convert_mouse_event(&event, 1920, 1080, false);
|
||||
assert!(events.len() >= 2);
|
||||
// Should have a button down event
|
||||
assert!(events
|
||||
@@ -542,6 +579,20 @@ mod tests {
|
||||
.any(|e| e.event_type == MouseEventType::Down && e.button == Some(MouseButton::Left)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_convert_mouse_move_relative() {
|
||||
let mut event = MouseEvent::new();
|
||||
event.x = -12;
|
||||
event.y = 8;
|
||||
event.mask = mouse_type::MOVE_RELATIVE;
|
||||
|
||||
let events = convert_mouse_event(&event, 1920, 1080, true);
|
||||
assert_eq!(events.len(), 1);
|
||||
assert_eq!(events[0].event_type, MouseEventType::Move);
|
||||
assert_eq!(events[0].x, -12);
|
||||
assert_eq!(events[0].y, 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_convert_key_event() {
|
||||
use protobuf::EnumOrUnknown;
|
||||
|
||||
Reference in New Issue
Block a user