diff --git a/src/keypress.h b/src/keypress.h index 4416d43..12b743f 100644 --- a/src/keypress.h +++ b/src/keypress.h @@ -23,6 +23,8 @@ extern "C" MOD_SHIFT = kCGEventFlagMaskShift } MMKeyFlags; + extern MMKeyFlags flagBuffer; + #elif defined(USE_X11) enum _MMKeyFlags { diff --git a/src/macos/keypress.c b/src/macos/keypress.c index b2e057c..96a3378 100644 --- a/src/macos/keypress.c +++ b/src/macos/keypress.c @@ -8,183 +8,183 @@ #import #import -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; +MMKeyFlags flagBuffer; + +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; } -void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags) -{ - /* 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 - { - CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStatePrivate); - CGEventRef keyEvent = CGEventCreateKeyboardEvent(src, (CGKeyCode)code, down); - assert(keyEvent != NULL); - - CGEventSetFlags(keyEvent, flags); - CGEventPost(kCGHIDEventTap, keyEvent); - CFRelease(keyEvent); - CFRelease(src); - } +void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags) { + /* 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 { + CGEventSourceRef src = + CGEventSourceCreate(kCGEventSourceStateHIDSystemState); + CGEventRef keyEvent = + CGEventCreateKeyboardEvent(src, (CGKeyCode)code, down); + assert(keyEvent != NULL); + + // Check if keycode is one of the available modifier keys and set keyflags + // accordingly + if (code == K_META) { + flags |= MOD_META; + } + if (code == K_ALT) { + flags |= MOD_ALT; + } + if (code == K_CONTROL) { + flags |= MOD_CONTROL; + } + if (code == K_SHIFT || code == K_RIGHTSHIFT) { + flags |= MOD_SHIFT; + } + + MMKeyFlags activeKeyFlags; + if (down) { + activeKeyFlags = flags | flagBuffer; + flagBuffer |= activeKeyFlags; + } else { + activeKeyFlags = flags ^ flagBuffer; + flagBuffer ^= flags; + } + + CGEventSetFlags(keyEvent, activeKeyFlags); + CGEventPost(kCGHIDEventTap, keyEvent); + CFRelease(keyEvent); + CFRelease(src); + } } -void tapKeyCode(MMKeyCode code, MMKeyFlags flags) -{ - toggleKeyCode(code, true, flags); - toggleKeyCode(code, false, flags); +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); +void toggleKey(char c, const bool down, MMKeyFlags flags) { + MMKeyCode keyCode = keyCodeForChar(c); - if (isupper(c) && !(flags & MOD_SHIFT)) - { - flags |= MOD_SHIFT; /* Not sure if this is safe for all layouts. */ - } + if (isupper(c) && !(flags & MOD_SHIFT)) { + flags |= MOD_SHIFT; /* Not sure if this is safe for all layouts. */ + } - toggleKeyCode(keyCode, down, flags); + toggleKeyCode(keyCode, down, flags); } -void tapKey(char c, MMKeyFlags flags) -{ - toggleKey(c, true, flags); - toggleKey(c, false, flags); +void tapKey(char c, MMKeyFlags flags) { + toggleKey(c, true, flags); + toggleKey(c, false, flags); } -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. */ - CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStatePrivate); - CGEventRef keyEvent = CGEventCreateKeyboardEvent(src, 0, down); - if (keyEvent == NULL) - { - fputs("Could not create keyboard event.\n", stderr); - return; - } - - if (ch > 0xFFFF) - { - // encode to utf-16 if necessary - unsigned short surrogates[] = { - 0xD800 + ((ch - 0x10000) >> 10), - 0xDC00 + (ch & 0x3FF)}; - - CGEventKeyboardSetUnicodeString(keyEvent, 2, &surrogates); - } - else - { - CGEventKeyboardSetUnicodeString(keyEvent, 1, &ch); - } - - CGEventPost(kCGHIDEventTap, keyEvent); - CFRelease(keyEvent); - CFRelease(src); +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. */ + CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); + CGEventRef keyEvent = CGEventCreateKeyboardEvent(src, 0, down); + if (keyEvent == NULL) { + fputs("Could not create keyboard event.\n", stderr); + return; + } + + if (ch > 0xFFFF) { + // encode to utf-16 if necessary + unsigned short surrogates[] = {0xD800 + ((ch - 0x10000) >> 10), + 0xDC00 + (ch & 0x3FF)}; + + CGEventKeyboardSetUnicodeString(keyEvent, 2, &surrogates); + } else { + CGEventKeyboardSetUnicodeString(keyEvent, 1, &ch); + } + + CGEventPost(kCGHIDEventTap, keyEvent); + CFRelease(keyEvent); + CFRelease(src); } -void toggleUniKey(char c, const bool down) -{ - toggleUnicodeKey(c, down); -} +void toggleUniKey(char c, const bool down) { toggleUnicodeKey(c, down); } -static void tapUniKey(char c) -{ - toggleUniKey(c, true); - toggleUniKey(c, false); +static void tapUniKey(char c) { + toggleUniKey(c, true); + toggleUniKey(c, false); } -void typeString(const char *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; - } - - toggleUnicodeKey(n, true); - toggleUnicodeKey(n, false); - } +void typeString(const char *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; + } + + toggleUnicodeKey(n, true); + toggleUnicodeKey(n, false); + } } -void typeStringDelayed(const char *str, const unsigned cpm) -{ - /* Characters per second */ - const double cps = (double)cpm / 60.0; +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; + /* 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))); - } + while (*str != '\0') { + tapUniKey(*str++); + microsleep(mspc + (DEADBEEF_UNIFORM(0.0, 62.5))); + } } diff --git a/src/macos/mouse.c b/src/macos/mouse.c index 97dbe6e..8e66783 100644 --- a/src/macos/mouse.c +++ b/src/macos/mouse.c @@ -1,5 +1,6 @@ #include "../mouse.h" #include "../deadbeef_rand.h" +#include "../keypress.h" #include "../microsleep.h" #include "mouse_utils.h" @@ -115,21 +116,28 @@ MMPoint getMousePos() { * Press down a button, or release it. * @param down True for down, false for up. * @param button The button to press down or release. - * - * This function ships a manual implementation to handle double clicks by tracking the time interval between mouse events. - * Reason for this is the fact that https://developer.apple.com/documentation/coregraphics/1408790-cgeventsourcesecondssincelasteve?language=objc - * has a bit of latency and will stop working correctly if the time between two consecutive clicks is not long enough. - * - * This implementation captures the current timestamp for up/down events on each of left/middle/right mouse buttons. - * If the interval between two clicks is lower than https://developer.apple.com/documentation/appkit/nsevent/1528384-doubleclickinterval?language=objc - * and both clicks happen at the same position, we alter the mouse event to trigger a double click by setting kCGMouseEventClickState = 2 on the event + * + * This function ships a manual implementation to handle double clicks by + * tracking the time interval between mouse events. Reason for this is the fact + * that + * https://developer.apple.com/documentation/coregraphics/1408790-cgeventsourcesecondssincelasteve?language=objc + * has a bit of latency and will stop working correctly if the time between two + * consecutive clicks is not long enough. + * + * This implementation captures the current timestamp for up/down events on each + * of left/middle/right mouse buttons. If the interval between two clicks is + * lower than + * https://developer.apple.com/documentation/appkit/nsevent/1528384-doubleclickinterval?language=objc + * and both clicks happen at the same position, we alter the mouse event to + * trigger a double click by setting kCGMouseEventClickState = 2 on the event */ void toggleMouse(bool down, MMMouseButton button) { static ClickTimer clickTimer = {{0, 0}, {0, 0}, {0, 0}, {0, 0}}; - MMPoint currentMMPoint= getMousePos(); + MMPoint currentMMPoint = getMousePos(); - clock_t intervalSinceLastClick = timeSinceLastClick(&clickTimer, down, button, clock()); + clock_t intervalSinceLastClick = + timeSinceLastClick(&clickTimer, down, button, clock()); const CGPoint currentPos = CGPointFromMMPoint(currentMMPoint); const CGEventType mouseType = MMMouseToCGEventType(down, button); @@ -141,6 +149,7 @@ void toggleMouse(bool down, MMMouseButton button) { areSamePoint(currentMMPoint, clickTimer.clickLocation)) { CGEventSetIntegerValueField(event, kCGMouseEventClickState, 2); } + CGEventSetFlags(event, flagBuffer); CGEventPost(kCGHIDEventTap, event); CFRelease(event); CFRelease(src);