mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-11-05 04:03:59 +08:00
fix(hid): share macOS drag compatibility across OTG and CH9329
This commit is contained in:
@@ -80,6 +80,8 @@ pub struct OtgBackend {
|
||||
keyboard_leds_enabled: bool,
|
||||
keyboard_state: Mutex<KeyboardReport>,
|
||||
mouse_buttons: AtomicU8,
|
||||
macos_drag: bool,
|
||||
macos_drag_state: Mutex<super::macos_drag::MacosDrag>,
|
||||
led_state: Arc<parking_lot::RwLock<LedState>>,
|
||||
screen_resolution: parking_lot::RwLock<Option<(u32, u32)>>,
|
||||
udc_name: Arc<parking_lot::RwLock<Option<String>>>,
|
||||
@@ -99,6 +101,15 @@ const OTG_RUNTIME_POLL_INTERVAL: Duration = Duration::from_millis(500);
|
||||
impl OtgBackend {
|
||||
/// Gadget must already exist; paths come from `OtgService`.
|
||||
pub fn from_handles(paths: HidDevicePaths) -> Result<Self> {
|
||||
Self::with_macos_drag(paths, false)
|
||||
}
|
||||
|
||||
pub fn with_macos_drag(paths: HidDevicePaths, macos_drag: bool) -> Result<Self> {
|
||||
if macos_drag && (paths.mouse_relative.is_none() || paths.mouse_absolute.is_none()) {
|
||||
return Err(AppError::Config(
|
||||
"macOS drag compatibility requires both OTG mouse interfaces".into(),
|
||||
));
|
||||
}
|
||||
let (runtime_notify_tx, _runtime_notify_rx) = watch::channel(());
|
||||
Ok(Self {
|
||||
keyboard_path: paths.keyboard,
|
||||
@@ -112,6 +123,8 @@ impl OtgBackend {
|
||||
keyboard_leds_enabled: paths.keyboard_leds_enabled,
|
||||
keyboard_state: Mutex::new(KeyboardReport::default()),
|
||||
mouse_buttons: AtomicU8::new(0),
|
||||
macos_drag,
|
||||
macos_drag_state: Mutex::new(super::macos_drag::MacosDrag::default()),
|
||||
led_state: Arc::new(parking_lot::RwLock::new(LedState::default())),
|
||||
screen_resolution: parking_lot::RwLock::new(Some((1920, 1080))),
|
||||
udc_name: Arc::new(parking_lot::RwLock::new(paths.udc)),
|
||||
@@ -852,6 +865,28 @@ impl HidBackend for OtgBackend {
|
||||
async fn send_mouse(&self, event: MouseEvent) -> Result<()> {
|
||||
let buttons = self.mouse_buttons.load(Ordering::Relaxed);
|
||||
|
||||
if self.macos_drag {
|
||||
use super::macos_drag::MouseReport;
|
||||
let mut state = self.macos_drag_state.lock();
|
||||
let extent = self.screen_resolution.read().unwrap_or((1920, 1080));
|
||||
let (buttons, reports) = state.plan(event, buttons, extent);
|
||||
self.mouse_buttons.store(buttons, Ordering::Relaxed);
|
||||
for report in reports {
|
||||
match report {
|
||||
MouseReport::Absolute { buttons, x, y } => {
|
||||
self.send_mouse_report_absolute(buttons, x, y, 0)?
|
||||
}
|
||||
MouseReport::Relative {
|
||||
buttons,
|
||||
dx,
|
||||
dy,
|
||||
wheel,
|
||||
} => self.send_mouse_report_relative(buttons, dx, dy, wheel)?,
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match event.event_type {
|
||||
MouseEventType::Move => {
|
||||
let dx = event.x.clamp(-127, 127) as i8;
|
||||
@@ -896,6 +931,7 @@ impl HidBackend for OtgBackend {
|
||||
}
|
||||
|
||||
self.mouse_buttons.store(0, Ordering::Relaxed);
|
||||
self.macos_drag_state.lock().reset();
|
||||
self.send_mouse_report_relative(0, 0, 0, 0)?;
|
||||
self.send_mouse_report_absolute(0, 0, 0, 0)?;
|
||||
|
||||
@@ -988,6 +1024,51 @@ mod tests {
|
||||
assert_eq!(kb_report.to_bytes().len(), 8);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mouse_compatibility_writes_both_endpoints_and_preserves_default() {
|
||||
use crate::hid::MouseButton;
|
||||
for enabled in [false, true] {
|
||||
let relative = tempfile::NamedTempFile::new().unwrap();
|
||||
let absolute = tempfile::NamedTempFile::new().unwrap();
|
||||
let backend = OtgBackend::with_macos_drag(
|
||||
HidDevicePaths {
|
||||
mouse_relative: Some(relative.path().to_path_buf()),
|
||||
mouse_absolute: Some(absolute.path().to_path_buf()),
|
||||
..Default::default()
|
||||
},
|
||||
enabled,
|
||||
)
|
||||
.unwrap();
|
||||
for event in [
|
||||
MouseEvent::move_abs(8000, 8000),
|
||||
MouseEvent::button_down(MouseButton::Left),
|
||||
MouseEvent::move_abs(16000, 16000),
|
||||
MouseEvent::button_up(MouseButton::Left),
|
||||
] {
|
||||
backend.send_mouse(event).await.unwrap();
|
||||
}
|
||||
let abs = fs::read(absolute.path()).unwrap();
|
||||
let rel = fs::read(relative.path()).unwrap();
|
||||
let buttons: Vec<_> = abs.chunks_exact(6).map(|packet| packet[0]).collect();
|
||||
assert_eq!(buttons, if enabled { vec![0, 1, 0] } else { vec![0, 0] });
|
||||
let dx: i32 = rel
|
||||
.chunks_exact(4)
|
||||
.map(|packet| i32::from(packet[1] as i8))
|
||||
.sum();
|
||||
let dy: i32 = rel
|
||||
.chunks_exact(4)
|
||||
.map(|packet| i32::from(packet[2] as i8))
|
||||
.sum();
|
||||
assert_eq!((dx, dy), if enabled { (468, 263) } else { (0, 0) });
|
||||
assert_eq!(&rel[rel.len() - 4..], &[0, 0, 0, 0]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compatibility_requires_both_mouse_endpoints() {
|
||||
assert!(OtgBackend::with_macos_drag(HidDevicePaths::default(), true).is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn prepare_rebuild_closes_devices_without_writing_reset_reports() {
|
||||
let mut file = tempfile::tempfile().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user