diff --git a/src/video/uikit/SDL_uikitpen.h b/src/video/uikit/SDL_uikitpen.h new file mode 100644 index 0000000000000..c63df2c2857b6 --- /dev/null +++ b/src/video/uikit/SDL_uikitpen.h @@ -0,0 +1,37 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL_uikitpen_h_ +#define SDL_uikitpen_h_ + +#include "SDL_uikitvideo.h" +#include "SDL_uikitwindow.h" + +extern bool UIKit_InitPen(SDL_VideoDevice *_this); +extern void UIKit_HandlePenEnter(); +extern void UIKit_HandlePenLeave(); +extern void UIKit_HandlePenHover(SDL_uikitview *view, CGPoint point); +extern void UIKit_HandlePenMotion(SDL_uikitview *view, UITouch *pencil); +extern void UIKit_HandlePenPress(SDL_uikitview *view, UITouch *pencil); +extern void UIKit_HandlePenRelease(SDL_uikitview *view, UITouch *pencil); +extern void UIKit_QuitPen(SDL_VideoDevice *_this); + +#endif // SDL_uikitpen_h_ diff --git a/src/video/uikit/SDL_uikitpen.m b/src/video/uikit/SDL_uikitpen.m new file mode 100644 index 0000000000000..bfa62ebc00679 --- /dev/null +++ b/src/video/uikit/SDL_uikitpen.m @@ -0,0 +1,93 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_VIDEO_DRIVER_UIKIT + +#include "SDL_uikitevents.h" +#include "SDL_uikitpen.h" +#include "SDL_uikitwindow.h" + +#include "../../events/SDL_pen_c.h" + +SDL_PenID penId; + +typedef struct UIKit_PenHandle +{ + SDL_PenID pen; +} UIKit_PenHandle; + +bool UIKit_InitPen(SDL_VideoDevice *_this) +{ + return true; +} + +void UIKit_HandlePenEnter() +{ + SDL_PenInfo penInfo; + SDL_zero(penInfo); + penInfo.capabilities = SDL_PEN_CAPABILITY_PRESSURE | SDL_PEN_CAPABILITY_ROTATION | SDL_PEN_CAPABILITY_XTILT | SDL_PEN_CAPABILITY_YTILT | SDL_PEN_CAPABILITY_TANGENTIAL_PRESSURE; + penInfo.max_tilt = 90.0f; + penInfo.num_buttons = 0; + penInfo.subtype = SDL_PEN_TYPE_PENCIL; + + // probably make this better + penId = SDL_AddPenDevice(0, [@"Apple Pencil" UTF8String], &penInfo, calloc(1, sizeof(UIKit_PenHandle))); +} + +void UIKit_HandlePenHover(SDL_uikitview *view, CGPoint point) +{ + SDL_SendPenMotion(0, penId, [view getSDLWindow], point.x, point.y); +} + +void UIKit_HandlePenMotion(SDL_uikitview *view, UITouch *pencil) +{ + CGPoint point = [pencil locationInView:view]; + SDL_SendPenMotion(UIKit_GetEventTimestamp([pencil timestamp]), penId, [view getSDLWindow], point.x, point.y); + SDL_SendPenAxis(UIKit_GetEventTimestamp([pencil timestamp]), penId, [view getSDLWindow], SDL_PEN_AXIS_PRESSURE, [pencil force] / [pencil maximumPossibleForce]); + NSLog(@"ALTITUDE: %f", [pencil altitudeAngle]); + NSLog(@"AZIMUTH VECTOR: %@", [NSValue valueWithCGVector: [pencil azimuthUnitVectorInView:view]]); + NSLog(@"AZIMUTH ANGLE: %f", [pencil azimuthAngleInView:view]); + // hold it + // SDL_SendPenAxis(0, penId, [view getSDLWindow], SDL_PEN_AXIS_XTILT, [pencil altitudeAngle] / M_PI); +} + +void UIKit_HandlePenPress(SDL_uikitview *view, UITouch *pencil) +{ + SDL_SendPenTouch(UIKit_GetEventTimestamp([pencil timestamp]), penId, [view getSDLWindow], false, true); +} + +void UIKit_HandlePenRelease(SDL_uikitview *view, UITouch *pencil) +{ + SDL_SendPenTouch(UIKit_GetEventTimestamp([pencil timestamp]), penId, [view getSDLWindow], false, false); +} + +void UIKit_HandlePenLeave() +{ + SDL_RemovePenDevice(0, penId); + penId = 0; +} + +void UIKit_QuitPen(SDL_VideoDevice *_this) +{ +} + +#endif // SDL_VIDEO_DRIVER_UIKIT diff --git a/src/video/uikit/SDL_uikitview.h b/src/video/uikit/SDL_uikitview.h index 6169ccfa022b4..5d2121428712c 100644 --- a/src/video/uikit/SDL_uikitview.h +++ b/src/video/uikit/SDL_uikitview.h @@ -34,9 +34,16 @@ - (void)setSDLWindow:(SDL_Window *)window; - (SDL_Window *)getSDLWindow; +#if defined(__IPHONE_13_0) +- (void)pencilHovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.0)); +#endif + #if !defined(SDL_PLATFORM_TVOS) && defined(__IPHONE_13_4) - (UIPointerRegion *)pointerInteraction:(UIPointerInteraction *)interaction regionForRequest:(UIPointerRegionRequest *)request defaultRegion:(UIPointerRegion *)defaultRegion API_AVAILABLE(ios(13.4)); - (UIPointerStyle *)pointerInteraction:(UIPointerInteraction *)interaction styleForRegion:(UIPointerRegion *)region API_AVAILABLE(ios(13.4)); +- (void)indirectPointerHovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.4)); +- (void)updateIndirectPointerFromTouch:(UITouch *)touch; +- (void)updateIndirectPointerButtonState:(UITouch *)touch fromEvent:(UIEvent *)event; #endif - (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize; diff --git a/src/video/uikit/SDL_uikitview.m b/src/video/uikit/SDL_uikitview.m index 215f84712d835..99ebc774d63f0 100644 --- a/src/video/uikit/SDL_uikitview.m +++ b/src/video/uikit/SDL_uikitview.m @@ -31,6 +31,7 @@ #include "SDL_uikitappdelegate.h" #include "SDL_uikitevents.h" #include "SDL_uikitmodes.h" +#include "SDL_uikitpen.h" #include "SDL_uikitwindow.h" // The maximum number of mouse buttons we support @@ -47,6 +48,10 @@ @implementation SDL_uikitview SDL_TouchID directTouchId; SDL_TouchID indirectTouchId; + +#if defined(__IPHONE_13_4) + UIPointerInteraction *indirectPointerInteraction API_AVAILABLE(ios(13.4)); +#endif } - (instancetype)initWithFrame:(CGRect)frame @@ -81,10 +86,23 @@ - (instancetype)initWithFrame:(CGRect)frame self.multipleTouchEnabled = YES; SDL_AddTouch(directTouchId, SDL_TOUCH_DEVICE_DIRECT, ""); #endif + +#if defined(__IPHONE_13_0) + if (@available(iOS 13.0, *)) { + UIHoverGestureRecognizer *pencilRecognizer = [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(pencilHovering:)]; + pencilRecognizer.allowedTouchTypes = @[@(UITouchTypePencil)]; + [self addGestureRecognizer:pencilRecognizer]; + } +#endif #if !defined(SDL_PLATFORM_TVOS) && defined(__IPHONE_13_4) if (@available(iOS 13.4, *)) { - [self addInteraction:[[UIPointerInteraction alloc] initWithDelegate:self]]; + indirectPointerInteraction = [[UIPointerInteraction alloc] initWithDelegate:self]; + [self addInteraction:indirectPointerInteraction]; + + UIHoverGestureRecognizer *indirectPointerRecognizer = [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(indirectPointerHovering:)]; + indirectPointerRecognizer.allowedTouchTypes = @[@(UITouchTypeIndirectPointer)]; + [self addGestureRecognizer:indirectPointerRecognizer]; } #endif } @@ -156,15 +174,6 @@ - (SDL_Window *)getSDLWindow #if !defined(SDL_PLATFORM_TVOS) && defined(__IPHONE_13_4) - (UIPointerRegion *)pointerInteraction:(UIPointerInteraction *)interaction regionForRequest:(UIPointerRegionRequest *)request defaultRegion:(UIPointerRegion *)defaultRegion API_AVAILABLE(ios(13.4)) { - if (request != nil && !SDL_GCMouseRelativeMode()) { - CGPoint origin = self.bounds.origin; - CGPoint point = request.location; - - point.x -= origin.x; - point.y -= origin.y; - - SDL_SendMouseMotion(0, sdlwindow, SDL_GLOBAL_MOUSE_ID, false, point.x, point.y); - } return [UIPointerRegion regionWithRect:self.bounds identifier:nil]; } @@ -176,8 +185,115 @@ - (UIPointerStyle *)pointerInteraction:(UIPointerInteraction *)interaction style return [UIPointerStyle hiddenPointerStyle]; } } + +- (void)indirectPointerHovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.4)) +{ + switch (recognizer.state) { + case UIGestureRecognizerStateBegan: + case UIGestureRecognizerStateChanged: + { + CGPoint point = [recognizer locationInView:self]; + SDL_SendMouseMotion(0, sdlwindow, SDL_GLOBAL_MOUSE_ID, false, point.x, point.y); + break; + } + + default: + break; + } +} + +- (void)indirectPointerMoving:(UITouch *)touch API_AVAILABLE(ios(13.4)) +{ + CGPoint locationInView = [self touchLocation:touch shouldNormalize:NO]; + SDL_SendMouseMotion(0, sdlwindow, SDL_GLOBAL_MOUSE_ID, false, locationInView.x, locationInView.y); +} + +- (void)indirectPointerPressed:(UITouch *)touch fromEvent:(UIEvent *)event API_AVAILABLE(ios(13.4)) +{ + if (!SDL_HasMouse()) { + int i; + + for (i = 1; i <= MAX_MOUSE_BUTTONS; ++i) { + if (event.buttonMask & SDL_BUTTON_MASK(i)) { + Uint8 button; + + switch (i) { + case 1: + button = SDL_BUTTON_LEFT; + break; + case 2: + button = SDL_BUTTON_RIGHT; + break; + case 3: + button = SDL_BUTTON_MIDDLE; + break; + default: + button = (Uint8)i; + break; + } + SDL_SendMouseButton(UIKit_GetEventTimestamp([touch timestamp]), sdlwindow, SDL_GLOBAL_MOUSE_ID, button, true); + } + } + } +} + +- (void)indirectPointerReleased:(UITouch *)touch fromEvent:(UIEvent *)event API_AVAILABLE(ios(13.4)) +{ + if (!SDL_HasMouse()) { + int i; + SDL_MouseButtonFlags buttons = SDL_GetMouseState(NULL, NULL); + + for (i = 0; i < MAX_MOUSE_BUTTONS; ++i) { + if (buttons & SDL_BUTTON_MASK(i)) { + SDL_SendMouseButton(UIKit_GetEventTimestamp([touch timestamp]), sdlwindow, SDL_GLOBAL_MOUSE_ID, (Uint8)i, false); + } + } + } +} + #endif // !defined(SDL_PLATFORM_TVOS) && __IPHONE_13_4 +#if defined(__IPHONE_13_0) + +- (void)pencilHovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.0)) +{ + switch (recognizer.state) { + case UIGestureRecognizerStateBegan: + UIKit_HandlePenEnter(); + UIKit_HandlePenHover(self, [recognizer locationInView:self]); + break; + + case UIGestureRecognizerStateChanged: + UIKit_HandlePenHover(self, [recognizer locationInView:self]); + break; + + case UIGestureRecognizerStateEnded: + case UIGestureRecognizerStateCancelled: + UIKit_HandlePenLeave(); + break; + + default: + break; + } +} + +- (void)pencilMoving:(UITouch *)touch +{ + UIKit_HandlePenMotion(self, touch); +} + +- (void)pencilPressed:(UITouch *)touch +{ + UIKit_HandlePenPress(self, touch); +} + +- (void)pencilReleased:(UITouch *)touch +{ + UIKit_HandlePenRelease(self, touch); +} + +#endif // defined(__IPHONE_13_0) + - (SDL_TouchDeviceType)touchTypeForTouch:(UITouch *)touch { #ifdef __IPHONE_9_0 @@ -231,34 +347,19 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event for (UITouch *touch in touches) { BOOL handled = NO; +#if defined(__IPHONE_13_0) + if (@available(iOS 13.0, *)) { + if (touch.type == UITouchTypePencil) { + [self pencilPressed:touch]; + continue; + } + } +#endif + #if !defined(SDL_PLATFORM_TVOS) && defined(__IPHONE_13_4) if (@available(iOS 13.4, *)) { if (touch.type == UITouchTypeIndirectPointer) { - if (!SDL_HasMouse()) { - int i; - - for (i = 1; i <= MAX_MOUSE_BUTTONS; ++i) { - if (event.buttonMask & SDL_BUTTON_MASK(i)) { - Uint8 button; - - switch (i) { - case 1: - button = SDL_BUTTON_LEFT; - break; - case 2: - button = SDL_BUTTON_RIGHT; - break; - case 3: - button = SDL_BUTTON_MIDDLE; - break; - default: - button = (Uint8)i; - break; - } - SDL_SendMouseButton(UIKit_GetEventTimestamp([event timestamp]), sdlwindow, SDL_GLOBAL_MOUSE_ID, button, true); - } - } - } + [self indirectPointerPressed:touch fromEvent:event]; handled = YES; } } @@ -286,40 +387,38 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { for (UITouch *touch in touches) { BOOL handled = NO; - + +#if defined(__IPHONE_13_0) + if (@available(iOS 13.0, *)) { + if (touch.type == UITouchTypePencil) { + [self pencilReleased:touch]; + continue; + } + } +#endif + #if !defined(SDL_PLATFORM_TVOS) && defined(__IPHONE_13_4) if (@available(iOS 13.4, *)) { if (touch.type == UITouchTypeIndirectPointer) { - if (!SDL_HasMouse()) { - int i; - SDL_MouseButtonFlags buttons = SDL_GetMouseState(NULL, NULL); - - for (i = 0; i < MAX_MOUSE_BUTTONS; ++i) { - if (buttons & SDL_BUTTON_MASK(i)) { - SDL_SendMouseButton(UIKit_GetEventTimestamp([event timestamp]), sdlwindow, SDL_GLOBAL_MOUSE_ID, (Uint8)i, false); - } - } - } - handled = YES; + [self indirectPointerReleased:touch fromEvent:event]; + continue; } } #endif - if (!handled) { - SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch]; - SDL_TouchID touchId = [self touchIdForType:touchType]; - float pressure = [self pressureForTouch:touch]; + SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch]; + SDL_TouchID touchId = [self touchIdForType:touchType]; + float pressure = [self pressureForTouch:touch]; - if (SDL_AddTouch(touchId, touchType, "") < 0) { - continue; - } + if (SDL_AddTouch(touchId, touchType, "") < 0) { + continue; + } - // FIXME, need to send: int clicks = (int) touch.tapCount; ? + // FIXME, need to send: int clicks = (int) touch.tapCount; ? - CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; - SDL_SendTouch(UIKit_GetEventTimestamp([event timestamp]), - touchId, (SDL_FingerID)(uintptr_t)touch, sdlwindow, - false, locationInView.x, locationInView.y, pressure); - } + CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; + SDL_SendTouch(UIKit_GetEventTimestamp([event timestamp]), + touchId, (SDL_FingerID)(uintptr_t)touch, sdlwindow, + false, locationInView.x, locationInView.y, pressure); } } @@ -332,29 +431,36 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { for (UITouch *touch in touches) { BOOL handled = NO; + +#if defined(__IPHONE_13_0) + if (@available(iOS 13.0, *)) { + if (touch.type == UITouchTypePencil) { + [self pencilMoving:touch]; + continue; + } + } +#endif #if !defined(SDL_PLATFORM_TVOS) && defined(__IPHONE_13_4) if (@available(iOS 13.4, *)) { if (touch.type == UITouchTypeIndirectPointer) { - // Already handled in pointerInteraction callback - handled = YES; + [self indirectPointerMoving:touch]; + continue; } } #endif - if (!handled) { - SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch]; - SDL_TouchID touchId = [self touchIdForType:touchType]; - float pressure = [self pressureForTouch:touch]; + SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch]; + SDL_TouchID touchId = [self touchIdForType:touchType]; + float pressure = [self pressureForTouch:touch]; - if (SDL_AddTouch(touchId, touchType, "") < 0) { - continue; - } - - CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; - SDL_SendTouchMotion(UIKit_GetEventTimestamp([event timestamp]), - touchId, (SDL_FingerID)(uintptr_t)touch, sdlwindow, - locationInView.x, locationInView.y, pressure); + if (SDL_AddTouch(touchId, touchType, "") < 0) { + continue; } + + CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES]; + SDL_SendTouchMotion(UIKit_GetEventTimestamp([event timestamp]), + touchId, (SDL_FingerID)(uintptr_t)touch, sdlwindow, + locationInView.x, locationInView.y, pressure); } }