#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_UIKIT
#include "SDL_uikitview.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_touch_c.h"
#include "../../events/SDL_events_c.h"
#include "../../joystick/SDL_joystick_c.h"
#include "SDL_uikitappdelegate.h"
#include "SDL_uikitevents.h"
#include "SDL_uikitmodes.h"
#include "SDL_uikitpen.h"
#include "SDL_uikitwindow.h"
#define MAX_MOUSE_BUTTONS 5
extern int SDL_AppleTVRemoteOpenedAsJoystick;
@implementation SDL_uikitview
{
SDL_Window *sdlwindow;
SDL_TouchID directTouchId;
SDL_TouchID indirectTouchId;
float pinch_scale;
#if !defined(SDL_PLATFORM_TVOS)
UIPointerInteraction *indirectPointerInteraction API_AVAILABLE(ios(13.4));
#endif
}
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
#ifdef SDL_PLATFORM_TVOS
UISwipeGestureRecognizer *swipeUp = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
swipeUp.direction = UISwipeGestureRecognizerDirectionUp;
[self addGestureRecognizer:swipeUp];
UISwipeGestureRecognizer *swipeDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
swipeDown.direction = UISwipeGestureRecognizerDirectionDown;
[self addGestureRecognizer:swipeDown];
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self addGestureRecognizer:swipeLeft];
UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGesture:)];
swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
[self addGestureRecognizer:swipeRight];
#endif
#if !defined(SDL_PLATFORM_TVOS)
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(sdlPinchGesture:)];
pinchGesture.cancelsTouchesInView = NO;
pinchGesture.delaysTouchesBegan = NO;
pinchGesture.delaysTouchesEnded = NO;
[self addGestureRecognizer:pinchGesture];
#endif
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.autoresizesSubviews = YES;
directTouchId = 1;
indirectTouchId = 2;
#ifndef SDL_PLATFORM_TVOS
self.multipleTouchEnabled = YES;
SDL_AddTouch(directTouchId, SDL_TOUCH_DEVICE_DIRECT, "");
if (@available(iOS 13.0, *)) {
UIHoverGestureRecognizer *pencilRecognizer = [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(pencilHovering:)];
pencilRecognizer.allowedTouchTypes = @[@(UITouchTypePencil)];
[self addGestureRecognizer:pencilRecognizer];
}
if (@available(iOS 13.4, *)) {
indirectPointerInteraction = [[UIPointerInteraction alloc] initWithDelegate:self];
[self addInteraction:indirectPointerInteraction];
UIHoverGestureRecognizer *indirectPointerRecognizer = [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(indirectPointerHovering:)];
indirectPointerRecognizer.allowedTouchTypes = @[@(UITouchTypeIndirectPointer)];
[self addGestureRecognizer:indirectPointerRecognizer];
}
#endif }
return self;
}
- (void)setSDLWindow:(SDL_Window *)window
{
SDL_UIKitWindowData *data = nil;
if (window == sdlwindow) {
return;
}
if (sdlwindow) {
SDL_uikitview *view = nil;
data = (__bridge SDL_UIKitWindowData *)sdlwindow->internal;
[data.views removeObject:self];
[self removeFromSuperview];
view = data.views.lastObject;
data.viewcontroller.view = view;
data.uiwindow.rootViewController = nil;
data.uiwindow.rootViewController = data.viewcontroller;
[data.uiwindow layoutIfNeeded];
}
sdlwindow = window;
if (window) {
data = (__bridge SDL_UIKitWindowData *)window->internal;
[data.views addObject:self];
[data.viewcontroller.view removeFromSuperview];
data.viewcontroller.view = self;
data.uiwindow.rootViewController = nil;
data.uiwindow.rootViewController = data.viewcontroller;
[data.uiwindow layoutIfNeeded];
}
}
- (SDL_Window *)getSDLWindow
{
return sdlwindow;
}
#if !defined(SDL_PLATFORM_TVOS)
- (UIPointerRegion *)pointerInteraction:(UIPointerInteraction *)interaction regionForRequest:(UIPointerRegionRequest *)request defaultRegion:(UIPointerRegion *)defaultRegion API_AVAILABLE(ios(13.4))
{
return [UIPointerRegion regionWithRect:self.bounds identifier:nil];
}
- (UIPointerStyle *)pointerInteraction:(UIPointerInteraction *)interaction styleForRegion:(UIPointerRegion *)region API_AVAILABLE(ios(13.4))
{
if (SDL_CursorVisible()) {
return nil;
} else {
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 = 1; 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);
}
}
}
}
- (void)pencilHovering:(UIHoverGestureRecognizer *)recognizer API_AVAILABLE(ios(13.0))
{
switch (recognizer.state) {
case UIGestureRecognizerStateBegan:
case UIGestureRecognizerStateChanged:
UIKit_HandlePenHover(self, recognizer);
break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
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
- (SDL_TouchDeviceType)touchTypeForTouch:(UITouch *)touch
{
if (touch.type == UITouchTypeIndirect) {
return SDL_TOUCH_DEVICE_INDIRECT_RELATIVE;
}
return SDL_TOUCH_DEVICE_DIRECT;
}
- (SDL_TouchID)touchIdForType:(SDL_TouchDeviceType)type
{
switch (type) {
case SDL_TOUCH_DEVICE_DIRECT:
default:
return directTouchId;
case SDL_TOUCH_DEVICE_INDIRECT_RELATIVE:
return indirectTouchId;
}
}
- (CGPoint)touchLocation:(UITouch *)touch shouldNormalize:(BOOL)normalize
{
CGPoint point = [touch locationInView:self];
if (normalize) {
CGRect bounds = self.bounds;
point.x /= bounds.size.width;
point.y /= bounds.size.height;
}
return point;
}
- (float)pressureForTouch:(UITouch *)touch
{
return (float)touch.force;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
#if !defined(SDL_PLATFORM_TVOS)
if (@available(iOS 13.0, *)) {
if (touch.type == UITouchTypePencil) {
[self pencilPressed:touch];
continue;
}
}
if (@available(iOS 13.4, *)) {
if (touch.type == UITouchTypeIndirectPointer) {
[self indirectPointerPressed:touch fromEvent:event];
continue;
}
}
#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;
}
CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
SDL_SendTouch(UIKit_GetEventTimestamp([event timestamp]),
touchId, (SDL_FingerID)(uintptr_t)touch, sdlwindow,
SDL_EVENT_FINGER_DOWN, locationInView.x, locationInView.y, pressure);
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
#if !defined(SDL_PLATFORM_TVOS)
if (@available(iOS 13.0, *)) {
if (touch.type == UITouchTypePencil) {
[self pencilReleased:touch];
continue;
}
}
if (@available(iOS 13.4, *)) {
if (touch.type == UITouchTypeIndirectPointer) {
[self indirectPointerReleased:touch fromEvent:event];
continue;
}
}
#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;
}
CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
SDL_SendTouch(UIKit_GetEventTimestamp([event timestamp]),
touchId, (SDL_FingerID)(uintptr_t)touch, sdlwindow,
SDL_EVENT_FINGER_UP, locationInView.x, locationInView.y, pressure);
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
#if !defined(SDL_PLATFORM_TVOS)
if (@available(iOS 13.0, *)) {
if (touch.type == UITouchTypePencil) {
[self pencilReleased:touch];
continue;
}
}
if (@available(iOS 13.4, *)) {
if (touch.type == UITouchTypeIndirectPointer) {
[self indirectPointerReleased:touch fromEvent:event];
continue;
}
}
#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;
}
CGPoint locationInView = [self touchLocation:touch shouldNormalize:YES];
SDL_SendTouch(UIKit_GetEventTimestamp([event timestamp]),
touchId, (SDL_FingerID)(uintptr_t)touch, sdlwindow,
SDL_EVENT_FINGER_CANCELED, locationInView.x, locationInView.y, pressure);
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
#if !defined(SDL_PLATFORM_TVOS)
if (@available(iOS 13.0, *)) {
if (touch.type == UITouchTypePencil) {
[self pencilMoving:touch];
continue;
}
}
if (@available(iOS 13.4, *)) {
if (touch.type == UITouchTypeIndirectPointer) {
[self indirectPointerMoving:touch];
continue;
}
}
#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;
}
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);
}
}
- (void)safeAreaInsetsDidChange
{
SDL_SetWindowSafeAreaInsets(sdlwindow,
(int)SDL_ceilf(self.safeAreaInsets.left),
(int)SDL_ceilf(self.safeAreaInsets.right),
(int)SDL_ceilf(self.safeAreaInsets.top),
(int)SDL_ceilf(self.safeAreaInsets.bottom));
}
#if !defined(SDL_PLATFORM_TVOS)
- (IBAction)sdlPinchGesture:(UIPinchGestureRecognizer *)sender
{
CGFloat scale = sender.scale;
UIGestureRecognizerState state = sender.state;
switch (state) {
case UIGestureRecognizerStateBegan:
pinch_scale = 1.0f;
SDL_SendPinch(SDL_EVENT_PINCH_BEGIN, 0, sdlwindow, 0);
break;
case UIGestureRecognizerStateChanged:
if (pinch_scale > 0.0f) {
SDL_SendPinch(SDL_EVENT_PINCH_UPDATE, 0, sdlwindow, scale / pinch_scale);
}
pinch_scale = scale;
break;
case UIGestureRecognizerStateFailed:
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
SDL_SendPinch(SDL_EVENT_PINCH_END, 0, sdlwindow, 0);
break;
default:
break;
}
}
#endif
- (SDL_Scancode)scancodeFromPress:(UIPress *)press
{
if (press.key != nil) {
return (SDL_Scancode)press.key.keyCode;
}
bool controller_opened = false;
#ifdef SDL_PLATFORM_TVOS
controller_opened = (SDL_AppleTVRemoteOpenedAsJoystick > 0);
#else
controller_opened = SDL_JoysticksOpened();
#endif
if (!controller_opened) {
switch (press.type) {
case UIPressTypeUpArrow:
return SDL_SCANCODE_UP;
case UIPressTypeDownArrow:
return SDL_SCANCODE_DOWN;
case UIPressTypeLeftArrow:
return SDL_SCANCODE_LEFT;
case UIPressTypeRightArrow:
return SDL_SCANCODE_RIGHT;
case UIPressTypeSelect:
return SDL_SCANCODE_RETURN;
case UIPressTypeMenu:
return SDL_SCANCODE_ESCAPE;
case UIPressTypePlayPause:
return SDL_SCANCODE_PAUSE;
default:
break;
}
}
return SDL_SCANCODE_UNKNOWN;
}
- (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
{
if (!SDL_HasKeyboard()) {
for (UIPress *press in presses) {
SDL_Scancode scancode = [self scancodeFromPress:press];
SDL_SendKeyboardKey(UIKit_GetEventTimestamp([event timestamp]), SDL_GLOBAL_KEYBOARD_ID, 0, scancode, true);
}
}
if (SDL_TextInputActive(sdlwindow)) {
[super pressesBegan:presses withEvent:event];
}
}
- (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
{
if (!SDL_HasKeyboard()) {
for (UIPress *press in presses) {
SDL_Scancode scancode = [self scancodeFromPress:press];
SDL_SendKeyboardKey(UIKit_GetEventTimestamp([event timestamp]), SDL_GLOBAL_KEYBOARD_ID, 0, scancode, false);
}
}
if (SDL_TextInputActive(sdlwindow)) {
[super pressesEnded:presses withEvent:event];
}
}
- (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
{
if (!SDL_HasKeyboard()) {
for (UIPress *press in presses) {
SDL_Scancode scancode = [self scancodeFromPress:press];
SDL_SendKeyboardKey(UIKit_GetEventTimestamp([event timestamp]), SDL_GLOBAL_KEYBOARD_ID, 0, scancode, false);
}
}
if (SDL_TextInputActive(sdlwindow)) {
[super pressesCancelled:presses withEvent:event];
}
}
- (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
{
if (SDL_TextInputActive(sdlwindow)) {
[super pressesChanged:presses withEvent:event];
}
}
#ifdef SDL_PLATFORM_TVOS
- (void)swipeGesture:(UISwipeGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateEnded) {
bool controller_opened = (SDL_AppleTVRemoteOpenedAsJoystick > 0);
if (!controller_opened) {
switch (gesture.direction) {
case UISwipeGestureRecognizerDirectionUp:
SDL_SendKeyboardKeyAutoRelease(0, SDL_SCANCODE_UP);
break;
case UISwipeGestureRecognizerDirectionDown:
SDL_SendKeyboardKeyAutoRelease(0, SDL_SCANCODE_DOWN);
break;
case UISwipeGestureRecognizerDirectionLeft:
SDL_SendKeyboardKeyAutoRelease(0, SDL_SCANCODE_LEFT);
break;
case UISwipeGestureRecognizerDirectionRight:
SDL_SendKeyboardKeyAutoRelease(0, SDL_SCANCODE_RIGHT);
break;
}
}
}
}
#endif
@end
#endif