diff options
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/uikit/SDL_uikitevents.m')
| -rw-r--r-- | contrib/SDL-3.2.8/src/video/uikit/SDL_uikitevents.m | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitevents.m b/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitevents.m new file mode 100644 index 0000000..86224f7 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitevents.m | |||
| @@ -0,0 +1,461 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> | ||
| 4 | |||
| 5 | This software is provided 'as-is', without any express or implied | ||
| 6 | warranty. In no event will the authors be held liable for any damages | ||
| 7 | arising from the use of this software. | ||
| 8 | |||
| 9 | Permission is granted to anyone to use this software for any purpose, | ||
| 10 | including commercial applications, and to alter it and redistribute it | ||
| 11 | freely, subject to the following restrictions: | ||
| 12 | |||
| 13 | 1. The origin of this software must not be misrepresented; you must not | ||
| 14 | claim that you wrote the original software. If you use this software | ||
| 15 | in a product, an acknowledgment in the product documentation would be | ||
| 16 | appreciated but is not required. | ||
| 17 | 2. Altered source versions must be plainly marked as such, and must not be | ||
| 18 | misrepresented as being the original software. | ||
| 19 | 3. This notice may not be removed or altered from any source distribution. | ||
| 20 | */ | ||
| 21 | #include "SDL_internal.h" | ||
| 22 | |||
| 23 | #ifdef SDL_VIDEO_DRIVER_UIKIT | ||
| 24 | |||
| 25 | #include "../../events/SDL_events_c.h" | ||
| 26 | #include "../../main/SDL_main_callbacks.h" | ||
| 27 | |||
| 28 | #include "SDL_uikitevents.h" | ||
| 29 | #include "SDL_uikitopengles.h" | ||
| 30 | #include "SDL_uikitvideo.h" | ||
| 31 | #include "SDL_uikitwindow.h" | ||
| 32 | |||
| 33 | #import <Foundation/Foundation.h> | ||
| 34 | #import <GameController/GameController.h> | ||
| 35 | |||
| 36 | static BOOL UIKit_EventPumpEnabled = YES; | ||
| 37 | |||
| 38 | @interface SDL_LifecycleObserver : NSObject | ||
| 39 | @property(nonatomic, assign) BOOL isObservingNotifications; | ||
| 40 | @end | ||
| 41 | |||
| 42 | @implementation SDL_LifecycleObserver | ||
| 43 | |||
| 44 | - (void)update | ||
| 45 | { | ||
| 46 | NSNotificationCenter *notificationCenter = NSNotificationCenter.defaultCenter; | ||
| 47 | bool wants_observation = (UIKit_EventPumpEnabled || SDL_HasMainCallbacks()); | ||
| 48 | if (!wants_observation) { | ||
| 49 | // Make sure no windows have active animation callbacks | ||
| 50 | int num_windows = 0; | ||
| 51 | SDL_free(SDL_GetWindows(&num_windows)); | ||
| 52 | if (num_windows > 0) { | ||
| 53 | wants_observation = true; | ||
| 54 | } | ||
| 55 | } | ||
| 56 | if (wants_observation && !self.isObservingNotifications) { | ||
| 57 | self.isObservingNotifications = YES; | ||
| 58 | [notificationCenter addObserver:self selector:@selector(applicationDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil]; | ||
| 59 | [notificationCenter addObserver:self selector:@selector(applicationWillResignActive) name:UIApplicationWillResignActiveNotification object:nil]; | ||
| 60 | [notificationCenter addObserver:self selector:@selector(applicationDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil]; | ||
| 61 | [notificationCenter addObserver:self selector:@selector(applicationWillEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil]; | ||
| 62 | [notificationCenter addObserver:self selector:@selector(applicationWillTerminate) name:UIApplicationWillTerminateNotification object:nil]; | ||
| 63 | [notificationCenter addObserver:self selector:@selector(applicationDidReceiveMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; | ||
| 64 | #if !defined(SDL_PLATFORM_TVOS) && !defined(SDL_PLATFORM_VISIONOS) | ||
| 65 | [notificationCenter addObserver:self | ||
| 66 | selector:@selector(applicationDidChangeStatusBarOrientation) | ||
| 67 | name:UIApplicationDidChangeStatusBarOrientationNotification | ||
| 68 | object:nil]; | ||
| 69 | #endif | ||
| 70 | } else if (!wants_observation && self.isObservingNotifications) { | ||
| 71 | self.isObservingNotifications = NO; | ||
| 72 | [notificationCenter removeObserver:self]; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | - (void)applicationDidBecomeActive | ||
| 77 | { | ||
| 78 | SDL_OnApplicationDidEnterForeground(); | ||
| 79 | } | ||
| 80 | |||
| 81 | - (void)applicationWillResignActive | ||
| 82 | { | ||
| 83 | SDL_OnApplicationWillEnterBackground(); | ||
| 84 | } | ||
| 85 | |||
| 86 | - (void)applicationDidEnterBackground | ||
| 87 | { | ||
| 88 | SDL_OnApplicationDidEnterBackground(); | ||
| 89 | } | ||
| 90 | |||
| 91 | - (void)applicationWillEnterForeground | ||
| 92 | { | ||
| 93 | SDL_OnApplicationWillEnterForeground(); | ||
| 94 | } | ||
| 95 | |||
| 96 | - (void)applicationWillTerminate | ||
| 97 | { | ||
| 98 | SDL_OnApplicationWillTerminate(); | ||
| 99 | } | ||
| 100 | |||
| 101 | - (void)applicationDidReceiveMemoryWarning | ||
| 102 | { | ||
| 103 | SDL_OnApplicationDidReceiveMemoryWarning(); | ||
| 104 | } | ||
| 105 | |||
| 106 | #if !defined(SDL_PLATFORM_TVOS) && !defined(SDL_PLATFORM_VISIONOS) | ||
| 107 | - (void)applicationDidChangeStatusBarOrientation | ||
| 108 | { | ||
| 109 | SDL_OnApplicationDidChangeStatusBarOrientation(); | ||
| 110 | } | ||
| 111 | #endif | ||
| 112 | |||
| 113 | @end | ||
| 114 | |||
| 115 | void SDL_UpdateLifecycleObserver(void) | ||
| 116 | { | ||
| 117 | static SDL_LifecycleObserver *lifecycleObserver; | ||
| 118 | static dispatch_once_t onceToken; | ||
| 119 | dispatch_once(&onceToken, ^{ | ||
| 120 | lifecycleObserver = [SDL_LifecycleObserver new]; | ||
| 121 | }); | ||
| 122 | [lifecycleObserver update]; | ||
| 123 | } | ||
| 124 | |||
| 125 | void SDL_SetiOSEventPump(bool enabled) | ||
| 126 | { | ||
| 127 | UIKit_EventPumpEnabled = enabled; | ||
| 128 | |||
| 129 | SDL_UpdateLifecycleObserver(); | ||
| 130 | } | ||
| 131 | |||
| 132 | Uint64 UIKit_GetEventTimestamp(NSTimeInterval nsTimestamp) | ||
| 133 | { | ||
| 134 | static Uint64 timestamp_offset; | ||
| 135 | Uint64 timestamp = (Uint64)(nsTimestamp * SDL_NS_PER_SECOND); | ||
| 136 | Uint64 now = SDL_GetTicksNS(); | ||
| 137 | |||
| 138 | if (!timestamp_offset) { | ||
| 139 | timestamp_offset = (now - timestamp); | ||
| 140 | } | ||
| 141 | timestamp += timestamp_offset; | ||
| 142 | |||
| 143 | if (timestamp > now) { | ||
| 144 | timestamp_offset -= (timestamp - now); | ||
| 145 | timestamp = now; | ||
| 146 | } | ||
| 147 | return timestamp; | ||
| 148 | } | ||
| 149 | |||
| 150 | void UIKit_PumpEvents(SDL_VideoDevice *_this) | ||
| 151 | { | ||
| 152 | if (!UIKit_EventPumpEnabled) { | ||
| 153 | return; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* Let the run loop run for a short amount of time: long enough for | ||
| 157 | touch events to get processed (which is important to get certain | ||
| 158 | elements of Game Center's GKLeaderboardViewController to respond | ||
| 159 | to touch input), but not long enough to introduce a significant | ||
| 160 | delay in the rest of the app. | ||
| 161 | */ | ||
| 162 | const CFTimeInterval seconds = 0.000002; | ||
| 163 | |||
| 164 | // Pump most event types. | ||
| 165 | SInt32 result; | ||
| 166 | do { | ||
| 167 | result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, TRUE); | ||
| 168 | } while (result == kCFRunLoopRunHandledSource); | ||
| 169 | |||
| 170 | // Make sure UIScrollView objects scroll properly. | ||
| 171 | do { | ||
| 172 | result = CFRunLoopRunInMode((CFStringRef)UITrackingRunLoopMode, seconds, TRUE); | ||
| 173 | } while (result == kCFRunLoopRunHandledSource); | ||
| 174 | |||
| 175 | // See the comment in the function definition. | ||
| 176 | #if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) | ||
| 177 | UIKit_GL_RestoreCurrentContext(); | ||
| 178 | #endif | ||
| 179 | } | ||
| 180 | |||
| 181 | static id keyboard_connect_observer = nil; | ||
| 182 | static id keyboard_disconnect_observer = nil; | ||
| 183 | |||
| 184 | static void OnGCKeyboardConnected(GCKeyboard *keyboard) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) | ||
| 185 | { | ||
| 186 | SDL_KeyboardID keyboardID = (SDL_KeyboardID)(uintptr_t)keyboard; | ||
| 187 | |||
| 188 | SDL_AddKeyboard(keyboardID, NULL, true); | ||
| 189 | |||
| 190 | keyboard.keyboardInput.keyChangedHandler = ^(GCKeyboardInput *kbrd, GCControllerButtonInput *key, GCKeyCode keyCode, BOOL pressed) { | ||
| 191 | Uint64 timestamp = SDL_GetTicksNS(); | ||
| 192 | SDL_SendKeyboardKey(timestamp, keyboardID, 0, (SDL_Scancode)keyCode, pressed); | ||
| 193 | }; | ||
| 194 | |||
| 195 | dispatch_queue_t queue = dispatch_queue_create("org.libsdl.input.keyboard", DISPATCH_QUEUE_SERIAL); | ||
| 196 | dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)); | ||
| 197 | keyboard.handlerQueue = queue; | ||
| 198 | } | ||
| 199 | |||
| 200 | static void OnGCKeyboardDisconnected(GCKeyboard *keyboard) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) | ||
| 201 | { | ||
| 202 | SDL_KeyboardID keyboardID = (SDL_KeyboardID)(uintptr_t)keyboard; | ||
| 203 | |||
| 204 | SDL_RemoveKeyboard(keyboardID, true); | ||
| 205 | |||
| 206 | keyboard.keyboardInput.keyChangedHandler = nil; | ||
| 207 | } | ||
| 208 | |||
| 209 | void SDL_InitGCKeyboard(void) | ||
| 210 | { | ||
| 211 | @autoreleasepool { | ||
| 212 | if (@available(iOS 14.0, tvOS 14.0, *)) { | ||
| 213 | NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; | ||
| 214 | |||
| 215 | keyboard_connect_observer = [center addObserverForName:GCKeyboardDidConnectNotification | ||
| 216 | object:nil | ||
| 217 | queue:nil | ||
| 218 | usingBlock:^(NSNotification *note) { | ||
| 219 | GCKeyboard *keyboard = note.object; | ||
| 220 | OnGCKeyboardConnected(keyboard); | ||
| 221 | }]; | ||
| 222 | |||
| 223 | keyboard_disconnect_observer = [center addObserverForName:GCKeyboardDidDisconnectNotification | ||
| 224 | object:nil | ||
| 225 | queue:nil | ||
| 226 | usingBlock:^(NSNotification *note) { | ||
| 227 | GCKeyboard *keyboard = note.object; | ||
| 228 | OnGCKeyboardDisconnected(keyboard); | ||
| 229 | }]; | ||
| 230 | |||
| 231 | if (GCKeyboard.coalescedKeyboard != nil) { | ||
| 232 | OnGCKeyboardConnected(GCKeyboard.coalescedKeyboard); | ||
| 233 | } | ||
| 234 | } | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | void SDL_QuitGCKeyboard(void) | ||
| 239 | { | ||
| 240 | @autoreleasepool { | ||
| 241 | if (@available(iOS 14.0, tvOS 14.0, *)) { | ||
| 242 | NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; | ||
| 243 | |||
| 244 | if (keyboard_connect_observer) { | ||
| 245 | [center removeObserver:keyboard_connect_observer name:GCKeyboardDidConnectNotification object:nil]; | ||
| 246 | keyboard_connect_observer = nil; | ||
| 247 | } | ||
| 248 | |||
| 249 | if (keyboard_disconnect_observer) { | ||
| 250 | [center removeObserver:keyboard_disconnect_observer name:GCKeyboardDidDisconnectNotification object:nil]; | ||
| 251 | keyboard_disconnect_observer = nil; | ||
| 252 | } | ||
| 253 | |||
| 254 | if (GCKeyboard.coalescedKeyboard != nil) { | ||
| 255 | OnGCKeyboardDisconnected(GCKeyboard.coalescedKeyboard); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | static id mouse_connect_observer = nil; | ||
| 262 | static id mouse_disconnect_observer = nil; | ||
| 263 | static bool mouse_relative_mode = false; | ||
| 264 | static SDL_MouseWheelDirection mouse_scroll_direction = SDL_MOUSEWHEEL_NORMAL; | ||
| 265 | |||
| 266 | static void UpdateScrollDirection(void) | ||
| 267 | { | ||
| 268 | #if 0 // This code doesn't work for some reason | ||
| 269 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; | ||
| 270 | if ([userDefaults boolForKey:@"com.apple.swipescrolldirection"]) { | ||
| 271 | mouse_scroll_direction = SDL_MOUSEWHEEL_FLIPPED; | ||
| 272 | } else { | ||
| 273 | mouse_scroll_direction = SDL_MOUSEWHEEL_NORMAL; | ||
| 274 | } | ||
| 275 | #else | ||
| 276 | Boolean keyExistsAndHasValidFormat = NO; | ||
| 277 | Boolean naturalScrollDirection = CFPreferencesGetAppBooleanValue(CFSTR("com.apple.swipescrolldirection"), kCFPreferencesAnyApplication, &keyExistsAndHasValidFormat); | ||
| 278 | if (!keyExistsAndHasValidFormat) { | ||
| 279 | // Couldn't read the preference, assume natural scrolling direction | ||
| 280 | naturalScrollDirection = YES; | ||
| 281 | } | ||
| 282 | if (naturalScrollDirection) { | ||
| 283 | mouse_scroll_direction = SDL_MOUSEWHEEL_FLIPPED; | ||
| 284 | } else { | ||
| 285 | mouse_scroll_direction = SDL_MOUSEWHEEL_NORMAL; | ||
| 286 | } | ||
| 287 | #endif | ||
| 288 | } | ||
| 289 | |||
| 290 | static void UpdatePointerLock(void) | ||
| 291 | { | ||
| 292 | SDL_VideoDevice *_this = SDL_GetVideoDevice(); | ||
| 293 | SDL_Window *window; | ||
| 294 | |||
| 295 | for (window = _this->windows; window != NULL; window = window->next) { | ||
| 296 | UIKit_UpdatePointerLock(_this, window); | ||
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | static bool SetGCMouseRelativeMode(bool enabled) | ||
| 301 | { | ||
| 302 | mouse_relative_mode = enabled; | ||
| 303 | UpdatePointerLock(); | ||
| 304 | return true; | ||
| 305 | } | ||
| 306 | |||
| 307 | static void OnGCMouseButtonChanged(SDL_MouseID mouseID, Uint8 button, BOOL pressed) | ||
| 308 | { | ||
| 309 | Uint64 timestamp = SDL_GetTicksNS(); | ||
| 310 | SDL_SendMouseButton(timestamp, SDL_GetMouseFocus(), mouseID, button, pressed); | ||
| 311 | } | ||
| 312 | |||
| 313 | static void OnGCMouseConnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) | ||
| 314 | { | ||
| 315 | SDL_MouseID mouseID = (SDL_MouseID)(uintptr_t)mouse; | ||
| 316 | |||
| 317 | SDL_AddMouse(mouseID, NULL, true); | ||
| 318 | |||
| 319 | mouse.mouseInput.leftButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { | ||
| 320 | OnGCMouseButtonChanged(mouseID, SDL_BUTTON_LEFT, pressed); | ||
| 321 | }; | ||
| 322 | mouse.mouseInput.middleButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { | ||
| 323 | OnGCMouseButtonChanged(mouseID, SDL_BUTTON_MIDDLE, pressed); | ||
| 324 | }; | ||
| 325 | mouse.mouseInput.rightButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { | ||
| 326 | OnGCMouseButtonChanged(mouseID, SDL_BUTTON_RIGHT, pressed); | ||
| 327 | }; | ||
| 328 | |||
| 329 | int auxiliary_button = SDL_BUTTON_X1; | ||
| 330 | for (GCControllerButtonInput *btn in mouse.mouseInput.auxiliaryButtons) { | ||
| 331 | btn.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) { | ||
| 332 | OnGCMouseButtonChanged(mouseID, auxiliary_button, pressed); | ||
| 333 | }; | ||
| 334 | ++auxiliary_button; | ||
| 335 | } | ||
| 336 | |||
| 337 | mouse.mouseInput.mouseMovedHandler = ^(GCMouseInput *mouseInput, float deltaX, float deltaY) { | ||
| 338 | Uint64 timestamp = SDL_GetTicksNS(); | ||
| 339 | |||
| 340 | if (SDL_GCMouseRelativeMode()) { | ||
| 341 | SDL_SendMouseMotion(timestamp, SDL_GetMouseFocus(), mouseID, true, deltaX, -deltaY); | ||
| 342 | } | ||
| 343 | }; | ||
| 344 | |||
| 345 | mouse.mouseInput.scroll.valueChangedHandler = ^(GCControllerDirectionPad *dpad, float xValue, float yValue) { | ||
| 346 | Uint64 timestamp = SDL_GetTicksNS(); | ||
| 347 | |||
| 348 | /* Raw scroll values come in here, vertical values in the first axis, horizontal values in the second axis. | ||
| 349 | * The vertical values are negative moving the mouse wheel up and positive moving it down. | ||
| 350 | * The horizontal values are negative moving the mouse wheel left and positive moving it right. | ||
| 351 | * The vertical values are inverted compared to SDL, and the horizontal values are as expected. | ||
| 352 | */ | ||
| 353 | float vertical = -xValue; | ||
| 354 | float horizontal = yValue; | ||
| 355 | |||
| 356 | if (mouse_scroll_direction == SDL_MOUSEWHEEL_FLIPPED) { | ||
| 357 | // Since these are raw values, we need to flip them ourselves | ||
| 358 | vertical = -vertical; | ||
| 359 | horizontal = -horizontal; | ||
| 360 | } | ||
| 361 | SDL_SendMouseWheel(timestamp, SDL_GetMouseFocus(), mouseID, horizontal, vertical, mouse_scroll_direction); | ||
| 362 | }; | ||
| 363 | UpdateScrollDirection(); | ||
| 364 | |||
| 365 | dispatch_queue_t queue = dispatch_queue_create("org.libsdl.input.mouse", DISPATCH_QUEUE_SERIAL); | ||
| 366 | dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)); | ||
| 367 | mouse.handlerQueue = queue; | ||
| 368 | |||
| 369 | UpdatePointerLock(); | ||
| 370 | } | ||
| 371 | |||
| 372 | static void OnGCMouseDisconnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) | ||
| 373 | { | ||
| 374 | SDL_MouseID mouseID = (SDL_MouseID)(uintptr_t)mouse; | ||
| 375 | |||
| 376 | mouse.mouseInput.mouseMovedHandler = nil; | ||
| 377 | |||
| 378 | mouse.mouseInput.leftButton.pressedChangedHandler = nil; | ||
| 379 | mouse.mouseInput.middleButton.pressedChangedHandler = nil; | ||
| 380 | mouse.mouseInput.rightButton.pressedChangedHandler = nil; | ||
| 381 | |||
| 382 | for (GCControllerButtonInput *button in mouse.mouseInput.auxiliaryButtons) { | ||
| 383 | button.pressedChangedHandler = nil; | ||
| 384 | } | ||
| 385 | |||
| 386 | UpdatePointerLock(); | ||
| 387 | |||
| 388 | SDL_RemoveMouse(mouseID, true); | ||
| 389 | } | ||
| 390 | |||
| 391 | void SDL_InitGCMouse(void) | ||
| 392 | { | ||
| 393 | @autoreleasepool { | ||
| 394 | // There is a bug where mouse accumulates duplicate deltas over time in iOS 14.0 | ||
| 395 | if (@available(iOS 14.1, tvOS 14.1, *)) { | ||
| 396 | /* iOS will not send the new pointer touch events if you don't have this key, | ||
| 397 | * and we need them to differentiate between mouse events and real touch events. | ||
| 398 | */ | ||
| 399 | BOOL indirect_input_available = [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"UIApplicationSupportsIndirectInputEvents"] boolValue]; | ||
| 400 | if (indirect_input_available) { | ||
| 401 | NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; | ||
| 402 | |||
| 403 | mouse_connect_observer = [center addObserverForName:GCMouseDidConnectNotification | ||
| 404 | object:nil | ||
| 405 | queue:nil | ||
| 406 | usingBlock:^(NSNotification *note) { | ||
| 407 | GCMouse *mouse = note.object; | ||
| 408 | OnGCMouseConnected(mouse); | ||
| 409 | }]; | ||
| 410 | |||
| 411 | mouse_disconnect_observer = [center addObserverForName:GCMouseDidDisconnectNotification | ||
| 412 | object:nil | ||
| 413 | queue:nil | ||
| 414 | usingBlock:^(NSNotification *note) { | ||
| 415 | GCMouse *mouse = note.object; | ||
| 416 | OnGCMouseDisconnected(mouse); | ||
| 417 | }]; | ||
| 418 | |||
| 419 | for (GCMouse *mouse in [GCMouse mice]) { | ||
| 420 | OnGCMouseConnected(mouse); | ||
| 421 | } | ||
| 422 | |||
| 423 | SDL_GetMouse()->SetRelativeMouseMode = SetGCMouseRelativeMode; | ||
| 424 | } else { | ||
| 425 | NSLog(@"You need UIApplicationSupportsIndirectInputEvents in your Info.plist for mouse support"); | ||
| 426 | } | ||
| 427 | } | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | bool SDL_GCMouseRelativeMode(void) | ||
| 432 | { | ||
| 433 | return mouse_relative_mode; | ||
| 434 | } | ||
| 435 | |||
| 436 | void SDL_QuitGCMouse(void) | ||
| 437 | { | ||
| 438 | @autoreleasepool { | ||
| 439 | if (@available(iOS 14.1, tvOS 14.1, *)) { | ||
| 440 | NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; | ||
| 441 | |||
| 442 | if (mouse_connect_observer) { | ||
| 443 | [center removeObserver:mouse_connect_observer name:GCMouseDidConnectNotification object:nil]; | ||
| 444 | mouse_connect_observer = nil; | ||
| 445 | } | ||
| 446 | |||
| 447 | if (mouse_disconnect_observer) { | ||
| 448 | [center removeObserver:mouse_disconnect_observer name:GCMouseDidDisconnectNotification object:nil]; | ||
| 449 | mouse_disconnect_observer = nil; | ||
| 450 | } | ||
| 451 | |||
| 452 | for (GCMouse *mouse in [GCMouse mice]) { | ||
| 453 | OnGCMouseDisconnected(mouse); | ||
| 454 | } | ||
| 455 | |||
| 456 | SDL_GetMouse()->SetRelativeMouseMode = NULL; | ||
| 457 | } | ||
| 458 | } | ||
| 459 | } | ||
| 460 | |||
| 461 | #endif // SDL_VIDEO_DRIVER_UIKIT | ||
