Added support for low latency mouse and keyboard handling in iOS 14

The mouse support in iOS 14.0 has a bug with accumulating duplicate mouse deltas that won't be fixed until iOS 14.1, so we don't enable it until then.
diff --git a/src/video/uikit/SDL_uikitevents.h b/src/video/uikit/SDL_uikitevents.h
index f4ae9c7..86b12a8 100644
--- a/src/video/uikit/SDL_uikitevents.h
+++ b/src/video/uikit/SDL_uikitevents.h
@@ -25,6 +25,14 @@
 
 extern void UIKit_PumpEvents(_THIS);
 
+extern void SDL_InitGCKeyboard(void);
+extern SDL_bool SDL_HasGCKeyboard(void);
+extern void SDL_QuitGCKeyboard(void);
+
+extern void SDL_InitGCMouse(void);
+extern SDL_bool SDL_HasGCMouse(void);
+extern void SDL_QuitGCMouse(void);
+
 #endif /* SDL_uikitevents_h_ */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/uikit/SDL_uikitevents.m b/src/video/uikit/SDL_uikitevents.m
index c9645c4..8206d74 100644
--- a/src/video/uikit/SDL_uikitevents.m
+++ b/src/video/uikit/SDL_uikitevents.m
@@ -30,6 +30,13 @@
 
 #import <Foundation/Foundation.h>
 
+#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000) || (__APPLETV_OS_VERSION_MAX_ALLOWED >= 140000) || (__MAC_OS_VERSION_MAX_ALLOWED > 1500000)
+#import <GameController/GameController.h>
+
+#define ENABLE_GCKEYBOARD
+#define ENABLE_GCMOUSE
+#endif
+
 static BOOL UIKit_EventPumpEnabled = YES;
 
 void
@@ -70,6 +77,247 @@
 #endif
 }
 
