From 46f19c311deb4c6d0f61cc19a198ac40922bb595 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 8 Jul 2021 07:23:29 -0700
Subject: [PATCH] Implemented mouse relative mode for iOS 14.1 and newer
---
Xcode/SDL/SDL.xcodeproj/project.pbxproj | 0
build-scripts/config.guess | 0
build-scripts/config.sub | 0
src/video/uikit/SDL_uikitevents.h | 1 +
src/video/uikit/SDL_uikitevents.m | 46 ++++++++++++++++++-----
src/video/uikit/SDL_uikitvideo.m | 9 +++--
src/video/uikit/SDL_uikitviewcontroller.m | 20 ++++++++--
src/video/uikit/SDL_uikitwindow.h | 1 +
src/video/uikit/SDL_uikitwindow.m | 26 ++++++++++---
9 files changed, 80 insertions(+), 23 deletions(-)
mode change 100644 => 100755 Xcode/SDL/SDL.xcodeproj/project.pbxproj
mode change 100755 => 100644 build-scripts/config.guess
mode change 100755 => 100644 build-scripts/config.sub
diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj
old mode 100644
new mode 100755
diff --git a/build-scripts/config.guess b/build-scripts/config.guess
old mode 100755
new mode 100644
diff --git a/build-scripts/config.sub b/build-scripts/config.sub
old mode 100755
new mode 100644
diff --git a/src/video/uikit/SDL_uikitevents.h b/src/video/uikit/SDL_uikitevents.h
index 58aaf35b2c..610a468807 100644
--- a/src/video/uikit/SDL_uikitevents.h
+++ b/src/video/uikit/SDL_uikitevents.h
@@ -31,6 +31,7 @@ extern void SDL_QuitGCKeyboard(void);
extern void SDL_InitGCMouse(void);
extern SDL_bool SDL_HasGCMouse(void);
+extern SDL_bool SDL_GCMouseRelativeMode(void);
extern void SDL_QuitGCMouse(void);
#endif /* SDL_uikitevents_h_ */
diff --git a/src/video/uikit/SDL_uikitevents.m b/src/video/uikit/SDL_uikitevents.m
index 4219164c34..54f88879c3 100644
--- a/src/video/uikit/SDL_uikitevents.m
+++ b/src/video/uikit/SDL_uikitevents.m
@@ -91,9 +91,9 @@ static void OnGCKeyboardConnected(GCKeyboard *keyboard) API_AVAILABLE(macos(11.0
SDL_SendKeyboardKey(pressed ? SDL_PRESSED : SDL_RELEASED, (SDL_Scancode)keyCode);
};
- dispatch_queue_t queue = dispatch_queue_create( "org.libsdl.input.keyboard", DISPATCH_QUEUE_SERIAL );
- dispatch_set_target_queue( queue, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 ) );
- keyboard.handlerQueue = queue;
+ dispatch_queue_t queue = dispatch_queue_create( "org.libsdl.input.keyboard", DISPATCH_QUEUE_SERIAL );
+ dispatch_set_target_queue( queue, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 ) );
+ keyboard.handlerQueue = queue;
}
static void OnGCKeyboardDisconnected(GCKeyboard *keyboard) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
@@ -182,10 +182,22 @@ void SDL_QuitGCKeyboard(void)
static int mice_connected = 0;
static id mouse_connect_observer = nil;
static id mouse_disconnect_observer = nil;
+static bool mouse_relative_mode = SDL_FALSE;
+
+static void UpdateMouseGrab()
+{
+ SDL_VideoDevice *_this = SDL_GetVideoDevice();
+ SDL_Window *window;
+
+ for (window = _this->windows; window != NULL; window = window->next) {
+ SDL_UpdateWindowGrab(window);
+ }
+}
static int SetGCMouseRelativeMode(SDL_bool enabled)
{
- /* We'll always send relative motion and we can't warp, so nothing to do here */
+ mouse_relative_mode = enabled;
+ UpdateMouseGrab();
return 0;
}
@@ -222,14 +234,16 @@ static void OnGCMouseConnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios(14
mouse.mouseInput.mouseMovedHandler = ^(GCMouseInput *mouse, float deltaX, float deltaY)
{
- SDL_SendMouseMotion(SDL_GetMouseFocus(), mouseID, SDL_TRUE, (int)deltaX, -(int)deltaY);
+ SDL_SendMouseMotion(SDL_GetMouseFocus(), mouseID, SDL_TRUE, (int)deltaX, -(int)deltaY);
};
- dispatch_queue_t queue = dispatch_queue_create( "org.libsdl.input.mouse", DISPATCH_QUEUE_SERIAL );
- dispatch_set_target_queue( queue, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 ) );
- mouse.handlerQueue = queue;
+ dispatch_queue_t queue = dispatch_queue_create( "org.libsdl.input.mouse", DISPATCH_QUEUE_SERIAL );
+ dispatch_set_target_queue( queue, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 ) );
+ mouse.handlerQueue = queue;
++mice_connected;
+
+ UpdateMouseGrab();
}
static void OnGCMouseDisconnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0))
@@ -245,12 +259,14 @@ static void OnGCMouseDisconnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios
for (GCControllerButtonInput *button in mouse.mouseInput.auxiliaryButtons) {
button.pressedChangedHandler = nil;
}
+
+ UpdateMouseGrab();
}
void SDL_InitGCMouse(void)
{
- @autoreleasepool {
- /* There is a bug where mouse accumulates duplicate deltas over time in iOS 14.0 */
+ @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];
@@ -284,6 +300,11 @@ SDL_bool SDL_HasGCMouse(void)
return (mice_connected > 0);
}
+SDL_bool SDL_GCMouseRelativeMode(void)
+{
+ return mouse_relative_mode;
+}
+
void SDL_QuitGCMouse(void)
{
@autoreleasepool {
@@ -320,6 +341,11 @@ SDL_bool SDL_HasGCMouse(void)
return SDL_FALSE;
}
+SDL_bool SDL_GCMouseRelativeMode(void)
+{
+ return SDL_FALSE;
+}
+
void SDL_QuitGCMouse(void)
{
}
diff --git a/src/video/uikit/SDL_uikitvideo.m b/src/video/uikit/SDL_uikitvideo.m
index f302e6494f..ed95f536d1 100644
--- a/src/video/uikit/SDL_uikitvideo.m
+++ b/src/video/uikit/SDL_uikitvideo.m
@@ -93,6 +93,7 @@ static void UIKit_DeleteDevice(SDL_VideoDevice * device)
device->RaiseWindow = UIKit_RaiseWindow;
device->SetWindowBordered = UIKit_SetWindowBordered;
device->SetWindowFullscreen = UIKit_SetWindowFullscreen;
+ device->SetWindowMouseGrab = UIKit_SetWindowMouseGrab;
device->DestroyWindow = UIKit_DestroyWindow;
device->GetWindowWMInfo = UIKit_GetWindowWMInfo;
device->GetDisplayUsableBounds = UIKit_GetDisplayUsableBounds;
@@ -159,8 +160,8 @@ static void UIKit_DeleteDevice(SDL_VideoDevice * device)
return -1;
}
- SDL_InitGCKeyboard();
- SDL_InitGCMouse();
+ SDL_InitGCKeyboard();
+ SDL_InitGCMouse();
return 0;
}
@@ -168,8 +169,8 @@ static void UIKit_DeleteDevice(SDL_VideoDevice * device)
void
UIKit_VideoQuit(_THIS)
{
- SDL_QuitGCKeyboard();
- SDL_QuitGCMouse();
+ SDL_QuitGCKeyboard();
+ SDL_QuitGCMouse();
UIKit_QuitModes(_this);
}
diff --git a/src/video/uikit/SDL_uikitviewcontroller.m b/src/video/uikit/SDL_uikitviewcontroller.m
index 7b677e9833..c51d1aed25 100644
--- a/src/video/uikit/SDL_uikitviewcontroller.m
+++ b/src/video/uikit/SDL_uikitviewcontroller.m
@@ -27,8 +27,9 @@
#include "../SDL_sysvideo.h"
#include "../../events/SDL_events_c.h"
-#import "SDL_uikitviewcontroller.h"
-#import "SDL_uikitmessagebox.h"
+#include "SDL_uikitviewcontroller.h"
+#include "SDL_uikitmessagebox.h"
+#include "SDL_uikitevents.h"
#include "SDL_uikitvideo.h"
#include "SDL_uikitmodes.h"
#include "SDL_uikitwindow.h"
@@ -246,7 +247,20 @@ - (UIRectEdge)preferredScreenEdgesDeferringSystemGestures
return UIRectEdgeNone;
}
}
-#endif
+
+- (BOOL)prefersPointerLocked
+{
+ SDL_VideoDevice *_this = SDL_GetVideoDevice();
+
+ if (SDL_HasGCMouse() &&
+ (SDL_GCMouseRelativeMode() || _this->grabbed_window == window)) {
+ return YES;
+ } else {
+ return NO;
+ }
+}
+
+#endif /* !TARGET_OS_TV */
/*
---- Keyboard related functionality below this line ----
diff --git a/src/video/uikit/SDL_uikitwindow.h b/src/video/uikit/SDL_uikitwindow.h
index 4d9fd34a2c..23beea7769 100644
--- a/src/video/uikit/SDL_uikitwindow.h
+++ b/src/video/uikit/SDL_uikitwindow.h
@@ -33,6 +33,7 @@ extern void UIKit_HideWindow(_THIS, SDL_Window * window);
extern void UIKit_RaiseWindow(_THIS, SDL_Window * window);
extern void UIKit_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered);
extern void UIKit_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen);
+extern void UIKit_SetWindowMouseGrab(_THIS, SDL_Window * window, SDL_bool grabbed);
extern void UIKit_DestroyWindow(_THIS, SDL_Window * window);
extern SDL_bool UIKit_GetWindowWMInfo(_THIS, SDL_Window * window,
struct SDL_SysWMinfo * info);
diff --git a/src/video/uikit/SDL_uikitwindow.m b/src/video/uikit/SDL_uikitwindow.m
index 39e51813b7..1c84203265 100644
--- a/src/video/uikit/SDL_uikitwindow.m
+++ b/src/video/uikit/SDL_uikitwindow.m
@@ -161,14 +161,14 @@ - (void)layoutSubviews
@autoreleasepool {
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
SDL_DisplayData *data = (__bridge SDL_DisplayData *) display->driverdata;
- SDL_Window *other;
+ SDL_Window *other;
/* We currently only handle a single window per display on iOS */
- for (other = _this->windows; other; other = other->next) {
- if (other != window && SDL_GetDisplayForWindow(other) == display) {
- return SDL_SetError("Only one window allowed per display.");
- }
- }
+ for (other = _this->windows; other; other = other->next) {
+ if (other != window && SDL_GetDisplayForWindow(other) == display) {
+ return SDL_SetError("Only one window allowed per display.");
+ }
+ }
/* If monitor has a resolution of 0x0 (hasn't been explicitly set by the
* user, so it's in standby), try to force the display to a resolution
@@ -320,6 +320,20 @@ - (void)layoutSubviews
}
}
+void
+UIKit_SetWindowMouseGrab(_THIS, SDL_Window * window, SDL_bool grabbed)
+{
+#if !TARGET_OS_TV
+ @autoreleasepool {
+ SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
+ SDL_uikitviewcontroller *viewcontroller = data.viewcontroller;
+ if (@available(iOS 14.0, *)) {
+ [viewcontroller setNeedsUpdateOfPrefersPointerLocked];
+ }
+ }
+#endif /* !TARGET_OS_TV */
+}
+
void
UIKit_DestroyWindow(_THIS, SDL_Window * window)
{