Add basic MacOS typing functionality
This commit is contained in:
		
							parent
							
								
									ab93bb6879
								
							
						
					
					
						commit
						02c08c18da
					
				
							
								
								
									
										1
									
								
								build.rs
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								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");
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
#import <AppKit/AppKit.h>
 | 
			
		||||
#import <Foundation/Foundation.h>
 | 
			
		||||
 | 
			
		||||
#include "bridge.h"
 | 
			
		||||
 | 
			
		||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
 | 
			
		||||
 | 
			
		||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			@ -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 .)
 | 
			
		||||
| 
						 | 
				
			
			@ -3,14 +3,44 @@
 | 
			
		|||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,17 @@
 | 
			
		|||
#include "bridge.h"
 | 
			
		||||
 | 
			
		||||
#import <Foundation/Foundation.h>
 | 
			
		||||
 | 
			
		||||
#include "AppDelegate.h"
 | 
			
		||||
extern "C" {
 | 
			
		||||
    #include "AppDelegate.h"
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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<UniChar> 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<count; i++) {
 | 
			
		||||
        CGEventRef keydown;
 | 
			
		||||
        keydown = CGEventCreateKeyboardEvent (NULL, 0x33, true);
 | 
			
		||||
        CGEventPost(kCGHIDEventTap, keydown);
 | 
			
		||||
        CFRelease(keydown);
 | 
			
		||||
 | 
			
		||||
        usleep(2000);
 | 
			
		||||
 | 
			
		||||
        CGEventRef keyup;
 | 
			
		||||
        keyup = CGEventCreateKeyboardEvent (NULL, 0x33, false);
 | 
			
		||||
        CGEventPost(kCGHIDEventTap, keyup);
 | 
			
		||||
        CFRelease(keyup);
 | 
			
		||||
 | 
			
		||||
        usleep(2000);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -9,7 +9,10 @@ pub struct MacKeyboardInterceptor {
 | 
			
		|||
 | 
			
		||||
impl super::KeyboardInterceptor for MacKeyboardInterceptor {
 | 
			
		||||
    fn initialize(&self) {
 | 
			
		||||
        unsafe { initialize(); }  // TODO: check initialization return codes
 | 
			
		||||
        unsafe {
 | 
			
		||||
            register_keypress_callback(self,keypress_callback);
 | 
			
		||||
            initialize();
 | 
			
		||||
        }  // TODO: check initialization return codes
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn start(&self) {
 | 
			
		||||
| 
						 | 
				
			
			@ -22,11 +25,15 @@ pub struct MacKeyboardSender {
 | 
			
		|||
 | 
			
		||||
impl super::KeyboardSender for MacKeyboardSender {
 | 
			
		||||
    fn send_string(&self, s: &str) {
 | 
			
		||||
 | 
			
		||||
        let res = CString::new(s);
 | 
			
		||||
        match res {
 | 
			
		||||
            Ok(cstr) => 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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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<Match>,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user