+#ifdef ENABLE_GCKEYBOARD
+
+static SDL_bool keyboard_connected = SDL_FALSE;
+static id keyboard_connect_observer = nil;
+static id keyboard_disconnect_observer = nil;
+
+static void OnGCKeyboardConnected(GCKeyboard *keyboard) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
+{
+    keyboard_connected = SDL_TRUE;
+    keyboard.keyboardInput.keyChangedHandler = ^(GCKeyboardInput *keyboard, GCControllerButtonInput *key, GCKeyCode keyCode, BOOL pressed)
+    {
+        SDL_SendKeyboardKey(pressed ? SDL_PRESSED : SDL_RELEASED, (SDL_Scancode)keyCode);
+    };
+}
+
+static void OnGCKeyboardDisconnected(GCKeyboard *keyboard) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
+{
+    keyboard.keyboardInput.keyChangedHandler = nil;
+    keyboard_connected = SDL_FALSE;
+}
+
+void SDL_InitGCKeyboard(void)
+{
+    @autoreleasepool {
+        if (@available(iOS 14.0, tvOS 14.0, *)) {
+            NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+
+            keyboard_connect_observer = [center addObserverForName:GCKeyboardDidConnectNotification
+                                                            object:nil
+                                                             queue:nil
+                                                        usingBlock:^(NSNotification *note) {
+                                                            GCKeyboard *keyboard = note.object;
+                                                            OnGCKeyboardConnected(keyboard);
+                                                        }];
+
+            keyboard_disconnect_observer = [center addObserverForName:GCKeyboardDidDisconnectNotification
+                                                               object:nil
+                                                                queue:nil
+                                                           usingBlock:^(NSNotification *note) {
+                                                                GCKeyboard *keyboard = note.object;
+                                                                OnGCKeyboardDisconnected(keyboard);
+                                                           }];
+
+            if (GCKeyboard.coalescedKeyboard != nil) {
+                OnGCKeyboardConnected(GCKeyboard.coalescedKeyboard);
+            }
+        }
+    }
+}
+
+SDL_bool SDL_HasGCKeyboard(void)
+{
+    return keyboard_connected;
+}
+
+void SDL_QuitGCKeyboard(void)
+{
+    @autoreleasepool {
+        if (@available(iOS 14.0, tvOS 14.0, *)) {
+            NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+
+            if (keyboard_connect_observer) {
+                [center removeObserver:keyboard_connect_observer name:GCKeyboardDidConnectNotification object:nil];
+                keyboard_connect_observer = nil;
+            }
+
+            if (keyboard_disconnect_observer) {
+                [center removeObserver:keyboard_disconnect_observer name:GCKeyboardDidDisconnectNotification object:nil];
+                keyboard_disconnect_observer = nil;
+            }
+
+            if (GCKeyboard.coalescedKeyboard != nil) {
+                OnGCKeyboardDisconnected(GCKeyboard.coalescedKeyboard);
+            }
+        }
+    }
+}
+
+#else
+
+void SDL_InitGCKeyboard(void)
+{
+}
+
+SDL_bool SDL_HasGCKeyboard(void)
+{
+    return SDL_FALSE;
+}
+
+void SDL_QuitGCKeyboard(void)
+{
+}
+
+#endif /* ENABLE_GCKEYBOARD */
+
+
+#ifdef ENABLE_GCMOUSE
+
+static int mice_connected = 0;
+static id mouse_connect_observer = nil;
+static id mouse_disconnect_observer = nil;
+
+static int SetGCMouseRelativeMode(SDL_bool enabled)
+{
+    /* We'll always send relative motion and we can't warp, so nothing to do here */
+    return 0;
+}
+
+static void OnGCMouseButtonChanged(SDL_MouseID mouseID, Uint8 button, BOOL pressed)
+{
+    SDL_SendMouseButton(SDL_GetMouseFocus(), mouseID, pressed ? SDL_PRESSED : SDL_RELEASED, button);
+}
+
+static void OnGCMouseConnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
+{
+    SDL_MouseID mouseID = mice_connected;
+
+    mouse.mouseInput.leftButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed)
+    {
+        OnGCMouseButtonChanged(mouseID, SDL_BUTTON_LEFT, pressed);
+    };
+    mouse.mouseInput.middleButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed)
+    {
+        OnGCMouseButtonChanged(mouseID, SDL_BUTTON_MIDDLE, pressed);
+    };
+    mouse.mouseInput.rightButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed)
+    {
+        OnGCMouseButtonChanged(mouseID, SDL_BUTTON_RIGHT, pressed);
+    };
+
+    int auxiliary_button = SDL_BUTTON_X1;
+    for (GCControllerButtonInput *button in mouse.mouseInput.auxiliaryButtons) {
+        button.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed)
+        {
+            OnGCMouseButtonChanged(mouseID, auxiliary_button, pressed);
+        };
+        ++auxiliary_button;
+    }
+
+    mouse.mouseInput.mouseMovedHandler = ^(GCMouseInput *mouse, float deltaX, float deltaY)
+    {
+		SDL_SendMouseMotion(SDL_GetMouseFocus(), mouseID, SDL_TRUE, (int)deltaX, -(int)deltaY);
+    };
+
+    ++mice_connected;
+}
+
+static void OnGCMouseDisconnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
+{
+    --mice_connected;
+
+    mouse.mouseInput.mouseMovedHandler = nil;
+
+    mouse.mouseInput.leftButton.pressedChangedHandler = nil;
+    mouse.mouseInput.middleButton.pressedChangedHandler = nil;
+    mouse.mouseInput.rightButton.pressedChangedHandler = nil;
+
+    for (GCControllerButtonInput *button in mouse.mouseInput.auxiliaryButtons) {
+        button.pressedChangedHandler = nil;
+    }
+}
+
+void SDL_InitGCMouse(void)
+{
+	@autoreleasepool {
+		/* There is a bug where mouse accumulates duplicate deltas over time in iOS 14.0 */
+        if (@available(iOS 14.1, tvOS 14.1, *)) {
+            NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+
+            mouse_connect_observer = [center addObserverForName:GCMouseDidConnectNotification
+                                                         object:nil
+                                                          queue:nil
+                                                     usingBlock:^(NSNotification *note) {
+                                                         GCMouse *mouse = note.object;
+                                                         OnGCMouseConnected(mouse);
+                                                     }];
+
+            mouse_disconnect_observer = [center addObserverForName:GCMouseDidDisconnectNotification
+                                                            object:nil
+                                                             queue:nil
+                                                        usingBlock:^(NSNotification *note) {
+                                                            GCMouse *mouse = note.object;
+                                                            OnGCMouseDisconnected(mouse);
+                                                       }];
+
+            for (GCMouse *mouse in [GCMouse mice]) {
+                OnGCMouseConnected(mouse);
+            }
+
+            SDL_GetMouse()->SetRelativeMouseMode = SetGCMouseRelativeMode;
+        }
+    }
+}
+
+SDL_bool SDL_HasGCMouse(void)
+{
+    return (mice_connected > 0);
+}
+
+void SDL_QuitGCMouse(void)
+{
+    @autoreleasepool {
+        if (@available(iOS 14.0, tvOS 14.0, *)) {
+            NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+
+            if (mouse_connect_observer) {
+                [center removeObserver:mouse_connect_observer name:GCMouseDidConnectNotification object:nil];
+                mouse_connect_observer = nil;
+            }
+
+            if (mouse_disconnect_observer) {
+                [center removeObserver:mouse_disconnect_observer name:GCMouseDidDisconnectNotification object:nil];
+                mouse_disconnect_observer = nil;
+            }
+
+            for (GCMouse *mouse in [GCMouse mice]) {
+                OnGCMouseDisconnected(mouse);
+            }
+
+            SDL_GetMouse()->SetRelativeMouseMode = NULL;
+        }
+    }
+}
+
+#else
+
+void SDL_InitGCMouse(void)
+{
+}
+
+SDL_bool SDL_HasGCMouse(void)
+{
+    return SDL_FALSE;
+}
+
+void SDL_QuitGCMouse(void)
+{
+}
+
+#endif /* ENABLE_GCMOUSE */
+
 #endif /* SDL_VIDEO_DRIVER_UIKIT */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/uikit/SDL_uikitvideo.m b/src/video/uikit/SDL_uikitvideo.m
