From 5b05b34b4e9c8eb5769bceb1f0b6885aa493c491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 13 Oct 2026 02:21:14 +0200 Subject: [PATCH] tvOS: Fewer ifdefs, capability checks instead The Siri Remote setup builds on iOS too, the device type comes from the interface idiom, and requests for things the platform says it lacks are refused by checking the property. What's left ifdeffed is API tvOS doesn't have at all. Reports say tvOS. Co-Authored-By: Claude Opus 5.5 --- Core/Reporting.cpp | 2 ++ ios/Controls.h | 2 -- ios/Controls.mm | 4 ---- ios/SceneDelegate.mm | 6 +----- ios/ViewController.h | 1 + ios/ViewController.mm | 19 ++++++++----------- ios/main.mm | 37 ++++++++++++++++--------------------- 7 files changed, 28 insertions(+), 43 deletions(-) diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index 2a767cdcf1..9e1e053de4 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.cpp @@ -330,6 +330,8 @@ namespace Reporting return "Windows 64"; #elif defined(_WIN32) return "Windows"; +#elif PPSSPP_PLATFORM(TVOS) + return "tvOS"; #elif PPSSPP_PLATFORM(IOS) return "iOS"; #elif defined(__APPLE__) diff --git a/ios/Controls.h b/ios/Controls.h index 1783fd2d2d..5e1af38e2d 100644 --- a/ios/Controls.h +++ b/ios/Controls.h @@ -17,10 +17,8 @@ bool InitController(GCController *controller); void ShutdownController(GCController *controller); -#if PPSSPP_PLATFORM(TVOS) // Returns false unless the controller is a Siri Remote (a micro gamepad profile and no extended one). bool InitSiriRemote(GCController *controller); -#endif struct TouchTracker { public: diff --git a/ios/Controls.mm b/ios/Controls.mm index 8a5e41f3b4..a43b5d8240 100644 --- a/ios/Controls.mm +++ b/ios/Controls.mm @@ -204,8 +204,6 @@ void ShutdownController(GCController *controller) { KeyMap::NotifyPadDisconnected(DEVICE_ID_PAD_0); } -#if PPSSPP_PLATFORM(TVOS) - static void remoteButtonPressed(BOOL pressed, InputKeyCode keyCode) { // As keyboard keys, which the UI already understands (arrows, enter, escape), and which have // default in-game mappings. @@ -301,8 +299,6 @@ bool InitSiriRemote(GCController *controller) { return true; } -#endif - void TouchTracker::SendTouchEvent(CGPoint point, UIView *view, int code, int pointerId) { // From points to dp. The view covers the whole display. CGSize size = view.bounds.size; diff --git a/ios/SceneDelegate.mm b/ios/SceneDelegate.mm index 7552a4c8b9..0f7bfeed37 100644 --- a/ios/SceneDelegate.mm +++ b/ios/SceneDelegate.mm @@ -168,12 +168,8 @@ static NSString *ExtractGameInfoScheme(NSURL *url) { break; } -#if PPSSPP_PLATFORM(TVOS) - // No persistent storage on tvOS. The memory stick folder doubles as the documents folder. + // Documents, except on tvOS, which has no persistent storage. See defaultMemoryStickPath(). NSString *documentsPath = @(DarwinFileSystemServices::defaultMemoryStickPath().c_str()); -#else - NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; -#endif NSString *bundlePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/assets/"]; // Caches the system may clear when space runs low, and the zips ShareFilesAsZip hands to other apps. NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0]; diff --git a/ios/ViewController.h b/ios/ViewController.h index 07a7105963..69e2b27701 100644 --- a/ios/ViewController.h +++ b/ios/ViewController.h @@ -55,3 +55,4 @@ extern PPSSPPViewController *sharedViewController; #define IS_IPAD() ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) #define IS_IPHONE() ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) +#define IS_TV() ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomTV) diff --git a/ios/ViewController.mm b/ios/ViewController.mm index 0f2b1bf64f..0e00e878e8 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -374,11 +374,9 @@ static int GetPickerRequestId(id picker) { { if (![[GCController controllers] containsObject:self.gameController]) self.gameController = nil; -#if PPSSPP_PLATFORM(TVOS) if (InitSiriRemote((GCController *)note.object)) { return; } -#endif if (self.gameController != nil) return; // already have a connected controller @@ -404,11 +402,9 @@ static int GetPickerRequestId(id picker) { // Picks the first gamepad. On tvOS, the Siri Remote is in the list too, and gets set up separately. - (void)setupFirstController { for (GCController *controller in [GCController controllers]) { -#if PPSSPP_PLATFORM(TVOS) if (InitSiriRemote(controller)) { continue; } -#endif if (self.gameController == nil) { [self setupController:controller]; } @@ -665,7 +661,7 @@ extern float g_safeInsetBottom; - (void)shareItems:(NSArray *)items { #if PPSSPP_PLATFORM(TVOS) - // No share sheet on tvOS. SYSPROP_SUPPORTS_SHARE_* is false there, so this shouldn't be reached. + // No share sheet on tvOS. SYSPROP_SUPPORTS_SHARE_* is false there, so this isn't reached. WARN_LOG(Log::System, "Sharing isn't available on tvOS"); #else UIActivityViewController *viewController = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil]; @@ -799,12 +795,13 @@ extern float g_safeInsetBottom; // The bounds are already corrected for orientation. CGSize size = view.bounds.size; -#if PPSSPP_PLATFORM(TVOS) - // Like a 1080p Android TV, which reports 320 dpi: 1920x1080 points come out as 1440x810 dp. - float dpi = 320.0f * scale; -#else - float dpi = (IS_IPAD() ? 200.0f : 150.0f) * scale; -#endif + float dpi; + if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_TV) { + // Like a 1080p Android TV, which reports 320 dpi: 1920x1080 points come out as 1440x810 dp. + dpi = 320.0f * scale; + } else { + dpi = (IS_IPAD() ? 200.0f : 150.0f) * scale; + } PendingResolution res{ (float)(size.width * scale), (float)(size.height * scale), 240.0f / dpi, diff --git a/ios/main.mm b/ios/main.mm index 16baf21089..85231886f8 100644 --- a/ios/main.mm +++ b/ios/main.mm @@ -285,11 +285,7 @@ int64_t System_GetPropertyInt(SystemProperty prop) { case SYSPROP_AUDIO_SAMPLE_RATE: return 44100; case SYSPROP_DEVICE_TYPE: -#if PPSSPP_PLATFORM(TVOS) - return DEVICE_TYPE_TV; -#else - return DEVICE_TYPE_MOBILE; -#endif + return IS_TV() ? DEVICE_TYPE_TV : DEVICE_TYPE_MOBILE; case SYSPROP_SYSTEMVERSION: return g_iosVersionMajor; #if !PPSSPP_PLATFORM(TVOS) @@ -467,22 +463,6 @@ void System_Notify(SystemNotification notification) { } bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int64_t param3, int64_t param4) { -#if PPSSPP_PLATFORM(TVOS) - switch (type) { - case SystemRequestType::BROWSE_FOR_FILE: - case SystemRequestType::BROWSE_FOR_FOLDER: - case SystemRequestType::BROWSE_FOR_IMAGE: - case SystemRequestType::CAMERA_COMMAND: - case SystemRequestType::GPS_COMMAND: - case SystemRequestType::MICROPHONE_COMMAND: - case SystemRequestType::SHARE_TEXT: - case SystemRequestType::SHARE_FILE: - case SystemRequestType::COPY_TO_CLIPBOARD: - return false; - default: - break; - } -#endif switch (type) { case SystemRequestType::RESTART_APP: { @@ -515,6 +495,9 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string break; case SystemRequestType::BROWSE_FOR_FILE: { + if (!System_GetPropertyBool(SYSPROP_HAS_FILE_BROWSER)) { + return false; + } DarwinDirectoryPanelCallback callback = [requestId] (bool success, Path path) { if (success) { g_requestManager.PostSystemSuccess(requestId, path.c_str()); @@ -527,6 +510,9 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string } case SystemRequestType::BROWSE_FOR_FOLDER: { + if (!System_GetPropertyBool(SYSPROP_HAS_FOLDER_BROWSER)) { + return false; + } DarwinDirectoryPanelCallback callback = [requestId] (bool success, Path path) { if (success) { g_requestManager.PostSystemSuccess(requestId, path.c_str()); @@ -539,6 +525,9 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string } case SystemRequestType::BROWSE_FOR_IMAGE: { + if (!System_GetPropertyBool(SYSPROP_HAS_IMAGE_BROWSER)) { + return false; + } NSString *filename = [NSString stringWithUTF8String:param2.c_str()]; dispatch_async(dispatch_get_main_queue(), ^{ [sharedViewController pickPhoto:filename requestId:requestId]; @@ -591,6 +580,9 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string return true; case SystemRequestType::SHARE_TEXT: { + if (!System_GetPropertyBool(SYSPROP_SUPPORTS_SHARE_TEXT)) { + return false; + } NSString *text = [NSString stringWithUTF8String:param1.c_str()]; dispatch_async(dispatch_get_main_queue(), ^{ [sharedViewController shareText:text]; @@ -599,6 +591,9 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string } case SystemRequestType::SHARE_FILE: { + if (!System_GetPropertyBool(SYSPROP_SUPPORTS_SHARE_FILE)) { + return false; + } // param2, the MIME type, isn't needed. The share sheet goes by the extension. NSString *path = [NSString stringWithUTF8String:param1.c_str()]; if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {