Add modifier keys to macos
This commit is contained in:
parent
02c08c18da
commit
2d9af53e74
|
@ -24,13 +24,24 @@ void * interceptor_instance;
|
||||||
NSLog(@"registering keydown mask");
|
NSLog(@"registering keydown mask");
|
||||||
[NSEvent addGlobalMonitorForEventsMatchingMask:(NSEventMaskKeyDown | NSEventMaskFlagsChanged)
|
[NSEvent addGlobalMonitorForEventsMatchingMask:(NSEventMaskKeyDown | NSEventMaskFlagsChanged)
|
||||||
handler:^(NSEvent *event){
|
handler:^(NSEvent *event){
|
||||||
if (event.type == NSEventTypeKeyDown) {
|
if (event.type == NSEventTypeKeyDown
|
||||||
|
&& event.keyCode != 0x33) { // Send backspace as a modifier
|
||||||
|
|
||||||
const char * chars = [event.characters UTF8String];
|
const char * chars = [event.characters UTF8String];
|
||||||
int len = event.characters.length;
|
int len = event.characters.length;
|
||||||
keypress_callback(interceptor_instance, chars, len);
|
|
||||||
NSLog(@"keydown: %@, %d", event.characters, event.keyCode);
|
keypress_callback(interceptor_instance, chars, len, 0, event.keyCode);
|
||||||
|
//NSLog(@"keydown: %@, %d", event.characters, event.keyCode);
|
||||||
}else{
|
}else{
|
||||||
NSLog(@"keydown: %d", event.keyCode);
|
// Because this event is triggered for both the press and release of a modifier, trigger the callback
|
||||||
|
// only on release
|
||||||
|
if (([event modifierFlags] & (NSEventModifierFlagShift | NSEventModifierFlagCommand |
|
||||||
|
NSEventModifierFlagControl | NSEventModifierFlagOption)) == 0) {
|
||||||
|
|
||||||
|
keypress_callback(interceptor_instance, NULL, 0, 1, event.keyCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
//NSLog(@"keydown: %d", event.keyCode);
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ int32_t eventloop();
|
||||||
* Called when a new keypress is made, the first argument is an char array,
|
* Called when a new keypress is made, the first argument is an char array,
|
||||||
* while the second is the size of the array.
|
* while the second is the size of the array.
|
||||||
*/
|
*/
|
||||||
typedef void (*KeypressCallback)(void * self, const char *buffer, int32_t len);
|
typedef void (*KeypressCallback)(void * self, const char *buffer, int32_t len, int32_t is_modifier, int32_t key_code);
|
||||||
|
|
||||||
extern KeypressCallback keypress_callback;
|
extern KeypressCallback keypress_callback;
|
||||||
extern void * interceptor_instance;
|
extern void * interceptor_instance;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#include "AppDelegate.h"
|
#include "AppDelegate.h"
|
||||||
|
#include <string.h>
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,33 +26,40 @@ int32_t eventloop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_string(const char * string) {
|
void send_string(const char * string) {
|
||||||
|
char * stringCopy = strdup(string);
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||||
// Convert the c string to a UniChar array as required by the CGEventKeyboardSetUnicodeString method
|
// Convert the c string to a UniChar array as required by the CGEventKeyboardSetUnicodeString method
|
||||||
NSString * nsString = [NSString stringWithUTF8String:string];
|
NSString *nsString = [NSString stringWithUTF8String:stringCopy];
|
||||||
CFStringRef cfString = (__bridge CFStringRef)nsString;
|
CFStringRef cfString = (__bridge CFStringRef) nsString;
|
||||||
std::vector<UniChar> buffer(nsString.length);
|
std::vector <UniChar> buffer(nsString.length);
|
||||||
CFStringGetCharacters(cfString, CFRangeMake(0, nsString.length), buffer.data());
|
CFStringGetCharacters(cfString, CFRangeMake(0, nsString.length), buffer.data());
|
||||||
|
|
||||||
|
free(stringCopy);
|
||||||
|
|
||||||
// Send the event
|
// Send the event
|
||||||
CGEventRef e = CGEventCreateKeyboardEvent(NULL, 0x31, true);
|
CGEventRef e = CGEventCreateKeyboardEvent(NULL, 0x31, true);
|
||||||
CGEventKeyboardSetUnicodeString(e, buffer.size(), buffer.data());
|
CGEventKeyboardSetUnicodeString(e, buffer.size(), buffer.data());
|
||||||
CGEventPost(kCGHIDEventTap, e);
|
CGEventPost(kCGHIDEventTap, e);
|
||||||
CFRelease(e);
|
CFRelease(e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void delete_string(int32_t count) {
|
void delete_string(int32_t count) {
|
||||||
for (int i = 0; i<count; i++) {
|
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
CGEventRef keydown;
|
CGEventRef keydown;
|
||||||
keydown = CGEventCreateKeyboardEvent (NULL, 0x33, true);
|
keydown = CGEventCreateKeyboardEvent(NULL, 0x33, true);
|
||||||
CGEventPost(kCGHIDEventTap, keydown);
|
CGEventPost(kCGHIDEventTap, keydown);
|
||||||
CFRelease(keydown);
|
CFRelease(keydown);
|
||||||
|
|
||||||
usleep(2000);
|
usleep(2000);
|
||||||
|
|
||||||
CGEventRef keyup;
|
CGEventRef keyup;
|
||||||
keyup = CGEventCreateKeyboardEvent (NULL, 0x33, false);
|
keyup = CGEventCreateKeyboardEvent(NULL, 0x33, false);
|
||||||
CGEventPost(kCGHIDEventTap, keyup);
|
CGEventPost(kCGHIDEventTap, keyup);
|
||||||
CFRelease(keyup);
|
CFRelease(keyup);
|
||||||
|
|
||||||
usleep(2000);
|
usleep(2000);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
|
@ -12,9 +12,21 @@ const DEFAULT_CONFIG_FILE_CONTENT : &str = include_str!("res/config.yaml");
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct Configs {
|
pub struct Configs {
|
||||||
|
#[serde(default)]
|
||||||
|
pub backspace_enabled: bool,
|
||||||
|
|
||||||
pub matches: Vec<Match>
|
pub matches: Vec<Match>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Configs {
|
||||||
|
fn default() -> Self {
|
||||||
|
Configs {
|
||||||
|
backspace_enabled: false,
|
||||||
|
matches: Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Configs {
|
impl Configs {
|
||||||
pub fn load(path: &Path) -> Configs {
|
pub fn load(path: &Path) -> Configs {
|
||||||
let file_res = File::open(path);
|
let file_res = File::open(path);
|
||||||
|
|
|
@ -2,10 +2,11 @@ use std::thread;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
use crate::keyboard::KeyEvent;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct LinuxKeyboardInterceptor {
|
pub struct LinuxKeyboardInterceptor {
|
||||||
pub sender: mpsc::Sender<char>
|
pub sender: mpsc::Sender<KeyEvent>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::KeyboardInterceptor for LinuxKeyboardInterceptor {
|
impl super::KeyboardInterceptor for LinuxKeyboardInterceptor {
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
use crate::keyboard::{KeyEvent, KeyModifier};
|
||||||
|
use crate::keyboard::KeyModifier::*;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct MacKeyboardInterceptor {
|
pub struct MacKeyboardInterceptor {
|
||||||
pub sender: mpsc::Sender<char>
|
pub sender: mpsc::Sender<KeyEvent>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::KeyboardInterceptor for MacKeyboardInterceptor {
|
impl super::KeyboardInterceptor for MacKeyboardInterceptor {
|
||||||
|
@ -39,16 +41,31 @@ impl super::KeyboardSender for MacKeyboardSender {
|
||||||
|
|
||||||
// Native bridge code
|
// Native bridge code
|
||||||
|
|
||||||
extern fn keypress_callback(_self: *mut MacKeyboardInterceptor, raw_buffer: *const u8, len: i32) {
|
extern fn keypress_callback(_self: *mut MacKeyboardInterceptor, raw_buffer: *const u8, len: i32,
|
||||||
|
is_modifier: i32, key_code: i32) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
if is_modifier == 0 { // Char event
|
||||||
// Convert the received buffer to a character
|
// Convert the received buffer to a character
|
||||||
let buffer = std::slice::from_raw_parts(raw_buffer, len as usize);
|
let buffer = std::slice::from_raw_parts(raw_buffer, len as usize);
|
||||||
let r = String::from_utf8_lossy(buffer).chars().nth(0);
|
let r = String::from_utf8_lossy(buffer).chars().nth(0);
|
||||||
|
|
||||||
// Send the char through the channel
|
// Send the char through the channel
|
||||||
if let Some(c) = r {
|
if let Some(c) = r {
|
||||||
//println!("'{}'",c);
|
(*_self).sender.send(KeyEvent::Char(c)).unwrap();
|
||||||
(*_self).sender.send(c).unwrap();
|
}
|
||||||
|
}else{ // Modifier event
|
||||||
|
let modifier: Option<KeyModifier> = match key_code {
|
||||||
|
0x37 => Some(META),
|
||||||
|
0x38 => Some(SHIFT),
|
||||||
|
0x3A => Some(ALT),
|
||||||
|
0x3B => Some(CTRL),
|
||||||
|
0x33 => Some(BACKSPACE),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(modifier) = modifier {
|
||||||
|
(*_self).sender.send(KeyEvent::Modifier(modifier)).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,7 +73,9 @@ extern fn keypress_callback(_self: *mut MacKeyboardInterceptor, raw_buffer: *con
|
||||||
#[allow(improper_ctypes)]
|
#[allow(improper_ctypes)]
|
||||||
#[link(name="macbridge", kind="static")]
|
#[link(name="macbridge", kind="static")]
|
||||||
extern {
|
extern {
|
||||||
fn register_keypress_callback(s: *const MacKeyboardInterceptor, cb: extern fn(_self: *mut MacKeyboardInterceptor, *const u8, i32));
|
fn register_keypress_callback(s: *const MacKeyboardInterceptor,
|
||||||
|
cb: extern fn(_self: *mut MacKeyboardInterceptor, *const u8,
|
||||||
|
i32, i32, i32));
|
||||||
fn initialize();
|
fn initialize();
|
||||||
fn eventloop();
|
fn eventloop();
|
||||||
fn send_string(string: *const c_char);
|
fn send_string(string: *const c_char);
|
||||||
|
|
|
@ -14,6 +14,21 @@ pub trait KeyboardInterceptor {
|
||||||
fn start(&self);
|
fn start(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum KeyModifier {
|
||||||
|
CTRL,
|
||||||
|
SHIFT,
|
||||||
|
ALT,
|
||||||
|
META,
|
||||||
|
BACKSPACE,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum KeyEvent {
|
||||||
|
Char(char),
|
||||||
|
Modifier(KeyModifier)
|
||||||
|
}
|
||||||
|
|
||||||
pub trait KeyboardSender {
|
pub trait KeyboardSender {
|
||||||
fn send_string(&self, s: &str);
|
fn send_string(&self, s: &str);
|
||||||
fn delete_string(&self, count: i32);
|
fn delete_string(&self, count: i32);
|
||||||
|
@ -22,7 +37,7 @@ pub trait KeyboardSender {
|
||||||
// WINDOWS IMPLEMENTATIONS
|
// WINDOWS IMPLEMENTATIONS
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub fn get_interceptor(sender: mpsc::Sender<char>) -> impl KeyboardInterceptor {
|
pub fn get_interceptor(sender: mpsc::Sender<KeyEvent>) -> impl KeyboardInterceptor {
|
||||||
windows::WindowsKeyboardInterceptor {sender}
|
windows::WindowsKeyboardInterceptor {sender}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +49,7 @@ pub fn get_sender() -> impl KeyboardSender {
|
||||||
// LINUX IMPLEMENTATIONS
|
// LINUX IMPLEMENTATIONS
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn get_interceptor(sender: mpsc::Sender<char>) -> impl KeyboardInterceptor {
|
pub fn get_interceptor(sender: mpsc::Sender<KeyEvent>) -> impl KeyboardInterceptor {
|
||||||
linux::LinuxKeyboardInterceptor {sender}
|
linux::LinuxKeyboardInterceptor {sender}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +60,7 @@ pub fn get_sender() -> impl KeyboardSender {
|
||||||
|
|
||||||
// MAC IMPLEMENTATION
|
// MAC IMPLEMENTATION
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub fn get_interceptor(sender: mpsc::Sender<char>) -> impl KeyboardInterceptor {
|
pub fn get_interceptor(sender: mpsc::Sender<KeyEvent>) -> impl KeyboardInterceptor {
|
||||||
macos::MacKeyboardInterceptor {sender}
|
macos::MacKeyboardInterceptor {sender}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use widestring::{U16CString};
|
use widestring::{U16CString};
|
||||||
|
use crate::keyboard::KeyEvent;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct WindowsKeyboardInterceptor {
|
pub struct WindowsKeyboardInterceptor {
|
||||||
pub sender: mpsc::Sender<char>
|
pub sender: mpsc::Sender<KeyEvent>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::KeyboardInterceptor for WindowsKeyboardInterceptor {
|
impl super::KeyboardInterceptor for WindowsKeyboardInterceptor {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::Receiver;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
use crate::keyboard::{KeyEvent, KeyModifier};
|
||||||
|
|
||||||
pub(crate) mod scrolling;
|
pub(crate) mod scrolling;
|
||||||
|
|
||||||
|
@ -15,12 +16,20 @@ pub trait MatchReceiver {
|
||||||
|
|
||||||
pub trait Matcher<'a>: Send {
|
pub trait Matcher<'a>: Send {
|
||||||
fn handle_char(&'a self, c: char);
|
fn handle_char(&'a self, c: char);
|
||||||
fn watch(&'a self, receiver: Receiver<char>) {
|
fn handle_modifier(&'a self, m: KeyModifier);
|
||||||
|
fn watch(&'a self, receiver: Receiver<KeyEvent>) {
|
||||||
loop {
|
loop {
|
||||||
match receiver.recv() {
|
match receiver.recv() {
|
||||||
Ok(c) => {
|
Ok(event) => {
|
||||||
|
match event {
|
||||||
|
KeyEvent::Char(c) => {
|
||||||
self.handle_char(c);
|
self.handle_char(c);
|
||||||
},
|
},
|
||||||
|
KeyEvent::Modifier(m) => {
|
||||||
|
self.handle_modifier(m);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
Err(_) => panic!("Keyboard interceptor broke receiver stream."),
|
Err(_) => panic!("Keyboard interceptor broke receiver stream."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::matcher::{Match, MatchReceiver};
|
use crate::matcher::{Match, MatchReceiver};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use crate::keyboard::KeyModifier;
|
||||||
|
|
||||||
pub struct ScrollingMatcher<'a, R> where R: MatchReceiver{
|
pub struct ScrollingMatcher<'a, R> where R: MatchReceiver{
|
||||||
matches: Vec<Match>,
|
matches: Vec<Match>,
|
||||||
|
@ -44,6 +45,10 @@ impl <'a, R> super::Matcher<'a> for ScrollingMatcher<'a, R> where R: MatchReceiv
|
||||||
self.receiver.on_match(_match);
|
self.receiver.on_match(_match);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_modifier(&'a self, m: KeyModifier) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a, R> ScrollingMatcher<'a, R> where R: MatchReceiver {
|
impl <'a, R> ScrollingMatcher<'a, R> where R: MatchReceiver {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user