Merge pull request #19170 from hrydgard/ios-more-window-refactor

iOS: More ViewController code refactor
This commit is contained in:
Henrik Rydgård 2024-05-22 13:46:21 +02:00 committed by GitHub
commit 832b21ad45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 508 additions and 465 deletions

View File

@ -1290,6 +1290,8 @@ elseif(IOS AND NOT LIBRETRO)
ios/AppDelegate.h
ios/DisplayManager.h
ios/DisplayManager.mm
ios/Controls.h
ios/Controls.mm
ios/ViewController.mm
ios/ViewController.h
ios/iOSCoreAudio.mm

40
ios/Controls.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include <map>
#import <GameController/GameController.h>
#include "iCade/iCadeState.h"
#include "Common/Input/InputState.h"
// Code extracted from ViewController.mm, in order to modularize
// and share it between multiple view controllers.
bool SetupController(GCController *controller);
struct TouchTracker {
public:
void Began(NSSet *touches, UIView *view);
void Moved(NSSet *touches, UIView *view);
void Ended(NSSet *touches, UIView *view);
void Cancelled(NSSet *touches, UIView *view);
private:
void SendTouchEvent(float x, float y, int code, int pointerId);
int ToTouchID(UITouch *uiTouch, bool allowAllocate);
UITouch *touches_[10]{};
};
// Can probably get rid of this, but let's keep it for now.
struct ICadeTracker {
public:
void ButtonDown(iCadeState button);
void ButtonUp(iCadeState button);
void InitKeyMap();
private:
bool simulateAnalog = false;
bool iCadeConnectNotified = false;
std::map<uint16_t, InputKeyCode> iCadeToKeyMap;
double lastSelectPress = 0.0f;
double lastStartPress = 0.0f;
};

358
ios/Controls.mm Normal file
View File

