Add modifier keys to macos

This commit is contained in:
Federico Terzi 2019-09-05 19:18:55 +02:00
parent 02c08c18da
commit 2d9af53e74
10 changed files with 126 additions and 45 deletions

View File

@ -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);
} }
}]; }];
} }

View File

@ -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;

View File

@ -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);
} }
});
} }

View File

@ -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);

View File

@ -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 {

View File

@ -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);

View File

@ -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}
} }

View File

@ -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 {

View File

@ -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."),
} }
} }

View File

@ -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 {