Files
ppsspp/ios/ViewController.mm
T
Henrik RydgårdandClaude Opus 5.5 6fe0f420b1 iOS: Drop version checks for iOS 12 and older, use one deployment target
The minimum is iOS 13 (15 for the App Store build), so the iOS 10
display link fallbacks, the iOS 13 orientation check, the weak import
of GCController and the respondsToSelector checks for APIs from iOS 8
to 13 can go.

The keyboard presses checked for iOS 13.0 but used UIPress.key outside
the check, which needs 13.4, so it would crash on 13.0-13.3 if it ever
runs there. It only runs in App Store builds now. Folded the two copies
into one function with the right check.

The deployment target was 13.0 in the toolchain's compile flags but
12.0 in CMAKE_OSX_DEPLOYMENT_TARGET and 11.0 in the Xcode attribute for
non-App Store builds. All three now come from the toolchain file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:28:27 +02:00

260 lines
6.6 KiB
Plaintext

//
// ViewController.m
//
// Created by rock88
// Modified by xSacha
// Reworked by hrydgard
#import "ViewController.h"
#import "iOSCoreAudio.h"
#import <GLKit/GLKit.h>
#import <QuartzCore/QuartzCore.h>
#include <cassert>
#include "Common/Net/Resolve.h"
#include "Common/UI/Screen.h"
#include "Common/GPU/thin3d.h"
#include "Common/GPU/thin3d_create.h"
#include "Common/GPU/OpenGL/GLRenderManager.h"
#include "Common/GPU/OpenGL/OpenGLGraphicsContext.h"
#include "Common/System/Display.h"
#include "Common/System/System.h"
#include "Common/System/OSD.h"
#include "Common/System/NativeApp.h"
#include "Common/System/Request.h"
#include "Common/File/VFS/VFS.h"
#include "Common/Thread/ThreadUtil.h"
#include "Common/Log.h"
#include "Common/TimeUtil.h"
#include "Common/Input/InputState.h"
#include "Common/Input/KeyCodes.h"
#include "Common/GPU/GraphicsContext.h"
#include "Core/Config.h"
#include "Core/ConfigValues.h"
#include "Core/EmuThread.h"
#include "Core/KeyMap.h"
#include "Core/System.h"
#if !__has_feature(objc_arc)
#error Must be built with ARC, please revise the flags for ViewController.mm to include -fobjc-arc.
#endif
static std::thread g_emuThread;
PPSSPPBaseViewController *sharedViewController;
@interface PPSSPPViewControllerGL () {
GraphicsContext *graphicsContext;
}
@property (nonatomic, strong) EAGLContext *glContext;
@property (nonatomic, strong) GLKView *glView;
@property (nonatomic, strong) CADisplayLink *displayLink;
@end
@implementation PPSSPPViewControllerGL {}
-(id) init {
self = [super init];
if (self) {
_preferredFramesPerSecond = 60; // default
}
return self;
}
- (bool)runGLRenderLoop {
if (!graphicsContext) {
ERROR_LOG(Log::G3D, "runGLRenderLoop: Tried to enter without a created graphics context.");
return false;
}
if (g_emuThread.joinable()) {
ERROR_LOG(Log::G3D, "runGLRenderLoop: Already running");
return false;
}
g_emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), [](GraphicsContext *graphicsContext){
NativeFrame(graphicsContext);
return true;
});
return true;
}
- (void)requestExitGLRenderLoop {
if (!g_emuThread.joinable()) {
INFO_LOG(Log::G3D, "requestExitGLRenderLoop: Not running");
return;
}
EmuThread_Join(graphicsContext, g_emuThread);
_assert_(!g_emuThread.joinable());
}
- (void)viewDidLoad {
[super viewDidLoad];
// 1) Create GL context
self.glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
if (!self.glContext) {
self.glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
}
NSAssert(self.glContext != nil, @"Failed to create EAGLContext");
// 2) Create GLKView
self.glView = [[GLKView alloc] initWithFrame:self.view.bounds context:self.glContext];
self.glView.delegate = self;
self.glView.enableSetNeedsDisplay = NO; // We'll call display manually
self.glView.drawableDepthFormat = GLKViewDrawableDepthFormat24;
self.glView.drawableStencilFormat = GLKViewDrawableStencilFormat8;
self.glView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.glView.multipleTouchEnabled = YES;
[self.view addSubview:self.glView];
// self.view.insetsLayoutMarginsFromSafeArea = NO;
// self.view.clipsToBounds = YES;
// Put context current for initial GL setup
[EAGLContext setCurrentContext:self.glContext];
// Here we can do one time GL init if we want.
// 3) Setup display link
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkFired:)];
self.displayLink.preferredFramesPerSecond = (NSInteger)self.preferredFramesPerSecond;
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
self.view.multipleTouchEnabled = YES;
graphicsContext = new OpenGLGraphicsContext();
std::string errorMessage;
if (!graphicsContext->InitAPI(nullptr, nullptr, &errorMessage)) {
ERROR_LOG(Log::G3D, "InitAPI failed: %s", errorMessage.c_str());
}
if (!graphicsContext->InitSurface(WINDOWSYSTEM_NONE, nullptr, nullptr, &errorMessage)) {
ERROR_LOG(Log::G3D, "InitSurface failed: %s", errorMessage.c_str());
}
/*self.iCadeView = [[iCadeReaderView alloc] init];
[self.view addSubview:self.iCadeView];
self.iCadeView.delegate = self;
self.iCadeView.active = YES;*/
if ([[GCController controllers] count] > 0) {
[self setupController:[[GCController controllers] firstObject]];
}
[self hideKeyboard];
INFO_LOG(Log::G3D, "Done with viewDidLoad.");
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.glView.frame = self.view.bounds;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
INFO_LOG(Log::G3D, "viewWillAppear");
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
INFO_LOG(Log::G3D, "viewWillDisappear");
}
- (void)dealloc {
[self.displayLink invalidate];
self.displayLink = nil;
if ([EAGLContext currentContext] == self.glContext) {
[EAGLContext setCurrentContext:nil];
}
self.glContext = nil;
}
- (void)setPreferredFramesPerSecond:(NSInteger)preferredFramesPerSecond {
_preferredFramesPerSecond = preferredFramesPerSecond;
self.displayLink.preferredFramesPerSecond = (NSInteger)preferredFramesPerSecond;
}
- (void)displayLinkFired:(CADisplayLink *)dl {
// Ensure context is current before drawing
[EAGLContext setCurrentContext:self.glContext];
// Trigger GLKView draw
[self.glView display];
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
if (!g_emuThread.joinable()) {
INFO_LOG(Log::G3D, "Ignoring drawInRect");
return;
}
if (sharedViewController) {
if (!graphicsContext->ThreadFrame()) {
INFO_LOG(Log::G3D, "ThreadFrame returned false");
}
}
}
- (void)didBecomeActive {
[super didBecomeActive];
INFO_LOG(Log::System, "didBecomeActive begin");
[self updateResolutionWithView:self.view];
[self runGLRenderLoop];
INFO_LOG(Log::System, "didBecomeActive end");
self.displayLink.paused = NO;
}
- (void)willResignActive {
INFO_LOG(Log::System, "willResignActive GL");
[self requestExitGLRenderLoop];
self.displayLink.paused = YES;
[super willResignActive];
}
- (void)shutdown {
[super shutdown];
INFO_LOG(Log::System, "shutdown GL");
g_Config.Save("shutdown GL");
_dbg_assert_(graphicsContext);
// Normally already done by willResignActive, but not when the app is terminated.
[self requestExitGLRenderLoop];
graphicsContext->ShutdownSurface();
graphicsContext->ShutdownAPI();
delete graphicsContext;
graphicsContext = nullptr;
INFO_LOG(Log::System, "Done shutting down GL");
}
- (void)bindDefaultFBO
{
[(GLKView*)self.glView bindDrawable];
}
@end
void bindDefaultFBO()
{
[sharedViewController bindDefaultFBO];
}
void EnableFZ(){};
void DisableFZ(){};