@ -0,0 +1,358 @@
#include "Controls.h"
#include "Common/Log.h"
#include "Common/TimeUtil.h"
#include "Common/Input/InputState.h"
#include "Common/System/NativeApp.h"
#include "Common/System/Display.h"
#include "Core/KeyMap.h"
static void controllerButtonPressed(BOOL pressed, InputKeyCode keyCode) {
KeyInput key;
key.deviceId = DEVICE_ID_PAD_0;
key.flags = pressed ? KEY_DOWN : KEY_UP;
key.keyCode = keyCode;
NativeKey(key);
}
bool SetupController(GCController *controller) {
GCExtendedGamepad *extendedProfile = controller.extendedGamepad;
if (extendedProfile == nil) {
return false;
}
controller.controllerPausedHandler = ^(GCController *controller) {
KeyInput key;
key.flags = KEY_DOWN;
key.keyCode = NKCODE_ESCAPE;
key.deviceId = DEVICE_ID_KEYBOARD;
NativeKey(key);
};
extendedProfile.buttonA.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_BUTTON_2); // Cross
};
extendedProfile.buttonB.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_BUTTON_3); // Circle
};
extendedProfile.buttonX.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_BUTTON_4); // Square
};
extendedProfile.buttonY.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_BUTTON_1); // Triangle
};
extendedProfile.leftShoulder.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_BUTTON_7); // LTrigger
};
extendedProfile.rightShoulder.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_BUTTON_8); // RTrigger
};
extendedProfile.dpad.up.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_DPAD_UP);
};
extendedProfile.dpad.down.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_DPAD_DOWN);
};
extendedProfile.dpad.left.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_DPAD_LEFT);
};
extendedProfile.dpad.right.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_DPAD_RIGHT);
};
extendedProfile.leftTrigger.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_BUTTON_9); // Select
};
extendedProfile.rightTrigger.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_BUTTON_10); // Start
};
#if defined(__IPHONE_12_1) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_1
if ([extendedProfile respondsToSelector:@selector(leftThumbstickButton)] && extendedProfile.leftThumbstickButton != nil) {
extendedProfile.leftThumbstickButton.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_BUTTON_11);
};
}
if ([extendedProfile respondsToSelector:@selector(rightThumbstickButton)] && extendedProfile.rightThumbstickButton != nil) {
extendedProfile.rightThumbstickButton.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_BUTTON_12);
};
}
#endif
#if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
if ([extendedProfile respondsToSelector:@selector(buttonOptions)] && extendedProfile.buttonOptions != nil) {
extendedProfile.buttonOptions.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_BUTTON_13);
};
}
if ([extendedProfile respondsToSelector:@selector(buttonMenu)] && extendedProfile.buttonMenu != nil) {
extendedProfile.buttonMenu.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_BUTTON_14);
};
}
#endif
#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
if ([extendedProfile respondsToSelector:@selector(buttonHome)] && extendedProfile.buttonHome != nil) {
extendedProfile.buttonHome.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_BUTTON_15);
};
}
#endif
extendedProfile.leftThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) {
AxisInput axisInput;
axisInput.deviceId = DEVICE_ID_PAD_0;
axisInput.axisId = JOYSTICK_AXIS_X;
axisInput.value = value;
NativeAxis(&axisInput, 1);
};
extendedProfile.leftThumbstick.yAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) {
AxisInput axisInput;
axisInput.deviceId = DEVICE_ID_PAD_0;
axisInput.axisId = JOYSTICK_AXIS_Y;
axisInput.value = -value;
NativeAxis(&axisInput, 1);
};
// Map right thumbstick as another analog stick, particularly useful for controllers
// like the DualShock 3/4 when connected to an iOS device
extendedProfile.rightThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) {
AxisInput axisInput;
axisInput.deviceId = DEVICE_ID_PAD_0;
axisInput.axisId = JOYSTICK_AXIS_Z;
axisInput.value = value;
NativeAxis(&axisInput, 1);
};
extendedProfile.rightThumbstick.yAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) {
AxisInput axisInput;
axisInput.deviceId = DEVICE_ID_PAD_0;
axisInput.axisId = JOYSTICK_AXIS_RZ;
axisInput.value = -value;
NativeAxis(&axisInput, 1);
};
return true;
}
void TouchTracker::SendTouchEvent(float x, float y, int code, int pointerId) {
float scale = [UIScreen mainScreen].scale;
if ([[UIScreen mainScreen] respondsToSelector:@selector(nativeScale)]) {
scale = [UIScreen mainScreen].nativeScale;
}
float dp_xscale = (float)g_display.dp_xres / (float)g_display.pixel_xres;
float dp_yscale = (float)g_display.dp_yres / (float)g_display.pixel_yres;
float scaledX = (int)(x * dp_xscale) * scale;
float scaledY = (int)(y * dp_yscale) * scale;
TouchInput input;
input.x = scaledX;
input.y = scaledY;
switch (code) {
case 1: input.flags = TOUCH_DOWN; break;
case 2: input.flags = TOUCH_UP; break;
default: input.flags = TOUCH_MOVE; break;
}
input.id = pointerId;
NativeTouch(input);
}
int TouchTracker::ToTouchID(UITouch *uiTouch, bool allowAllocate) {
// Find the id for the touch.
for (int localId = 0; localId < (int)ARRAY_SIZE(touches_); ++localId) {
if (touches_[localId] == uiTouch) {
return localId;
}
}
// Allocate a new one, perhaps?
if (allowAllocate) {
for (int localId = 0; localId < (int)ARRAY_SIZE(touches_); ++localId) {
if (touches_[localId] == 0) {
touches_[localId] = uiTouch;
return localId;
}
}
// None were free. Ignore?
return 0;
}
return -1;
}
void TouchTracker::Began(NSSet *touches, UIView *view) {
for (UITouch* touch in touches) {
CGPoint point = [touch locationInView:view];
int touchId = ToTouchID(touch, true);
SendTouchEvent(point.x, point.y, 1, touchId);
}
}
void TouchTracker::Moved(NSSet *touches, UIView *view) {
for (UITouch* touch in touches) {
CGPoint point = [touch locationInView:view];
int touchId = ToTouchID(touch, true);
SendTouchEvent(point.x, point.y, 0, touchId);
}
}
void TouchTracker::Ended(NSSet *touches, UIView *view) {
for (UITouch* touch in touches) {
CGPoint point = [touch locationInView:view];
int touchId = ToTouchID(touch, false);
if (touchId >= 0) {
SendTouchEvent(point.x, point.y, 2, touchId);
touches_[touchId] = nullptr;
}
}
}
void TouchTracker::Cancelled(NSSet *touches, UIView *view) {
for (UITouch* touch in touches) {
CGPoint point = [touch locationInView:view];
int touchId = ToTouchID(touch, false);
if (touchId >= 0) {
SendTouchEvent(point.x, point.y, 2, touchId);
touches_[touchId] = nullptr;
}
}
}
void ICadeTracker::InitKeyMap() {
iCadeToKeyMap[iCadeJoystickUp] = NKCODE_DPAD_UP;
iCadeToKeyMap[iCadeJoystickRight] = NKCODE_DPAD_RIGHT;
iCadeToKeyMap[iCadeJoystickDown] = NKCODE_DPAD_DOWN;
iCadeToKeyMap[iCadeJoystickLeft] = NKCODE_DPAD_LEFT;
iCadeToKeyMap[iCadeButtonA] = NKCODE_BUTTON_9; // Select
iCadeToKeyMap[iCadeButtonB] = NKCODE_BUTTON_7; // LTrigger
iCadeToKeyMap[iCadeButtonC] = NKCODE_BUTTON_10; // Start
iCadeToKeyMap[iCadeButtonD] = NKCODE_BUTTON_8; // RTrigger
iCadeToKeyMap[iCadeButtonE] = NKCODE_BUTTON_4; // Square
iCadeToKeyMap[iCadeButtonF] = NKCODE_BUTTON_2; // Cross
iCadeToKeyMap[iCadeButtonG] = NKCODE_BUTTON_1; // Triangle
iCadeToKeyMap[iCadeButtonH] = NKCODE_BUTTON_3; // Circle
}
void ICadeTracker::ButtonDown(iCadeState button) {
if (simulateAnalog &&
((button == iCadeJoystickUp) ||
(button == iCadeJoystickDown) ||
(button == iCadeJoystickLeft) ||
(button == iCadeJoystickRight))) {
AxisInput axis;
switch (button) {
case iCadeJoystickUp :
axis.axisId = JOYSTICK_AXIS_Y;
axis.value = -1.0f;
break;
case iCadeJoystickDown :
axis.axisId = JOYSTICK_AXIS_Y;
axis.value = 1.0f;
break;
case iCadeJoystickLeft :
axis.axisId = JOYSTICK_AXIS_X;
axis.value = -1.0f;
break;
case iCadeJoystickRight :
axis.axisId = JOYSTICK_AXIS_X;
axis.value = 1.0f;
break;
default:
break;
}
axis.deviceId = DEVICE_ID_PAD_0;
NativeAxis(&axis, 1);
} else {
KeyInput key;
key.flags = KEY_DOWN;
key.keyCode = iCadeToKeyMap[button];
key.deviceId = DEVICE_ID_PAD_0;
NativeKey(key);
}
}
void ICadeTracker::ButtonUp(iCadeState button) {
if (!iCadeConnectNotified) {
iCadeConnectNotified = true;
KeyMap::NotifyPadConnected(DEVICE_ID_PAD_0, "iCade");
}
if (button == iCadeButtonA) {
// Pressing Select twice within 1 second toggles the DPad between
// normal operation and simulating the Analog stick.
if ((lastSelectPress + 1.0f) > time_now_d())
simulateAnalog = !simulateAnalog;
lastSelectPress = time_now_d();
}
if (button == iCadeButtonC) {
// Pressing Start twice within 1 second will take to the Emu menu
if ((lastStartPress + 1.0f) > time_now_d()) {
KeyInput key;
key.flags = KEY_DOWN;
key.keyCode = NKCODE_ESCAPE;
key.deviceId = DEVICE_ID_KEYBOARD;
NativeKey(key);
return;
}
lastStartPress = time_now_d();
}
if (simulateAnalog &&
((button == iCadeJoystickUp) ||
(button == iCadeJoystickDown) ||
(button == iCadeJoystickLeft) ||
(button == iCadeJoystickRight))) {
AxisInput axis;
switch (button) {
case iCadeJoystickUp :
axis.axisId = JOYSTICK_AXIS_Y;
axis.value = 0.0f;
break;
case iCadeJoystickDown :
axis.axisId = JOYSTICK_AXIS_Y;
axis.value = 0.0f;
break;
case iCadeJoystickLeft :
axis.axisId = JOYSTICK_AXIS_X;
axis.value = 0.0f;
break;
case iCadeJoystickRight :
axis.axisId = JOYSTICK_AXIS_X;
axis.value = 0.0f;
break;
default:
break;
}
axis.deviceId = DEVICE_ID_PAD_0;
NativeAxis(&axis, 1);
} else {
KeyInput key;
key.flags = KEY_UP;
key.keyCode = iCadeToKeyMap[button];
key.deviceId = DEVICE_ID_PAD_0;
NativeKey(key);
}
}

