cocoa: Check for capslock in -[NSResponder flagsChanged], not with IOKit.
authorRyan C. Gordon <icculus@icculus.org>
Wed, 26 Jun 2019 13:21:43 -0400
changeset 129076a3b2cc9d66c
parent 12906 bd5a067d1fd8
child 12908 ec53745bdb23
cocoa: Check for capslock in -[NSResponder flagsChanged], not with IOKit.

Using IOKit for this pops up a warning at startup on macOS 10.15 ("Catalina"),
asking the user to authorize the app to listen to all keyboard input in the
system, which is unacceptable.

I _think_ we were using IOKit under incorrect presumptions here; the Stack
Overflow link mentioned in it was complaining about not being able to use
flagsChanged to differentiate between left and right mod keys, but that's not
an issue for capslock.

It's also possible this code was trying to deal with capslock changing when
the window didn't have focus, but we handle this elsewhere now, if we didn't
at the time.
src/video/cocoa/SDL_cocoakeyboard.m
src/video/cocoa/SDL_cocoawindow.m
     1.1 --- a/src/video/cocoa/SDL_cocoakeyboard.m	Wed Jun 26 01:29:01 2019 -0400
     1.2 +++ b/src/video/cocoa/SDL_cocoakeyboard.m	Wed Jun 26 13:21:43 2019 -0400
     1.3 @@ -29,7 +29,6 @@
     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 @@ -187,115 +186,6 @@
    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 -    if (context != s_hidManager) {
    1.27 -        /* An old callback, ignore it (related to bug 2157 below) */
    1.28 -        return;
    1.29 -    }
    1.30 -
    1.31 -    IOHIDElementRef elem = IOHIDValueGetElement(value);
    1.32 -    if (IOHIDElementGetUsagePage(elem) != kHIDPage_KeyboardOrKeypad
    1.33 -        || IOHIDElementGetUsage(elem) != kHIDUsage_KeyboardCapsLock) {
    1.34 -        return;
    1.35 -    }
    1.36 -    CFIndex pressed = IOHIDValueGetIntegerValue(value);
    1.37 -    SDL_SendKeyboardKey(pressed ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_CAPSLOCK);
    1.38 -}
    1.39 -
    1.40 -static CFDictionaryRef
    1.41 -CreateHIDDeviceMatchingDictionary(UInt32 usagePage, UInt32 usage)
    1.42 -{
    1.43 -    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault,
    1.44 -        0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    1.45 -    if (dict) {
    1.46 -        CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePage);
    1.47 -        if (number) {
    1.48 -            CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), number);
    1.49 -            CFRelease(number);
    1.50 -            number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
    1.51 -            if (number) {
    1.52 -                CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), number);
    1.53 -                CFRelease(number);
    1.54 -                return dict;
    1.55 -            }
    1.56 -        }
    1.57 -        CFRelease(dict);
    1.58 -    }
    1.59 -    return NULL;
    1.60 -}
    1.61 -
    1.62 -static void
    1.63 -QuitHIDCallback()
    1.64 -{
    1.65 -    if (!s_hidManager) {
    1.66 -        return;
    1.67 -    }
    1.68 -
    1.69 -#if 0 /* Releasing here causes a crash on Mac OS X 10.10 and earlier,
    1.70 -       * so just leak it for now. See bug 2157 for details.
    1.71 -       */
    1.72 -    IOHIDManagerUnscheduleFromRunLoop(s_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    1.73 -    IOHIDManagerRegisterInputValueCallback(s_hidManager, NULL, NULL);
    1.74 -    IOHIDManagerClose(s_hidManager, 0);
    1.75 -
    1.76 -    CFRelease(s_hidManager);
    1.77 -#endif
    1.78 -    s_hidManager = NULL;
    1.79 -}
    1.80 -
    1.81 -static void
    1.82 -InitHIDCallback()
    1.83 -{
    1.84 -    s_hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
    1.85 -    if (!s_hidManager) {
    1.86 -        return;
    1.87 -    }
    1.88 -    CFDictionaryRef keyboard = NULL, keypad = NULL;
    1.89 -    CFArrayRef matches = NULL;
    1.90 -    keyboard = CreateHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
    1.91 -    if (!keyboard) {
    1.92 -        goto fail;
    1.93 -    }
    1.94 -    keypad = CreateHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keypad);
    1.95 -    if (!keypad) {
    1.96 -        goto fail;
    1.97 -    }
    1.98 -    CFDictionaryRef matchesList[] = { keyboard, keypad };
    1.99 -    matches = CFArrayCreate(kCFAllocatorDefault, (const void **)matchesList, 2, NULL);
   1.100 -    if (!matches) {
   1.101 -        goto fail;
   1.102 -    }
   1.103 -    IOHIDManagerSetDeviceMatchingMultiple(s_hidManager, matches);
   1.104 -    IOHIDManagerRegisterInputValueCallback(s_hidManager, HIDCallback, s_hidManager);
   1.105 -    IOHIDManagerScheduleWithRunLoop(s_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
   1.106 -    if (IOHIDManagerOpen(s_hidManager, kIOHIDOptionsTypeNone) == kIOReturnSuccess) {
   1.107 -        goto cleanup;
   1.108 -    }
   1.109 -
   1.110 -fail:
   1.111 -    QuitHIDCallback();
   1.112 -
   1.113 -cleanup:
   1.114 -    if (matches) {
   1.115 -        CFRelease(matches);
   1.116 -    }
   1.117 -    if (keypad) {
   1.118 -        CFRelease(keypad);
   1.119 -    }
   1.120 -    if (keyboard) {
   1.121 -        CFRelease(keyboard);
   1.122 -    }
   1.123 -}
   1.124  
   1.125  /* This is a helper function for HandleModifierSide. This
   1.126   * function reverts back to behavior before the distinction between
   1.127 @@ -585,8 +475,6 @@
   1.128  
   1.129      data->modifierFlags = [NSEvent modifierFlags];
   1.130      SDL_ToggleModState(KMOD_CAPS, (data->modifierFlags & NSEventModifierFlagCapsLock) != 0);
   1.131 -
   1.132 -    InitHIDCallback();
   1.133  }
   1.134  
   1.135  void
   1.136 @@ -712,7 +600,6 @@
   1.137  void
   1.138  Cocoa_QuitKeyboard(_THIS)
   1.139  {
   1.140 -    QuitHIDCallback();
   1.141  }
   1.142  
   1.143  #endif /* SDL_VIDEO_DRIVER_COCOA */
     2.1 --- a/src/video/cocoa/SDL_cocoawindow.m	Wed Jun 26 01:29:01 2019 -0400
     2.2 +++ b/src/video/cocoa/SDL_cocoawindow.m	Wed Jun 26 13:21:43 2019 -0400
     2.3 @@ -54,6 +54,9 @@
     2.4  
     2.5  #define FULLSCREEN_MASK (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN)
     2.6  
     2.7 +#ifndef MAC_OS_X_VERSION_10_12
     2.8 +#define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask
     2.9 +#endif
    2.10  
    2.11  @interface SDLWindow : NSWindow <NSDraggingDestination>
    2.12  /* These are needed for borderless/fullscreen windows */
    2.13 @@ -849,14 +852,21 @@
    2.14      }
    2.15  }
    2.16  
    2.17 -
    2.18 -/* We'll respond to key events by doing nothing so we don't beep.
    2.19 +/* We'll respond to key events by mostly doing nothing so we don't beep.
    2.20   * We could handle key messages here, but we lose some in the NSApp dispatch,
    2.21   * where they get converted to action messages, etc.
    2.22   */
    2.23  - (void)flagsChanged:(NSEvent *)theEvent
    2.24  {
    2.25      /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
    2.26 +
    2.27 +    /* Catch capslock in here as a special case:
    2.28 +       https://developer.apple.com/library/archive/qa/qa1519/_index.html
    2.29 +       Note that technote's check of keyCode doesn't work. At least on the
    2.30 +       10.15 beta, capslock comes through here as keycode 255, but it's safe
    2.31 +       to send duplicate key events; SDL filters them out quickly in
    2.32 +       SDL_SendKeyboardKey(). */
    2.33 +    SDL_SendKeyboardKey(([theEvent modifierFlags] & NSEventModifierFlagCapsLock) ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_CAPSLOCK);
    2.34  }
    2.35  - (void)keyDown:(NSEvent *)theEvent
    2.36  {