mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-07 20:09:59 +03:00
Compare commits
4 Commits
fix-duplic
...
improve-ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7730f3b985 | ||
|
|
35773dfd07 | ||
|
|
f91b6bd3c1 | ||
|
|
2d1a037eba |
@@ -1,31 +1,39 @@
|
||||
use super::{error::MacosCaptureCreationError, Capture, CaptureError, CaptureEvent, Position};
|
||||
use async_trait::async_trait;
|
||||
use bitflags::bitflags;
|
||||
use core_foundation::base::{kCFAllocatorDefault, CFRelease};
|
||||
use core_foundation::date::CFTimeInterval;
|
||||
use core_foundation::number::{kCFBooleanTrue, CFBooleanRef};
|
||||
use core_foundation::runloop::{kCFRunLoopCommonModes, CFRunLoop, CFRunLoopSource};
|
||||
use core_foundation::string::{kCFStringEncodingUTF8, CFStringCreateWithCString, CFStringRef};
|
||||
use core_graphics::base::{kCGErrorSuccess, CGError};
|
||||
use core_graphics::display::{CGDisplay, CGPoint};
|
||||
use core_graphics::event::{
|
||||
CGEvent, CGEventFlags, CGEventTap, CGEventTapLocation, CGEventTapOptions, CGEventTapPlacement,
|
||||
CGEventTapProxy, CGEventType, CallbackResult, EventField,
|
||||
use core_foundation::{
|
||||
base::{kCFAllocatorDefault, CFRelease},
|
||||
date::CFTimeInterval,
|
||||
number::{kCFBooleanTrue, CFBooleanRef},
|
||||
runloop::{kCFRunLoopCommonModes, CFRunLoop, CFRunLoopSource},
|
||||
string::{kCFStringEncodingUTF8, CFStringCreateWithCString, CFStringRef},
|
||||
};
|
||||
use core_graphics::{
|
||||
base::{kCGErrorSuccess, CGError},
|
||||
display::{CGDisplay, CGPoint},
|
||||
event::{
|
||||
CGEvent, CGEventFlags, CGEventTap, CGEventTapLocation, CGEventTapOptions,
|
||||
CGEventTapPlacement, CGEventTapProxy, CGEventType, CallbackResult, EventField,
|
||||
},
|
||||
event_source::{CGEventSource, CGEventSourceStateID},
|
||||
};
|
||||
use core_graphics::event_source::{CGEventSource, CGEventSourceStateID};
|
||||
use futures_core::Stream;
|
||||
use input_event::{Event, KeyboardEvent, PointerEvent, BTN_LEFT, BTN_MIDDLE, BTN_RIGHT};
|
||||
use keycode::{KeyMap, KeyMapping};
|
||||
use libc::c_void;
|
||||
use once_cell::unsync::Lazy;
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::{c_char, CString};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{ready, Context, Poll};
|
||||
use std::thread::{self};
|
||||
use tokio::sync::mpsc::{self, Receiver, Sender};
|
||||
use tokio::sync::{oneshot, Mutex};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
ffi::{c_char, CString},
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{ready, Context, Poll},
|
||||
thread::{self},
|
||||
};
|
||||
use tokio::sync::{
|
||||
mpsc::{self, Receiver, Sender},
|
||||
oneshot, Mutex,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Bounds {
|
||||
@@ -37,9 +45,16 @@ struct Bounds {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct InputCaptureState {
|
||||
/// active capture positions
|
||||
active_clients: Lazy<HashSet<Position>>,
|
||||
/// the currently entered capture position, if any
|
||||
current_pos: Option<Position>,
|
||||
/// position where the cursor was captured
|
||||
enter_position: Option<CGPoint>,
|
||||
/// bounds of the input capture area
|
||||
bounds: Bounds,
|
||||
/// current state of modifier keys
|
||||
modifier_state: XMods,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -56,7 +71,9 @@ impl InputCaptureState {
|
||||
let mut res = Self {
|
||||
active_clients: Lazy::new(HashSet::new),
|
||||
current_pos: None,
|
||||
enter_position: None,
|
||||
bounds: Bounds::default(),
|
||||
modifier_state: Default::default(),
|
||||
};
|
||||
res.update_bounds()?;
|
||||
Ok(res)
|
||||
@@ -96,45 +113,34 @@ impl InputCaptureState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// We can't disable mouse movement when in a client so we need to reset the cursor position
|
||||
// to the edge of the screen, the cursor will be hidden but we dont want it to appear in a
|
||||
// random location when we exit the client
|
||||
fn reset_mouse_position(&self, event: &CGEvent) -> Result<(), CaptureError> {
|
||||
if let Some(pos) = self.current_pos {
|
||||
let location = event.location();
|
||||
let edge_offset = 1.0;
|
||||
/// start the input capture by
|
||||
fn start_capture(&mut self, event: &CGEvent, position: Position) -> Result<(), CaptureError> {
|
||||
let mut location = event.location();
|
||||
let edge_offset = 1.0;
|
||||
// move cursor location to display bounds
|
||||
match position {
|
||||
Position::Left => location.x = self.bounds.xmin + edge_offset,
|
||||
Position::Right => location.x = self.bounds.xmax - edge_offset,
|
||||
Position::Top => location.y = self.bounds.ymin + edge_offset,
|
||||
Position::Bottom => location.y = self.bounds.ymax - edge_offset,
|
||||
};
|
||||
self.enter_position = Some(location);
|
||||
self.reset_cursor()
|
||||
}
|
||||
|
||||
// After the cursor is warped no event is produced but the next event
|
||||
// will carry the delta from the warp so only half the delta is needed to move the cursor
|
||||
let delta_y = event.get_double_value_field(EventField::MOUSE_EVENT_DELTA_Y) / 2.0;
|
||||
let delta_x = event.get_double_value_field(EventField::MOUSE_EVENT_DELTA_X) / 2.0;
|
||||
/// resets the cursor to the position, where the capture started
|
||||
fn reset_cursor(&mut self) -> Result<(), CaptureError> {
|
||||
let pos = self.enter_position.expect("capture active");
|
||||
log::trace!("Resetting cursor position to: {}, {}", pos.x, pos.y);
|
||||
CGDisplay::warp_mouse_cursor_position(pos).map_err(CaptureError::WarpCursor)
|
||||
}
|
||||
|
||||
let mut new_x = location.x + delta_x;
|
||||
let mut new_y = location.y + delta_y;
|
||||
fn hide_cursor(&self) -> Result<(), CaptureError> {
|
||||
CGDisplay::hide_cursor(&CGDisplay::main()).map_err(CaptureError::CoreGraphics)
|
||||
}
|
||||
|
||||
match pos {
|
||||
Position::Left => {
|
||||
new_x = self.bounds.xmin + edge_offset;
|
||||
}
|
||||
Position::Right => {
|
||||
new_x = self.bounds.xmax - edge_offset;
|
||||
}
|
||||
Position::Top => {
|
||||
new_y = self.bounds.ymin + edge_offset;
|
||||
}
|
||||
Position::Bottom => {
|
||||
new_y = self.bounds.ymax - edge_offset;
|
||||
}
|
||||
}
|
||||
let new_pos = CGPoint::new(new_x, new_y);
|
||||
|
||||
log::trace!("Resetting cursor position to: {new_x}, {new_y}");
|
||||
|
||||
return CGDisplay::warp_mouse_cursor_position(new_pos)
|
||||
.map_err(CaptureError::WarpCursor);
|
||||
}
|
||||
|
||||
Err(CaptureError::ResetMouseWithoutClient)
|
||||
fn show_cursor(&self) -> Result<(), CaptureError> {
|
||||
CGDisplay::show_cursor(&CGDisplay::main()).map_err(CaptureError::CoreGraphics)
|
||||
}
|
||||
|
||||
async fn handle_producer_event(
|
||||
@@ -145,15 +151,13 @@ impl InputCaptureState {
|
||||
match producer_event {
|
||||
ProducerEvent::Release => {
|
||||
if self.current_pos.is_some() {
|
||||
CGDisplay::show_cursor(&CGDisplay::main())
|
||||
.map_err(CaptureError::CoreGraphics)?;
|
||||
self.show_cursor()?;
|
||||
self.current_pos = None;
|
||||
}
|
||||
}
|
||||
ProducerEvent::Grab(pos) => {
|
||||
if self.current_pos.is_none() {
|
||||
CGDisplay::hide_cursor(&CGDisplay::main())
|
||||
.map_err(CaptureError::CoreGraphics)?;
|
||||
self.hide_cursor()?;
|
||||
self.current_pos = Some(pos);
|
||||
}
|
||||
}
|
||||
@@ -163,8 +167,7 @@ impl InputCaptureState {
|
||||
ProducerEvent::Destroy(p) => {
|
||||
if let Some(current) = self.current_pos {
|
||||
if current == p {
|
||||
CGDisplay::show_cursor(&CGDisplay::main())
|
||||
.map_err(CaptureError::CoreGraphics)?;
|
||||
self.show_cursor()?;
|
||||
self.current_pos = None;
|
||||
};
|
||||
}
|
||||
@@ -180,6 +183,7 @@ fn get_events(
|
||||
ev_type: &CGEventType,
|
||||
ev: &CGEvent,
|
||||
result: &mut Vec<CaptureEvent>,
|
||||
modifier_state: &mut XMods,
|
||||
) -> Result<(), CaptureError> {
|
||||
fn map_pointer_event(ev: &CGEvent) -> PointerEvent {
|
||||
PointerEvent::Motion {
|
||||
@@ -215,29 +219,42 @@ fn get_events(
|
||||
})));
|
||||
}
|
||||
CGEventType::FlagsChanged => {
|
||||
let mut mods = XMods::empty();
|
||||
let mut depressed = XMods::empty();
|
||||
let mut mods_locked = XMods::empty();
|
||||
let cg_flags = ev.get_flags();
|
||||
|
||||
if cg_flags.contains(CGEventFlags::CGEventFlagShift) {
|
||||
mods |= XMods::ShiftMask;
|
||||
depressed |= XMods::ShiftMask;
|
||||
}
|
||||
if cg_flags.contains(CGEventFlags::CGEventFlagControl) {
|
||||
mods |= XMods::ControlMask;
|
||||
depressed |= XMods::ControlMask;
|
||||
}
|
||||
if cg_flags.contains(CGEventFlags::CGEventFlagAlternate) {
|
||||
mods |= XMods::Mod1Mask;
|
||||
depressed |= XMods::Mod1Mask;
|
||||
}
|
||||
if cg_flags.contains(CGEventFlags::CGEventFlagCommand) {
|
||||
mods |= XMods::Mod4Mask;
|
||||
depressed |= XMods::Mod4Mask;
|
||||
}
|
||||
if cg_flags.contains(CGEventFlags::CGEventFlagAlphaShift) {
|
||||
mods |= XMods::LockMask;
|
||||
depressed |= XMods::LockMask;
|
||||
mods_locked |= XMods::LockMask;
|
||||
}
|
||||
|
||||
// check if pressed or released
|
||||
let state = if depressed > *modifier_state { 1 } else { 0 };
|
||||
*modifier_state = depressed;
|
||||
|
||||
if let Ok(key) = map_key(ev) {
|
||||
let key_event = CaptureEvent::Input(Event::Keyboard(KeyboardEvent::Key {
|
||||
time: 0,
|
||||
key,
|
||||
state,
|
||||
}));
|
||||
result.push(key_event);
|
||||
}
|
||||
|
||||
let modifier_event = KeyboardEvent::Modifiers {
|
||||
depressed: mods.bits(),
|
||||
depressed: depressed.bits(),
|
||||
latched: 0,
|
||||
locked: mods_locked.bits(),
|
||||
group: 0,
|
||||
@@ -348,7 +365,7 @@ fn create_event_tap<'a>(
|
||||
move |_proxy: CGEventTapProxy, event_type: CGEventType, cg_ev: &CGEvent| {
|
||||
log::trace!("Got event from tap: {event_type:?}");
|
||||
let mut state = client_state.blocking_lock();
|
||||
let mut pos = None;
|
||||
let mut capture_position = None;
|
||||
let mut res_events = vec![];
|
||||
|
||||
if matches!(
|
||||
@@ -365,22 +382,34 @@ fn create_event_tap<'a>(
|
||||
|
||||
// Are we in a client?
|
||||
if let Some(current_pos) = state.current_pos {
|
||||
pos = Some(current_pos);
|
||||
get_events(&event_type, cg_ev, &mut res_events).unwrap_or_else(|e| {
|
||||
capture_position = Some(current_pos);
|
||||
get_events(
|
||||
&event_type,
|
||||
cg_ev,
|
||||
&mut res_events,
|
||||
&mut state.modifier_state,
|
||||
)
|
||||
.unwrap_or_else(|e| {
|
||||
log::error!("Failed to get events: {e}");
|
||||
});
|
||||
|
||||
// Keep (hidden) cursor at the edge of the screen
|
||||
if matches!(event_type, CGEventType::MouseMoved) {
|
||||
state.reset_mouse_position(cg_ev).unwrap_or_else(|e| {
|
||||
log::error!("Failed to reset mouse position: {e}");
|
||||
})
|
||||
if matches!(
|
||||
event_type,
|
||||
CGEventType::MouseMoved
|
||||
| CGEventType::LeftMouseDragged
|
||||
| CGEventType::RightMouseDragged
|
||||
| CGEventType::OtherMouseDragged
|
||||
) {
|
||||
state.reset_cursor().unwrap_or_else(|e| log::warn!("{e}"));
|
||||
}
|
||||
}
|
||||
// Did we cross a barrier?
|
||||
else if matches!(event_type, CGEventType::MouseMoved) {
|
||||
} else if matches!(event_type, CGEventType::MouseMoved) {
|
||||
// Did we cross a barrier?
|
||||
if let Some(new_pos) = state.crossed(cg_ev) {
|
||||
pos = Some(new_pos);
|
||||
capture_position = Some(new_pos);
|
||||
state
|
||||
.start_capture(cg_ev, new_pos)
|
||||
.unwrap_or_else(|e| log::warn!("{e}"));
|
||||
res_events.push(CaptureEvent::Begin);
|
||||
notify_tx
|
||||
.blocking_send(ProducerEvent::Grab(new_pos))
|
||||
@@ -388,7 +417,7 @@ fn create_event_tap<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pos) = pos {
|
||||
if let Some(pos) = capture_position {
|
||||
res_events.iter().for_each(|e| {
|
||||
// error must be ignored, since the event channel
|
||||
// may already be closed when the InputCapture instance is dropped.
|
||||
@@ -493,10 +522,7 @@ impl MacOSInputCapture {
|
||||
log::error!("Failed to handle producer event: {e}");
|
||||
})
|
||||
}
|
||||
|
||||
_ = &mut tap_exit_rx => {
|
||||
break;
|
||||
}
|
||||
_ = &mut tap_exit_rx => break,
|
||||
}
|
||||
}
|
||||
// show cursor
|
||||
|
||||
@@ -88,7 +88,7 @@ impl MacOSEmulation {
|
||||
button_state,
|
||||
previous_button: None,
|
||||
previous_button_click: None,
|
||||
button_click_state: 1,
|
||||
button_click_state: 0,
|
||||
repeat_task: None,
|
||||
notify_repeat_task: Arc::new(Notify::new()),
|
||||
modifier_state: Rc::new(Cell::new(XMods::empty())),
|
||||
@@ -104,6 +104,9 @@ impl MacOSEmulation {
|
||||
// there can only be one repeating key and it's
|
||||
// always the last to be pressed
|
||||
self.cancel_repeat_task().await;
|
||||
// initial key event
|
||||
key_event(self.event_source.clone(), key, 1, self.modifier_state.get());
|
||||
// repeat task
|
||||
let event_source = self.event_source.clone();
|
||||
let notify = self.notify_repeat_task.clone();
|
||||
let modifiers = self.modifier_state.clone();
|
||||
@@ -239,159 +242,167 @@ impl Emulation for MacOSEmulation {
|
||||
event: Event,
|
||||
_handle: EmulationHandle,
|
||||
) -> Result<(), EmulationError> {
|
||||
log::trace!("{event:?}");
|
||||
match event {
|
||||
Event::Pointer(pointer_event) => match pointer_event {
|
||||
PointerEvent::Motion { time: _, dx, dy } => {
|
||||
let mut mouse_location = match self.get_mouse_location() {
|
||||
Some(l) => l,
|
||||
None => {
|
||||
log::warn!("could not get mouse location!");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
Event::Pointer(pointer_event) => {
|
||||
match pointer_event {
|
||||
PointerEvent::Motion { time: _, dx, dy } => {
|
||||
let mut mouse_location = match self.get_mouse_location() {
|
||||
Some(l) => l,
|
||||
None => {
|
||||
log::warn!("could not get mouse location!");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let (new_mouse_x, new_mouse_y) =
|
||||
clamp_to_screen_space(mouse_location.x, mouse_location.y, dx, dy);
|
||||
let (new_mouse_x, new_mouse_y) =
|
||||
clamp_to_screen_space(mouse_location.x, mouse_location.y, dx, dy);
|
||||
|
||||
mouse_location.x = new_mouse_x;
|
||||
mouse_location.y = new_mouse_y;
|
||||
mouse_location.x = new_mouse_x;
|
||||
mouse_location.y = new_mouse_y;
|
||||
|
||||
let mut event_type = CGEventType::MouseMoved;
|
||||
if self.button_state.left {
|
||||
event_type = CGEventType::LeftMouseDragged
|
||||
} else if self.button_state.right {
|
||||
event_type = CGEventType::RightMouseDragged
|
||||
} else if self.button_state.center {
|
||||
event_type = CGEventType::OtherMouseDragged
|
||||
};
|
||||
let event = match CGEvent::new_mouse_event(
|
||||
self.event_source.clone(),
|
||||
event_type,
|
||||
mouse_location,
|
||||
CGMouseButton::Left,
|
||||
) {
|
||||
Ok(e) => e,
|
||||
Err(_) => {
|
||||
log::warn!("mouse event creation failed!");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
event.set_integer_value_field(EventField::MOUSE_EVENT_DELTA_X, dx as i64);
|
||||
event.set_integer_value_field(EventField::MOUSE_EVENT_DELTA_Y, dy as i64);
|
||||
event.post(CGEventTapLocation::HID);
|
||||
}
|
||||
PointerEvent::Button {
|
||||
time: _,
|
||||
button,
|
||||
state,
|
||||
} => {
|
||||
let (event_type, mouse_button) = match (button, state) {
|
||||
(BTN_LEFT, 1) => (CGEventType::LeftMouseDown, CGMouseButton::Left),
|
||||
(BTN_LEFT, 0) => (CGEventType::LeftMouseUp, CGMouseButton::Left),
|
||||
(BTN_RIGHT, 1) => (CGEventType::RightMouseDown, CGMouseButton::Right),
|
||||
(BTN_RIGHT, 0) => (CGEventType::RightMouseUp, CGMouseButton::Right),
|
||||
(BTN_MIDDLE, 1) => (CGEventType::OtherMouseDown, CGMouseButton::Center),
|
||||
(BTN_MIDDLE, 0) => (CGEventType::OtherMouseUp, CGMouseButton::Center),
|
||||
_ => {
|
||||
log::warn!("invalid button event: {button},{state}");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
// store button state
|
||||
self.button_state[mouse_button] = state == 1;
|
||||
|
||||
// update previous button state
|
||||
if state == 1 {
|
||||
if self.previous_button.is_some_and(|b| b.eq(&mouse_button))
|
||||
&& self
|
||||
.previous_button_click
|
||||
.is_some_and(|i| i.elapsed() < DOUBLE_CLICK_INTERVAL)
|
||||
{
|
||||
self.button_click_state += 1;
|
||||
} else {
|
||||
self.button_click_state = 1;
|
||||
}
|
||||
self.previous_button = Some(mouse_button);
|
||||
self.previous_button_click = Some(Instant::now());
|
||||
let mut event_type = CGEventType::MouseMoved;
|
||||
if self.button_state.left {
|
||||
event_type = CGEventType::LeftMouseDragged
|
||||
} else if self.button_state.right {
|
||||
event_type = CGEventType::RightMouseDragged
|
||||
} else if self.button_state.center {
|
||||
event_type = CGEventType::OtherMouseDragged
|
||||
};
|
||||
let event = match CGEvent::new_mouse_event(
|
||||
self.event_source.clone(),
|
||||
event_type,
|
||||
mouse_location,
|
||||
CGMouseButton::Left,
|
||||
) {
|
||||
Ok(e) => e,
|
||||
Err(_) => {
|
||||
log::warn!("mouse event creation failed!");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
event.set_integer_value_field(EventField::MOUSE_EVENT_DELTA_X, dx as i64);
|
||||
event.set_integer_value_field(EventField::MOUSE_EVENT_DELTA_Y, dy as i64);
|
||||
event.post(CGEventTapLocation::HID);
|
||||
}
|
||||
PointerEvent::Button {
|
||||
time: _,
|
||||
button,
|
||||
state,
|
||||
} => {
|
||||
let (event_type, mouse_button) = match (button, state) {
|
||||
(BTN_LEFT, 1) => (CGEventType::LeftMouseDown, CGMouseButton::Left),
|
||||
(BTN_LEFT, 0) => (CGEventType::LeftMouseUp, CGMouseButton::Left),
|
||||
(BTN_RIGHT, 1) => (CGEventType::RightMouseDown, CGMouseButton::Right),
|
||||
(BTN_RIGHT, 0) => (CGEventType::RightMouseUp, CGMouseButton::Right),
|
||||
(BTN_MIDDLE, 1) => (CGEventType::OtherMouseDown, CGMouseButton::Center),
|
||||
(BTN_MIDDLE, 0) => (CGEventType::OtherMouseUp, CGMouseButton::Center),
|
||||
_ => {
|
||||
log::warn!("invalid button event: {button},{state}");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
// store button state
|
||||
self.button_state[mouse_button] = state == 1;
|
||||
|
||||
log::debug!("click_state: {}", self.button_click_state);
|
||||
let location = self.get_mouse_location().unwrap();
|
||||
let event = match CGEvent::new_mouse_event(
|
||||
self.event_source.clone(),
|
||||
event_type,
|
||||
location,
|
||||
mouse_button,
|
||||
) {
|
||||
Ok(e) => e,
|
||||
Err(()) => {
|
||||
log::warn!("mouse event creation failed!");
|
||||
return Ok(());
|
||||
// update previous button state
|
||||
if state == 1 {
|
||||
if self.previous_button.is_some_and(|b| b.eq(&mouse_button))
|
||||
&& self
|
||||
.previous_button_click
|
||||
.is_some_and(|i| i.elapsed() < DOUBLE_CLICK_INTERVAL)
|
||||
{
|
||||
self.button_click_state += 1;
|
||||
} else {
|
||||
self.button_click_state = 1;
|
||||
}
|
||||
self.previous_button = Some(mouse_button);
|
||||
self.previous_button_click = Some(Instant::now());
|
||||
}
|
||||
};
|
||||
event.set_integer_value_field(
|
||||
EventField::MOUSE_EVENT_CLICK_STATE,
|
||||
self.button_click_state,
|
||||
);
|
||||
event.post(CGEventTapLocation::HID);
|
||||
|
||||
log::debug!("click_state: {}", self.button_click_state);
|
||||
let location = self.get_mouse_location().unwrap();
|
||||
let event = match CGEvent::new_mouse_event(
|
||||
self.event_source.clone(),
|
||||
event_type,
|
||||
location,
|
||||
mouse_button,
|
||||
) {
|
||||
Ok(e) => e,
|
||||
Err(()) => {
|
||||
log::warn!("mouse event creation failed!");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
event.set_integer_value_field(
|
||||
EventField::MOUSE_EVENT_CLICK_STATE,
|
||||
self.button_click_state,
|
||||
);
|
||||
event.post(CGEventTapLocation::HID);
|
||||
}
|
||||
PointerEvent::Axis {
|
||||
time: _,
|
||||
axis,
|
||||
value,
|
||||
} => {
|
||||
let value = value as i32;
|
||||
let (count, wheel1, wheel2, wheel3) = match axis {
|
||||
0 => (1, value, 0, 0), // 0 = vertical => 1 scroll wheel device (y axis)
|
||||
1 => (2, 0, value, 0), // 1 = horizontal => 2 scroll wheel devices (y, x) -> (0, x)
|
||||
_ => {
|
||||
log::warn!("invalid scroll event: {axis}, {value}");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let event = match CGEvent::new_scroll_event(
|
||||
self.event_source.clone(),
|
||||
ScrollEventUnit::PIXEL,
|
||||
count,
|
||||
wheel1,
|
||||
wheel2,
|
||||
wheel3,
|
||||
) {
|
||||
Ok(e) => e,
|
||||
Err(()) => {
|
||||
log::warn!("scroll event creation failed!");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
event.post(CGEventTapLocation::HID);
|
||||
}
|
||||
PointerEvent::AxisDiscrete120 { axis, value } => {
|
||||
const LINES_PER_STEP: i32 = 3;
|
||||
let (count, wheel1, wheel2, wheel3) = match axis {
|
||||
0 => (1, value / (120 / LINES_PER_STEP), 0, 0), // 0 = vertical => 1 scroll wheel device (y axis)
|
||||
1 => (2, 0, value / (120 / LINES_PER_STEP), 0), // 1 = horizontal => 2 scroll wheel devices (y, x) -> (0, x)
|
||||
_ => {
|
||||
log::warn!("invalid scroll event: {axis}, {value}");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let event = match CGEvent::new_scroll_event(
|
||||
self.event_source.clone(),
|
||||
ScrollEventUnit::LINE,
|
||||
count,
|
||||
wheel1,
|
||||
wheel2,
|
||||
wheel3,
|
||||
) {
|
||||
Ok(e) => e,
|
||||
Err(()) => {
|
||||
log::warn!("scroll event creation failed!");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
event.post(CGEventTapLocation::HID);
|
||||
}
|
||||
}
|
||||
PointerEvent::Axis {
|
||||
time: _,
|
||||
axis,
|
||||
value,
|
||||
} => {
|
||||
let value = value as i32;
|
||||
let (count, wheel1, wheel2, wheel3) = match axis {
|
||||
0 => (1, value, 0, 0), // 0 = vertical => 1 scroll wheel device (y axis)
|
||||
1 => (2, 0, value, 0), // 1 = horizontal => 2 scroll wheel devices (y, x) -> (0, x)
|
||||
_ => {
|
||||
log::warn!("invalid scroll event: {axis}, {value}");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let event = match CGEvent::new_scroll_event(
|
||||
self.event_source.clone(),
|
||||
ScrollEventUnit::PIXEL,
|
||||
count,
|
||||
wheel1,
|
||||
wheel2,
|
||||
wheel3,
|
||||
) {
|
||||
Ok(e) => e,
|
||||
Err(()) => {
|
||||
log::warn!("scroll event creation failed!");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
event.post(CGEventTapLocation::HID);
|
||||
|
||||
// reset button click state in case it's not a button event
|
||||
if !matches!(pointer_event, PointerEvent::Button { .. }) {
|
||||
self.button_click_state = 0;
|
||||
}
|
||||
PointerEvent::AxisDiscrete120 { axis, value } => {
|
||||
const LINES_PER_STEP: i32 = 3;
|
||||
let (count, wheel1, wheel2, wheel3) = match axis {
|
||||
0 => (1, value / (120 / LINES_PER_STEP), 0, 0), // 0 = vertical => 1 scroll wheel device (y axis)
|
||||
1 => (2, 0, value / (120 / LINES_PER_STEP), 0), // 1 = horizontal => 2 scroll wheel devices (y, x) -> (0, x)
|
||||
_ => {
|
||||
log::warn!("invalid scroll event: {axis}, {value}");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let event = match CGEvent::new_scroll_event(
|
||||
self.event_source.clone(),
|
||||
ScrollEventUnit::LINE,
|
||||
count,
|
||||
wheel1,
|
||||
wheel2,
|
||||
wheel3,
|
||||
) {
|
||||
Ok(e) => e,
|
||||
Err(()) => {
|
||||
log::warn!("scroll event creation failed!");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
event.post(CGEventTapLocation::HID);
|
||||
}
|
||||
},
|
||||
}
|
||||
Event::Keyboard(keyboard_event) => match keyboard_event {
|
||||
KeyboardEvent::Key {
|
||||
time: _,
|
||||
@@ -405,18 +416,12 @@ impl Emulation for MacOSEmulation {
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
update_modifiers(&self.modifier_state, key, state);
|
||||
match state {
|
||||
// pressed
|
||||
1 => self.spawn_repeat_task(code).await,
|
||||
_ => self.cancel_repeat_task().await,
|
||||
}
|
||||
update_modifiers(&self.modifier_state, key, state);
|
||||
key_event(
|
||||
self.event_source.clone(),
|
||||
code,
|
||||
state,
|
||||
self.modifier_state.get(),
|
||||
);
|
||||
}
|
||||
KeyboardEvent::Modifiers {
|
||||
depressed,
|
||||
|
||||
Reference in New Issue
Block a user