Commit 57e08c27ef4a54e48c20c6bfd7c841216a6b70b5

Ryan C. Gordon 2019-06-26T13:21:43

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.

diff --git a/src/video/cocoa/SDL_cocoakeyboard.m b/src/video/cocoa/SDL_cocoakeyboard.m
index f986ded..574536b 100644
--- a/src/video/cocoa/SDL_cocoakeyboard.m
+++ b/src/video/cocoa/SDL_cocoakeyboard.m
@@ -29,7 +29,6 @@
 #include "../../events/scancodes_darwin.h"
 
 #include <Carbon/Carbon.h>
-#include <IOKit/hid/IOHIDLib.h>
 
 /*#define DEBUG_IME NSLog */
 #define DEBUG_IME(...)
@@ -187,115 +186,6 @@
 
 @end
 
-/*------------------------------------------------------------------------------
-Set up a HID callback to properly detect Caps Lock up/down events.
-Derived from:
-http://stackoverflow.com/questions/7190852/using-iohidmanager-to-get-modifier-key-events
-*/
-
-static IOHIDManagerRef s_hidManager = NULL;
-
-static void
-HIDCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value)
-{
-    if (context != s_hidManager) {
-        /* An old callback, ignore it (related to bug 2157 below) */
-        return;
-    }
-
-    IOHIDElementRef elem = IOHIDValueGetElement(value);
-    if (IOHIDElementGetUsagePage(elem) != kHIDPage_KeyboardOrKeypad
-        || IOHIDElementGetUsage(elem) != kHIDUsage_KeyboardCapsLock) {
-        return;
-    }
-    CFIndex pressed = IOHIDValueGetIntegerValue(value);
-    SDL_SendKeyboardKey(pressed ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_CAPSLOCK);
-}
-
-static CFDictionaryRef
-CreateHIDDeviceMatchingDictionary(UInt32 usagePage, UInt32 usage)
-{
-    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault,
-        0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-    if (dict) {
-        CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usagePage);
-        if (number) {
-            CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), number);
-            CFRelease(number);
-            number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
-            if (number) {
-                CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), number);
-                CFRelease(number);
-                return dict;
-            }
-        }
-        CFRelease(dict);
-    }
-    return NULL;
-}
-
-static void
-QuitHIDCallback()
-{
-    if (!s_hidManager) {
-        return;
-    }
-
-#if 0 /* Releasing here causes a crash on Mac OS X 10.10 and earlier,
-       * so just leak it for now. See bug 2157 for details.
-       */
-    IOHIDManagerUnscheduleFromRunLoop(s_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-    IOHIDManagerRegisterInputValueCallback(s_hidManager, NULL, NULL);
-    IOHIDManagerClose(s_hidManager, 0);
-
-    CFRelease(s_hidManager);
-#endif
-    s_hidManager = NULL;
-}
-
-static void
-InitHIDCallback()
-{
-    s_hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
-    if (!s_hidManager) {
-        return;
-    }
-    CFDictionaryRef keyboard = NULL, keypad = NULL;
-    CFArrayRef matches = NULL;
-    keyboard = CreateHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
-    if (!keyboard) {
-        goto fail;
-    }
-    keypad = CreateHIDDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keypad);
-    if (!keypad) {
-        goto fail;
-    }
-    CFDictionaryRef matchesList[] = { keyboard, keypad };
-    matches = CFArrayCreate(kCFAllocatorDefault, (const void **)matchesList, 2, NULL);
-    if (!matches) {
-        goto fail;
-    }
-    IOHIDManagerSetDeviceMatchingMultiple(s_hidManager, matches);
-    IOHIDManagerRegisterInputValueCallback(s_hidManager, HIDCallback, s_hidManager);
-    IOHIDManagerScheduleWithRunLoop(s_hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
-    if (IOHIDManagerOpen(s_hidManager, kIOHIDOptionsTypeNone) == kIOReturnSuccess) {
-        goto cleanup;
-    }
-
-fail:
-    QuitHIDCallback();
-
-cleanup:
-    if (matches) {
-        CFRelease(matches);
-    }
-    if (keypad) {
-        CFRelease(keypad);
-    }
-    if (keyboard) {
-        CFRelease(keyboard);
-    }
-}
 
 /* This is a helper function for HandleModifierSide. This
  * function reverts back to behavior before the distinction between
@@ -585,8 +475,6 @@ Cocoa_InitKeyboard(_THIS)
 
     data->modifierFlags = [NSEvent modifierFlags];
     SDL_ToggleModState(KMOD_CAPS, (data->modifierFlags & NSEventModifierFlagCapsLock) != 0);
-
-    InitHIDCallback();
 }
 
 void
@@ -712,7 +600,6 @@ Cocoa_HandleKeyEvent(_THIS, NSEvent *event)
 void
 Cocoa_QuitKeyboard(_THIS)
 {
-    QuitHIDCallback();
 }
 
 #endif /* SDL_VIDEO_DRIVER_COCOA */
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index e922ef1..e030d9e 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -54,6 +54,9 @@
 
 #define FULLSCREEN_MASK (SDL_WINDOW_FULLSCREEN_DESKTOP | SDL_WINDOW_FULLSCREEN)
 
+#ifndef MAC_OS_X_VERSION_10_12
+#define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask
+#endif
 
 @interface SDLWindow : NSWindow <NSDraggingDestination>
 /* These are needed for borderless/fullscreen windows */
@@ -849,14 +852,21 @@ SetWindowStyle(SDL_Window * window, NSUInteger style)
     }
 }
 
-
-/* We'll respond to key events by doing nothing so we don't beep.
+/* We'll respond to key events by mostly doing nothing so we don't beep.
  * We could handle key messages here, but we lose some in the NSApp dispatch,
  * where they get converted to action messages, etc.
  */
 - (void)flagsChanged:(NSEvent *)theEvent
 {
     /*Cocoa_HandleKeyEvent(SDL_GetVideoDevice(), theEvent);*/
+
+    /* Catch capslock in here as a special case:
+       https://developer.apple.com/library/archive/qa/qa1519/_index.html
+       Note that technote's check of keyCode doesn't work. At least on the
+       10.15 beta, capslock comes through here as keycode 255, but it's safe
+       to send duplicate key events; SDL filters them out quickly in
+       SDL_SendKeyboardKey(). */
+    SDL_SendKeyboardKey(([theEvent modifierFlags] & NSEventModifierFlagCapsLock) ? SDL_PRESSED : SDL_RELEASED, SDL_SCANCODE_CAPSLOCK);
 }
 - (void)keyDown:(NSEvent *)theEvent
 {