From 2f06100b436360bf42aa800d1f9592a0ab60ff13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 17 Oct 2025 13:43:41 +0200 Subject: [PATCH 1/4] iOS: Initial attempt at switching to SceneDelegate (UIScene) Fixes #20884 --- CMakeLists.txt | 4 +- UI/MainScreen.cpp | 2 +- cmake/Toolchains/ios.cmake | 2 +- ios/AppDelegate.h | 3 +- ios/AppDelegate.mm | 98 +++++------------------------- ios/IAPManager.mm | 8 +++ ios/PPSSPP-Info.plist | 15 +++++ ios/SceneDelegate.h | 9 +++ ios/SceneDelegate.mm | 119 +++++++++++++++++++++++++++++++++++++ 9 files changed, 173 insertions(+), 87 deletions(-) create mode 100644 ios/SceneDelegate.h create mode 100644 ios/SceneDelegate.mm diff --git a/CMakeLists.txt b/CMakeLists.txt index f6735209f4..e4bd4a582a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1340,6 +1340,8 @@ elseif(IOS AND NOT LIBRETRO) ios/main.mm ios/AppDelegate.mm ios/AppDelegate.h + ios/SceneDelegate.mm + ios/SceneDelegate.h ios/DisplayManager.h ios/DisplayManager.mm ios/Controls.h @@ -2964,7 +2966,7 @@ endif() # packaging and code signing if(IOS AND NOT LIBRETRO) if(IOS_APP_STORE) - set(DEPLOYMENT_TARGET 12.0) + set(DEPLOYMENT_TARGET 13.0) else() set(DEPLOYMENT_TARGET 11.0) endif() diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index c8ee057c44..49a6c8ac39 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -1531,7 +1531,7 @@ void LaunchBuyGold(ScreenManager *screenManager) { screenManager->push(new IAPScreen()); } else { #if PPSSPP_PLATFORM(IOS_APP_STORE) - System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://apps.apple.com/us/app/ppsspp-gold-psp-emulator/id6502287918"); + System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.ppsspp.org/buygold_ios"); #elif PPSSPP_PLATFORM(ANDROID) System_LaunchUrl(LaunchUrlType::BROWSER_URL, "market://details?id=org.ppsspp.ppssppgold"); #else diff --git a/cmake/Toolchains/ios.cmake b/cmake/Toolchains/ios.cmake index 5ffad1d907..df3e460ec3 100644 --- a/cmake/Toolchains/ios.cmake +++ b/cmake/Toolchains/ios.cmake @@ -12,7 +12,7 @@ # PPSSPP platform flags set(MOBILE_DEVICE ON) set(USING_GLES2 ON) -set(IPHONEOS_DEPLOYMENT_TARGET 12.0) +set(IPHONEOS_DEPLOYMENT_TARGET 13.0) add_definitions( -DGL_ETC1_RGB8_OES=0 -U__STRICT_ANSI__ diff --git a/ios/AppDelegate.h b/ios/AppDelegate.h index 06ab74d32a..ecadddacff 100644 --- a/ios/AppDelegate.h +++ b/ios/AppDelegate.h @@ -8,8 +8,7 @@ @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) UIScreen *screen; - -@property (strong, nonatomic) id viewController; +@property (nonatomic, strong) NSDictionary *launchOptions; - (void)restart:(const char *)args; - (BOOL)launchPPSSPP:(int)argc argv:(char**)argv; diff --git a/ios/AppDelegate.mm b/ios/AppDelegate.mm index 6d7d9b6785..0b9a30d0b0 100644 --- a/ios/AppDelegate.mm +++ b/ios/AppDelegate.mm @@ -1,7 +1,5 @@ #import "AppDelegate.h" -#import "ViewControllerCommon.h" -#import "ViewController.h" -#import "ViewControllerMetal.h" +#import "SceneDelegate.h" #import "iOSCoreAudio.h" #import "Common/System/System.h" #import "Common/System/NativeApp.h" @@ -11,6 +9,12 @@ #import "IAPManager.h" #import +#import + +// TODO: Unfortunate hack to force link SceneDelegate class. +// This is necessary because otherwise classes from static libraries aren't loaded. +// We should move all the iOS code into the main binary directly to avoid this problem. +__attribute__((used)) static Class _forceLinkSceneDelegate = [SceneDelegate class]; @implementation AppDelegate @@ -77,49 +81,21 @@ } -(BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - int argc = 1; - char *argv[5]{}; - NSURL *nsUrl = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey]; - - if (nsUrl != nullptr && nsUrl.isFileURL) { - NSString *nsString = nsUrl.path; - const char *string = nsString.UTF8String; - argv[argc++] = (char*)string; + self.launchOptions = launchOptions; + // Make sure SceneDelegate class is loaded + Class cls = objc_getClass("SceneDelegate"); + if (!cls) { + NSLog(@"⚠️ SceneDelegate not found via objc_getClass"); + } else { + NSLog(@"✅ SceneDelegate loaded via objc_getClass"); } - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAudioSessionInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMediaServicesWereReset:) name:AVAudioSessionMediaServicesWereResetNotification object:nil]; - #if PPSSPP_PLATFORM(IOS_APP_STORE) [IAPManager sharedIAPManager]; // Kick off the IAPManager early. #endif // IOS_APP_STORE - return [self launchPPSSPP:argc argv:argv]; -} - -- (BOOL)launchPPSSPP:(int)argc argv:(char**)argv; -{ - NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; - NSString *bundlePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/assets/"]; - NativeInit(argc, (const char**)argv, documentsPath.UTF8String, bundlePath.UTF8String, NULL); - - self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - - // Choose viewcontroller depending on backend. - if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) { - PPSSPPViewControllerMetal *vc = [[PPSSPPViewControllerMetal alloc] init]; - - self.viewController = vc; - self.window.rootViewController = vc; - - } else { - PPSSPPViewControllerGL *vc = [[PPSSPPViewControllerGL alloc] init]; - // Here we can switch viewcontroller depending on backend. - self.viewController = vc; - self.window.rootViewController = vc; - } - - [self.window makeKeyAndVisible]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAudioSessionInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMediaServicesWereReset:) name:AVAudioSessionMediaServicesWereResetNotification object:nil]; return YES; } @@ -128,49 +104,7 @@ [[NSNotificationCenter defaultCenter] removeObserver:self]; } -- (void)restart:(const char*)restartArgs { - INFO_LOG(Log::G3D, "Restart requested: %s", restartArgs); - [self.viewController willResignActive]; - [self.viewController shutdown]; - self.window.rootViewController = nil; - self.viewController = nil; - - // App was requested to restart, probably. - INFO_LOG(Log::G3D, "viewController nilled"); - - NativeShutdown(); - - // TODO: Ignoring the command line for now. - // Hoping that overwriting the viewController works as expected... - [self launchPPSSPP:0 argv:nullptr]; - [self.viewController didBecomeActive]; -} - -- (void)applicationWillResignActive:(UIApplication *)application { - INFO_LOG(Log::G3D, "willResignActive"); - - [self.viewController willResignActive]; - - if (g_Config.bEnableSound) { - iOSCoreAudioShutdown(); - } - - System_PostUIMessage(UIMessage::LOST_FOCUS); -} - -- (void)applicationDidBecomeActive:(UIApplication *)application { - INFO_LOG(Log::G3D, "didBecomeActive"); - if (g_Config.bEnableSound) { - iOSCoreAudioInit(); - } - - System_PostUIMessage(UIMessage::GOT_FOCUS); - [self.viewController didBecomeActive]; -} - - (void)applicationWillTerminate:(UIApplication *)application { - // Seems like a bad idea. - // exit(0); } @end diff --git a/ios/IAPManager.mm b/ios/IAPManager.mm index 505cb2c3a9..e40cbb544d 100644 --- a/ios/IAPManager.mm +++ b/ios/IAPManager.mm @@ -215,4 +215,12 @@ static bool SafeStringEqual(NSString *a, NSString *b) { } } +- (void)productsRequest:(nonnull SKProductsRequest *)request didReceiveResponse:(nonnull SKProductsResponse *)response { + NSLog(@"Ignoring [productsRequest]"); +} + +- (void)paymentQueue:(nonnull SKPaymentQueue *)queue updatedTransactions:(nonnull NSArray *)transactions { + NSLog(@"Ignoring [paymentQueue]"); +} + @end diff --git a/ios/PPSSPP-Info.plist b/ios/PPSSPP-Info.plist index bbc2f42837..c23fc0655d 100644 --- a/ios/PPSSPP-Info.plist +++ b/ios/PPSSPP-Info.plist @@ -206,5 +206,20 @@ + UIApplicationSceneManifest + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + SceneDelegate + + + + diff --git a/ios/SceneDelegate.h b/ios/SceneDelegate.h new file mode 100644 index 0000000000..318e17628d --- /dev/null +++ b/ios/SceneDelegate.h @@ -0,0 +1,9 @@ +// SceneDelegate.h +#import + +@interface SceneDelegate : UIResponder + +@property (strong, nonatomic) UIWindow * window; +@property (strong, nonatomic) UIViewController *viewController; + +@end diff --git a/ios/SceneDelegate.mm b/ios/SceneDelegate.mm new file mode 100644 index 0000000000..e86c55dff1 --- /dev/null +++ b/ios/SceneDelegate.mm @@ -0,0 +1,119 @@ +// SceneDelegate.mm + +#import "SceneDelegate.h" +#import "AppDelegate.h" + +#import "iOSCoreAudio.h" +#import "ViewControllerCommon.h" +#import "ViewController.h" +#import "ViewControllerMetal.h" +#import "Common/System/NativeApp.h" +#import "Common/System/System.h" +#import "Core/System.h" +#import "Core/Config.h" +#import "Common/Log.h" +#import "IAPManager.h" + +#import + +@implementation SceneDelegate + ++ (void)load { + NSLog(@"✅ SceneDelegate class was loaded!"); +} + +- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { + if (![scene isKindOfClass:[UIWindowScene class]]) { + return; + } + + UIWindowScene *windowScene = (UIWindowScene *)scene; + if (self.window) { + NSLog(@"✅ Window already exists, not creating again!"); + return; + } + + AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate; + NSDictionary *launchOptions = appDelegate.launchOptions; + + int argc = 1; + char *argv[5]{}; + NSURL *nsUrl = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey]; + + if (nsUrl != nullptr && nsUrl.isFileURL) { + NSString *nsString = nsUrl.path; + const char *string = nsString.UTF8String; + argv[argc++] = (char*)string; + } + + NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; + NSString *bundlePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/assets/"]; + NativeInit(argc, (const char**)argv, documentsPath.UTF8String, bundlePath.UTF8String, NULL); + + self.window = [[UIWindow alloc] initWithWindowScene:windowScene]; +// self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + + // Choose viewcontroller depending on backend. + if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) { + PPSSPPViewControllerMetal *vc = [[PPSSPPViewControllerMetal alloc] init]; + + self.viewController = vc; + self.window.rootViewController = vc; + + } else { + PPSSPPViewControllerGL *vc = [[PPSSPPViewControllerGL alloc] init]; + // Here we can switch viewcontroller depending on backend. + self.viewController = vc; + self.window.rootViewController = vc; + } + + [self.window makeKeyAndVisible]; +} + +- (void)restart:(const char*)restartArgs { + INFO_LOG(Log::G3D, "SceneDelegate: Restart requested: %s", restartArgs); + + // Notify current view controller + [self.viewController willResignActive]; + [self.viewController shutdown]; + + // Remove the current root view controller + self.window.rootViewController = nil; + self.viewController = nil; + + INFO_LOG(Log::G3D, "SceneDelegate: viewController nilled"); + + // Shut down native systems + NativeShutdown(); + + // Launch a fresh instance + [self launchPPSSPP:0 argv:nullptr]; + + // Notify new view controller + [self.viewController didBecomeActive]; +} + +- (void)sceneWillResignActive:(UIScene *)scene { + INFO_LOG(Log::G3D, "sceneWillResignActive"); + + [self.viewController willResignActive]; + + if (g_Config.bEnableSound) { + iOSCoreAudioShutdown(); + } + + System_PostUIMessage(UIMessage::LOST_FOCUS); +} + +- (void)sceneDidBecomeActive:(UIScene *)scene { + INFO_LOG(Log::G3D, "sceneDidBecomeActive"); + + if (g_Config.bEnableSound) { + iOSCoreAudioInit(); + } + + System_PostUIMessage(UIMessage::GOT_FOCUS); + + [self.viewController didBecomeActive]; +} +@end From 1d44bf25bdfc8f8d05e81bcf18b0f74d3273c938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 17 Oct 2025 16:12:07 +0200 Subject: [PATCH 2/4] iOS: Default Vulkan presentation mode to Fifo as it's the only mode. --- Core/Config.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index c0362b5cfb..e0a5dc7a2a 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -202,6 +202,15 @@ static bool DefaultVSync() { #endif } +static int DefaultVulkanPresentMode() { +#if PPSSPP_PLATFORM(IOS) + // iOS only supports FIFO. + return (int)PresentMode::Fifo; +#else + return (int)PresentMode::Mailbox; +#endif +} + static bool DefaultEnableStateUndo() { #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) // Off on mobile to save disk space. @@ -730,7 +739,7 @@ static const ConfigSetting graphicsSettings[] = { ConfigSetting("TexDeposterize", &g_Config.bTexDeposterize, false, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("TexHardwareScaling", &g_Config.bTexHardwareScaling, false, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("VSync", &g_Config.bVSync, &DefaultVSync, CfgFlag::PER_GAME), - ConfigSetting("VulkanPresentMode", &g_Config.iVulkanPresentationMode, (int)PresentMode::Mailbox, CfgFlag::PER_GAME), + ConfigSetting("VulkanPresentMode", &g_Config.iVulkanPresentationMode, &DefaultVulkanPresentMode, CfgFlag::PER_GAME), ConfigSetting("BloomHack", &g_Config.iBloomHack, 0, CfgFlag::PER_GAME | CfgFlag::REPORT), // Not really a graphics setting... From 34385ea1fd78435826b9e2b3ac697f44ec74970a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 17 Oct 2025 21:07:00 +0200 Subject: [PATCH 3/4] Vulkan: Don't show the present mode setting if there's only one viable value. --- UI/GameSettingsScreen.cpp | 60 +++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index c987860497..193876f699 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -31,7 +31,7 @@ #include "Common/UI/Context.h" #include "Common/Render/ManagedTexture.h" #include "Common/VR/PPSSPPVR.h" - +#include "Common/BitSet.h" #include "Common/System/Display.h" // Only to check screen aspect ratio with pixel_yres/pixel_xres #include "Common/System/Request.h" #include "Common/System/OSD.h" @@ -376,36 +376,40 @@ void GameSettingsScreen::CreateGraphicsSettings(UI::ViewGroup *graphicsSettings) "FIFO: relaxed", "FIFO (higher latency, framerate stability)", }; - UI::PopupMultiChoice *presentationMode = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iVulkanPresentationMode, gr->T("Frame presentation mode"), presentationModes, 0, ARRAY_SIZE(presentationModes), I18NCat::GRAPHICS, screenManager())); - presentationMode->SetChoiceIcon(0, ImageID("I_WARNING")); - if (!(draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::IMMEDIATE)) { - presentationMode->HideChoice(0); - } - if (!(draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::MAILBOX)) { - presentationMode->HideChoice(1); - } - if (!(draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::FIFO_LATEST_READY)) { - presentationMode->HideChoice(2); - } - if (!(draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::FIFO_RELAXED)) { - presentationMode->HideChoice(3); - } - if (!(draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::FIFO)) { - presentationMode->HideChoice(4); - } - // Force the setting to a good supported mode if the current one is not supported, otherwise this will look weird. - if (presentationMode->IsChoiceHidden(g_Config.iVulkanPresentationMode)) { - if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::MAILBOX) { - g_Config.iVulkanPresentationMode = (int)Draw::PresentMode::MAILBOX; - } else if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::FIFO) { - g_Config.iVulkanPresentationMode = (int)Draw::PresentMode::FIFO; + // If only one mode is supported (like FIFO on iOS), no need to show the option. + if (CountSetBits((u32)draw->GetDeviceCaps().presentModesSupported) > 1) { + UI::PopupMultiChoice *presentationMode = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iVulkanPresentationMode, gr->T("Frame presentation mode"), presentationModes, 0, ARRAY_SIZE(presentationModes), I18NCat::GRAPHICS, screenManager())); + presentationMode->SetChoiceIcon(0, ImageID("I_WARNING")); + if (!(draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::IMMEDIATE)) { + presentationMode->HideChoice(0); + } + if (!(draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::MAILBOX)) { + presentationMode->HideChoice(1); + } + if (!(draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::FIFO_LATEST_READY)) { + presentationMode->HideChoice(2); + } + if (!(draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::FIFO_RELAXED)) { + presentationMode->HideChoice(3); + } + if (!(draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::FIFO)) { + presentationMode->HideChoice(4); } - } - presentationMode->OnChoice.Add([=](EventParams &e) { - NativeResized(); - }); + // Force the setting to a good supported mode if the current one is not supported, otherwise this will look weird. + if (presentationMode->IsChoiceHidden(g_Config.iVulkanPresentationMode)) { + if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::MAILBOX) { + g_Config.iVulkanPresentationMode = (int)Draw::PresentMode::MAILBOX; + } else if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::FIFO) { + g_Config.iVulkanPresentationMode = (int)Draw::PresentMode::FIFO; + } + } + + presentationMode->OnChoice.Add([=](EventParams &e) { + NativeResized(); + }); + } } else { // All backends support FIFO. Check if any immediate modes are supported, if so we can allow the user to choose. if (draw->GetDeviceCaps().presentModesSupported & (Draw::PresentMode::IMMEDIATE | Draw::PresentMode::MAILBOX)) { From 3cc57b7a2b9bf0c7690f182ef15a498037ed6107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 17 Oct 2025 21:07:53 +0200 Subject: [PATCH 4/4] iOS: Fix restarts after the scene change (for backend change etc) --- ios/AppDelegate.h | 1 - ios/SceneDelegate.h | 3 +++ ios/SceneDelegate.mm | 9 +++++++-- ios/main.mm | 14 +++++++++++++- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/ios/AppDelegate.h b/ios/AppDelegate.h index ecadddacff..3e48c9bfda 100644 --- a/ios/AppDelegate.h +++ b/ios/AppDelegate.h @@ -10,7 +10,6 @@ @property (strong, nonatomic) UIScreen *screen; @property (nonatomic, strong) NSDictionary *launchOptions; -- (void)restart:(const char *)args; - (BOOL)launchPPSSPP:(int)argc argv:(char**)argv; @end diff --git a/ios/SceneDelegate.h b/ios/SceneDelegate.h index 318e17628d..dba6229282 100644 --- a/ios/SceneDelegate.h +++ b/ios/SceneDelegate.h @@ -3,7 +3,10 @@ @interface SceneDelegate : UIResponder +- (void)restart:(const char *)args; + @property (strong, nonatomic) UIWindow * window; @property (strong, nonatomic) UIViewController *viewController; +@property (strong, nonatomic) UIWindowScene *windowScene; @end diff --git a/ios/SceneDelegate.mm b/ios/SceneDelegate.mm index e86c55dff1..cc722e336a 100644 --- a/ios/SceneDelegate.mm +++ b/ios/SceneDelegate.mm @@ -32,7 +32,12 @@ NSLog(@"✅ Window already exists, not creating again!"); return; } + self.windowScene = windowScene; + [self launchPPSSPP]; +} +-(void) launchPPSSPP { + INFO_LOG(Log::G3D, "SceneDelegate: Launching PPSSPP"); AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate; NSDictionary *launchOptions = appDelegate.launchOptions; @@ -50,7 +55,7 @@ NSString *bundlePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/assets/"]; NativeInit(argc, (const char**)argv, documentsPath.UTF8String, bundlePath.UTF8String, NULL); - self.window = [[UIWindow alloc] initWithWindowScene:windowScene]; + self.window = [[UIWindow alloc] initWithWindowScene:self.windowScene]; // self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Choose viewcontroller depending on backend. @@ -87,7 +92,7 @@ NativeShutdown(); // Launch a fresh instance - [self launchPPSSPP:0 argv:nullptr]; + [self launchPPSSPP]; // Notify new view controller [self.viewController didBecomeActive]; diff --git a/ios/main.mm b/ios/main.mm index 6d9f1b7689..47597ccaf4 100644 --- a/ios/main.mm +++ b/ios/main.mm @@ -26,6 +26,7 @@ #import "ViewController.h" #import "iOSCoreAudio.h" #import "IAPManager.h" +#import "SceneDelegate.h" #include "Common/MemoryUtil.h" #include "Common/Audio/AudioBackend.h" @@ -439,7 +440,18 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string switch (type) { case SystemRequestType::RESTART_APP: dispatch_async(dispatch_get_main_queue(), ^{ - [(AppDelegate *)[[UIApplication sharedApplication] delegate] restart:param1.c_str()]; + // Get the connected scenes + NSSet *scenes = [UIApplication sharedApplication].connectedScenes; + + // Loop through scenes to find a UIWindowScene that is active + for (UIScene *scene in scenes) { + if ([scene isKindOfClass:[UIWindowScene class]]) { + UIWindowScene *windowScene = (UIWindowScene *)scene; + SceneDelegate *sceneDelegate = (SceneDelegate *)windowScene.delegate; + [sceneDelegate restart:param1.c_str()]; + break; // call only on the first active scene + } + } }); return true;