mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-10 14:50:55 +03:00
Compare commits
1 Commits
fix-key-re
...
cleanup-se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c7bf3807c |
@@ -20,7 +20,7 @@ Focus lies on performance and a clean, manageable implementation that can easily
|
|||||||
|
|
||||||
***blazingly fast™*** because it's written in rust.
|
***blazingly fast™*** because it's written in rust.
|
||||||
|
|
||||||
For an alternative (with slightly different goals) you may check out [Synergy 1 Community Edition](https://github.com/symless/synergy) or [Input Leap](https://github.com/input-leap) (Synergy fork).
|
For an alternative (with slightly different goals) you may check out [Input Leap](https://github.com/input-leap).
|
||||||
|
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
use super::{error::EmulationError, Emulation, EmulationHandle};
|
use super::{error::EmulationError, Emulation, EmulationHandle};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use core_graphics::base::CGFloat;
|
use core_graphics::display::{CGDisplayBounds, CGMainDisplayID, CGPoint};
|
||||||
use core_graphics::display::{
|
|
||||||
CGDirectDisplayID, CGDisplayBounds, CGGetDisplaysWithRect, CGPoint, CGRect, CGSize,
|
|
||||||
};
|
|
||||||
use core_graphics::event::{
|
use core_graphics::event::{
|
||||||
CGEvent, CGEventTapLocation, CGEventType, CGKeyCode, CGMouseButton, EventField, ScrollEventUnit,
|
CGEvent, CGEventTapLocation, CGEventType, CGKeyCode, CGMouseButton, EventField, ScrollEventUnit,
|
||||||
};
|
};
|
||||||
@@ -11,9 +8,8 @@ use core_graphics::event_source::{CGEventSource, CGEventSourceStateID};
|
|||||||
use input_event::{Event, KeyboardEvent, PointerEvent};
|
use input_event::{Event, KeyboardEvent, PointerEvent};
|
||||||
use keycode::{KeyMap, KeyMapping};
|
use keycode::{KeyMap, KeyMapping};
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::{sync::Notify, task::JoinHandle};
|
use tokio::task::AbortHandle;
|
||||||
|
|
||||||
use super::error::MacOSEmulationCreationError;
|
use super::error::MacOSEmulationCreationError;
|
||||||
|
|
||||||
@@ -22,9 +18,8 @@ const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis(32);
|
|||||||
|
|
||||||
pub(crate) struct MacOSEmulation {
|
pub(crate) struct MacOSEmulation {
|
||||||
event_source: CGEventSource,
|
event_source: CGEventSource,
|
||||||
repeat_task: Option<JoinHandle<()>>,
|
repeat_task: Option<AbortHandle>,
|
||||||
button_state: ButtonState,
|
button_state: ButtonState,
|
||||||
notify_repeat_task: Arc<Notify>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ButtonState {
|
struct ButtonState {
|
||||||
@@ -70,7 +65,6 @@ impl MacOSEmulation {
|
|||||||
event_source,
|
event_source,
|
||||||
button_state,
|
button_state,
|
||||||
repeat_task: None,
|
repeat_task: None,
|
||||||
notify_repeat_task: Arc::new(Notify::new()),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,33 +76,20 @@ impl MacOSEmulation {
|
|||||||
async fn spawn_repeat_task(&mut self, key: u16) {
|
async fn spawn_repeat_task(&mut self, key: u16) {
|
||||||
// there can only be one repeating key and it's
|
// there can only be one repeating key and it's
|
||||||
// always the last to be pressed
|
// always the last to be pressed
|
||||||
self.cancel_repeat_task().await;
|
self.kill_repeat_task();
|
||||||
let event_source = self.event_source.clone();
|
let event_source = self.event_source.clone();
|
||||||
let notify = self.notify_repeat_task.clone();
|
|
||||||
let repeat_task = tokio::task::spawn_local(async move {
|
let repeat_task = tokio::task::spawn_local(async move {
|
||||||
let stop = tokio::select! {
|
tokio::time::sleep(DEFAULT_REPEAT_DELAY).await;
|
||||||
_ = tokio::time::sleep(DEFAULT_REPEAT_DELAY) => false,
|
loop {
|
||||||
_ = notify.notified() => true,
|
key_event(event_source.clone(), key, 1);
|
||||||
};
|
tokio::time::sleep(DEFAULT_REPEAT_INTERVAL).await;
|
||||||
if !stop {
|
|
||||||
loop {
|
|
||||||
key_event(event_source.clone(), key, 1);
|
|
||||||
tokio::select! {
|
|
||||||
_ = tokio::time::sleep(DEFAULT_REPEAT_INTERVAL) => {},
|
|
||||||
_ = notify.notified() => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// release key when cancelled
|
|
||||||
key_event(event_source.clone(), key, 0);
|
|
||||||
});
|
});
|
||||||
self.repeat_task = Some(repeat_task);
|
self.repeat_task = Some(repeat_task.abort_handle());
|
||||||
}
|
}
|
||||||
|
fn kill_repeat_task(&mut self) {
|
||||||
async fn cancel_repeat_task(&mut self) {
|
|
||||||
if let Some(task) = self.repeat_task.take() {
|
if let Some(task) = self.repeat_task.take() {
|
||||||
self.notify_repeat_task.notify_waiters();
|
task.abort();
|
||||||
let _ = task.await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,77 +105,6 @@ fn key_event(event_source: CGEventSource, key: u16, state: u8) {
|
|||||||
event.post(CGEventTapLocation::HID);
|
event.post(CGEventTapLocation::HID);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_display_at_point(x: CGFloat, y: CGFloat) -> Option<CGDirectDisplayID> {
|
|
||||||
let mut displays: [CGDirectDisplayID; 16] = [0; 16];
|
|
||||||
let mut display_count: u32 = 0;
|
|
||||||
let rect = CGRect::new(&CGPoint::new(x, y), &CGSize::new(0.0, 0.0));
|
|
||||||
|
|
||||||
let error = unsafe {
|
|
||||||
CGGetDisplaysWithRect(
|
|
||||||
rect,
|
|
||||||
1,
|
|
||||||
displays.as_mut_ptr(),
|
|
||||||
&mut display_count as *mut u32,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if error != 0 {
|
|
||||||
log::warn!("error getting displays at point ({}, {}): {}", x, y, error);
|
|
||||||
return Option::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if display_count == 0 {
|
|
||||||
log::debug!("no displays found at point ({}, {})", x, y);
|
|
||||||
return Option::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
return displays.first().copied();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_display_bounds(display: CGDirectDisplayID) -> (CGFloat, CGFloat, CGFloat, CGFloat) {
|
|
||||||
unsafe {
|
|
||||||
let bounds = CGDisplayBounds(display);
|
|
||||||
let min_x = bounds.origin.x;
|
|
||||||
let max_x = bounds.origin.x + bounds.size.width;
|
|
||||||
let min_y = bounds.origin.y;
|
|
||||||
let max_y = bounds.origin.y + bounds.size.height;
|
|
||||||
(min_x as f64, min_y as f64, max_x as f64, max_y as f64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clamp_to_screen_space(
|
|
||||||
current_x: CGFloat,
|
|
||||||
current_y: CGFloat,
|
|
||||||
dx: CGFloat,
|
|
||||||
dy: CGFloat,
|
|
||||||
) -> (CGFloat, CGFloat) {
|
|
||||||
// Check which display the mouse is currently on
|
|
||||||
// Determine what the location of the mouse would be after applying the move
|
|
||||||
// Get the display at the new location
|
|
||||||
// If the point is not on a display
|
|
||||||
// Clamp the mouse to the current display
|
|
||||||
// Else If the point is on a display
|
|
||||||
// Clamp the mouse to the new display
|
|
||||||
let current_display = match get_display_at_point(current_x, current_y) {
|
|
||||||
Some(display) => display,
|
|
||||||
None => {
|
|
||||||
log::warn!("could not get current display!");
|
|
||||||
return (current_x, current_y);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_x = current_x + dx;
|
|
||||||
let new_y = current_y + dy;
|
|
||||||
|
|
||||||
let final_display = get_display_at_point(new_x, new_y).unwrap_or(current_display);
|
|
||||||
let (min_x, min_y, max_x, max_y) = get_display_bounds(final_display);
|
|
||||||
|
|
||||||
(
|
|
||||||
new_x.clamp(min_x, max_x - 1.),
|
|
||||||
new_y.clamp(min_y, max_y - 1.),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Emulation for MacOSEmulation {
|
impl Emulation for MacOSEmulation {
|
||||||
async fn consume(
|
async fn consume(
|
||||||
@@ -205,6 +115,16 @@ impl Emulation for MacOSEmulation {
|
|||||||
match event {
|
match event {
|
||||||
Event::Pointer(pointer_event) => match pointer_event {
|
Event::Pointer(pointer_event) => match pointer_event {
|
||||||
PointerEvent::Motion { time: _, dx, dy } => {
|
PointerEvent::Motion { time: _, dx, dy } => {
|
||||||
|
// FIXME secondary displays?
|
||||||
|
let (min_x, min_y, max_x, max_y) = unsafe {
|
||||||
|
let display = CGMainDisplayID();
|
||||||
|
let bounds = CGDisplayBounds(display);
|
||||||
|
let min_x = bounds.origin.x;
|
||||||
|
let max_x = bounds.origin.x + bounds.size.width;
|
||||||
|
let min_y = bounds.origin.y;
|
||||||
|
let max_y = bounds.origin.y + bounds.size.height;
|
||||||
|
(min_x as f64, min_y as f64, max_x as f64, max_y as f64)
|
||||||
|
};
|
||||||
let mut mouse_location = match self.get_mouse_location() {
|
let mut mouse_location = match self.get_mouse_location() {
|
||||||
Some(l) => l,
|
Some(l) => l,
|
||||||
None => {
|
None => {
|
||||||
@@ -213,11 +133,8 @@ impl Emulation for MacOSEmulation {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let (new_mouse_x, new_mouse_y) =
|
mouse_location.x = (mouse_location.x + dx).clamp(min_x, max_x - 1.);
|
||||||
clamp_to_screen_space(mouse_location.x, mouse_location.y, dx, dy);
|
mouse_location.y = (mouse_location.y + dy).clamp(min_y, max_y - 1.);
|
||||||
|
|
||||||
mouse_location.x = new_mouse_x;
|
|
||||||
mouse_location.y = new_mouse_y;
|
|
||||||
|
|
||||||
let mut event_type = CGEventType::MouseMoved;
|
let mut event_type = CGEventType::MouseMoved;
|
||||||
if self.button_state.left {
|
if self.button_state.left {
|
||||||
@@ -362,7 +279,7 @@ impl Emulation for MacOSEmulation {
|
|||||||
match state {
|
match state {
|
||||||
// pressed
|
// pressed
|
||||||
1 => self.spawn_repeat_task(code).await,
|
1 => self.spawn_repeat_task(code).await,
|
||||||
_ => self.cancel_repeat_task().await,
|
_ => self.kill_repeat_task(),
|
||||||
}
|
}
|
||||||
key_event(self.event_source.clone(), code, state)
|
key_event(self.event_source.clone(), code, state)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user