index 45bda2d..8adaaff 100644
--- a/src/video/uikit/SDL_uikitvideo.m
+++ b/src/video/uikit/SDL_uikitvideo.m
@@ -158,12 +158,19 @@
     if (UIKit_InitModes(_this) < 0) {
         return -1;
     }
+
+	SDL_InitGCKeyboard();
+	SDL_InitGCMouse();
+
     return 0;
 }
 
 void
 UIKit_VideoQuit(_THIS)
 {
+	SDL_QuitGCKeyboard();
+	SDL_QuitGCMouse();
+
     UIKit_QuitModes(_this);
 }
 
diff --git a/src/video/uikit/SDL_uikitview.m b/src/video/uikit/SDL_uikitview.m
index 9953072..2057471 100644
--- a/src/video/uikit/SDL_uikitview.m
+++ b/src/video/uikit/SDL_uikitview.m
@@ -29,9 +29,10 @@
 #include "../../events/SDL_touch_c.h"
 #include "../../events/SDL_events_c.h"
 
-#import "SDL_uikitappdelegate.h"
-#import "SDL_uikitmodes.h"
-#import "SDL_uikitwindow.h"
+#include "SDL_uikitappdelegate.h"
+#include "SDL_uikitevents.h"
+#include "SDL_uikitmodes.h"
+#include "SDL_uikitwindow.h"
 
 /* The maximum number of mouse buttons we support */
 #define MAX_MOUSE_BUTTONS    5
