mirror of
https://github.com/feschber/lan-mouse.git
synced 2026-03-10 06:40:56 +03:00
Compare commits
1 Commits
remove-git
...
macos-clie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df85f0baf8 |
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1226,6 +1226,7 @@ version = "0.2.1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"ashpd",
|
"ashpd",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"bitflags 2.6.0",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"futures",
|
"futures",
|
||||||
"input-event",
|
"input-event",
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ ashpd = { version = "0.9", default-features = false, features = [
|
|||||||
reis = { version = "0.2", features = ["tokio"], optional = true }
|
reis = { version = "0.2", features = ["tokio"], optional = true }
|
||||||
|
|
||||||
[target.'cfg(target_os="macos")'.dependencies]
|
[target.'cfg(target_os="macos")'.dependencies]
|
||||||
|
bitflags = "2.5.0"
|
||||||
core-graphics = { version = "0.23", features = ["highsierra"] }
|
core-graphics = { version = "0.23", features = ["highsierra"] }
|
||||||
keycode = "0.4.0"
|
keycode = "0.4.0"
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
use super::{error::EmulationError, Emulation, EmulationHandle};
|
use super::{error::EmulationError, Emulation, EmulationHandle};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use bitflags::bitflags;
|
||||||
use core_graphics::base::CGFloat;
|
use core_graphics::base::CGFloat;
|
||||||
use core_graphics::display::{
|
use core_graphics::display::{
|
||||||
CGDirectDisplayID, CGDisplayBounds, CGGetDisplaysWithRect, CGPoint, CGRect, CGSize,
|
CGDirectDisplayID, CGDisplayBounds, CGGetDisplaysWithRect, CGPoint, CGRect, CGSize,
|
||||||
};
|
};
|
||||||
use core_graphics::event::{
|
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 core_graphics::event_source::{CGEventSource, CGEventSourceStateID};
|
||||||
use input_event::{Event, KeyboardEvent, PointerEvent};
|
use input_event::{scancode, Event, KeyboardEvent, PointerEvent};
|
||||||
use keycode::{KeyMap, KeyMapping};
|
use keycode::{KeyMap, KeyMapping};
|
||||||
|
use std::cell::Cell;
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::{sync::Notify, task::JoinHandle};
|
use tokio::{sync::Notify, task::JoinHandle};
|
||||||
@@ -24,6 +28,7 @@ pub(crate) struct MacOSEmulation {
|
|||||||
event_source: CGEventSource,
|
event_source: CGEventSource,
|
||||||
repeat_task: Option<JoinHandle<()>>,
|
repeat_task: Option<JoinHandle<()>>,
|
||||||
button_state: ButtonState,
|
button_state: ButtonState,
|
||||||
|
modifier_state: Rc<Cell<XMods>>,
|
||||||
notify_repeat_task: Arc<Notify>,
|
notify_repeat_task: Arc<Notify>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +76,7 @@ impl MacOSEmulation {
|
|||||||
button_state,
|
button_state,
|
||||||
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())),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +91,7 @@ impl MacOSEmulation {
|
|||||||
self.cancel_repeat_task().await;
|
self.cancel_repeat_task().await;
|
||||||
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 repeat_task = tokio::task::spawn_local(async move {
|
let repeat_task = tokio::task::spawn_local(async move {
|
||||||
let stop = tokio::select! {
|
let stop = tokio::select! {
|
||||||
_ = tokio::time::sleep(DEFAULT_REPEAT_DELAY) => false,
|
_ = tokio::time::sleep(DEFAULT_REPEAT_DELAY) => false,
|
||||||
@@ -92,7 +99,7 @@ impl MacOSEmulation {
|
|||||||
};
|
};
|
||||||
if !stop {
|
if !stop {
|
||||||
loop {
|
loop {
|
||||||
key_event(event_source.clone(), key, 1);
|
key_event(event_source.clone(), key, 1, modifiers.get());
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_ = tokio::time::sleep(DEFAULT_REPEAT_INTERVAL) => {},
|
_ = tokio::time::sleep(DEFAULT_REPEAT_INTERVAL) => {},
|
||||||
_ = notify.notified() => break,
|
_ = notify.notified() => break,
|
||||||
@@ -100,7 +107,8 @@ impl MacOSEmulation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// release key when cancelled
|
// 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);
|
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) {
|
let event = match CGEvent::new_keyboard_event(event_source, key, state != 0) {
|
||||||
Ok(e) => e,
|
Ok(e) => e,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
@@ -121,7 +129,21 @@ fn key_event(event_source: CGEventSource, key: u16, state: u8) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
event.set_flags(to_cgevent_flags(modifiers));
|
||||||
event.post(CGEventTapLocation::HID);
|
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> {
|
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,
|
1 => self.spawn_repeat_task(code).await,
|
||||||
_ => self.cancel_repeat_task().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
|
// FIXME
|
||||||
@@ -379,3 +415,81 @@ impl Emulation for MacOSEmulation {
|
|||||||
|
|
||||||
async fn terminate(&mut self) {}
|
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