//go:build darwin
|
|
package input
|
|
/*
|
#cgo CFLAGS: -x objective-c
|
#cgo LDFLAGS: -framework AppKit -framework CoreGraphics -framework Carbon -framework ApplicationServices
|
#include <AppKit/AppKit.h>
|
#include <CoreGraphics/CoreGraphics.h>
|
#include <Carbon/Carbon.h>
|
#include <ApplicationServices/ApplicationServices.h>
|
#include <stdlib.h>
|
#include <string.h>
|
|
static int ensureInputPermission(void) {
|
NSDictionary* opts = @{(__bridge NSString*)kAXTrustedCheckOptionPrompt: @YES};
|
return AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)opts) ? 1 : 0;
|
}
|
|
static void setClipboardText(const char* text) {
|
NSPasteboard* pb = [NSPasteboard generalPasteboard];
|
[pb clearContents];
|
NSString* str = [NSString stringWithUTF8String:text];
|
[pb setString:str forType:NSPasteboardTypeString];
|
}
|
|
static char* getClipboardText(void) {
|
NSPasteboard* pb = [NSPasteboard generalPasteboard];
|
NSString* str = [pb stringForType:NSPasteboardTypeString];
|
if (!str) return NULL;
|
const char* utf8 = [str UTF8String];
|
if (!utf8) return NULL;
|
return strdup(utf8);
|
}
|
|
static void simulateCmdV() {
|
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
|
|
CGEventRef cmdDown = CGEventCreateKeyboardEvent(source, (CGKeyCode)kVK_Command, true);
|
CGEventSetFlags(cmdDown, kCGEventFlagMaskCommand);
|
CGEventPost(kCGHIDEventTap, cmdDown);
|
|
CGEventRef vDown = CGEventCreateKeyboardEvent(source, (CGKeyCode)kVK_ANSI_V, true);
|
CGEventSetFlags(vDown, kCGEventFlagMaskCommand);
|
CGEventPost(kCGHIDEventTap, vDown);
|
|
CGEventRef vUp = CGEventCreateKeyboardEvent(source, (CGKeyCode)kVK_ANSI_V, false);
|
CGEventSetFlags(vUp, kCGEventFlagMaskCommand);
|
CGEventPost(kCGHIDEventTap, vUp);
|
|
CGEventRef cmdUp = CGEventCreateKeyboardEvent(source, (CGKeyCode)kVK_Command, false);
|
CGEventSetFlags(cmdUp, 0);
|
CGEventPost(kCGHIDEventTap, cmdUp);
|
|
CFRelease(cmdDown);
|
CFRelease(vDown);
|
CFRelease(vUp);
|
CFRelease(cmdUp);
|
CFRelease(source);
|
}
|
|
static void typeUnicodeChar(unsigned int codepoint) {
|
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
|
UniChar chars[2];
|
int len = 0;
|
|
if (codepoint <= 0xFFFF) {
|
chars[0] = (UniChar)codepoint;
|
len = 1;
|
} else {
|
codepoint -= 0x10000;
|
chars[0] = (UniChar)(0xD800 + (codepoint >> 10));
|
chars[1] = (UniChar)(0xDC00 + (codepoint & 0x3FF));
|
len = 2;
|
}
|
|
CGEventRef keyDown = CGEventCreateKeyboardEvent(source, 0, true);
|
CGEventKeyboardSetUnicodeString(keyDown, len, chars);
|
CGEventPost(kCGHIDEventTap, keyDown);
|
|
CGEventRef keyUp = CGEventCreateKeyboardEvent(source, 0, false);
|
CGEventKeyboardSetUnicodeString(keyUp, len, chars);
|
CGEventPost(kCGHIDEventTap, keyUp);
|
|
CFRelease(keyDown);
|
CFRelease(keyUp);
|
CFRelease(source);
|
}
|
*/
|
import "C"
|
|
import (
|
"fmt"
|
"time"
|
"unsafe"
|
)
|
|
type darwinPaster struct{}
|
|
func newPlatformPaster() Paster {
|
return &darwinPaster{}
|
}
|
|
func (p *darwinPaster) Paste(text string, keepClipboard bool) error {
|
if C.ensureInputPermission() == 0 {
|
return fmt.Errorf("accessibility permission is required to paste")
|
}
|
|
var oldClip *string
|
if !keepClipboard {
|
cOld := C.getClipboardText()
|
if cOld != nil {
|
old := C.GoString(cOld)
|
oldClip = &old
|
C.free(unsafe.Pointer(cOld))
|
}
|
}
|
|
cstr := C.CString(text)
|
defer C.free(unsafe.Pointer(cstr))
|
C.setClipboardText(cstr)
|
time.Sleep(60 * time.Millisecond)
|
C.simulateCmdV()
|
|
if !keepClipboard && oldClip != nil {
|
go func(old string) {
|
time.Sleep(600 * time.Millisecond)
|
cOld := C.CString(old)
|
defer C.free(unsafe.Pointer(cOld))
|
C.setClipboardText(cOld)
|
}(*oldClip)
|
}
|
|
return nil
|
}
|
|
func (p *darwinPaster) TypeText(text string) error {
|
if C.ensureInputPermission() == 0 {
|
return fmt.Errorf("accessibility permission is required to type")
|
}
|
for _, r := range text {
|
C.typeUnicodeChar(C.uint(r))
|
}
|
return nil
|
}
|