mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-16 09:40:54 +03:00
Compare commits
6 Commits
prevent-au
...
macos-capt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
138c43febd | ||
|
|
f91b6bd3c1 | ||
|
|
2d1a037eba | ||
|
|
057f6e2567 | ||
|
|
99c8bc5567 | ||
|
|
0dd413e989 |
@@ -40,6 +40,7 @@ struct InputCaptureState {
|
|||||||
active_clients: Lazy<HashSet<Position>>,
|
active_clients: Lazy<HashSet<Position>>,
|
||||||
current_pos: Option<Position>,
|
current_pos: Option<Position>,
|
||||||
bounds: Bounds,
|
bounds: Bounds,
|
||||||
|
modifier_state: XMods,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -57,6 +58,7 @@ impl InputCaptureState {
|
|||||||
active_clients: Lazy::new(HashSet::new),
|
active_clients: Lazy::new(HashSet::new),
|
||||||
current_pos: None,
|
current_pos: None,
|
||||||
bounds: Bounds::default(),
|
bounds: Bounds::default(),
|
||||||
|
modifier_state: Default::default(),
|
||||||
};
|
};
|
||||||
res.update_bounds()?;
|
res.update_bounds()?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
@@ -180,6 +182,7 @@ fn get_events(
|
|||||||
ev_type: &CGEventType,
|
ev_type: &CGEventType,
|
||||||
ev: &CGEvent,
|
ev: &CGEvent,
|
||||||
result: &mut Vec<CaptureEvent>,
|
result: &mut Vec<CaptureEvent>,
|
||||||
|
modifier_state: &mut XMods,
|
||||||
) -> Result<(), CaptureError> {
|
) -> Result<(), CaptureError> {
|
||||||
fn map_pointer_event(ev: &CGEvent) -> PointerEvent {
|
fn map_pointer_event(ev: &CGEvent) -> PointerEvent {
|
||||||
PointerEvent::Motion {
|
PointerEvent::Motion {
|
||||||
@@ -215,29 +218,42 @@ fn get_events(
|
|||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
CGEventType::FlagsChanged => {
|
CGEventType::FlagsChanged => {
|
||||||
let mut mods = XMods::empty();
|
let mut depressed = XMods::empty();
|
||||||
let mut mods_locked = XMods::empty();
|
let mut mods_locked = XMods::empty();
|
||||||
let cg_flags = ev.get_flags();
|
let cg_flags = ev.get_flags();
|
||||||
|
|
||||||
if cg_flags.contains(CGEventFlags::CGEventFlagShift) {
|
if cg_flags.contains(CGEventFlags::CGEventFlagShift) {
|
||||||
mods |= XMods::ShiftMask;
|
depressed |= XMods::ShiftMask;
|
||||||
}
|
}
|
||||||
if cg_flags.contains(CGEventFlags::CGEventFlagControl) {
|
if cg_flags.contains(CGEventFlags::CGEventFlagControl) {
|
||||||
mods |= XMods::ControlMask;
|
depressed |= XMods::ControlMask;
|
||||||
}
|
}
|
||||||
if cg_flags.contains(CGEventFlags::CGEventFlagAlternate) {
|
if cg_flags.contains(CGEventFlags::CGEventFlagAlternate) {
|
||||||
mods |= XMods::Mod1Mask;
|
depressed |= XMods::Mod1Mask;
|
||||||
}
|
}
|
||||||
if cg_flags.contains(CGEventFlags::CGEventFlagCommand) {
|
if cg_flags.contains(CGEventFlags::CGEventFlagCommand) {
|
||||||
mods |= XMods::Mod4Mask;
|
depressed |= XMods::Mod4Mask;
|
||||||
}
|
}
|
||||||
if cg_flags.contains(CGEventFlags::CGEventFlagAlphaShift) {
|
if cg_flags.contains(CGEventFlags::CGEventFlagAlphaShift) {
|
||||||
mods |= XMods::LockMask;
|
depressed |= XMods::LockMask;
|
||||||
mods_locked |= 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 {
|
let modifier_event = KeyboardEvent::Modifiers {
|
||||||
depressed: mods.bits(),
|
depressed: depressed.bits(),
|
||||||
latched: 0,
|
latched: 0,
|
||||||
locked: mods_locked.bits(),
|
locked: mods_locked.bits(),
|
||||||
group: 0,
|
group: 0,
|
||||||
@@ -366,7 +382,13 @@ fn create_event_tap<'a>(
|
|||||||
// Are we in a client?
|
// Are we in a client?
|
||||||
if let Some(current_pos) = state.current_pos {
|
if let Some(current_pos) = state.current_pos {
|
||||||
pos = Some(current_pos);
|
pos = Some(current_pos);
|
||||||
get_events(&event_type, cg_ev, &mut res_events).unwrap_or_else(|e| {
|
get_events(
|
||||||
|
&event_type,
|
||||||
|
cg_ev,
|
||||||
|
&mut res_events,
|
||||||
|
&mut state.modifier_state,
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
log::error!("Failed to get events: {e}");
|
log::error!("Failed to get events: {e}");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ tokio = { version = "1.32.0", features = [
|
|||||||
"rt",
|
"rt",
|
||||||
"sync",
|
"sync",
|
||||||
"signal",
|
"signal",
|
||||||
|
"time"
|
||||||
] }
|
] }
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
|
|
||||||
|
|||||||
@@ -10,25 +10,37 @@ use core_graphics::event::{
|
|||||||
ScrollEventUnit,
|
ScrollEventUnit,
|
||||||
};
|
};
|
||||||
use core_graphics::event_source::{CGEventSource, CGEventSourceStateID};
|
use core_graphics::event_source::{CGEventSource, CGEventSourceStateID};
|
||||||
use input_event::{scancode, Event, KeyboardEvent, PointerEvent};
|
use input_event::{scancode, Event, KeyboardEvent, PointerEvent, BTN_LEFT, BTN_MIDDLE, BTN_RIGHT};
|
||||||
use keycode::{KeyMap, KeyMapping};
|
use keycode::{KeyMap, KeyMapping};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::{Duration, Instant};
|
||||||
use tokio::{sync::Notify, task::JoinHandle};
|
use tokio::{sync::Notify, task::JoinHandle};
|
||||||
|
|
||||||
use super::error::MacOSEmulationCreationError;
|
use super::error::MacOSEmulationCreationError;
|
||||||
|
|
||||||
const DEFAULT_REPEAT_DELAY: Duration = Duration::from_millis(500);
|
const DEFAULT_REPEAT_DELAY: Duration = Duration::from_millis(500);
|
||||||
const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis(32);
|
const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis(32);
|
||||||
|
const DOUBLE_CLICK_INTERVAL: Duration = Duration::from_millis(500);
|
||||||
|
|
||||||
pub(crate) struct MacOSEmulation {
|
pub(crate) struct MacOSEmulation {
|
||||||
|
/// global event source for all events
|
||||||
event_source: CGEventSource,
|
event_source: CGEventSource,
|
||||||
|
/// task handle for key repeats
|
||||||
repeat_task: Option<JoinHandle<()>>,
|
repeat_task: Option<JoinHandle<()>>,
|
||||||
|
/// current state of the mouse buttons
|
||||||
button_state: ButtonState,
|
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>>,
|
modifier_state: Rc<Cell<XMods>>,
|
||||||
|
/// notify to cancel key repeats
|
||||||
notify_repeat_task: Arc<Notify>,
|
notify_repeat_task: Arc<Notify>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,6 +86,9 @@ impl MacOSEmulation {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
event_source,
|
event_source,
|
||||||
button_state,
|
button_state,
|
||||||
|
previous_button: None,
|
||||||
|
previous_button_click: None,
|
||||||
|
button_click_state: 0,
|
||||||
repeat_task: None,
|
repeat_task: None,
|
||||||
notify_repeat_task: Arc::new(Notify::new()),
|
notify_repeat_task: Arc::new(Notify::new()),
|
||||||
modifier_state: Rc::new(Cell::new(XMods::empty())),
|
modifier_state: Rc::new(Cell::new(XMods::empty())),
|
||||||
@@ -89,6 +104,9 @@ impl MacOSEmulation {
|
|||||||
// 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.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 event_source = self.event_source.clone();
|
||||||
let notify = self.notify_repeat_task.clone();
|
let notify = self.notify_repeat_task.clone();
|
||||||
let modifiers = self.modifier_state.clone();
|
let modifiers = self.modifier_state.clone();
|
||||||
@@ -224,150 +242,167 @@ impl Emulation for MacOSEmulation {
|
|||||||
event: Event,
|
event: Event,
|
||||||
_handle: EmulationHandle,
|
_handle: EmulationHandle,
|
||||||
) -> Result<(), EmulationError> {
|
) -> Result<(), EmulationError> {
|
||||||
|
log::trace!("{event:?}");
|
||||||
match event {
|
match event {
|
||||||
Event::Pointer(pointer_event) => match pointer_event {
|
Event::Pointer(pointer_event) => {
|
||||||
PointerEvent::Motion { time: _, dx, dy } => {
|
match pointer_event {
|
||||||
let mut mouse_location = match self.get_mouse_location() {
|
PointerEvent::Motion { time: _, dx, dy } => {
|
||||||
Some(l) => l,
|
let mut mouse_location = match self.get_mouse_location() {
|
||||||
None => {
|
Some(l) => l,
|
||||||
log::warn!("could not get mouse location!");
|
None => {
|
||||||
return Ok(());
|
log::warn!("could not get mouse location!");
|
||||||
}
|
return Ok(());
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let (new_mouse_x, new_mouse_y) =
|
let (new_mouse_x, new_mouse_y) =
|
||||||
clamp_to_screen_space(mouse_location.x, mouse_location.y, dx, dy);
|
clamp_to_screen_space(mouse_location.x, mouse_location.y, dx, dy);
|
||||||
|
|
||||||
mouse_location.x = new_mouse_x;
|
mouse_location.x = new_mouse_x;
|
||||||
mouse_location.y = new_mouse_y;
|
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 {
|
||||||
event_type = CGEventType::LeftMouseDragged
|
event_type = CGEventType::LeftMouseDragged
|
||||||
} else if self.button_state.right {
|
} else if self.button_state.right {
|
||||||
event_type = CGEventType::RightMouseDragged
|
event_type = CGEventType::RightMouseDragged
|
||||||
} else if self.button_state.center {
|
} else if self.button_state.center {
|
||||||
event_type = CGEventType::OtherMouseDragged
|
event_type = CGEventType::OtherMouseDragged
|
||||||
};
|
};
|
||||||
let event = match CGEvent::new_mouse_event(
|
let event = match CGEvent::new_mouse_event(
|
||||||
self.event_source.clone(),
|
self.event_source.clone(),
|
||||||
event_type,
|
event_type,
|
||||||
mouse_location,
|
mouse_location,
|
||||||
CGMouseButton::Left,
|
CGMouseButton::Left,
|
||||||
) {
|
) {
|
||||||
Ok(e) => e,
|
Ok(e) => e,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
log::warn!("mouse event creation failed!");
|
log::warn!("mouse event creation failed!");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
event.set_integer_value_field(EventField::MOUSE_EVENT_DELTA_X, dx as i64);
|
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.set_integer_value_field(EventField::MOUSE_EVENT_DELTA_Y, dy as i64);
|
||||||
event.post(CGEventTapLocation::HID);
|
event.post(CGEventTapLocation::HID);
|
||||||
}
|
}
|
||||||
PointerEvent::Button {
|
PointerEvent::Button {
|
||||||
time: _,
|
time: _,
|
||||||
button,
|
button,
|
||||||
state,
|
state,
|
||||||
} => {
|
} => {
|
||||||
let (event_type, mouse_button) = match (button, state) {
|
let (event_type, mouse_button) = match (button, state) {
|
||||||
(b, 1) if b == input_event::BTN_LEFT => {
|
(BTN_LEFT, 1) => (CGEventType::LeftMouseDown, CGMouseButton::Left),
|
||||||
(CGEventType::LeftMouseDown, CGMouseButton::Left)
|
(BTN_LEFT, 0) => (CGEventType::LeftMouseUp, CGMouseButton::Left),
|
||||||
}
|
(BTN_RIGHT, 1) => (CGEventType::RightMouseDown, CGMouseButton::Right),
|
||||||
(b, 0) if b == input_event::BTN_LEFT => {
|
(BTN_RIGHT, 0) => (CGEventType::RightMouseUp, CGMouseButton::Right),
|
||||||
(CGEventType::LeftMouseUp, CGMouseButton::Left)
|
(BTN_MIDDLE, 1) => (CGEventType::OtherMouseDown, CGMouseButton::Center),
|
||||||
}
|
(BTN_MIDDLE, 0) => (CGEventType::OtherMouseUp, CGMouseButton::Center),
|
||||||
(b, 1) if b == input_event::BTN_RIGHT => {
|
_ => {
|
||||||
(CGEventType::RightMouseDown, CGMouseButton::Right)
|
log::warn!("invalid button event: {button},{state}");
|
||||||
}
|
return Ok(());
|
||||||
(b, 0) if b == input_event::BTN_RIGHT => {
|
}
|
||||||
(CGEventType::RightMouseUp, CGMouseButton::Right)
|
};
|
||||||
}
|
// store button state
|
||||||
(b, 1) if b == input_event::BTN_MIDDLE => {
|
self.button_state[mouse_button] = state == 1;
|
||||||
(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();
|
// update previous button state
|
||||||
let event = match CGEvent::new_mouse_event(
|
if state == 1 {
|
||||||
self.event_source.clone(),
|
if self.previous_button.is_some_and(|b| b.eq(&mouse_button))
|
||||||
event_type,
|
&& self
|
||||||
location,
|
.previous_button_click
|
||||||
mouse_button,
|
.is_some_and(|i| i.elapsed() < DOUBLE_CLICK_INTERVAL)
|
||||||
) {
|
{
|
||||||
Ok(e) => e,
|
self.button_click_state += 1;
|
||||||
Err(()) => {
|
} else {
|
||||||
log::warn!("mouse event creation failed!");
|
self.button_click_state = 1;
|
||||||
return Ok(());
|
}
|
||||||
|
self.previous_button = Some(mouse_button);
|
||||||
|
self.previous_button_click = Some(Instant::now());
|
||||||
}
|
}
|
||||||
};
|
|
||||||
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: _,
|
// reset button click state in case it's not a button event
|
||||||
axis,
|
if !matches!(pointer_event, PointerEvent::Button { .. }) {
|
||||||
value,
|
self.button_click_state = 0;
|
||||||
} => {
|
|
||||||
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 {
|
Event::Keyboard(keyboard_event) => match keyboard_event {
|
||||||
KeyboardEvent::Key {
|
KeyboardEvent::Key {
|
||||||
time: _,
|
time: _,
|
||||||
@@ -381,18 +416,12 @@ impl Emulation for MacOSEmulation {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
update_modifiers(&self.modifier_state, key, state);
|
||||||
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.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 {
|
KeyboardEvent::Modifiers {
|
||||||
depressed,
|
depressed,
|
||||||
@@ -416,6 +445,21 @@ impl Emulation for MacOSEmulation {
|
|||||||
async fn terminate(&mut self) {}
|
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 {
|
fn update_modifiers(modifiers: &Cell<XMods>, key: u32, state: u8) -> bool {
|
||||||
if let Ok(key) = scancode::Linux::try_from(key) {
|
if let Ok(key) = scancode::Linux::try_from(key) {
|
||||||
let mask = match key {
|
let mask = match key {
|
||||||
|
|||||||
@@ -474,6 +474,9 @@ impl Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn request_authorization(&self, fingerprint: &str) {
|
pub(super) fn request_authorization(&self, fingerprint: &str) {
|
||||||
|
if let Some(w) = self.imp().authorization_window.borrow_mut().take() {
|
||||||
|
w.close();
|
||||||
|
}
|
||||||
let window = AuthorizationWindow::new(fingerprint);
|
let window = AuthorizationWindow::new(fingerprint);
|
||||||
window.set_transient_for(Some(self));
|
window.set_transient_for(Some(self));
|
||||||
window.connect_closure(
|
window.connect_closure(
|
||||||
@@ -496,5 +499,6 @@ impl Window {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
window.present();
|
window.present();
|
||||||
|
self.imp().authorization_window.replace(Some(window));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ use gtk::{gdk, gio, glib, Button, CompositeTemplate, Entry, Image, Label, ListBo
|
|||||||
|
|
||||||
use lan_mouse_ipc::{FrontendRequestWriter, DEFAULT_PORT};
|
use lan_mouse_ipc::{FrontendRequestWriter, DEFAULT_PORT};
|
||||||
|
|
||||||
|
use crate::authorization_window::AuthorizationWindow;
|
||||||
|
|
||||||
#[derive(CompositeTemplate, Default)]
|
#[derive(CompositeTemplate, Default)]
|
||||||
#[template(resource = "/de/feschber/LanMouse/window.ui")]
|
#[template(resource = "/de/feschber/LanMouse/window.ui")]
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
@@ -49,6 +51,7 @@ pub struct Window {
|
|||||||
pub port: Cell<u16>,
|
pub port: Cell<u16>,
|
||||||
pub capture_active: Cell<bool>,
|
pub capture_active: Cell<bool>,
|
||||||
pub emulation_active: Cell<bool>,
|
pub emulation_active: Cell<bool>,
|
||||||
|
pub authorization_window: RefCell<Option<AuthorizationWindow>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ impl ListenTask {
|
|||||||
async fn run(mut self) {
|
async fn run(mut self) {
|
||||||
let mut interval = tokio::time::interval(Duration::from_secs(5));
|
let mut interval = tokio::time::interval(Duration::from_secs(5));
|
||||||
let mut last_response = HashMap::new();
|
let mut last_response = HashMap::new();
|
||||||
|
let mut rejected_connections = HashMap::new();
|
||||||
loop {
|
loop {
|
||||||
select! {
|
select! {
|
||||||
e = self.listener.next() => {match e {
|
e = self.listener.next() => {match e {
|
||||||
@@ -156,7 +157,10 @@ impl ListenTask {
|
|||||||
self.event_tx.send(EmulationEvent::Connected { addr, fingerprint }).expect("channel closed");
|
self.event_tx.send(EmulationEvent::Connected { addr, fingerprint }).expect("channel closed");
|
||||||
}
|
}
|
||||||
Some(ListenEvent::Rejected { fingerprint }) => {
|
Some(ListenEvent::Rejected { fingerprint }) => {
|
||||||
self.event_tx.send(EmulationEvent::ConnectionAttempt { fingerprint }).expect("channel closed");
|
if rejected_connections.insert(fingerprint.clone(), Instant::now())
|
||||||
|
.is_none_or(|i| i.elapsed() >= Duration::from_secs(2)) {
|
||||||
|
self.event_tx.send(EmulationEvent::ConnectionAttempt { fingerprint }).expect("channel closed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => break
|
None => break
|
||||||
}}
|
}}
|
||||||
|
|||||||
Reference in New Issue
Block a user