From 02c08c18dacba605e226b1ccd8e3e9ff11eab54b Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Thu, 5 Sep 2019 18:34:03 +0200 Subject: [PATCH] Add basic MacOS typing functionality --- build.rs | 1 + native/libmacbridge/AppDelegate.h | 2 + .../{AppDelegate.m => AppDelegate.mm} | 6 +++ native/libmacbridge/CMakeLists.txt | 4 +- native/libmacbridge/bridge.h | 38 ++++++++++++++-- native/libmacbridge/bridge.mm | 44 ++++++++++++++++++- src/keyboard/macos.rs | 24 ++++++++-- src/matcher/scrolling.rs | 1 - 8 files changed, 108 insertions(+), 12 deletions(-) rename native/libmacbridge/{AppDelegate.m => AppDelegate.mm} (79%) diff --git a/build.rs b/build.rs index fe985d1..d49eab8 100644 --- a/build.rs +++ b/build.rs @@ -41,6 +41,7 @@ fn print_config() { #[cfg(target_os = "macos")] fn print_config() { + println!("cargo:rustc-link-lib=dylib=c++"); println!("cargo:rustc-link-lib=static=macbridge"); println!("cargo:rustc-link-lib=framework=Cocoa"); } diff --git a/native/libmacbridge/AppDelegate.h b/native/libmacbridge/AppDelegate.h index 1a0714c..86b0dc3 100644 --- a/native/libmacbridge/AppDelegate.h +++ b/native/libmacbridge/AppDelegate.h @@ -1,6 +1,8 @@ #import #import +#include "bridge.h" + @interface AppDelegate : NSObject - (void)applicationDidFinishLaunching:(NSNotification *)aNotification; diff --git a/native/libmacbridge/AppDelegate.m b/native/libmacbridge/AppDelegate.mm similarity index 79% rename from native/libmacbridge/AppDelegate.m rename to native/libmacbridge/AppDelegate.mm index 9b6e3fd..2d02544 100644 --- a/native/libmacbridge/AppDelegate.m +++ b/native/libmacbridge/AppDelegate.mm @@ -10,6 +10,9 @@ BOOL checkAccessibility() return AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)opts); } +KeypressCallback keypress_callback; +void * interceptor_instance; + - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { if (checkAccessibility()) { @@ -22,6 +25,9 @@ BOOL checkAccessibility() [NSEvent addGlobalMonitorForEventsMatchingMask:(NSEventMaskKeyDown | NSEventMaskFlagsChanged) handler:^(NSEvent *event){ if (event.type == NSEventTypeKeyDown) { + const char * chars = [event.characters UTF8String]; + int len = event.characters.length; + keypress_callback(interceptor_instance, chars, len); NSLog(@"keydown: %@, %d", event.characters, event.keyCode); }else{ NSLog(@"keydown: %d", event.keyCode); diff --git a/native/libmacbridge/CMakeLists.txt b/native/libmacbridge/CMakeLists.txt index b944d7e..78689a7 100644 --- a/native/libmacbridge/CMakeLists.txt +++ b/native/libmacbridge/CMakeLists.txt @@ -1,9 +1,9 @@ cmake_minimum_required(VERSION 3.0) project(libmacbridge) -set (CMAKE_CXX_STANDARD 14) +set (CMAKE_CXX_STANDARD 11) set(CMAKE_C_FLAGS "-x objective-c") -add_library(macbridge STATIC bridge.mm bridge.h AppDelegate.h AppDelegate.m) +add_library(macbridge STATIC bridge.mm bridge.h AppDelegate.h AppDelegate.mm) install(TARGETS macbridge DESTINATION .) \ No newline at end of file diff --git a/native/libmacbridge/bridge.h b/native/libmacbridge/bridge.h index 52150cb..f9fa950 100644 --- a/native/libmacbridge/bridge.h +++ b/native/libmacbridge/bridge.h @@ -3,14 +3,44 @@ #include +extern "C" { + /* - * Initialize the X11 context and parameters - */ -extern "C" int32_t initialize(); +* Initialize the AppDelegate and check for accessibility permissions +*/ +int32_t initialize(); /* * Start the event loop indefinitely. Blocking call. */ -extern "C" int32_t eventloop(); +int32_t eventloop(); + +/* + * Called when a new keypress is made, the first argument is an char array, + * while the second is the size of the array. + */ +typedef void (*KeypressCallback)(void * self, const char *buffer, int32_t len); + +extern KeypressCallback keypress_callback; +extern void * interceptor_instance; + +/* + * Register the callback that will be called when a keypress was made + */ +void register_keypress_callback(void *self, KeypressCallback callback); + +/* + * Type the given string by using the CGEventKeyboardSetUnicodeString call + */ +void send_string(const char * string); + +/* + * Send the backspace keypress, *count* times. + */ +extern "C" void delete_string(int32_t count); + +}; + + #endif //ESPANSO_BRIDGE_H diff --git a/native/libmacbridge/bridge.mm b/native/libmacbridge/bridge.mm index a302c68..6fa24d0 100644 --- a/native/libmacbridge/bridge.mm +++ b/native/libmacbridge/bridge.mm @@ -1,9 +1,17 @@ #include "bridge.h" #import - +#include "AppDelegate.h" extern "C" { - #include "AppDelegate.h" + +} + +#include + + +void register_keypress_callback(void * self, KeypressCallback callback) { + keypress_callback = callback; + interceptor_instance = self; } int32_t initialize() { @@ -14,4 +22,36 @@ int32_t initialize() { int32_t eventloop() { [NSApp run]; +} + +void send_string(const char * string) { + // Convert the c string to a UniChar array as required by the CGEventKeyboardSetUnicodeString method + NSString * nsString = [NSString stringWithUTF8String:string]; + CFStringRef cfString = (__bridge CFStringRef)nsString; + std::vector buffer(nsString.length); + CFStringGetCharacters(cfString, CFRangeMake(0, nsString.length), buffer.data()); + + // Send the event + CGEventRef e = CGEventCreateKeyboardEvent(NULL, 0x31, true); + CGEventKeyboardSetUnicodeString(e, buffer.size(), buffer.data()); + CGEventPost(kCGHIDEventTap, e); + CFRelease(e); +} + +void delete_string(int32_t count) { + for (int i = 0; i unsafe { send_string(cstr.as_ptr()); } + Err(e) => panic!(e.to_string()) + } } fn delete_string(&self, count: i32) { - + unsafe {delete_string(count)} } } @@ -34,13 +41,24 @@ impl super::KeyboardSender for MacKeyboardSender { extern fn keypress_callback(_self: *mut MacKeyboardInterceptor, raw_buffer: *const u8, len: i32) { unsafe { + // Convert the received buffer to a character + let buffer = std::slice::from_raw_parts(raw_buffer, len as usize); + let r = String::from_utf8_lossy(buffer).chars().nth(0); + // Send the char through the channel + if let Some(c) = r { + //println!("'{}'",c); + (*_self).sender.send(c).unwrap(); + } } } #[allow(improper_ctypes)] #[link(name="macbridge", kind="static")] extern { + fn register_keypress_callback(s: *const MacKeyboardInterceptor, cb: extern fn(_self: *mut MacKeyboardInterceptor, *const u8, i32)); fn initialize(); fn eventloop(); + fn send_string(string: *const c_char); + fn delete_string(count: i32); } \ No newline at end of file diff --git a/src/matcher/scrolling.rs b/src/matcher/scrolling.rs index 7ab9ad3..508559e 100644 --- a/src/matcher/scrolling.rs +++ b/src/matcher/scrolling.rs @@ -1,6 +1,5 @@ use crate::matcher::{Match, MatchReceiver}; use std::cell::RefCell; -use std::thread::current; pub struct ScrollingMatcher<'a, R> where R: MatchReceiver{ matches: Vec,