robotgo/key/keypress_c.h
2017-04-21 22:53:18 +08:00

318 lines
8.4 KiB
Objective-C

#include "keypress.h"
// #include "../base/deadbeef_rand_c.h"
#include "../base/microsleep.h"
#include <ctype.h> /* For isupper() */
#if defined(IS_MACOSX)
#include <ApplicationServices/ApplicationServices.h>
#import <IOKit/hidsystem/IOHIDLib.h>
#import <IOKit/hidsystem/ev_keymap.h>
#elif defined(USE_X11)
#include <X11/extensions/XTest.h>
// #include "../base/xdisplay_c.h"
#endif
/* Convenience wrappers around ugly APIs. */
#if defined(IS_WINDOWS)
#define WIN32_KEY_EVENT_WAIT(key, flags) \
(win32KeyEvent(key, flags), Sleep(DEADBEEF_RANDRANGE(0, 63)))
#elif defined(USE_X11)
#define X_KEY_EVENT(display, key, is_press) \
(XTestFakeKeyEvent(display, \
XKeysymToKeycode(display, key), \
is_press, CurrentTime), \
XFlush(display))
#define X_KEY_EVENT_WAIT(display, key, is_press) \
(X_KEY_EVENT(display, key, is_press), \
microsleep(DEADBEEF_UNIFORM(0.0, 62.5)))
#endif
#if defined(IS_MACOSX)
static io_connect_t _getAuxiliaryKeyDriver(void)
{
static mach_port_t sEventDrvrRef = 0;
mach_port_t masterPort, service, iter;
kern_return_t kr;
if (!sEventDrvrRef) {
kr = IOMasterPort( bootstrap_port, &masterPort );
assert(KERN_SUCCESS == kr);
kr = IOServiceGetMatchingServices(masterPort, IOServiceMatching( kIOHIDSystemClass), &iter );
assert(KERN_SUCCESS == kr);
service = IOIteratorNext( iter );
assert(service);
kr = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &sEventDrvrRef );
assert(KERN_SUCCESS == kr);
IOObjectRelease(service);
IOObjectRelease(iter);
}
return sEventDrvrRef;
}
#endif
#if defined(IS_WINDOWS)
void win32KeyEvent(int key, MMKeyFlags flags)
{
int scan = MapVirtualKey(key & 0xff, MAPVK_VK_TO_VSC);
/* Set the scan code for extended keys */
switch (key)
{
case VK_RCONTROL:
case VK_SNAPSHOT: /* Print Screen */
case VK_RMENU: /* Right Alt / Alt Gr */
case VK_PAUSE: /* Pause / Break */
case VK_HOME:
case VK_UP:
case VK_PRIOR: /* Page up */
case VK_LEFT:
case VK_RIGHT:
case VK_END:
case VK_DOWN:
case VK_NEXT: /* 'Page Down' */
case VK_INSERT:
case VK_DELETE:
case VK_LWIN:
case VK_RWIN:
case VK_APPS: /* Application */
case VK_VOLUME_MUTE:
case VK_VOLUME_DOWN:
case VK_VOLUME_UP:
case VK_MEDIA_NEXT_TRACK:
case VK_MEDIA_PREV_TRACK:
case VK_MEDIA_STOP:
case VK_MEDIA_PLAY_PAUSE:
case VK_BROWSER_BACK:
case VK_BROWSER_FORWARD:
case VK_BROWSER_REFRESH:
case VK_BROWSER_STOP:
case VK_BROWSER_SEARCH:
case VK_BROWSER_FAVORITES:
case VK_BROWSER_HOME:
case VK_LAUNCH_MAIL:
{
flags |= KEYEVENTF_EXTENDEDKEY;
break;
}
}
/* Set the scan code for keyup */
if ( flags & KEYEVENTF_KEYUP ) {
scan |= 0x80;
}
keybd_event(key, scan, flags, 0);
}
#endif
void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags)
{
#if defined(IS_MACOSX)
/* The media keys all have 1000 added to them to help us detect them. */
if (code >= 1000) {
code = code - 1000; /* Get the real keycode. */
NXEventData event;
kern_return_t kr;
IOGPoint loc = { 0, 0 };
UInt32 evtInfo = code << 16 | (down?NX_KEYDOWN:NX_KEYUP) << 8;
bzero(&event, sizeof(NXEventData));
event.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS;
event.compound.misc.L[0] = evtInfo;
kr = IOHIDPostEvent( _getAuxiliaryKeyDriver(), NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE );
assert( KERN_SUCCESS == kr );
} else {
CGEventRef keyEvent = CGEventCreateKeyboardEvent(NULL,
(CGKeyCode)code, down);
assert(keyEvent != NULL);
CGEventSetType(keyEvent, down ? kCGEventKeyDown : kCGEventKeyUp);
CGEventSetFlags(keyEvent, (int) flags);
// CGEventSetFlags(keyEvent, 0);
// CGEventSetFlags(keyEvent, kCGEventFlagMaskShift | kCGEventFlagMaskCommand);
CGEventPost(kCGSessionEventTap, keyEvent);
CFRelease(keyEvent);
}
#elif defined(IS_WINDOWS)
const DWORD dwFlags = down ? 0 : KEYEVENTF_KEYUP;
/* Parse modifier keys. */
if (flags & MOD_META) WIN32_KEY_EVENT_WAIT(K_META, dwFlags);
if (flags & MOD_ALT) WIN32_KEY_EVENT_WAIT(K_ALT, dwFlags);
if (flags & MOD_CONTROL) WIN32_KEY_EVENT_WAIT(K_CONTROL, dwFlags);
if (flags & MOD_SHIFT) WIN32_KEY_EVENT_WAIT(K_SHIFT, dwFlags);
win32KeyEvent(code, dwFlags);
#elif defined(USE_X11)
Display *display = XGetMainDisplay();
const Bool is_press = down ? True : False; /* Just to be safe. */
/* Parse modifier keys. */
if (flags & MOD_META) X_KEY_EVENT_WAIT(display, K_META, is_press);
if (flags & MOD_ALT) X_KEY_EVENT_WAIT(display, K_ALT, is_press);
if (flags & MOD_CONTROL) X_KEY_EVENT_WAIT(display, K_CONTROL, is_press);
if (flags & MOD_SHIFT) X_KEY_EVENT_WAIT(display, K_SHIFT, is_press);
X_KEY_EVENT(display, code, is_press);
#endif
}
void tapKeyCode(MMKeyCode code, MMKeyFlags flags)
{
toggleKeyCode(code, true, flags);
toggleKeyCode(code, false, flags);
}
void toggleKey(char c, const bool down, MMKeyFlags flags)
{
MMKeyCode keyCode = keyCodeForChar(c);
//Prevent unused variable warning for Mac and Linux.
#if defined(IS_WINDOWS)
int modifiers;
#endif
if (isupper(c) && !(flags & MOD_SHIFT)) {
flags |= MOD_SHIFT; /* Not sure if this is safe for all layouts. */
}
#if defined(IS_WINDOWS)
modifiers = keyCode >> 8; // Pull out modifers.
if ((modifiers & 1) != 0) flags |= MOD_SHIFT; // Uptdate flags from keycode modifiers.
if ((modifiers & 2) != 0) flags |= MOD_CONTROL;
if ((modifiers & 4) != 0) flags |= MOD_ALT;
keyCode = keyCode & 0xff; // Mask out modifiers.
#endif
toggleKeyCode(keyCode, down, flags);
}
void tapKey(char c, MMKeyFlags flags)
{
toggleKey(c, true, flags);
toggleKey(c, false, flags);
}
#if defined(IS_MACOSX)
void toggleUnicodeKey(unsigned long ch, const bool down){
/* This function relies on the convenient
* CGEventKeyboardSetUnicodeString(), which allows us to not have to
* convert characters to a keycode, but does not support adding modifier
* flags. It is therefore only used in typeString() and typeStringDelayed()
* -- if you need modifier keys, use the above functions instead. */
// UniChar ch = (UniChar)c; /* Convert to unsigned char */
CGEventRef keyEvent = CGEventCreateKeyboardEvent(NULL, 0, down);
if (keyEvent == NULL) {
fputs("Could not create keyboard event.\n", stderr);
return;
}
// CGEventKeyboardSetUnicodeString(keyEvent, 1, &ch);
///////
if (ch > 0xFFFF) {
// encode to utf-16 if necessary
unsigned short surrogates[] = {
0xD800 + ((ch - 0x10000) >> 10),
0xDC00 + (ch & 0x3FF)
};
// CGEventKeyboardSetUnicodeString(keyEvent, 2, &surrogates);
UniChar ach = (UniChar)surrogates;
CGEventKeyboardSetUnicodeString(keyEvent, 2, &ach);
} else {
// CGEventKeyboardSetUnicodeString(keyEvent, 1, &ch);
UniChar ach = (UniChar)ch;
CGEventKeyboardSetUnicodeString(keyEvent, 1, &ach);
}
///////
CGEventPost(kCGSessionEventTap, keyEvent);
CFRelease(keyEvent);
}
void toggleUniKey(char c, const bool down){
toggleUnicodeKey(c, down);
}
#else
#define toggleUniKey(c, down) toggleKey(c, down, MOD_NONE)
#endif
static void tapUniKey(char c)
{
toggleUniKey(c, true);
toggleUniKey(c, false);
}
void typeString(const char *str){
// while (*str != '\0') {
// tapUniKey(*str++);
// }
unsigned short c;
unsigned short c1;
unsigned short c2;
unsigned short c3;
unsigned long n;
while (*str != '\0') {
c = *str++;
// warning, the following utf8 decoder
// doesn't perform validation
if (c <= 0x7F) {
// 0xxxxxxx one byte
n = c;
} else if ((c & 0xE0) == 0xC0) {
// 110xxxxx two bytes
c1 = (*str++) & 0x3F;
n = ((c & 0x1F) << 6) | c1;
} else if ((c & 0xF0) == 0xE0) {
// 1110xxxx three bytes
c1 = (*str++) & 0x3F;
c2 = (*str++) & 0x3F;
n = ((c & 0x0F) << 12) | (c1 << 6) | c2;
} else if ((c & 0xF8) == 0xF0) {
// 11110xxx four bytes
c1 = (*str++) & 0x3F;
c2 = (*str++) & 0x3F;
c3 = (*str++) & 0x3F;
n = ((c & 0x07) << 18) | (c1 << 12) | (c2 << 6) | c3;
}
// printf("n-------------%zd\n", n);
#if defined(IS_MACOSX)
toggleUnicodeKey(n, true);
toggleUnicodeKey(n, false);
#else
toggleUniKey(n, true);
toggleUniKey(n, false);
#endif
}
}
// void typeString(const char *str)
// {
// while (*str != '\0') {
// tapUniKey(*str++);
// }
// }
void typeStringDelayed(const char *str, const unsigned cpm)
{
/* Characters per second */
const double cps = (double)cpm / 60.0;
/* Average milli-seconds per character */
const double mspc = (cps == 0.0) ? 0.0 : 1000.0 / cps;
while (*str != '\0') {
tapUniKey(*str++);
microsleep(mspc + (DEADBEEF_UNIFORM(0.0, 62.5)));
}
}