Compare commits

..

1 Commits

Author SHA1 Message Date
Ferdinand Schober
d49ba36b34 prevent authorization request spamming windows 2025-10-27 23:39:27 +01:00
2 changed files with 148 additions and 193 deletions

View File

@@ -21,7 +21,6 @@ tokio = { version = "1.32.0", features = [
"rt",
"sync",
"signal",
"time"
] }
once_cell = "1.19.0"

View File

@@ -10,37 +10,25 @@ use core_graphics::event::{
ScrollEventUnit,
};
use core_graphics::event_source::{CGEventSource, CGEventSourceStateID};
use input_event::{scancode, Event, KeyboardEvent, PointerEvent, BTN_LEFT, BTN_MIDDLE, BTN_RIGHT};
use input_event::{scancode, Event, KeyboardEvent, PointerEvent};
use keycode::{KeyMap, KeyMapping};
use std::cell::Cell;
use std::ops::{Index, IndexMut};
use std::rc::Rc;
use std::sync::Arc;
use std::time::{Duration, Instant};
use std::time::Duration;
use tokio::{sync::Notify, task::JoinHandle};
use super::error::MacOSEmulationCreationError;
const DEFAULT_REPEAT_DELAY: Duration = Duration::from_millis(500);
const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis(32);
const DOUBLE_CLICK_INTERVAL: Duration = Duration::from_millis(500);
pub(crate) struct MacOSEmulation {
/// global event source for all events
event_source: CGEventSource,
/// task handle for key repeats
repeat_task: Option<JoinHandle<()>>,
/// current state of the mouse buttons
button_state: ButtonState,
/// button previously pressed
previous_button: Option<CGMouseButton>,
/// timestamp of previous click (button down)
previous_button_click: Option<Instant>,
/// click state, i.e. number of clicks in quick succession
button_click_state: i64,
/// current modifier state
modifier_state: Rc<Cell<XMods>>,
/// notify to cancel key repeats
notify_repeat_task: Arc<Notify>,
}
@@ -86,9 +74,6 @@ impl MacOSEmulation {
Ok(Self {
event_source,
button_state,
previous_button: None,
previous_button_click: None,
button_click_state: 0,
repeat_task: None,
notify_repeat_task: Arc::new(Notify::new()),
modifier_state: Rc::new(Cell::new(XMods::empty())),
@@ -104,9 +89,6 @@ 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();
@@ -242,167 +224,150 @@ 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(());
}
};
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;
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());
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(());
}
};
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);
}
}
let (new_mouse_x, new_mouse_y) =
clamp_to_screen_space(mouse_location.x, mouse_location.y, dx, dy);
// reset button click state in case it's not a button event
if !matches!(pointer_event, PointerEvent::Button { .. }) {
self.button_click_state = 0;
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) {
(b, 1) if b == input_event::BTN_LEFT => {
(CGEventType::LeftMouseDown, CGMouseButton::Left)
}
(b, 0) if b == input_event::BTN_LEFT => {
(CGEventType::LeftMouseUp, CGMouseButton::Left)
}
(b, 1) if b == input_event::BTN_RIGHT => {
(CGEventType::RightMouseDown, CGMouseButton::Right)
}
(b, 0) if b == input_event::BTN_RIGHT => {
(CGEventType::RightMouseUp, CGMouseButton::Right)
}
(b, 1) if b == input_event::BTN_MIDDLE => {
(CGEventType::OtherMouseDown, CGMouseButton::Center)
}
(b, 0) if b == input_event::BTN_MIDDLE => {
(CGEventType::OtherMouseUp, CGMouseButton::Center)
}
_ => {
log::warn!("invalid button event: {button},{state}");
return Ok(());
}
};
// store button state
self.button_state[mouse_button] = state == 1;
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.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 } => {
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);
}
},
Event::Keyboard(keyboard_event) => match keyboard_event {
KeyboardEvent::Key {
time: _,
@@ -416,12 +381,18 @@ 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,
@@ -445,21 +416,6 @@ impl Emulation for MacOSEmulation {
async fn terminate(&mut self) {}
}
trait ButtonEq {
fn eq(&self, other: &Self) -> bool;
}
impl ButtonEq for CGMouseButton {
fn eq(&self, other: &Self) -> bool {
matches!(
(self, other),
(CGMouseButton::Left, CGMouseButton::Left)
| (CGMouseButton::Right, CGMouseButton::Right)
| (CGMouseButton::Center, CGMouseButton::Center)
)
}
}
fn update_modifiers(modifiers: &Cell<XMods>, key: u32, state: u8) -> bool {
if let Ok(key) = scancode::Linux::try_from(key) {
let mask = match key {