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")]
|
#[cfg(target_os = "macos")]
|
||||||
fn print_config() {
|
fn print_config() {
|
||||||
|
println!("cargo:rustc-link-lib=dylib=c++");
|
||||||
println!("cargo:rustc-link-lib=static=macbridge");
|
println!("cargo:rustc-link-lib=static=macbridge");
|
||||||
println!("cargo:rustc-link-lib=framework=Cocoa");
|
println!("cargo:rustc-link-lib=framework=Cocoa");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#import <AppKit/AppKit.h>
|
#import <AppKit/AppKit.h>
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#include "bridge.h"
|
||||||
|
|
||||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
||||||
|
|
||||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
|
||||||
|
|
|
@ -10,6 +10,9 @@ BOOL checkAccessibility()
|
||||||
return AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)opts);
|
return AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeypressCallback keypress_callback;
|
||||||
|
void * interceptor_instance;
|
||||||
|
|
||||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||||
{
|
{
|
||||||
if (checkAccessibility()) {
|
if (checkAccessibility()) {
|
||||||
|
@ -22,6 +25,9 @@ BOOL checkAccessibility()
|
||||||
[NSEvent addGlobalMonitorForEventsMatchingMask:(NSEventMaskKeyDown | NSEventMaskFlagsChanged)
|
[NSEvent addGlobalMonitorForEventsMatchingMask:(NSEventMaskKeyDown | NSEventMaskFlagsChanged)
|
||||||
handler:^(NSEvent *event){
|
handler:^(NSEvent *event){
|
||||||
if (event.type == NSEventTypeKeyDown) {
|
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);
|
NSLog(@"keydown: %@, %d", event.characters, event.keyCode);
|
||||||
}else{
|
}else{
|
||||||
NSLog(@"keydown: %d", event.keyCode);
|
NSLog(@"keydown: %d", event.keyCode);
|
|
@ -1,9 +1,9 @@
|
||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(libmacbridge)
|
project(libmacbridge)
|
||||||
|
|
||||||
set (CMAKE_CXX_STANDARD 14)
|
set (CMAKE_CXX_STANDARD 11)
|
||||||
set(CMAKE_C_FLAGS "-x objective-c")
|
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 .)
|
install(TARGETS macbridge DESTINATION .)
|
|
@ -3,14 +3,44 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the X11 context and parameters
|
* Initialize the AppDelegate and check for accessibility permissions
|
||||||
*/
|
*/
|
||||||
extern "C" int32_t initialize();
|
int32_t initialize();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start the event loop indefinitely. Blocking call.
|
* 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
|
#endif //ESPANSO_BRIDGE_H
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
#include "bridge.h"
|
#include "bridge.h"
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
#include "AppDelegate.h"
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "AppDelegate.h"
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
void register_keypress_callback(void * self, KeypressCallback callback) {
|
||||||
|
keypress_callback = callback;
|
||||||
|
interceptor_instance = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t initialize() {
|
int32_t initialize() {
|
||||||
|
@ -14,4 +22,36 @@ int32_t initialize() {
|
||||||
|
|
||||||
int32_t eventloop() {
|
int32_t eventloop() {
|
||||||
[NSApp run];
|
[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 {
|
impl super::KeyboardInterceptor for MacKeyboardInterceptor {
|
||||||
fn initialize(&self) {
|
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) {
|
fn start(&self) {
|
||||||
|
@ -22,11 +25,15 @@ pub struct MacKeyboardSender {
|
||||||
|
|
||||||
impl super::KeyboardSender for MacKeyboardSender {
|
impl super::KeyboardSender for MacKeyboardSender {
|
||||||
fn send_string(&self, s: &str) {
|
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) {
|
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) {
|
extern fn keypress_callback(_self: *mut MacKeyboardInterceptor, raw_buffer: *const u8, len: i32) {
|
||||||
unsafe {
|
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)]
|
#[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 initialize();
|
fn initialize();
|
||||||
fn eventloop();
|
fn eventloop();
|
||||||
|
fn send_string(string: *const c_char);
|
||||||
|
fn delete_string(count: i32);
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::matcher::{Match, MatchReceiver};
|
use crate::matcher::{Match, MatchReceiver};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::thread::current;
|
|
||||||
|
|
||||||
pub struct ScrollingMatcher<'a, R> where R: MatchReceiver{
|
pub struct ScrollingMatcher<'a, R> where R: MatchReceiver{
|
||||||
matches: Vec<Match>,
|
matches: Vec<Match>,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user