mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-07 20:09:59 +03:00
Compare commits
1 Commits
input-even
...
macos-clie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df85f0baf8 |
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1226,6 +1226,7 @@ version = "0.2.1"
|
||||
dependencies = [
|
||||
"ashpd",
|
||||
"async-trait",
|
||||
"bitflags 2.6.0",
|
||||
"core-graphics",
|
||||
"futures",
|
||||
"input-event",
|
||||
|
||||
@@ -44,6 +44,7 @@ ashpd = { version = "0.9", default-features = false, features = [
|
||||
reis = { version = "0.2", features = ["tokio"], optional = true }
|
||||
|
||||
[target.'cfg(target_os="macos")'.dependencies]
|
||||
bitflags = "2.5.0"
|
||||
core-graphics = { version = "0.23", features = ["highsierra"] }
|
||||
keycode = "0.4.0"
|
||||
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
use super::{error::EmulationError, Emulation, EmulationHandle};
|
||||
use async_trait::async_trait;
|
||||
use bitflags::bitflags;
|
||||
use core_graphics::base::CGFloat;
|
||||
use core_graphics::display::{
|
||||
CGDirectDisplayID, CGDisplayBounds, CGGetDisplaysWithRect, CGPoint, CGRect, CGSize,
|
||||
};
|
||||
use core_graphics::event::{
|
||||
CGEvent, CGEventTapLocation, CGEventType, CGKeyCode, CGMouseButton, EventField, ScrollEventUnit,
|
||||
CGEvent, CGEventFlags, CGEventTapLocation, CGEventType, CGKeyCode, CGMouseButton, EventField,
|
||||
ScrollEventUnit,
|
||||
};
|
||||
use core_graphics::event_source::{CGEventSource, CGEventSourceStateID};
|
||||
use input_event::{Event, KeyboardEvent, PointerEvent};
|
||||
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;
|
||||
use tokio::{sync::Notify, task::JoinHandle};
|
||||
@@ -24,6 +28,7 @@ pub(crate) struct MacOSEmulation {
|
||||
event_source: CGEventSource,
|
||||
repeat_task: Option<JoinHandle<()>>,
|
||||
button_state: ButtonState,
|
||||
modifier_state: Rc<Cell<XMods>>,
|
||||
notify_repeat_task: Arc<Notify>,
|
||||
}
|
||||
|
||||
@@ -71,6 +76,7 @@ impl MacOSEmulation {
|
||||
button_state,
|
||||
repeat_task: None,
|
||||
notify_repeat_task: Arc::new(Notify::new()),
|
||||
modifier_state: Rc::new(Cell::new(XMods::empty())),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -85,6 +91,7 @@ impl MacOSEmulation {
|
||||
self.cancel_repeat_task().await;
|
||||
let event_source = self.event_source.clone();
|
||||
let notify = self.notify_repeat_task.clone();
|
||||
let modifiers = self.modifier_state.clone();
|
||||
let repeat_task = tokio::task::spawn_local(async move {
|
||||
let stop = tokio::select! {
|
||||
_ = tokio::time::sleep(DEFAULT_REPEAT_DELAY) => false,
|
||||
@@ -92,7 +99,7 @@ impl MacOSEmulation {
|
||||
};
|
||||
if !stop {
|
||||
loop {
|
||||
key_event(event_source.clone(), key, 1);
|
||||
key_event(event_source.clone(), key, 1, modifiers.get());
|
||||
tokio::select! {
|
||||
_ = tokio::time::sleep(DEFAULT_REPEAT_INTERVAL) => {},
|
||||
_ = notify.notified() => break,
|
||||
@@ -100,7 +107,8 @@ impl MacOSEmulation {
|
||||
}
|
||||
}
|
||||
// release key when cancelled
|
||||
key_event(event_source.clone(), key, 0);
|
||||
update_modifiers(&modifiers, key as u32, 0);
|
||||
key_event(event_source.clone(), key, 0, modifiers.get());
|
||||
});
|
||||
self.repeat_task = Some(repeat_task);
|
||||
}
|
||||
@@ -113,7 +121,7 @@ impl MacOSEmulation {
|
||||
}
|
||||
}
|
||||
|
||||
fn key_event(event_source: CGEventSource, key: u16, state: u8) {
|
||||
fn key_event(event_source: CGEventSource, key: u16, state: u8, modifiers: XMods) {
|
||||
let event = match CGEvent::new_keyboard_event(event_source, key, state != 0) {
|
||||
Ok(e) => e,
|
||||
Err(_) => {
|
||||
@@ -121,7 +129,21 @@ fn key_event(event_source: CGEventSource, key: u16, state: u8) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
event.set_flags(to_cgevent_flags(modifiers));
|
||||
event.post(CGEventTapLocation::HID);
|
||||
log::trace!("key event: {key} {state}");
|
||||
}
|
||||
|
||||
fn modifier_event(event_source: CGEventSource, depressed: XMods) {
|
||||
let Ok(event) = CGEvent::new(event_source) else {
|
||||
log::warn!("could not create CGEvent");
|
||||
return;
|
||||
};
|
||||
let flags = to_cgevent_flags(depressed);
|
||||
event.set_type(CGEventType::FlagsChanged);
|
||||
event.set_flags(flags);
|
||||
event.post(CGEventTapLocation::HID);
|
||||
log::trace!("modifiers updated: {depressed:?}");
|
||||
}
|
||||
|
||||
fn get_display_at_point(x: CGFloat, y: CGFloat) -> Option<CGDirectDisplayID> {
|
||||
@@ -364,9 +386,23 @@ impl Emulation for MacOSEmulation {
|
||||
1 => self.spawn_repeat_task(code).await,
|
||||
_ => self.cancel_repeat_task().await,
|
||||
}
|
||||
key_event(self.event_source.clone(), code, state)
|
||||
update_modifiers(&self.modifier_state, key, state);
|
||||
key_event(
|
||||
self.event_source.clone(),
|
||||
code,
|
||||
state,
|
||||
self.modifier_state.get(),
|
||||
);
|
||||
}
|
||||
KeyboardEvent::Modifiers {
|
||||
depressed,
|
||||
latched,
|
||||
locked,
|
||||
group,
|
||||
} => {
|
||||
set_modifiers(&self.modifier_state, depressed, latched, locked, group);
|
||||
modifier_event(self.event_source.clone(), self.modifier_state.get());
|
||||
}
|
||||
KeyboardEvent::Modifiers { .. } => {}
|
||||
},
|
||||
}
|
||||
// FIXME
|
||||
@@ -379,3 +415,81 @@ impl Emulation for MacOSEmulation {
|
||||
|
||||
async fn terminate(&mut self) {}
|
||||
}
|
||||
|
||||
fn update_modifiers(modifiers: &Cell<XMods>, key: u32, state: u8) -> bool {
|
||||
if let Ok(key) = scancode::Linux::try_from(key) {
|
||||
let mask = match key {
|
||||
scancode::Linux::KeyLeftShift | scancode::Linux::KeyRightShift => XMods::ShiftMask,
|
||||
scancode::Linux::KeyCapsLock => XMods::LockMask,
|
||||
scancode::Linux::KeyLeftCtrl | scancode::Linux::KeyRightCtrl => XMods::ControlMask,
|
||||
scancode::Linux::KeyLeftAlt | scancode::Linux::KeyRightalt => XMods::Mod1Mask,
|
||||
scancode::Linux::KeyLeftMeta | scancode::Linux::KeyRightmeta => XMods::Mod4Mask,
|
||||
_ => XMods::empty(),
|
||||
};
|
||||
// unchanged
|
||||
if mask.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let mut mods = modifiers.get();
|
||||
match state {
|
||||
1 => mods.insert(mask),
|
||||
_ => mods.remove(mask),
|
||||
}
|
||||
modifiers.set(mods);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn set_modifiers(
|
||||
active_modifiers: &Cell<XMods>,
|
||||
depressed: u32,
|
||||
latched: u32,
|
||||
locked: u32,
|
||||
group: u32,
|
||||
) {
|
||||
let depressed = XMods::from_bits(depressed).unwrap_or_default();
|
||||
let _latched = XMods::from_bits(latched).unwrap_or_default();
|
||||
let _locked = XMods::from_bits(locked).unwrap_or_default();
|
||||
let _group = XMods::from_bits(group).unwrap_or_default();
|
||||
|
||||
// we only care about the depressed modifiers for now
|
||||
active_modifiers.replace(depressed);
|
||||
}
|
||||
|
||||
fn to_cgevent_flags(depressed: XMods) -> CGEventFlags {
|
||||
let mut flags = CGEventFlags::empty();
|
||||
if depressed.contains(XMods::ShiftMask) {
|
||||
flags |= CGEventFlags::CGEventFlagShift;
|
||||
}
|
||||
if depressed.contains(XMods::LockMask) {
|
||||
flags |= CGEventFlags::CGEventFlagAlphaShift;
|
||||
}
|
||||
if depressed.contains(XMods::ControlMask) {
|
||||
flags |= CGEventFlags::CGEventFlagControl;
|
||||
}
|
||||
if depressed.contains(XMods::Mod1Mask) {
|
||||
flags |= CGEventFlags::CGEventFlagAlternate;
|
||||
}
|
||||
if depressed.contains(XMods::Mod4Mask) {
|
||||
flags |= CGEventFlags::CGEventFlagCommand;
|
||||
}
|
||||
flags
|
||||
}
|
||||
|
||||
// From X11/X.h
|
||||
bitflags! {
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
struct XMods: u32 {
|
||||
const ShiftMask = (1<<0);
|
||||
const LockMask = (1<<1);
|
||||
const ControlMask = (1<<2);
|
||||
const Mod1Mask = (1<<3);
|
||||
const Mod2Mask = (1<<4);
|
||||
const Mod3Mask = (1<<5);
|
||||
const Mod4Mask = (1<<6);
|
||||
const Mod5Mask = (1<<7);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user