macos: add keyboard support (#81)

* macos: add keyboard support

* macos: handle key repeat

* update README
This commit is contained in:
Kai
2024-01-26 05:05:54 -05:00
committed by GitHub
parent 8084b52cfc
commit 5cc8cda19d
4 changed files with 122 additions and 21 deletions

69
Cargo.lock generated
View File

@@ -80,6 +80,21 @@ version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355"
[[package]]
name = "arraydeque"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0ffd3d69bd89910509a5d31d1f1353f38ccffdd116dd0099bbd6627f7bd8ad8"
[[package]]
name = "arrayvec"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
dependencies = [
"nodrop",
]
[[package]]
name = "ashpd"
version = "0.6.7"
@@ -397,7 +412,7 @@ version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
dependencies = [
"heck",
"heck 0.4.1",
"proc-macro2",
"quote",
"syn 2.0.42",
@@ -540,7 +555,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a"
dependencies = [
"heck",
"heck 0.4.1",
"proc-macro2",
"quote",
"syn 2.0.42",
@@ -961,7 +976,7 @@ version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72793962ceece3863c2965d7f10c8786323b17c7adea75a515809fa20ab799a5"
dependencies = [
"heck",
"heck 0.4.1",
"proc-macro-crate 2.0.1",
"proc-macro-error",
"proc-macro2",
@@ -1104,6 +1119,15 @@ version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "heck"
version = "0.4.1"
@@ -1224,6 +1248,30 @@ version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "keycode"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b07873c3182aec8a0eb1a5a4e7b197d42e9d167ba78497a6ee932a82d94673ed"
dependencies = [
"arraydeque",
"arrayvec",
"bitflags 1.3.2",
"keycode_macro",
]
[[package]]
name = "keycode_macro"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e521ea802f5b3c7194e169d75cab431b0ff08d022f2b6047b08754b4988b89df"
dependencies = [
"anyhow",
"heck 0.3.3",
"proc-macro2",
"quote",
]
[[package]]
name = "lan-mouse"
version = "0.5.1"
@@ -1239,6 +1287,7 @@ dependencies = [
"futures-core",
"glib-build-tools",
"gtk4",
"keycode",
"libadwaita",
"libc",
"log",
@@ -1421,6 +1470,12 @@ dependencies = [
"memoffset 0.7.1",
]
[[package]]
name = "nodrop"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
[[package]]
name = "num_cpus"
version = "1.16.0"
@@ -1959,7 +2014,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331"
dependencies = [
"cfg-expr",
"heck",
"heck 0.4.1",
"pkg-config",
"toml",
"version-compare",
@@ -2218,6 +2273,12 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "url"
version = "2.5.0"

View File

@@ -30,6 +30,7 @@ clap = { version="4.4.11", features = ["derive"] }
gtk = { package = "gtk4", version = "0.7.2", features = ["v4_2"], optional = true }
adw = { package = "libadwaita", version = "0.5.2", features = ["v1_1"], optional = true }
async-channel = { version = "2.1.1", optional = true }
keycode = "0.4.0"
[target.'cfg(unix)'.dependencies]
libc = "0.2.148"

View File

@@ -46,9 +46,7 @@ input capture (to send events *to* other clients) on different operating systems
| Wayland (Gnome) | :heavy_check_mark: | WIP |
| X11 | :heavy_check_mark: | WIP |
| Windows | :heavy_check_mark: | WIP |
| MacOS | ( :heavy_check_mark: ) | WIP |
Keycode translation is not yet implemented so on MacOS only mouse emulation works as of right now.
| MacOS | :heavy_check_mark: | WIP |
> [!Important]
> If you are using [Wayfire](https://github.com/WayfireWM/wayfire), make sure to use a recent version (must be newer than October 23rd) and **add `shortcuts-inhibit` to the list of plugins in your wayfire config!**

View File

@@ -5,13 +5,20 @@ use anyhow::{anyhow, Result};
use async_trait::async_trait;
use core_graphics::display::{CGDisplayBounds, CGMainDisplayID, CGPoint};
use core_graphics::event::{
CGEvent, CGEventTapLocation, CGEventType, CGMouseButton, EventField, ScrollEventUnit,
CGEvent, CGEventTapLocation, CGEventType, CGKeyCode, CGMouseButton, EventField, ScrollEventUnit,
};
use core_graphics::event_source::{CGEventSource, CGEventSourceStateID};
use keycode::{KeyMap, KeyMapping};
use std::ops::{Index, IndexMut};
use std::time::Duration;
use tokio::task::AbortHandle;
const DEFAULT_REPEAT_DELAY: Duration = Duration::from_millis(500);
const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis(32);
pub struct MacOSConsumer {
pub event_source: CGEventSource,
repeat_task: Option<AbortHandle>,
button_state: ButtonState,
}
@@ -59,6 +66,7 @@ impl MacOSConsumer {
Ok(Self {
event_source,
button_state,
repeat_task: None,
})
}
@@ -66,6 +74,37 @@ impl MacOSConsumer {
let event: CGEvent = CGEvent::new(self.event_source.clone()).ok()?;
Some(event.location())
}
async fn spawn_repeat_task(&mut self, key: u16) {
// there can only be one repeating key and it's
// always the last to be pressed
self.kill_repeat_task();
let event_source = self.event_source.clone();
let repeat_task = tokio::task::spawn_local(async move {
tokio::time::sleep(DEFAULT_REPEAT_DELAY).await;
loop {
key_event(event_source.clone(), key, 1);
tokio::time::sleep(DEFAULT_REPEAT_INTERVAL).await;
}
});
self.repeat_task = Some(repeat_task.abort_handle());
}
fn kill_repeat_task(&mut self) {
if let Some(task) = self.repeat_task.take() {
task.abort();
}
}
}
fn key_event(event_source: CGEventSource, key: u16, state: u8) {
let event = match CGEvent::new_keyboard_event(event_source, key, state != 0) {
Ok(e) => e,
Err(_) => {
log::warn!("unable to create key event");
return;
}
};
event.post(CGEventTapLocation::HID);
}
#[async_trait]
@@ -209,22 +248,24 @@ impl EventConsumer for MacOSConsumer {
PointerEvent::Frame { .. } => {}
},
Event::Keyboard(keyboard_event) => match keyboard_event {
KeyboardEvent::Key { .. } => {
/*
let code = CGKeyCode::from_le(key as u16);
let event = match CGEvent::new_keyboard_event(
self.event_source.clone(),
code,
match state { 1 => true, _ => false }
) {
Ok(e) => e,
KeyboardEvent::Key {
time: _,
key,
state,
} => {
let code = match KeyMap::from_key_mapping(KeyMapping::Evdev(key as u16)) {
Ok(k) => k.mac as CGKeyCode,
Err(_) => {
log::warn!("unable to create key event");
return
log::warn!("unable to map key event");
return;
}
};
event.post(CGEventTapLocation::HID);
*/
match state {
// pressed
1 => self.spawn_repeat_task(code).await,
_ => self.kill_repeat_task(),
}
key_event(self.event_source.clone(), code, state)
}
KeyboardEvent::Modifiers { .. } => {}
},