Fixed bug 2157 - Caps Lock key produces key down & key up events while key is still pressed.
authorSam Lantinga
Tue, 04 Oct 2016 02:11:52 -0700
changeset 10459d19f7d2b6ec8
parent 10458 b24c67bbb02f
child 10460 87cf824a5508
Fixed bug 2157 - Caps Lock key produces key down & key up events while key is still pressed.

Tim McDaniel

Using checkkeys test app:
* Press and hold Caps Lock key.
* checkkeys reports a CapsLock key pressed event and a CapsLock key released event.
* Release Caps Lock key.
* checkkeys reports no further events.

This patch fixes OSX Caps Lock up/down event detection by installing a HID callback.
src/video/cocoa/SDL_cocoakeyboard.m
     1.1 --- a/src/video/cocoa/SDL_cocoakeyboard.m	Mon Oct 03 03:42:10 2016 -0700
     1.2 +++ b/src/video/cocoa/SDL_cocoakeyboard.m	Tue Oct 04 02:11:52 2016 -0700
     1.3 @@ -29,6 +29,7 @@
     1.4  #include "../../events/scancodes_darwin.h"
     1.5  
     1.6  #include <Carbon/Carbon.h>
     1.7 +#include <IOKit/hid/IOHIDLib.h>
     1.8  
     1.9  /*#define DEBUG_IME NSLog */
    1.10  #define DEBUG_IME(...)
    1.11 @@ -183,6 +184,105 @@
    1.12  
    1.13  @end
    1.14  
    1.15 +/*------------------------------------------------------------------------------
    1.16 +Set up a HID callback to properly detect Caps Lock up/down events.
    1.17 +Derived from:
    1.18 +http://stackoverflow.com/questions/7190852/using-iohidmanager-to-get-modifier-key-events
    1.19 +*/
    1.20 +
    1.21 +static IOHIDManagerRef s_hidManager = NULL;
    1.22 +
    1.23 +static void
    1.24 +HIDCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value)
    1.25 +{
    1.26 +    IOHIDElementRef elem = IOHIDValueGetElement(value);
    1.27 +    if (IOHIDElementGetUsagePage(elem) != kHIDPage_KeyboardOrKeypad
    1.28 +        || IOHIDElementGetUsage(elem) != kHIDUsage_KeyboardCapsLock) {
    1.29 +        return;
    1.30 +    }
    1.31 +    int pressed = IOHIDValueGetIntegerValue(value);
    1.32 +    SDL_SendKeyboardKey(pressed ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_CAPSLOCK);
    1.33 +}
    1.34 +
    1.35 +static CFDictionaryRef
    1.36 +CreateHIDDeviceMatchingDictionary(UInt32 usagePage, UInt32 usage)
    1.37 +{
    1.38 +    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault,
    1.39 +        0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    1.40 +    if (dict) {
    1.41 +        CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePage);
    1.42 +        if (number) {
    1.43 +            CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), number);
    1.44 +            CFRelease(number);
    1.45 +            number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
    1.46 +            if (number) {
    1.47 +                CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), number);
    1.48 +                CFRelease(number);
    1.49 +                return dict;
    1.50 +            }
    1.51 +        }
    1.52 +        CFRelease(dict);
    1.53 +    }
    1.54 +    return NULL;
    1.55 +}
    1.56 +
    1.57 +static void
    1.58 +QuitHIDCallback()
    1.59 +{
    1.60 +    if (!s_hidManager) {
    1.61 +        return;
    1.62 +    }
    1.63 +    IOHIDManagerUnscheduleFromRunLoop(s_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    1.64 +    IOHIDManagerRegisterInputValueCallback(s_hidManager, NULL, NULL);
    1.65 +    IOHIDManagerClose(s_hidManager, 0);
    1.66 +    CFRelease(s_hidManager);
    1.67 +    s_hidManager = NULL;
    1.68 +}
    1.69 +
    1.70 +static void
    1.71 +InitHIDCallback()
    1.72 +{
    1.73 +    s_hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
    1.74 +    if (!s_hidManager) {
    1.75 +        return;
    1.76 +    }
    1.77 +    CFDictionaryRef keyboard = NULL, keypad = NULL;
    1.78 +    CFArrayRef matches = NULL;
    1.79 +    keyboard = CreateHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
    1.80 +    if (!keyboard) {
    1.81 +        goto fail;
    1.82 +    }
    1.83 +    keypad = CreateHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keypad);
    1.84 +    if (!keypad) {
    1.85 +        goto fail;
    1.86 +    }
    1.87 +    CFDictionaryRef matchesList[] = { keyboard, keypad };
    1.88 +    matches = CFArrayCreate(kCFAllocatorDefault, (const void **)matchesList, 2, NULL);
    1.89 +    if (!matches) {
    1.90 +        goto fail;
    1.91 +    }
    1.92 +    IOHIDManagerSetDeviceMatchingMultiple(s_hidManager, matches);
    1.93 +    IOHIDManagerRegisterInputValueCallback(s_hidManager, HIDCallback, NULL);
    1.94 +    IOHIDManagerScheduleWithRunLoop(s_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
    1.95 +    if (IOHIDManagerOpen(s_hidManager, kIOHIDOptionsTypeNone) == kIOReturnSuccess) {
    1.96 +        goto cleanup;
    1.97 +    }
    1.98 +
    1.99 +fail:
   1.100 +    QuitHIDCallback();
   1.101 +
   1.102 +cleanup:
   1.103 +    if (matches) {
   1.104 +        CFRelease(matches);
   1.105 +    }
   1.106 +    if (keypad) {
   1.107 +        CFRelease(keypad);
   1.108 +    }
   1.109 +    if (keyboard) {
   1.110 +        CFRelease(keyboard);
   1.111 +    }
   1.112 +}
   1.113 +
   1.114  /* This is a helper function for HandleModifierSide. This
   1.115   * function reverts back to behavior before the distinction between
   1.116   * sides was made.
   1.117 @@ -320,24 +420,6 @@
   1.118      }
   1.119  }
   1.120  
   1.121 -/* This is a helper function for DoSidedModifiers.
   1.122 - * This function handles the CapsLock case.
   1.123 - */
   1.124 -static void
   1.125 -HandleCapsLock(unsigned short scancode,
   1.126 -               unsigned int oldMods, unsigned int newMods)
   1.127 -{
   1.128 -    unsigned int oldMask, newMask;
   1.129 -
   1.130 -    oldMask = oldMods & NSAlphaShiftKeyMask;
   1.131 -    newMask = newMods & NSAlphaShiftKeyMask;
   1.132 -
   1.133 -    if (oldMask != newMask) {
   1.134 -        SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_CAPSLOCK);
   1.135 -        SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_CAPSLOCK);
   1.136 -    }
   1.137 -}
   1.138 -
   1.139  /* This function will handle the modifier keys and also determine the
   1.140   * correct side of the key.
   1.141   */
   1.142 @@ -366,9 +448,6 @@
   1.143  
   1.144      unsigned int i, bit;
   1.145  
   1.146 -    /* Handle CAPSLOCK separately because it doesn't have a left/right side */
   1.147 -    HandleCapsLock(scancode, oldMods, newMods);
   1.148 -
   1.149      /* Iterate through the bits, testing each against the old modifiers */
   1.150      for (i = 0, bit = NSShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
   1.151          unsigned int oldMask, newMask;
   1.152 @@ -492,6 +571,8 @@
   1.153  
   1.154      data->modifierFlags = [NSEvent modifierFlags];
   1.155      SDL_ToggleModState(KMOD_CAPS, (data->modifierFlags & NSAlphaShiftKeyMask) != 0);
   1.156 +
   1.157 +    InitHIDCallback();
   1.158  }
   1.159  
   1.160  void
   1.161 @@ -617,6 +698,7 @@
   1.162  void
   1.163  Cocoa_QuitKeyboard(_THIS)
   1.164  {
   1.165 +    QuitHIDCallback();
   1.166  }
   1.167  
   1.168  #endif /* SDL_VIDEO_DRIVER_COCOA */