2019-09-15 16:29:11 +00:00
|
|
|
/*
|
|
|
|
* This file is part of espanso.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2019 Federico Terzi
|
|
|
|
*
|
|
|
|
* espanso is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* espanso is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with espanso. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2019-09-05 15:20:52 +00:00
|
|
|
#include "bridge.h"
|
|
|
|
|
|
|
|
#import <Foundation/Foundation.h>
|
2019-09-05 16:34:03 +00:00
|
|
|
#include "AppDelegate.h"
|
2019-09-13 12:35:46 +00:00
|
|
|
#include <stdio.h>
|
2019-09-05 17:18:55 +00:00
|
|
|
#include <string.h>
|
2019-09-05 15:20:52 +00:00
|
|
|
extern "C" {
|
2019-09-05 16:34:03 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
2019-09-13 09:55:42 +00:00
|
|
|
void * context_instance;
|
2019-09-13 12:35:46 +00:00
|
|
|
char * icon_path;
|
|
|
|
AppDelegate * delegate_ptr;
|
|
|
|
|
|
|
|
KeypressCallback keypress_callback;
|
|
|
|
IconClickCallback icon_click_callback;
|
|
|
|
ContextMenuClickCallback context_menu_click_callback;
|
2019-09-05 16:34:03 +00:00
|
|
|
|
2019-09-13 12:35:46 +00:00
|
|
|
int32_t initialize(void * context, const char * _icon_path) {
|
2019-09-13 09:55:42 +00:00
|
|
|
context_instance = context;
|
2019-09-13 12:35:46 +00:00
|
|
|
icon_path = strdup(_icon_path);
|
2019-09-05 15:20:52 +00:00
|
|
|
|
|
|
|
AppDelegate *delegate = [[AppDelegate alloc] init];
|
2019-09-13 12:35:46 +00:00
|
|
|
delegate_ptr = delegate;
|
2019-09-05 15:20:52 +00:00
|
|
|
NSApplication * application = [NSApplication sharedApplication];
|
|
|
|
[application setDelegate:delegate];
|
|
|
|
}
|
|
|
|
|
2019-09-13 09:55:42 +00:00
|
|
|
void register_keypress_callback(KeypressCallback callback) {
|
|
|
|
keypress_callback = callback;
|
|
|
|
}
|
|
|
|
|
2019-09-13 12:35:46 +00:00
|
|
|
void register_icon_click_callback(IconClickCallback callback) {
|
|
|
|
icon_click_callback = callback;
|
|
|
|
}
|
|
|
|
|
|
|
|
void register_context_menu_click_callback(ContextMenuClickCallback callback) {
|
|
|
|
context_menu_click_callback = callback;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-05 15:20:52 +00:00
|
|
|
int32_t eventloop() {
|
|
|
|
[NSApp run];
|
2019-09-05 16:34:03 +00:00
|
|
|
}
|
|
|
|
|
2019-10-23 18:55:54 +00:00
|
|
|
int32_t headless_eventloop() {
|
|
|
|
NSApplication * application = [NSApplication sharedApplication];
|
|
|
|
[NSApp run];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-09-05 16:34:03 +00:00
|
|
|
void send_string(const char * string) {
|
2019-09-05 17:18:55 +00:00
|
|
|
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
|
2019-09-09 14:07:45 +00:00
|
|
|
NSString *nsString = [NSString stringWithUTF8String:stringCopy];
|
|
|
|
CFStringRef cfString = (__bridge CFStringRef) nsString;
|
2019-09-05 17:18:55 +00:00
|
|
|
std::vector <UniChar> buffer(nsString.length);
|
|
|
|
CFStringGetCharacters(cfString, CFRangeMake(0, nsString.length), buffer.data());
|
|
|
|
|
|
|
|
free(stringCopy);
|
|
|
|
|
|
|
|
// Send the event
|
2019-09-06 09:04:15 +00:00
|
|
|
|
|
|
|
// Because of a bug ( or undocumented limit ) of the CGEventKeyboardSetUnicodeString method
|
|
|
|
// the string gets truncated after 20 characters, so we need to send multiple events.
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
while (i < buffer.size()) {
|
|
|
|
int chunk_size = 20;
|
|
|
|
if ((i+chunk_size) > buffer.size()) {
|
|
|
|
chunk_size = buffer.size() - i;
|
|
|
|
}
|
|
|
|
|
|
|
|
UniChar * offset_buffer = buffer.data() + i;
|
|
|
|
CGEventRef e = CGEventCreateKeyboardEvent(NULL, 0x31, true);
|
|
|
|
CGEventKeyboardSetUnicodeString(e, chunk_size, offset_buffer);
|
|
|
|
CGEventPost(kCGHIDEventTap, e);
|
|
|
|
CFRelease(e);
|
|
|
|
|
|
|
|
usleep(2000);
|
|
|
|
|
|
|
|
i += chunk_size;
|
|
|
|
}
|
2019-09-05 17:18:55 +00:00
|
|
|
});
|
2019-09-05 16:34:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void delete_string(int32_t count) {
|
2019-10-23 17:13:21 +00:00
|
|
|
send_multi_vkey(0x33, count);
|
2019-09-06 08:49:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void send_vkey(int32_t vk) {
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
|
|
CGEventRef keydown;
|
|
|
|
keydown = CGEventCreateKeyboardEvent(NULL, vk, true);
|
|
|
|
CGEventPost(kCGHIDEventTap, keydown);
|
|
|
|
CFRelease(keydown);
|
|
|
|
|
2019-10-23 17:13:21 +00:00
|
|
|
usleep(500);
|
2019-09-06 08:49:05 +00:00
|
|
|
|
|
|
|
CGEventRef keyup;
|
|
|
|
keyup = CGEventCreateKeyboardEvent(NULL, vk, false);
|
|
|
|
CGEventPost(kCGHIDEventTap, keyup);
|
|
|
|
CFRelease(keyup);
|
|
|
|
|
2019-10-23 17:13:21 +00:00
|
|
|
usleep(500);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void send_multi_vkey(int32_t vk, int32_t count) {
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
CGEventRef keydown;
|
|
|
|
keydown = CGEventCreateKeyboardEvent(NULL, vk, true);
|
|
|
|
CGEventPost(kCGHIDEventTap, keydown);
|
|
|
|
CFRelease(keydown);
|
|
|
|
|
|
|
|
usleep(500);
|
|
|
|
|
|
|
|
CGEventRef keyup;
|
|
|
|
keyup = CGEventCreateKeyboardEvent(NULL, vk, false);
|
|
|
|
CGEventPost(kCGHIDEventTap, keyup);
|
|
|
|
CFRelease(keyup);
|
|
|
|
|
|
|
|
usleep(500);
|
|
|
|
}
|
2019-09-06 08:49:05 +00:00
|
|
|
});
|
2019-09-09 13:46:57 +00:00
|
|
|
}
|
|
|
|
|
2019-09-09 15:03:59 +00:00
|
|
|
void trigger_paste() {
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
|
|
CGEventRef keydown;
|
|
|
|
keydown = CGEventCreateKeyboardEvent(NULL, 0x37, true); // CMD
|
|
|
|
CGEventPost(kCGHIDEventTap, keydown);
|
|
|
|
CFRelease(keydown);
|
|
|
|
|
|
|
|
usleep(2000);
|
|
|
|
|
|
|
|
CGEventRef keydown2;
|
|
|
|
keydown2 = CGEventCreateKeyboardEvent(NULL, 0x09, true); // V key
|
|
|
|
CGEventPost(kCGHIDEventTap, keydown2);
|
|
|
|
CFRelease(keydown2);
|
|
|
|
|
|
|
|
usleep(2000);
|
|
|
|
|
|
|
|
CGEventRef keyup;
|
|
|
|
keyup = CGEventCreateKeyboardEvent(NULL, 0x09, false);
|
|
|
|
CGEventPost(kCGHIDEventTap, keyup);
|
|
|
|
CFRelease(keyup);
|
|
|
|
|
|
|
|
usleep(2000);
|
|
|
|
|
|
|
|
CGEventRef keyup2;
|
|
|
|
keyup2 = CGEventCreateKeyboardEvent(NULL, 0x37, false); // CMD
|
|
|
|
CGEventPost(kCGHIDEventTap, keyup2);
|
|
|
|
CFRelease(keyup2);
|
|
|
|
|
|
|
|
usleep(2000);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-22 20:51:36 +00:00
|
|
|
|
|
|
|
void trigger_copy() {
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
|
|
CGEventRef keydown;
|
|
|
|
keydown = CGEventCreateKeyboardEvent(NULL, 0x37, true); // CMD
|
|
|
|
CGEventPost(kCGHIDEventTap, keydown);
|
|
|
|
CFRelease(keydown);
|
|
|
|
|
|
|
|
usleep(2000);
|
|
|
|
|
|
|
|
CGEventRef keydown2;
|
|
|
|
keydown2 = CGEventCreateKeyboardEvent(NULL, 0x08, true); // C key
|
|
|
|
CGEventPost(kCGHIDEventTap, keydown2);
|
|
|
|
CFRelease(keydown2);
|
|
|
|
|
|
|
|
usleep(2000);
|
|
|
|
|
|
|
|
CGEventRef keyup;
|
|
|
|
keyup = CGEventCreateKeyboardEvent(NULL, 0x08, false);
|
|
|
|
CGEventPost(kCGHIDEventTap, keyup);
|
|
|
|
CFRelease(keyup);
|
|
|
|
|
|
|
|
usleep(2000);
|
|
|
|
|
|
|
|
CGEventRef keyup2;
|
|
|
|
keyup2 = CGEventCreateKeyboardEvent(NULL, 0x37, false); // CMD
|
|
|
|
CGEventPost(kCGHIDEventTap, keyup2);
|
|
|
|
CFRelease(keyup2);
|
|
|
|
|
|
|
|
usleep(2000);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-09-09 13:46:57 +00:00
|
|
|
int32_t get_active_app_bundle(char * buffer, int32_t size) {
|
|
|
|
NSRunningApplication *frontApp = [[NSWorkspace sharedWorkspace] frontmostApplication];
|
|
|
|
NSString *bundlePath = [frontApp bundleURL].path;
|
|
|
|
const char * path = [bundlePath UTF8String];
|
|
|
|
|
|
|
|
snprintf(buffer, size, "%s", path);
|
|
|
|
|
2019-09-09 14:07:45 +00:00
|
|
|
[bundlePath release];
|
|
|
|
|
2019-09-09 13:46:57 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t get_active_app_identifier(char * buffer, int32_t size) {
|
|
|
|
NSRunningApplication *frontApp = [[NSWorkspace sharedWorkspace] frontmostApplication];
|
2019-09-09 14:07:45 +00:00
|
|
|
NSString *bundleId = frontApp.bundleIdentifier;
|
|
|
|
const char * bundle = [bundleId UTF8String];
|
2019-09-09 13:46:57 +00:00
|
|
|
|
2019-09-09 14:07:45 +00:00
|
|
|
snprintf(buffer, size, "%s", bundle);
|
|
|
|
|
|
|
|
[bundleId release];
|
2019-09-09 13:46:57 +00:00
|
|
|
|
|
|
|
return 1;
|
2019-09-09 14:43:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int32_t get_clipboard(char * buffer, int32_t size) {
|
|
|
|
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
|
|
|
for (id element in pasteboard.pasteboardItems) {
|
|
|
|
NSString *string = [element stringForType: NSPasteboardTypeString];
|
|
|
|
if (string != NULL) {
|
|
|
|
const char * text = [string UTF8String];
|
|
|
|
snprintf(buffer, size, "%s", text);
|
|
|
|
|
|
|
|
[string release];
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t set_clipboard(char * text) {
|
|
|
|
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
|
|
|
NSArray *array = @[NSPasteboardTypeString];
|
|
|
|
[pasteboard declareTypes:array owner:nil];
|
|
|
|
|
|
|
|
NSString *nsText = [NSString stringWithUTF8String:text];
|
|
|
|
[pasteboard setString:nsText forType:NSPasteboardTypeString];
|
2019-09-13 12:35:46 +00:00
|
|
|
}
|
|
|
|
|
2019-11-28 21:56:00 +00:00
|
|
|
int32_t set_clipboard_image(char *path) {
|
|
|
|
NSString *pathString = [NSString stringWithUTF8String:path];
|
|
|
|
NSImage *image = [[NSImage alloc] initWithContentsOfFile:pathString];
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
if (image != nil) {
|
|
|
|
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
|
|
|
|
[pasteboard clearContents];
|
|
|
|
NSArray *copiedObjects = [NSArray arrayWithObject:image];
|
|
|
|
[pasteboard writeObjects:copiedObjects];
|
|
|
|
result = 1;
|
|
|
|
}
|
|
|
|
[image release];
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-13 12:35:46 +00:00
|
|
|
// CONTEXT MENU
|
|
|
|
|
|
|
|
int32_t show_context_menu(MenuItem * items, int32_t count) {
|
|
|
|
MenuItem * item_copy = (MenuItem*)malloc(sizeof(MenuItem)*count);
|
|
|
|
memcpy(item_copy, items, sizeof(MenuItem)*count);
|
|
|
|
int32_t count_copy = count;
|
|
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
|
|
|
|
|
|
NSMenu *espansoMenu = [[NSMenu alloc] initWithTitle:@"Espanso"];
|
|
|
|
|
|
|
|
for (int i = 0; i<count_copy; i++) {
|
|
|
|
if (item_copy[i].type == 1) {
|
|
|
|
NSString *title = [NSString stringWithUTF8String:item_copy[i].name];
|
|
|
|
NSMenuItem *newMenu = [[NSMenuItem alloc] initWithTitle:title action:@selector(contextMenuClick:) keyEquivalent:@""];
|
|
|
|
[newMenu setTag:(NSInteger)item_copy[i].id];
|
|
|
|
[espansoMenu addItem: newMenu];
|
|
|
|
}else{
|
|
|
|
[espansoMenu addItem: [NSMenuItem separatorItem]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(item_copy);
|
|
|
|
|
|
|
|
[delegate_ptr->myStatusItem popUpStatusItemMenu:espansoMenu];
|
|
|
|
});
|
2019-09-13 13:26:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 10.9+ only, see this url for compatibility:
|
|
|
|
// http://stackoverflow.com/questions/17693408/enable-access-for-assistive-devices-programmatically-on-10-9
|
|
|
|
int32_t check_accessibility() {
|
2019-09-16 22:11:31 +00:00
|
|
|
NSDictionary* opts = @{(__bridge id)kAXTrustedCheckOptionPrompt: @NO};
|
|
|
|
return AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)opts);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t prompt_accessibility() {
|
2019-09-13 13:26:17 +00:00
|
|
|
NSDictionary* opts = @{(__bridge id)kAXTrustedCheckOptionPrompt: @YES};
|
|
|
|
return AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)opts);
|
|
|
|
}
|
2019-09-16 22:11:31 +00:00
|
|
|
|
|
|
|
void open_settings_panel() {
|
|
|
|
NSString *urlString = @"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";
|
|
|
|
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]];
|
2020-01-22 20:51:36 +00:00
|
|
|
}
|
|
|
|
|