SDL_uikitevents.m (11355B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 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 #if SDL_VIDEO_DRIVER_UIKIT 24 25 #include "../../events/SDL_events_c.h" 26 27 #include "SDL_uikitvideo.h" 28 #include "SDL_uikitevents.h" 29 #include "SDL_uikitopengles.h" 30 31 #import <Foundation/Foundation.h> 32 33 #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 140000) || (__APPLETV_OS_VERSION_MAX_ALLOWED >= 140000) || (__MAC_OS_VERSION_MAX_ALLOWED > 1500000) 34 #import <GameController/GameController.h> 35 36 #define ENABLE_GCKEYBOARD 37 #define ENABLE_GCMOUSE 38 #endif 39 40 static BOOL UIKit_EventPumpEnabled = YES; 41 42 void 43 SDL_iPhoneSetEventPump(SDL_bool enabled) 44 { 45 UIKit_EventPumpEnabled = enabled; 46 } 47 48 void 49 UIKit_PumpEvents(_THIS) 50 { 51 if (!UIKit_EventPumpEnabled) { 52 return; 53 } 54 55 /* Let the run loop run for a short amount of time: long enough for 56 touch events to get processed (which is important to get certain 57 elements of Game Center's GKLeaderboardViewController to respond 58 to touch input), but not long enough to introduce a significant 59 delay in the rest of the app. 60 */ 61 const CFTimeInterval seconds = 0.000002; 62 63 /* Pump most event types. */ 64 SInt32 result; 65 do { 66 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, TRUE); 67 } while (result == kCFRunLoopRunHandledSource); 68 69 /* Make sure UIScrollView objects scroll properly. */ 70 do { 71 result = CFRunLoopRunInMode((CFStringRef)UITrackingRunLoopMode, seconds, TRUE); 72 } while(result == kCFRunLoopRunHandledSource); 73 74 /* See the comment in the function definition. */ 75 #if SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2 76 UIKit_GL_RestoreCurrentContext(); 77 #endif 78 } 79 80 #ifdef ENABLE_GCKEYBOARD 81 82 static SDL_bool keyboard_connected = SDL_FALSE; 83 static id keyboard_connect_observer = nil; 84 static id keyboard_disconnect_observer = nil; 85 86 static void OnGCKeyboardConnected(GCKeyboard *keyboard) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) 87 { 88 keyboard_connected = SDL_TRUE; 89 keyboard.keyboardInput.keyChangedHandler = ^(GCKeyboardInput *keyboard, GCControllerButtonInput *key, GCKeyCode keyCode, BOOL pressed) 90 { 91 SDL_SendKeyboardKey(pressed ? SDL_PRESSED : SDL_RELEASED, (SDL_Scancode)keyCode); 92 }; 93 94 dispatch_queue_t queue = dispatch_queue_create( "org.libsdl.input.keyboard", DISPATCH_QUEUE_SERIAL ); 95 dispatch_set_target_queue( queue, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 ) ); 96 keyboard.handlerQueue = queue; 97 } 98 99 static void OnGCKeyboardDisconnected(GCKeyboard *keyboard) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) 100 { 101 keyboard.keyboardInput.keyChangedHandler = nil; 102 keyboard_connected = SDL_FALSE; 103 } 104 105 void SDL_InitGCKeyboard(void) 106 { 107 @autoreleasepool { 108 if (@available(iOS 14.0, tvOS 14.0, *)) { 109 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 110 111 keyboard_connect_observer = [center addObserverForName:GCKeyboardDidConnectNotification 112 object:nil 113 queue:nil 114 usingBlock:^(NSNotification *note) { 115 GCKeyboard *keyboard = note.object; 116 OnGCKeyboardConnected(keyboard); 117 }]; 118 119 keyboard_disconnect_observer = [center addObserverForName:GCKeyboardDidDisconnectNotification 120 object:nil 121 queue:nil 122 usingBlock:^(NSNotification *note) { 123 GCKeyboard *keyboard = note.object; 124 OnGCKeyboardDisconnected(keyboard); 125 }]; 126 127 if (GCKeyboard.coalescedKeyboard != nil) { 128 OnGCKeyboardConnected(GCKeyboard.coalescedKeyboard); 129 } 130 } 131 } 132 } 133 134 SDL_bool SDL_HasGCKeyboard(void) 135 { 136 return keyboard_connected; 137 } 138 139 void SDL_QuitGCKeyboard(void) 140 { 141 @autoreleasepool { 142 if (@available(iOS 14.0, tvOS 14.0, *)) { 143 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 144 145 if (keyboard_connect_observer) { 146 [center removeObserver:keyboard_connect_observer name:GCKeyboardDidConnectNotification object:nil]; 147 keyboard_connect_observer = nil; 148 } 149 150 if (keyboard_disconnect_observer) { 151 [center removeObserver:keyboard_disconnect_observer name:GCKeyboardDidDisconnectNotification object:nil]; 152 keyboard_disconnect_observer = nil; 153 } 154 155 if (GCKeyboard.coalescedKeyboard != nil) { 156 OnGCKeyboardDisconnected(GCKeyboard.coalescedKeyboard); 157 } 158 } 159 } 160 } 161 162 #else 163 164 void SDL_InitGCKeyboard(void) 165 { 166 } 167 168 SDL_bool SDL_HasGCKeyboard(void) 169 { 170 return SDL_FALSE; 171 } 172 173 void SDL_QuitGCKeyboard(void) 174 { 175 } 176 177 #endif /* ENABLE_GCKEYBOARD */ 178 179 180 #ifdef ENABLE_GCMOUSE 181 182 static int mice_connected = 0; 183 static id mouse_connect_observer = nil; 184 static id mouse_disconnect_observer = nil; 185 186 static int SetGCMouseRelativeMode(SDL_bool enabled) 187 { 188 /* We'll always send relative motion and we can't warp, so nothing to do here */ 189 return 0; 190 } 191 192 static void OnGCMouseButtonChanged(SDL_MouseID mouseID, Uint8 button, BOOL pressed) 193 { 194 SDL_SendMouseButton(SDL_GetMouseFocus(), mouseID, pressed ? SDL_PRESSED : SDL_RELEASED, button); 195 } 196 197 static void OnGCMouseConnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) 198 { 199 SDL_MouseID mouseID = mice_connected; 200 201 mouse.mouseInput.leftButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) 202 { 203 OnGCMouseButtonChanged(mouseID, SDL_BUTTON_LEFT, pressed); 204 }; 205 mouse.mouseInput.middleButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) 206 { 207 OnGCMouseButtonChanged(mouseID, SDL_BUTTON_MIDDLE, pressed); 208 }; 209 mouse.mouseInput.rightButton.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) 210 { 211 OnGCMouseButtonChanged(mouseID, SDL_BUTTON_RIGHT, pressed); 212 }; 213 214 int auxiliary_button = SDL_BUTTON_X1; 215 for (GCControllerButtonInput *button in mouse.mouseInput.auxiliaryButtons) { 216 button.pressedChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) 217 { 218 OnGCMouseButtonChanged(mouseID, auxiliary_button, pressed); 219 }; 220 ++auxiliary_button; 221 } 222 223 mouse.mouseInput.mouseMovedHandler = ^(GCMouseInput *mouse, float deltaX, float deltaY) 224 { 225 SDL_SendMouseMotion(SDL_GetMouseFocus(), mouseID, SDL_TRUE, (int)deltaX, -(int)deltaY); 226 }; 227 228 dispatch_queue_t queue = dispatch_queue_create( "org.libsdl.input.mouse", DISPATCH_QUEUE_SERIAL ); 229 dispatch_set_target_queue( queue, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 ) ); 230 mouse.handlerQueue = queue; 231 232 ++mice_connected; 233 } 234 235 static void OnGCMouseDisconnected(GCMouse *mouse) API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) 236 { 237 --mice_connected; 238 239 mouse.mouseInput.mouseMovedHandler = nil; 240 241 mouse.mouseInput.leftButton.pressedChangedHandler = nil; 242 mouse.mouseInput.middleButton.pressedChangedHandler = nil; 243 mouse.mouseInput.rightButton.pressedChangedHandler = nil; 244 245 for (GCControllerButtonInput *button in mouse.mouseInput.auxiliaryButtons) { 246 button.pressedChangedHandler = nil; 247 } 248 } 249 250 void SDL_InitGCMouse(void) 251 { 252 @autoreleasepool { 253 /* There is a bug where mouse accumulates duplicate deltas over time in iOS 14.0 */ 254 if (@available(iOS 14.1, tvOS 14.1, *)) { 255 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 256 257 mouse_connect_observer = [center addObserverForName:GCMouseDidConnectNotification 258 object:nil 259 queue:nil 260 usingBlock:^(NSNotification *note) { 261 GCMouse *mouse = note.object; 262 OnGCMouseConnected(mouse); 263 }]; 264 265 mouse_disconnect_observer = [center addObserverForName:GCMouseDidDisconnectNotification 266 object:nil 267 queue:nil 268 usingBlock:^(NSNotification *note) { 269 GCMouse *mouse = note.object; 270 OnGCMouseDisconnected(mouse); 271 }]; 272 273 for (GCMouse *mouse in [GCMouse mice]) { 274 OnGCMouseConnected(mouse); 275 } 276 277 SDL_GetMouse()->SetRelativeMouseMode = SetGCMouseRelativeMode; 278 } 279 } 280 } 281 282 SDL_bool SDL_HasGCMouse(void) 283 { 284 return (mice_connected > 0); 285 } 286 287 void SDL_QuitGCMouse(void) 288 { 289 @autoreleasepool { 290 if (@available(iOS 14.1, tvOS 14.1, *)) { 291 NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; 292 293 if (mouse_connect_observer) { 294 [center removeObserver:mouse_connect_observer name:GCMouseDidConnectNotification object:nil]; 295 mouse_connect_observer = nil; 296 } 297 298 if (mouse_disconnect_observer) { 299 [center removeObserver:mouse_disconnect_observer name:GCMouseDidDisconnectNotification object:nil]; 300 mouse_disconnect_observer = nil; 301 } 302 303 for (GCMouse *mouse in [GCMouse mice]) { 304 OnGCMouseDisconnected(mouse); 305 } 306 307 SDL_GetMouse()->SetRelativeMouseMode = NULL; 308 } 309 } 310 } 311 312 #else 313 314 void SDL_InitGCMouse(void) 315 { 316 } 317 318 SDL_bool SDL_HasGCMouse(void) 319 { 320 return SDL_FALSE; 321 } 322 323 void SDL_QuitGCMouse(void) 324 { 325 } 326 327 #endif /* ENABLE_GCMOUSE */ 328 329 #endif /* SDL_VIDEO_DRIVER_UIKIT */ 330 331 /* vi: set ts=4 sw=4 expandtab: */