Simple Directmedia Layer

uikit: Initial Apple Pencil support.

Reference Issue #9911.
Reference Issue #10516.

authored by

Salman Alshamrani and committed by
Ryan C. Gordon
774e38d0 5acd7fe2

+317 -74
+37
src/video/uikit/SDL_uikitpen.h
··· 1 + /* 2 + Simple DirectMedia Layer 3 + Copyright (C) 1997-2024 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 + 22 + #ifndef SDL_uikitpen_h_ 23 + #define SDL_uikitpen_h_ 24 + 25 + #include "SDL_uikitvideo.h" 26 + #include "SDL_uikitwindow.h" 27 + 28 + extern bool UIKit_InitPen(SDL_VideoDevice *_this); 29 + extern void UIKit_HandlePenEnter(); 30 + extern void UIKit_HandlePenLeave(); 31 + extern void UIKit_HandlePenHover(SDL_uikitview *view, CGPoint point); 32 + extern void UIKit_HandlePenMotion(SDL_uikitview *view, UITouch *pencil); 33 + extern void UIKit_HandlePenPress(SDL_uikitview *view, UITouch *pencil); 34 + extern void UIKit_HandlePenRelease(SDL_uikitview *view, UITouch *pencil); 35 + extern void UIKit_QuitPen(SDL_VideoDevice *_this); 36 + 37 + #endif // SDL_uikitpen_h_
+93
src/video/uikit/SDL_uikitpen.m
··· 1 + /* 2 + Simple DirectMedia Layer 3 + Copyright (C) 1997-2024 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 "SDL_uikitevents.h" 26 + #include "SDL_uikitpen.h" 27 + #include "SDL_uikitwindow.h" 28 + 29 + #include "../../events/SDL_pen_c.h" 30 + 31 + SDL_PenID penId; 32 + 33 + typedef struct UIKit_PenHandle 34 + { 35 + SDL_PenID pen; 36 + } UIKit_PenHandle; 37 + 38 + bool UIKit_InitPen(SDL_VideoDevice *_this) 39 + { 40 + return true; 41 + } 42 + 43 + void UIKit_HandlePenEnter() 44 + { 45 + SDL_PenInfo penInfo; 46 + SDL_zero(penInfo); 47 + penInfo.capabilities = SDL_PEN_CAPABILITY_PRESSURE | SDL_PEN_CAPABILITY_ROTATION | SDL_PEN_CAPABILITY_XTILT | SDL_PEN_CAPABILITY_YTILT | SDL_PEN_CAPABILITY_TANGENTIAL_PRESSURE; 48 + penInfo.max_tilt = 90.0f; 49 + penInfo.num_buttons = 0; 50 + penInfo.subtype = SDL_PEN_TYPE_PENCIL; 51 + 52 + // probably make this better 53 + penId = SDL_AddPenDevice(0, [@"Apple Pencil" UTF8String], &penInfo, calloc(1, sizeof(UIKit_PenHandle))); 54 + } 55 + 56 + void UIKit_HandlePenHover(SDL_uikitview *view, CGPoint point) 57 + { 58 + SDL_SendPenMotion(0, penId, [view getSDLWindow], point.x, point.y); 59 + } 60 + 61 + void UIKit_HandlePenMotion(SDL_uikitview *view, UITouch *pencil) 62 + { 63 + CGPoint point = [pencil locationInView:view]; 64 + SDL_SendPenMotion(UIKit_GetEventTimestamp([pencil timestamp]), penId, [view getSDLWindow], point.x, point.y); 65 + SDL_SendPenAxis(UIKit_GetEventTimestamp([pencil timestamp]), penId, [view getSDLWindow], SDL_PEN_AXIS_PRESSURE, [pencil force] / [pencil maximumPossibleForce]); 66 + NSLog(@"ALTITUDE: %f", [pencil altitudeAngle]); 67 + NSLog(@"AZIMUTH VECTOR: %@", [NSValue valueWithCGVector: [pencil azimuthUnitVectorInView:view]]); 68 + NSLog(@"AZIMUTH ANGLE: %f", [pencil azimuthAngleInView:view]); 69 + // hold it 70 + // SDL_SendPenAxis(0, penId, [view getSDLWindow], SDL_PEN_AXIS_XTILT, [pencil altitudeAngle] / M_PI); 71 + } 72 + 73 + void UIKit_HandlePenPress(SDL_uikitview *view, UITouch *pencil) 74 + { 75 + SDL_SendPenTouch(UIKit_GetEventTimestamp([pencil timestamp]), penId, [view getSDLWindow], false, true); 76 + } 77 + 78 + void UIKit_HandlePenRelease(SDL_uikitview *view, UITouch *pencil) 79 + { 80 + SDL_SendPenTouch(UIKit_GetEventTimestamp([pencil timestamp]), penId, [view getSDLWindow], false, false); 81 + } 82 + 83 + void UIKit_HandlePenLeave() 84 + { 85 + SDL_RemovePenDevice(0, penId); 86 + penId = 0; 87 + } 88 + 89 + void UIKit_QuitPen(SDL_VideoDevice *_this) 90 + { 91 + } 92 + 93 + #endif // SDL_VIDEO_DRIVER_UIKIT
+7
src/video/uikit/SDL_uikitview.h
··· 34 34 - (void)setSDLWindow:(SDL_Window *)window; 35 35 - (SDL_Window *)getSDLWindow; 36 36 37 + #if defined(__IPHONE_13_0) 38 + - (void)pencilHovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.0)); 39 + #endif 40 + 37 41 #if !defined(SDL_PLATFORM_TVOS) && defined(__IPHONE_13_4) 38 42 - (UIPointerRegion *)pointerInteraction:(UIPointerInteraction *)interaction regionForRequest:(UIPointerRegionRequest *)request defaultRegion:(UIPointerRegion *)defaultRegion API_AVAILABLE(ios(13.4)); 39 43 - (UIPointerStyle *)pointerInteraction:(UIPointerInteraction *)interaction styleForRegion:(UIPointerRegion *)region API_AVAILABLE(ios(13.4)); 44 + - (void)indirectPointerHovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.4)); 45 + - (void)updateIndirectPointerFromTouch:(UITouch *)touch; 46 + - (void)updateIndirectPointerButtonState:(UITouch *)touch fromEvent:(UIEvent *)event; 40 47 #endif 41 48 42 49 - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize;
+180 -74
src/video/uikit/SDL_uikitview.m
··· 31 31 #include "SDL_uikitappdelegate.h" 32 32 #include "SDL_uikitevents.h" 33 33 #include "SDL_uikitmodes.h" 34 + #include "SDL_uikitpen.h" 34 35 #include "SDL_uikitwindow.h" 35 36 36 37 // The maximum number of mouse buttons we support ··· 47 48 48 49 SDL_TouchID directTouchId; 49 50 SDL_TouchID indirectTouchId; 51 + 52 + #if defined(__IPHONE_13_4) 53 + UIPointerInteraction *indirectPointerInteraction API_AVAILABLE(ios(13.4)); 54 + #endif 50 55 } 51 56 52 57 - (instancetype)initWithFrame:(CGRect)frame ··· 81 86 self.multipleTouchEnabled = YES; 82 87 SDL_AddTouch(directTouchId, SDL_TOUCH_DEVICE_DIRECT, ""); 83 88 #endif 89 + 90 + #if defined(__IPHONE_13_0) 91 + if (@available(iOS 13.0, *)) { 92 + UIHoverGestureRecognizer *pencilRecognizer = [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(pencilHovering:)]; 93 + pencilRecognizer.allowedTouchTypes = @[@(UITouchTypePencil)]; 94 + [self addGestureRecognizer:pencilRecognizer]; 95 + } 96 + #endif 84 97 85 98 #if !defined(SDL_PLATFORM_TVOS) && defined(__IPHONE_13_4) 86 99 if (@available(iOS 13.4, *)) { 87 - [self addInteraction:[[UIPointerInteraction alloc] initWithDelegate:self]]; 100 + indirectPointerInteraction = [[UIPointerInteraction alloc] initWithDelegate:self]; 101 + [self addInteraction:indirectPointerInteraction]; 102 + 103 + UIHoverGestureRecognizer *indirectPointerRecognizer = [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(indirectPointerHovering:)]; 104 + indirectPointerRecognizer.allowedTouchTypes = @[@(UITouchTypeIndirectPointer)]; 105 + [self addGestureRecognizer:indirectPointerRecognizer]; 88 106 } 89 107 #endif 90 108 } ··· 156 174 #if !defined(SDL_PLATFORM_TVOS) && defined(__IPHONE_13_4) 157 175 - (UIPointerRegion *)pointerInteraction:(UIPointerInteraction *)interaction regionForRequest:(UIPointerRegionRequest *)request defaultRegion:(UIPointerRegion *)defaultRegion API_AVAILABLE(ios(13.4)) 158 176 { 159 - if (request != nil && !SDL_GCMouseRelativeMode()) { 160 - CGPoint origin = self.bounds.origin; 161 - CGPoint point = request.location; 162 - 163 - point.x -= origin.x; 164 - point.y -= origin.y; 165 - 166 - SDL_SendMouseMotion(0, sdlwindow, SDL_GLOBAL_MOUSE_ID, false, point.x, point.y); 167 - } 168 177 return [UIPointerRegion regionWithRect:self.bounds identifier:nil]; 169 178 } 170 179 ··· 176 185 return [UIPointerStyle hiddenPointerStyle]; 177 186 } 178 187 } 188 + 189 + - (void)indirectPointerHovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.4)) 190 + { 191 + switch (recognizer.state) { 192 + case UIGestureRecognizerStateBegan: 193 + case UIGestureRecognizerStateChanged: 194 + { 195 + CGPoint point = [recognizer locationInView:self]; 196 + SDL_SendMouseMotion(0, sdlwindow, SDL_GLOBAL_MOUSE_ID, false, point.x, point.y); 197 + break; 198 + } 199 + 200 + default: 201 + break; 202 + } 203 + } 204 + 205 + - (void)indirectPointerMoving:(UITouch *)touch API_AVAILABLE(ios(13.4)) 206 + { 207 + CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO]; 208 + SDL_SendMouseMotion(0, sdlwindow, SDL_GLOBAL_MOUSE_ID, false, locationInView.x, locationInView.y); 209 + } 210 + 211 + - (void)indirectPointerPressed:(UITouch *)touch fromEvent:(UIEvent *)event API_AVAILABLE(ios(13.4)) 212 + { 213 + if (!SDL_HasMouse()) { 214 + int i; 215 + 216 + for (i = 1; i <= MAX_MOUSE_BUTTONS; ++i) { 217 + if (event.buttonMask & SDL_BUTTON_MASK(i)) { 218 + Uint8 button; 219 + 220 + switch (i) { 221 + case 1: 222 + button = SDL_BUTTON_LEFT; 223 + break; 224 + case 2: 225 + button = SDL_BUTTON_RIGHT; 226 + break; 227 + case 3: 228 + button = SDL_BUTTON_MIDDLE; 229 + break; 230 + default: 231 + button = (Uint8)i; 232 + break; 233 + } 234 + SDL_SendMouseButton(UIKit_GetEventTimestamp([touch timestamp]), sdlwindow, SDL_GLOBAL_MOUSE_ID, button, true); 235 + } 236 + } 237 + } 238 + } 239 + 240 + - (void)indirectPointerReleased:(UITouch *)touch fromEvent:(UIEvent *)event API_AVAILABLE(ios(13.4)) 241 + { 242 + if (!SDL_HasMouse()) { 243 + int i; 244 + SDL_MouseButtonFlags buttons = SDL_GetMouseState(NULL, NULL); 245 + 246 + for (i = 0; i < MAX_MOUSE_BUTTONS; ++i) { 247 + if (buttons & SDL_BUTTON_MASK(i)) { 248 + SDL_SendMouseButton(UIKit_GetEventTimestamp([touch timestamp]), sdlwindow, SDL_GLOBAL_MOUSE_ID, (Uint8)i, false); 249 + } 250 + } 251 + } 252 + } 253 + 179 254 #endif // !defined(SDL_PLATFORM_TVOS) && __IPHONE_13_4 180 255 256 + #if defined(__IPHONE_13_0) 257 + 258 + - (void)pencilHovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.0)) 259 + { 260 + switch (recognizer.state) { 261 + case UIGestureRecognizerStateBegan: 262 + UIKit_HandlePenEnter(); 263 + UIKit_HandlePenHover(self, [recognizer locationInView:self]); 264 + break; 265 + 266 + case UIGestureRecognizerStateChanged: 267 + UIKit_HandlePenHover(self, [recognizer locationInView:self]); 268 + break; 269 + 270 + case UIGestureRecognizerStateEnded: 271 + case UIGestureRecognizerStateCancelled: 272 + UIKit_HandlePenLeave(); 273 + break; 274 + 275 + default: 276 + break; 277 + } 278 + } 279 + 280 + - (void)pencilMoving:(UITouch *)touch 281 + { 282 + UIKit_HandlePenMotion(self, touch); 283 + } 284 + 285 + - (void)pencilPressed:(UITouch *)touch 286 + { 287 + UIKit_HandlePenPress(self, touch); 288 + } 289 + 290 + - (void)pencilReleased:(UITouch *)touch 291 + { 292 + UIKit_HandlePenRelease(self, touch); 293 + } 294 + 295 + #endif // defined(__IPHONE_13_0) 296 + 181 297 - (SDL_TouchDeviceType)touchTypeForTouch:(UITouch *)touch 182 298 { 183 299 #ifdef __IPHONE_9_0 ··· 231 347 for (UITouch *touch in touches) { 232 348 BOOL handled = NO; 233 349 350 + #if defined(__IPHONE_13_0) 351 + if (@available(iOS 13.0, *)) { 352 + if (touch.type == UITouchTypePencil) { 353 + [self pencilPressed:touch]; 354 + continue; 355 + } 356 + } 357 + #endif 358 + 234 359 #if !defined(SDL_PLATFORM_TVOS) && defined(__IPHONE_13_4) 235 360 if (@available(iOS 13.4, *)) { 236 361 if (touch.type == UITouchTypeIndirectPointer) { 237 - if (!SDL_HasMouse()) { 238 - int i; 239 - 240 - for (i = 1; i <= MAX_MOUSE_BUTTONS; ++i) { 241 - if (event.buttonMask & SDL_BUTTON_MASK(i)) { 242 - Uint8 button; 243 - 244 - switch (i) { 245 - case 1: 246 - button = SDL_BUTTON_LEFT; 247 - break; 248 - case 2: 249 - button = SDL_BUTTON_RIGHT; 250 - break; 251 - case 3: 252 - button = SDL_BUTTON_MIDDLE; 253 - break; 254 - default: 255 - button = (Uint8)i; 256 - break; 257 - } 258 - SDL_SendMouseButton(UIKit_GetEventTimestamp([event timestamp]), sdlwindow, SDL_GLOBAL_MOUSE_ID, button, true); 259 - } 260 - } 261 - } 362 + [self indirectPointerPressed:touch fromEvent:event]; 262 363 handled = YES; 263 364 } 264 365 } ··· 286 387 { 287 388 for (UITouch *touch in touches) { 288 389 BOOL handled = NO; 289 - 390 + 391 + #if defined(__IPHONE_13_0) 392 + if (@available(iOS 13.0, *)) { 393 + if (touch.type == UITouchTypePencil) { 394 + [self pencilReleased:touch]; 395 + continue; 396 + } 397 + } 398 + #endif 399 + 290 400 #if !defined(SDL_PLATFORM_TVOS) && defined(__IPHONE_13_4) 291 401 if (@available(iOS 13.4, *)) { 292 402 if (touch.type == UITouchTypeIndirectPointer) { 293 - if (!SDL_HasMouse()) { 294 - int i; 295 - SDL_MouseButtonFlags buttons = SDL_GetMouseState(NULL, NULL); 296 - 297 - for (i = 0; i < MAX_MOUSE_BUTTONS; ++i) { 298 - if (buttons & SDL_BUTTON_MASK(i)) { 299 - SDL_SendMouseButton(UIKit_GetEventTimestamp([event timestamp]), sdlwindow, SDL_GLOBAL_MOUSE_ID, (Uint8)i, false); 300 - } 301 - } 302 - } 303 - handled = YES; 403 + [self indirectPointerReleased:touch fromEvent:event]; 404 + continue; 304 405 } 305 406 } 306 407 #endif 307 - if (!handled) { 308 - SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch]; 309 - SDL_TouchID touchId = [self touchIdForType:touchType]; 310 - float pressure = [self pressureForTouch:touch]; 408 + SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch]; 409 + SDL_TouchID touchId = [self touchIdForType:touchType]; 410 + float pressure = [self pressureForTouch:touch]; 311 411 312 - if (SDL_AddTouch(touchId, touchType, "") < 0) { 313 - continue; 314 - } 412 + if (SDL_AddTouch(touchId, touchType, "") < 0) { 413 + continue; 414 + } 315 415 316 - // FIXME, need to send: int clicks = (int) touch.tapCount; ? 416 + // FIXME, need to send: int clicks = (int) touch.tapCount; ? 317 417 318 - CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; 319 - SDL_SendTouch(UIKit_GetEventTimestamp([event timestamp]), 320 - touchId, (SDL_FingerID)(uintptr_t)touch, sdlwindow, 321 - false, locationInView.x, locationInView.y, pressure); 322 - } 418 + CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; 419 + SDL_SendTouch(UIKit_GetEventTimestamp([event timestamp]), 420 + touchId, (SDL_FingerID)(uintptr_t)touch, sdlwindow, 421 + false, locationInView.x, locationInView.y, pressure); 323 422 } 324 423 } 325 424 ··· 332 431 { 333 432 for (UITouch *touch in touches) { 334 433 BOOL handled = NO; 434 + 435 + #if defined(__IPHONE_13_0) 436 + if (@available(iOS 13.0, *)) { 437 + if (touch.type == UITouchTypePencil) { 438 + [self pencilMoving:touch]; 439 + continue; 440 + } 441 + } 442 + #endif 335 443 336 444 #if !defined(SDL_PLATFORM_TVOS) && defined(__IPHONE_13_4) 337 445 if (@available(iOS 13.4, *)) { 338 446 if (touch.type == UITouchTypeIndirectPointer) { 339 - // Already handled in pointerInteraction callback 340 - handled = YES; 447 + [self indirectPointerMoving:touch]; 448 + continue; 341 449 } 342 450 } 343 451 #endif 344 - if (!handled) { 345 - SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch]; 346 - SDL_TouchID touchId = [self touchIdForType:touchType]; 347 - float pressure = [self pressureForTouch:touch]; 452 + SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch]; 453 + SDL_TouchID touchId = [self touchIdForType:touchType]; 454 + float pressure = [self pressureForTouch:touch]; 348 455 349 - if (SDL_AddTouch(touchId, touchType, "") < 0) { 350 - continue; 351 - } 456 + if (SDL_AddTouch(touchId, touchType, "") < 0) { 457 + continue; 458 + } 352 459 353 - CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; 354 - SDL_SendTouchMotion(UIKit_GetEventTimestamp([event timestamp]), 355 - touchId, (SDL_FingerID)(uintptr_t)touch, sdlwindow, 356 - locationInView.x, locationInView.y, pressure); 357 - } 460 + CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; 461 + SDL_SendTouchMotion(UIKit_GetEventTimestamp([event timestamp]), 462 + touchId, (SDL_FingerID)(uintptr_t)touch, sdlwindow, 463 + locationInView.x, locationInView.y, pressure); 358 464 } 359 465 } 360 466