SDL: Fixed command modifier on macOS

From 7e1088167a7cf47ca920667743e84cacb82af481 Mon Sep 17 00:00:00 2001
From: Deve <[EMAIL REDACTED]>
Date: Tue, 18 Oct 2022 01:07:54 +0200
Subject: [PATCH] Fixed command modifier on macOS

---
 src/video/cocoa/SDL_cocoakeyboard.m | 241 +++++-----------------------
 1 file changed, 40 insertions(+), 201 deletions(-)

diff --git a/src/video/cocoa/SDL_cocoakeyboard.m b/src/video/cocoa/SDL_cocoakeyboard.m
index e9f80a41231f..863e96087731 100644
--- a/src/video/cocoa/SDL_cocoakeyboard.m
+++ b/src/video/cocoa/SDL_cocoakeyboard.m
@@ -177,209 +177,49 @@ - (NSArray *)validAttributesForMarkedText
 
 @end
 
-
-/* This is a helper function for HandleModifierSide. This
- * function reverts back to behavior before the distinction between
- * sides was made.
- */
-static void
-HandleNonDeviceModifier(unsigned int device_independent_mask,
-                        unsigned int oldMods,
-                        unsigned int newMods,
-                        SDL_Scancode scancode)
-{
-    unsigned int oldMask, newMask;
-
-    /* Isolate just the bits we care about in the depedent bits so we can
-     * figure out what changed
-     */
-    oldMask = oldMods & device_independent_mask;
-    newMask = newMods & device_independent_mask;
-
-    if (oldMask && oldMask != newMask) {
-        SDL_SendKeyboardKey(SDL_RELEASED, scancode);
-    } else if (newMask && oldMask != newMask) {
-        SDL_SendKeyboardKey(SDL_PRESSED, scancode);
-    }
-}
-
-/* This is a helper function for HandleModifierSide.
- * This function sets the actual SDL_PrivateKeyboard event.
- */
-static void
-HandleModifierOneSide(unsigned int oldMods, unsigned int newMods,
-                      SDL_Scancode scancode,
-                      unsigned int sided_device_dependent_mask)
-{
-    unsigned int old_dep_mask, new_dep_mask;
-
-    /* Isolate just the bits we care about in the depedent bits so we can
-     * figure out what changed
-     */
-    old_dep_mask = oldMods & sided_device_dependent_mask;
-    new_dep_mask = newMods & sided_device_dependent_mask;
-
-    /* We now know that this side bit flipped. But we don't know if
-     * it went pressed to released or released to pressed, so we must
-     * find out which it is.
-     */
-    if (new_dep_mask && old_dep_mask != new_dep_mask) {
-        SDL_SendKeyboardKey(SDL_PRESSED, scancode);
-    } else {
-        SDL_SendKeyboardKey(SDL_RELEASED, scancode);
-    }
-}
-
-/* This is a helper function for DoSidedModifiers.
- * This function will figure out if the modifier key is the left or right side,
- * e.g. left-shift vs right-shift.
- */
-static void
-HandleModifierSide(int device_independent_mask,
-                   unsigned int oldMods, unsigned int newMods,
-                   SDL_Scancode left_scancode,
-                   SDL_Scancode right_scancode,
-                   unsigned int left_device_dependent_mask,
-                   unsigned int right_device_dependent_mask)
-{
-    unsigned int device_dependent_mask = (left_device_dependent_mask |
-                                         right_device_dependent_mask);
-    unsigned int diff_mod;
-
-    /* On the basis that the device independent mask is set, but there are
-     * no device dependent flags set, we'll assume that we can't detect this
-     * keyboard and revert to the unsided behavior.
-     */
-    if ((device_dependent_mask & newMods) == 0) {
-        /* Revert to the old behavior */
-        HandleNonDeviceModifier(device_independent_mask, oldMods, newMods, left_scancode);
-        return;
-    }
-
-    /* XOR the previous state against the new state to see if there's a change */
-    diff_mod = (device_dependent_mask & oldMods) ^
-               (device_dependent_mask & newMods);
-    if (diff_mod) {
-        /* A change in state was found. Isolate the left and right bits
-         * to handle them separately just in case the values can simulataneously
-         * change or if the bits don't both exist.
-         */
-        if (left_device_dependent_mask & diff_mod) {
-            HandleModifierOneSide(oldMods, newMods, left_scancode, left_device_dependent_mask);
-        }
-        if (right_device_dependent_mask & diff_mod) {
-            HandleModifierOneSide(oldMods, newMods, right_scancode, right_device_dependent_mask);
-        }
-    }
-}
-
-/* This is a helper function for DoSidedModifiers.
- * This function will release a key press in the case that
- * it is clear that the modifier has been released (i.e. one side
- * can't still be down).
- */
-static void
-ReleaseModifierSide(unsigned int device_independent_mask,
-                    unsigned int oldMods, unsigned int newMods,
-                    SDL_Scancode left_scancode,
-                    SDL_Scancode right_scancode,
-                    unsigned int left_device_dependent_mask,
-                    unsigned int right_device_dependent_mask)
-{
-    unsigned int device_dependent_mask = (left_device_dependent_mask |
-                                          right_device_dependent_mask);
-
-    /* On the basis that the device independent mask is set, but there are
-     * no device dependent flags set, we'll assume that we can't detect this
-     * keyboard and revert to the unsided behavior.
-     */
-    if ((device_dependent_mask & oldMods) == 0) {
-        /* In this case, we can't detect the keyboard, so use the left side
-         * to represent both, and release it.
-         */
-        SDL_SendKeyboardKey(SDL_RELEASED, left_scancode);
-        return;
-    }
-
-    /*
-     * This could have been done in an if-else case because at this point,
-     * we know that all keys have been released when calling this function.
-     * But I'm being paranoid so I want to handle each separately,
-     * so I hope this doesn't cause other problems.
-     */
-    if ( left_device_dependent_mask & oldMods ) {
-        SDL_SendKeyboardKey(SDL_RELEASED, left_scancode);
-    }
-    if ( right_device_dependent_mask & oldMods ) {
-        SDL_SendKeyboardKey(SDL_RELEASED, right_scancode);
-    }
-}
-
-/* This function will handle the modifier keys and also determine the
- * correct side of the key.
- */
-static void
-DoSidedModifiers(unsigned short scancode,
-                 unsigned int oldMods, unsigned int newMods)
-{
-    /* Set up arrays for the key syms for the left and right side. */
-    const SDL_Scancode left_mapping[]  = {
-        SDL_SCANCODE_LSHIFT,
-        SDL_SCANCODE_LCTRL,
-        SDL_SCANCODE_LALT,
-        SDL_SCANCODE_LGUI
-    };
-    const SDL_Scancode right_mapping[] = {
-        SDL_SCANCODE_RSHIFT,
-        SDL_SCANCODE_RCTRL,
-        SDL_SCANCODE_RALT,
-        SDL_SCANCODE_RGUI
-    };
-    /* Set up arrays for the device dependent masks with indices that
-     * correspond to the _mapping arrays
-     */
-    const unsigned int left_device_mapping[]  = { NX_DEVICELSHIFTKEYMASK, NX_DEVICELCTLKEYMASK, NX_DEVICELALTKEYMASK, NX_DEVICELCMDKEYMASK };
-    const unsigned int right_device_mapping[] = { NX_DEVICERSHIFTKEYMASK, NX_DEVICERCTLKEYMASK, NX_DEVICERALTKEYMASK, NX_DEVICERCMDKEYMASK };
-
-    unsigned int i, bit;
-
-    /* Iterate through the bits, testing each against the old modifiers */
-    for (i = 0, bit = NSEventModifierFlagShift; bit <= NSEventModifierFlagCommand; bit <<= 1, ++i) {
-        unsigned int oldMask, newMask;
-
-        oldMask = oldMods & bit;
-        newMask = newMods & bit;
-
-        /* If the bit is set, we must always examine it because the left
-         * and right side keys may alternate or both may be pressed.
-         */
-        if (newMask) {
-            HandleModifierSide(bit, oldMods, newMods,
-                               left_mapping[i], right_mapping[i],
-                               left_device_mapping[i], right_device_mapping[i]);
-        }
-        /* If the state changed from pressed to unpressed, we must examine
-            * the device dependent bits to release the correct keys.
-            */
-        else if (oldMask && oldMask != newMask) {
-            ReleaseModifierSide(bit, oldMods, newMods,
-                              left_mapping[i], right_mapping[i],
-                              left_device_mapping[i], right_device_mapping[i]);
-        }
-    }
-}
-
 static void
 HandleModifiers(_THIS, unsigned short scancode, unsigned int modifierFlags)
 {
-    SDL_VideoData *data = (__bridge SDL_VideoData *) _this->driverdata;
-
-    if (modifierFlags == data.modifierFlags) {
-        return;
+    SDL_Scancode code = darwin_scancode_table[scancode];
+
+    const SDL_Scancode codes[] = { 
+        SDL_SCANCODE_LSHIFT, 
+        SDL_SCANCODE_LCTRL, 
+        SDL_SCANCODE_LALT, 
+        SDL_SCANCODE_LGUI, 
+        SDL_SCANCODE_RSHIFT, 
+        SDL_SCANCODE_RCTRL, 
+        SDL_SCANCODE_RALT, 
+        SDL_SCANCODE_RGUI, 
+        SDL_SCANCODE_LSHIFT, 
+        SDL_SCANCODE_LCTRL, 
+        SDL_SCANCODE_LALT, 
+        SDL_SCANCODE_LGUI, };
+
+    const unsigned int modifiers[] = { 
+        NX_DEVICELSHIFTKEYMASK, 
+        NX_DEVICELCTLKEYMASK, 
+        NX_DEVICELALTKEYMASK, 
+        NX_DEVICELCMDKEYMASK, 
+        NX_DEVICERSHIFTKEYMASK, 
+        NX_DEVICERCTLKEYMASK, 
+        NX_DEVICERALTKEYMASK, 
+        NX_DEVICERCMDKEYMASK,
+        NX_SHIFTMASK,
+        NX_CONTROLMASK, 
+        NX_ALTERNATEMASK,
+        NX_COMMANDMASK };
+
+    for (int i = 0; i < 12; i++)
+    {
+        if (code == codes[i])
+        {
+            if (modifierFlags & modifiers[i])
+                SDL_SendKeyboardKey(SDL_PRESSED, code);
+            else
+                SDL_SendKeyboardKey(SDL_RELEASED, code);
+        }
     }
-
-    DoSidedModifiers(scancode, data.modifierFlags, modifierFlags);
-    data.modifierFlags = modifierFlags;
 }
 
 static void
@@ -579,8 +419,7 @@ - (NSArray *)validAttributesForMarkedText
         SDL_SendKeyboardKey(SDL_RELEASED, code);
         break;
     case NSEventTypeFlagsChanged:
-        /* FIXME CW 2007-08-14: check if this whole mess that takes up half of this file is really necessary */
-        HandleModifiers(_this, scancode, (unsigned int)[event modifierFlags]);
+        HandleModifiers(_this, scancode, (unsigned int)[event modifierFlags]);	
         break;
     default: /* just to avoid compiler warnings */
         break;