From 46f19c311deb4c6d0f61cc19a198ac40922bb595 Mon Sep 17 00:00:00 2001 From: Sam Lantinga 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 58aaf35b2..610a46880 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 4219164c3..54f88879c 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 f302e6494..ed95f536d 100644 --- a/src/video/uikit/SDL_uikitvideo.m +++ b/src/video/uikit/SDL_uikitvideo.m @@ -93,6 +93,7 @@ UIKit_CreateDevice(int devindex) 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 @@ UIKit_VideoInit(_THIS) return -1; } - SDL_InitGCKeyboard(); - SDL_InitGCMouse(); + SDL_InitGCKeyboard(); + SDL_InitGCMouse(); return 0; } @@ -168,8 +169,8 @@ UIKit_VideoInit(_THIS) 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 7b677e983..c51d1aed2 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 @@ SDL_HideHomeIndicatorHintChanged(void *userdata, const char *name, const char *o 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 4d9fd34a2..23beea776 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 39e51813b..1c8420326 100644 --- a/src/video/uikit/SDL_uikitwindow.m +++ b/src/video/uikit/SDL_uikitwindow.m @@ -161,14 +161,14 @@ UIKit_CreateWindow(_THIS, SDL_Window *window) @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 @@ UIKit_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display } } +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) {