@@ -159,7 +160,7 @@
 
 #if !TARGET_OS_TV && defined(__IPHONE_13_4)
 - (UIPointerRegion *)pointerInteraction:(UIPointerInteraction *)interaction regionForRequest:(UIPointerRegionRequest *)request defaultRegion:(UIPointerRegion *)defaultRegion API_AVAILABLE(ios(13.4)){
-    if (request != nil) {
+    if (request != nil && !SDL_HasGCMouse()) {
         CGPoint origin = self.bounds.origin;
         CGPoint point = request.location;
 
@@ -236,27 +237,29 @@
 #if !TARGET_OS_TV && defined(__IPHONE_13_4)
         if (@available(iOS 13.4, *)) {
             if (touch.type == UITouchTypeIndirectPointer) {
-                int i;
+                if (!SDL_HasGCMouse()) {
+                    int i;
 
-                for (i = 1; i <= MAX_MOUSE_BUTTONS; ++i) {
-                    if (event.buttonMask & SDL_BUTTON(i)) {
-                        Uint8 button;
+                    for (i = 1; i <= MAX_MOUSE_BUTTONS; ++i) {
+                        if ((event.buttonMask & SDL_BUTTON(i)) != 0) {
+                            Uint8 button;
 
-                        switch (i) {
-                        case 1:
-                            button = SDL_BUTTON_LEFT;
-                            break;
-                        case 2:
-                            button = SDL_BUTTON_RIGHT;
-                            break;
-                        case 3:
-                            button = SDL_BUTTON_MIDDLE;
-                            break;
-                        default:
-                            button = (Uint8)i;
-                            break;
+                            switch (i) {
+                            case 1:
+                                button = SDL_BUTTON_LEFT;
+                                break;
+                            case 2:
+                                button = SDL_BUTTON_RIGHT;
+                                break;
+                            case 3:
+                                button = SDL_BUTTON_MIDDLE;
+                                break;
+                            default:
+                                button = (Uint8)i;
+                                break;
+                            }
+                            SDL_SendMouseButton(sdlwindow, 0, SDL_PRESSED, button);
                         }
-                        SDL_SendMouseButton(sdlwindow, 0, SDL_PRESSED, button);
                     }
                 }
                 handled = YES;
@@ -289,27 +292,29 @@
 #if !TARGET_OS_TV && defined(__IPHONE_13_4)
         if (@available(iOS 13.4, *)) {
             if (touch.type == UITouchTypeIndirectPointer) {
-                int i;
+                if (!SDL_HasGCMouse()) {
+                    int i;
 
-                for (i = 1; i <= MAX_MOUSE_BUTTONS; ++i) {
-                    if (!(event.buttonMask & SDL_BUTTON(i))) {
-                        Uint8 button;
+                    for (i = 1; i <= MAX_MOUSE_BUTTONS; ++i) {
+                        if ((event.buttonMask & SDL_BUTTON(i)) != 0) {
+                            Uint8 button;
 
-                        switch (i) {
-                        case 1:
-                            button = SDL_BUTTON_LEFT;
-                            break;
-                        case 2:
-                            button = SDL_BUTTON_RIGHT;
-                            break;
-                        case 3:
-                            button = SDL_BUTTON_MIDDLE;
-                            break;
-                        default:
-                            button = (Uint8)i;
-                            break;
+                            switch (i) {
+                            case 1:
+                                button = SDL_BUTTON_LEFT;
+                                break;
+                            case 2:
+                                button = SDL_BUTTON_RIGHT;
+                                break;
+                            case 3:
+                                button = SDL_BUTTON_MIDDLE;
+                                break;
+                            default:
+                                button = (Uint8)i;
+                                break;
+                            }
+                            SDL_SendMouseButton(sdlwindow, 0, SDL_RELEASED, button);
                         }
-                        SDL_SendMouseButton(sdlwindow, 0, SDL_RELEASED, button);
                     }
                 }
                 handled = YES;
@@ -411,27 +416,33 @@
 
 - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
 {
-    for (UIPress *press in presses) {
-        SDL_Scancode scancode = [self scancodeFromPress:press];
-        SDL_SendKeyboardKey(SDL_PRESSED, scancode);
+    if (!SDL_HasGCKeyboard()) {
+        for (UIPress *press in presses) {
+            SDL_Scancode scancode = [self scancodeFromPress:press];
+            SDL_SendKeyboardKey(SDL_PRESSED, scancode);
+        }
     }
     [super pressesBegan:presses withEvent:event];
 }
 
 - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
 {
-    for (UIPress *press in presses) {
-        SDL_Scancode scancode = [self scancodeFromPress:press];
-        SDL_SendKeyboardKey(SDL_RELEASED, scancode);
+    if (!SDL_HasGCKeyboard()) {
+        for (UIPress *press in presses) {
+            SDL_Scancode scancode = [self scancodeFromPress:press];
+            SDL_SendKeyboardKey(SDL_RELEASED, scancode);
+        }
     }
     [super pressesEnded:presses withEvent:event];
 }
 
 - (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
 {
-    for (UIPress *press in presses) {
-        SDL_Scancode scancode = [self scancodeFromPress:press];
-        SDL_SendKeyboardKey(SDL_RELEASED, scancode);
+    if (!SDL_HasGCKeyboard()) {
+        for (UIPress *press in presses) {
+            SDL_Scancode scancode = [self scancodeFromPress:press];
+            SDL_SendKeyboardKey(SDL_RELEASED, scancode);
+        }
     }
     [super pressesCancelled:presses withEvent:event];
 }
diff --git a/src/video/uikit/SDL_uikitviewcontroller.m b/src/video/uikit/SDL_uikitviewcontroller.m
index bda8bf1..45e8a5f 100644
--- a/src/video/uikit/SDL_uikitviewcontroller.m
+++ b/src/video/uikit/SDL_uikitviewcontroller.m
@@ -430,7 +430,7 @@
                     }
 
                     if (mod & KMOD_SHIFT) {
-                        /* If character uses shift, press shift down */
+                        /* If character uses shift, press shift */
                         SDL_SendKeyboardKey(SDL_PRESSED, SDL_SCANCODE_LSHIFT);
                     }
 
@@ -439,7 +439,7 @@
                     SDL_SendKeyboardKey(SDL_RELEASED, code);
 
                     if (mod & KMOD_SHIFT) {
-                        /* If character uses shift, press shift back up */
+                        /* If character uses shift, release shift */
                         SDL_SendKeyboardKey(SDL_RELEASED, SDL_SCANCODE_LSHIFT);
                     }
                 }