uikit: Initial Apple Pencil support.

Reference Issue #9911.
Reference Issue #10516.
This commit is contained in:
Salman Alshamrani 2024-12-17 08:59:49 -05:00 committed by Ryan C. Gordon
parent 5acd7fe208
commit 774e38d073
4 changed files with 320 additions and 77 deletions

View File

@ -0,0 +1,37 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
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_

View File

@ -0,0 +1,93 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
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

View File

@ -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;

View File

@ -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 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick;
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 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick;
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 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick;
#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 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick;
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 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick;
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 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick;
{
for (UITouch *touch in touches) {
BOOL handled = NO;
#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;
#if defined(__IPHONE_13_0)
if (@available(iOS 13.0, *)) {
if (touch.type == UITouchTypePencil) {
[self pencilReleased:touch];
continue;
}
}
#endif
if (!handled) {
SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
SDL_TouchID touchId = [self touchIdForType:touchType];
float pressure = [self pressureForTouch:touch];
if (SDL_AddTouch(touchId, touchType, "") < 0) {
#if !defined(SDL_PLATFORM_TVOS) && defined(__IPHONE_13_4)
if (@available(iOS 13.4, *)) {
if (touch.type == UITouchTypeIndirectPointer) {
[self indirectPointerReleased:touch fromEvent:event];
continue;
}
// 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);
}
#endif
SDL_TouchDeviceType touchType = [self touchTypeForTouch:touch];
SDL_TouchID touchId = [self touchIdForType:touchType];
float pressure = [self pressureForTouch:touch];
if (SDL_AddTouch(touchId, touchType, "") < 0) {
continue;
}
// 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);
}
}
@ -332,29 +431,36 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick;
{
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);
}
}