View File

@ -139,14 +139,14 @@
scale = screen.nativeScale;
}
CGSize size = screen.applicationFrame.size;
CGSize size = screen.bounds.size;
if (size.height > size.width) {
float h = size.height;
size.height = size.width;
size.width = h;
}
if (screen == [UIScreen mainScreen]) {
g_display.dpi = (IS_IPAD() ? 200.0f : 150.0f) * scale;
} else {

View File

@ -2,9 +2,7 @@
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1
#import <GameController/GameController.h>
#endif
#import "iCade/iCadeReaderView.h"
#import "CameraHelper.h"
#import "LocationHelper.h"

View File

@ -8,6 +8,7 @@
#import "AppDelegate.h"
#import "ViewController.h"
#import "DisplayManager.h"
#include "Controls.h"
#import "iOSCoreAudio.h"
#import <GLKit/GLKit.h>
@ -87,32 +88,26 @@ private:
GLRenderManager *renderManager_;
};
static float dp_xscale = 1.0f;
static float dp_yscale = 1.0f;
static double lastSelectPress = 0.0f;
static double lastStartPress = 0.0f;
static bool simulateAnalog = false;
static bool iCadeConnectNotified = false;
static bool threadEnabled = true;
static bool threadStopped = false;
static UITouch *g_touches[10];
id<PPSSPPViewController> sharedViewController;
static GraphicsContext *graphicsContext;
// TODO: Reach these through sharedViewController
static CameraHelper *cameraHelper;
static LocationHelper *locationHelper;
@interface PPSSPPViewControllerGL () {
std::map<uint16_t, InputKeyCode> iCadeToKeyMap;
ICadeTracker g_iCadeTracker;
TouchTracker g_touchTracker;
GraphicsContext *graphicsContext;
}
@property (nonatomic, strong) EAGLContext* context;
//@property (nonatomic) iCadeReaderView* iCadeView;
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1
@property (nonatomic) GCController *gameController __attribute__((weak_import));
#endif
@end
@ -122,30 +117,15 @@ static LocationHelper *locationHelper;
self = [super init];
if (self) {
sharedViewController = self;
memset(g_touches, 0, sizeof(g_touches));
iCadeToKeyMap[iCadeJoystickUp] = NKCODE_DPAD_UP;
iCadeToKeyMap[iCadeJoystickRight] = NKCODE_DPAD_RIGHT;
iCadeToKeyMap[iCadeJoystickDown] = NKCODE_DPAD_DOWN;
iCadeToKeyMap[iCadeJoystickLeft] = NKCODE_DPAD_LEFT;
iCadeToKeyMap[iCadeButtonA] = NKCODE_BUTTON_9; // Select
iCadeToKeyMap[iCadeButtonB] = NKCODE_BUTTON_7; // LTrigger
iCadeToKeyMap[iCadeButtonC] = NKCODE_BUTTON_10; // Start
iCadeToKeyMap[iCadeButtonD] = NKCODE_BUTTON_8; // RTrigger
iCadeToKeyMap[iCadeButtonE] = NKCODE_BUTTON_4; // Square
iCadeToKeyMap[iCadeButtonF] = NKCODE_BUTTON_2; // Cross
iCadeToKeyMap[iCadeButtonG] = NKCODE_BUTTON_1; // Triangle
iCadeToKeyMap[iCadeButtonH] = NKCODE_BUTTON_3; // Circle
g_iCadeTracker.InitKeyMap();
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillTerminate:) name:UIApplicationWillTerminateNotification object:nil];
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1
if ([GCController class]) // Checking the availability of a GameController framework
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(controllerDidConnect:) name:GCControllerDidConnectNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(controllerDidDisconnect:) name:GCControllerDidDisconnectNotification object:nil];
}
#endif
}
return self;
}
@ -170,7 +150,6 @@ extern float g_safeInsetBottom;
- (void)viewSafeAreaInsetsDidChange {
if (@available(iOS 11.0, *)) {
[super viewSafeAreaInsetsDidChange];
char safeArea[100];
// we use 0.0f instead of safeAreaInsets.bottom because the bottom overlay isn't disturbing (for now)
g_safeInsetLeft = self.view.safeAreaInsets.left;
g_safeInsetRight = self.view.safeAreaInsets.right;
@ -181,6 +160,7 @@ extern float g_safeInsetBottom;
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self hideKeyboard];
}
- (void)viewDidLoad {
@ -217,21 +197,16 @@ extern float g_safeInsetBottom;
graphicsContext->ThreadStart();
dp_xscale = (float)g_display.dp_xres / (float)g_display.pixel_xres;
dp_yscale = (float)g_display.dp_yres / (float)g_display.pixel_yres;
/*self.iCadeView = [[iCadeReaderView alloc] init];
[self.view addSubview:self.iCadeView];
self.iCadeView.delegate = self;
self.iCadeView.active = YES;*/
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1
if ([GCController class]) {
if ([[GCController controllers] count] > 0) {
[self setupController:[[GCController controllers] firstObject]];
}
}
#endif
cameraHelper = [[CameraHelper alloc] init];
[cameraHelper setDelegate:self];
@ -305,11 +280,9 @@ extern float g_safeInsetBottom;
[[NSNotificationCenter defaultCenter] removeObserver:self];
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1
if ([GCController class]) {
self.gameController = nil;
}
#endif
if (graphicsContext) {
graphicsContext->Shutdown();
@ -325,13 +298,6 @@ extern float g_safeInsetBottom;
[self shutdown];
}
// For iOS before 6.0
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
}
// For iOS 6.0 and up
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
@ -343,106 +309,24 @@ extern float g_safeInsetBottom;
graphicsContext->ThreadFrame();
}
- (void)touchX:(float)x y:(float)y code:(int)code pointerId:(int)pointerId
{
float scale = [UIScreen mainScreen].scale;
if ([[UIScreen mainScreen] respondsToSelector:@selector(nativeScale)]) {
scale = [UIScreen mainScreen].nativeScale;
}
float scaledX = (int)(x * dp_xscale) * scale;
float scaledY = (int)(y * dp_yscale) * scale;
TouchInput input;
input.x = scaledX;
input.y = scaledY;
switch (code) {
case 1 :
input.flags = TOUCH_DOWN;
break;
case 2 :
input.flags = TOUCH_UP;
break;
default :
input.flags = TOUCH_MOVE;
break;
}
input.id = pointerId;
NativeTouch(input);
}
int ToTouchID(UITouch *uiTouch, bool allowAllocate) {
// Find the id for the touch.
for (int localId = 0; localId < (int)ARRAY_SIZE(g_touches); ++localId) {
if (g_touches[localId] == uiTouch) {
return localId;
}
}
// Allocate a new one, perhaps?
if (allowAllocate) {
for (int localId = 0; localId < (int)ARRAY_SIZE(g_touches); ++localId) {
if (g_touches[localId] == 0) {
g_touches[localId] = uiTouch;
return localId;
}
}
// None were free. Ignore?
return 0;
}
return -1;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch* touch in touches)
{
CGPoint point = [touch locationInView:self.view];
int touchId = ToTouchID(touch, true);
[self touchX:point.x y:point.y code:1 pointerId:touchId];
}
g_touchTracker.Began(touches, self.view);
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch* touch in touches)
{
CGPoint point = [touch locationInView:self.view];
int touchId = ToTouchID(touch, true);
[self touchX:point.x y:point.y code:0 pointerId: touchId];
}
g_touchTracker.Moved(touches, self.view);
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch* touch in touches)
{
CGPoint point = [touch locationInView:self.view];
int touchId = ToTouchID(touch, false);
if (touchId >= 0) {
[self touchX:point.x y:point.y code:2 pointerId: touchId];
g_touches[touchId] = nullptr;
}
}
g_touchTracker.Ended(touches, self.view);
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
for(UITouch* touch in touches)
{
CGPoint point = [touch locationInView:self.view];
int touchId = ToTouchID(touch, false);
if (touchId >= 0) {
[self touchX:point.x y:point.y code:2 pointerId: touchId];
g_touches[touchId] = nullptr;
}
}
g_touchTracker.Cancelled(touches, self.view);
}
- (void)bindDefaultFBO
@ -452,118 +336,14 @@ int ToTouchID(UITouch *uiTouch, bool allowAllocate) {
- (void)buttonDown:(iCadeState)button
{
if (simulateAnalog &&
((button == iCadeJoystickUp) ||
(button == iCadeJoystickDown) ||
(button == iCadeJoystickLeft) ||
(button == iCadeJoystickRight))) {
AxisInput axis;
switch (button) {
case iCadeJoystickUp :
axis.axisId = JOYSTICK_AXIS_Y;
axis.value = -1.0f;
break;
case iCadeJoystickDown :
axis.axisId = JOYSTICK_AXIS_Y;
axis.value = 1.0f;
break;
case iCadeJoystickLeft :
axis.axisId = JOYSTICK_AXIS_X;
axis.value = -1.0f;
break;
case iCadeJoystickRight :
axis.axisId = JOYSTICK_AXIS_X;
axis.value = 1.0f;
break;
default:
break;
}
axis.deviceId = DEVICE_ID_PAD_0;
NativeAxis(&axis, 1);
} else {
KeyInput key;
key.flags = KEY_DOWN;
key.keyCode = iCadeToKeyMap[button];
key.deviceId = DEVICE_ID_PAD_0;
NativeKey(key);
}
g_iCadeTracker.ButtonDown(button);
}
- (void)buttonUp:(iCadeState)button
{
if (!iCadeConnectNotified) {
iCadeConnectNotified = true;
KeyMap::NotifyPadConnected(DEVICE_ID_PAD_0, "iCade");
}
if (button == iCadeButtonA) {
// Pressing Select twice within 1 second toggles the DPad between
// normal operation and simulating the Analog stick.
if ((lastSelectPress + 1.0f) > time_now_d())
simulateAnalog = !simulateAnalog;
lastSelectPress = time_now_d();
}
if (button == iCadeButtonC) {
// Pressing Start twice within 1 second will take to the Emu menu
if ((lastStartPress + 1.0f) > time_now_d()) {
KeyInput key;
key.flags = KEY_DOWN;
key.keyCode = NKCODE_ESCAPE;
key.deviceId = DEVICE_ID_KEYBOARD;
NativeKey(key);
return;
}
lastStartPress = time_now_d();
}
if (simulateAnalog &&
((button == iCadeJoystickUp) ||
(button == iCadeJoystickDown) ||
(button == iCadeJoystickLeft) ||
(button == iCadeJoystickRight))) {
AxisInput axis;
switch (button) {
case iCadeJoystickUp :
axis.axisId = JOYSTICK_AXIS_Y;
axis.value = 0.0f;
break;
case iCadeJoystickDown :
axis.axisId = JOYSTICK_AXIS_Y;
axis.value = 0.0f;
break;
case iCadeJoystickLeft :
axis.axisId = JOYSTICK_AXIS_X;
axis.value = 0.0f;
break;
case iCadeJoystickRight :
axis.axisId = JOYSTICK_AXIS_X;
axis.value = 0.0f;
break;
default:
break;
}
axis.deviceId = DEVICE_ID_PAD_0;
NativeAxis(&axis, 1);
} else {
KeyInput key;
key.flags = KEY_UP;
key.keyCode = iCadeToKeyMap[button];
key.deviceId = DEVICE_ID_PAD_0;
NativeKey(key);
}
g_iCadeTracker.ButtonUp(button);
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1
- (void)controllerDidConnect:(NSNotification *)note
{
if (![[GCController controllers] containsObject:self.gameController]) self.gameController = nil;
@ -584,15 +364,6 @@ int ToTouchID(UITouch *uiTouch, bool allowAllocate) {
}
}
- (void)controllerButtonPressed:(BOOL)pressed keyCode:(InputKeyCode)keyCode
{
KeyInput key;
key.deviceId = DEVICE_ID_PAD_0;
key.flags = pressed ? KEY_DOWN : KEY_UP;
key.keyCode = keyCode;
NativeKey(key);
}
// Enables tapping for edge area.
-(UIRectEdge)preferredScreenEdgesDeferringSystemGestures
{
@ -606,139 +377,10 @@ int ToTouchID(UITouch *uiTouch, bool allowAllocate) {
- (void)setupController:(GCController *)controller
{
self.gameController = controller;
GCGamepad *baseProfile = self.gameController.gamepad;
if (baseProfile == nil) {
if (!SetupController(controller)) {
self.gameController = nil;
return;
}
self.gameController.controllerPausedHandler = ^(GCController *controller) {
KeyInput key;
key.flags = KEY_DOWN;
key.keyCode = NKCODE_ESCAPE;
key.deviceId = DEVICE_ID_KEYBOARD;
NativeKey(key);
};
baseProfile.buttonA.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_BUTTON_2]; // Cross
};
baseProfile.buttonB.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_BUTTON_3]; // Circle
};
baseProfile.buttonX.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_BUTTON_4]; // Square
};
baseProfile.buttonY.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_BUTTON_1]; // Triangle
};
baseProfile.leftShoulder.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_BUTTON_7]; // LTrigger
};
baseProfile.rightShoulder.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_BUTTON_8]; // RTrigger
};
baseProfile.dpad.up.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_DPAD_UP];
};
baseProfile.dpad.down.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_DPAD_DOWN];
};
baseProfile.dpad.left.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_DPAD_LEFT];
};
baseProfile.dpad.right.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_DPAD_RIGHT];
};
GCExtendedGamepad *extendedProfile = self.gameController.extendedGamepad;
if (extendedProfile == nil)
return; // controller doesn't support extendedGamepad profile
extendedProfile.leftTrigger.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_BUTTON_9]; // Select
};
extendedProfile.rightTrigger.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_BUTTON_10]; // Start
};
#if defined(__IPHONE_12_1) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_1
if ([extendedProfile respondsToSelector:@selector(leftThumbstickButton)] && extendedProfile.leftThumbstickButton != nil) {
extendedProfile.leftThumbstickButton.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_BUTTON_11];
};
}
if ([extendedProfile respondsToSelector:@selector(rightThumbstickButton)] && extendedProfile.rightThumbstickButton != nil) {
extendedProfile.rightThumbstickButton.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_BUTTON_12];
};
}
#endif
#if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
if ([extendedProfile respondsToSelector:@selector(buttonOptions)] && extendedProfile.buttonOptions != nil) {
extendedProfile.buttonOptions.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_BUTTON_13];
};
}
if ([extendedProfile respondsToSelector:@selector(buttonMenu)] && extendedProfile.buttonMenu != nil) {
extendedProfile.buttonMenu.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_BUTTON_14];
};
}
#endif
#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
if ([extendedProfile respondsToSelector:@selector(buttonHome)] && extendedProfile.buttonHome != nil) {
extendedProfile.buttonHome.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
[self controllerButtonPressed:pressed keyCode:NKCODE_BUTTON_15];
};
}
#endif
extendedProfile.leftThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) {
AxisInput axisInput;
axisInput.deviceId = DEVICE_ID_PAD_0;
axisInput.axisId = JOYSTICK_AXIS_X;
axisInput.value = value;
NativeAxis(&axisInput, 1);
};
extendedProfile.leftThumbstick.yAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) {
AxisInput axisInput;
axisInput.deviceId = DEVICE_ID_PAD_0;
axisInput.axisId = JOYSTICK_AXIS_Y;
axisInput.value = -value;
NativeAxis(&axisInput, 1);
};
// Map right thumbstick as another analog stick, particularly useful for controllers like the DualShock 3/4 when connected to an iOS device
extendedProfile.rightThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) {
AxisInput axisInput;
axisInput.deviceId = DEVICE_ID_PAD_0;
axisInput.axisId = JOYSTICK_AXIS_Z;
axisInput.value = value;
NativeAxis(&axisInput, 1);
};
extendedProfile.rightThumbstick.yAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) {
AxisInput axisInput;
axisInput.deviceId = DEVICE_ID_PAD_0;
axisInput.axisId = JOYSTICK_AXIS_RZ;
axisInput.value = -value;
NativeAxis(&axisInput, 1);
};
}
#endif
void setCameraSize(int width, int height) {
[cameraHelper setCameraSize: width h:height];
@ -824,14 +466,6 @@ void stopLocation() {
@end
void System_LaunchUrl(LaunchUrlType urlType, char const* url)
{
NSURL *nsUrl = [NSURL URLWithString:[NSString stringWithCString:url encoding:NSStringEncodingConversionAllowLossy]];
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] openURL:nsUrl options:@{} completionHandler:nil];
});
}
void bindDefaultFBO()
{
[sharedViewController bindDefaultFBO];

View File

@ -25,10 +25,9 @@
#include <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#define SAMPLE_RATE 44100
AudioComponentInstance audioInstance = nil;
static AudioComponentInstance audioInstance = nil;
int NativeMix(short *audio, int numSamples, int sampleRate);
@ -77,83 +76,85 @@ void iOSCoreAudioInit()
}
}
if (!audioInstance) {
OSErr err;
// first, grab the default output
AudioComponentDescription defaultOutputDescription;
defaultOutputDescription.componentType = kAudioUnitType_Output;
defaultOutputDescription.componentSubType = kAudioUnitSubType_RemoteIO;
defaultOutputDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
defaultOutputDescription.componentFlags = 0;
defaultOutputDescription.componentFlagsMask = 0;
AudioComponent defaultOutput = AudioComponentFindNext(NULL, &defaultOutputDescription);
// create our instance
err = AudioComponentInstanceNew(defaultOutput, &audioInstance);
if (err != noErr) {
audioInstance = nil;
return;
}
// create our callback so we can give it the audio data
AURenderCallbackStruct input;
input.inputProc = iOSCoreAudioCallback;
input.inputProcRefCon = NULL;
err = AudioUnitSetProperty(audioInstance,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&input,
sizeof(input));
if (err != noErr) {
AudioComponentInstanceDispose(audioInstance);
audioInstance = nil;
return;
}
// setup the audio format we'll be using (stereo pcm)
AudioStreamBasicDescription streamFormat;
memset(&streamFormat, 0, sizeof(streamFormat));
streamFormat.mSampleRate = SAMPLE_RATE;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
streamFormat.mBitsPerChannel = sizeof(short) * 8;
streamFormat.mChannelsPerFrame = 2;
streamFormat.mFramesPerPacket = 1;
streamFormat.mBytesPerFrame = (streamFormat.mBitsPerChannel / 8) * streamFormat.mChannelsPerFrame;
streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * streamFormat.mFramesPerPacket;
err = AudioUnitSetProperty(audioInstance,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&streamFormat,
sizeof(AudioStreamBasicDescription));
if (err != noErr) {
AudioComponentInstanceDispose(audioInstance);
audioInstance = nil;
return;
}
// k, all setup, so init
err = AudioUnitInitialize(audioInstance);
if (err != noErr) {
AudioComponentInstanceDispose(audioInstance);
audioInstance = nil;
return;
}
// finally start playback
err = AudioOutputUnitStart(audioInstance);
if (err != noErr) {
AudioUnitUninitialize(audioInstance);
AudioComponentInstanceDispose(audioInstance);
audioInstance = nil;
return;
}
// we're good to go
if (audioInstance) {
// Already running
return;
}
OSErr err;
// first, grab the default output
AudioComponentDescription defaultOutputDescription;
defaultOutputDescription.componentType = kAudioUnitType_Output;
defaultOutputDescription.componentSubType = kAudioUnitSubType_RemoteIO;
defaultOutputDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
defaultOutputDescription.componentFlags = 0;
defaultOutputDescription.componentFlagsMask = 0;
AudioComponent defaultOutput = AudioComponentFindNext(NULL, &defaultOutputDescription);
// create our instance
err = AudioComponentInstanceNew(defaultOutput, &audioInstance);
if (err != noErr) {
audioInstance = nil;
return;
}
// create our callback so we can give it the audio data
AURenderCallbackStruct input;
input.inputProc = iOSCoreAudioCallback;
input.inputProcRefCon = NULL;
err = AudioUnitSetProperty(audioInstance,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&input,
sizeof(input));
if (err != noErr) {
AudioComponentInstanceDispose(audioInstance);
audioInstance = nil;
return;
}
// setup the audio format we'll be using (stereo pcm)
AudioStreamBasicDescription streamFormat;
memset(&streamFormat, 0, sizeof(streamFormat));
streamFormat.mSampleRate = SAMPLE_RATE;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
streamFormat.mBitsPerChannel = sizeof(short) * 8;
streamFormat.mChannelsPerFrame = 2;
streamFormat.mFramesPerPacket = 1;
streamFormat.mBytesPerFrame = (streamFormat.mBitsPerChannel / 8) * streamFormat.mChannelsPerFrame;
streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * streamFormat.mFramesPerPacket;
err = AudioUnitSetProperty(audioInstance,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&streamFormat,
sizeof(AudioStreamBasicDescription));
if (err != noErr) {
AudioComponentInstanceDispose(audioInstance);
audioInstance = nil;
return;
}
// k, all setup, so init
err = AudioUnitInitialize(audioInstance);
if (err != noErr) {
AudioComponentInstanceDispose(audioInstance);
audioInstance = nil;
return;
}
// finally start playback
err = AudioOutputUnitStart(audioInstance);
if (err != noErr) {
AudioUnitUninitialize(audioInstance);
AudioComponentInstanceDispose(audioInstance);
audioInstance = nil;
return;
}
// we're good to go
}
void iOSCoreAudioShutdown()

View File

@ -489,7 +489,17 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string
void System_Toast(std::string_view text) {}
void System_AskForPermission(SystemPermission permission) {}
PermissionStatus System_GetPermissionStatus(SystemPermission permission) { return PERMISSION_STATUS_GRANTED; }
void System_LaunchUrl(LaunchUrlType urlType, const char *url)
{
NSURL *nsUrl = [NSURL URLWithString:[NSString stringWithCString:url encoding:NSStringEncodingConversionAllowLossy]];
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] openURL:nsUrl options:@{} completionHandler:nil];
});
}
PermissionStatus System_GetPermissionStatus(SystemPermission permission) {
return PERMISSION_STATUS_GRANTED;
}
#if !PPSSPP_PLATFORM(IOS_APP_STORE)
FOUNDATION_EXTERN void AudioServicesPlaySystemSoundWithVibration(unsigned long, objc_object*, NSDictionary*);