Files
ppsspp/ios/ViewController.mm
T
Henrik RydgårdandClaude Opus 5.5 cfcf8cc17c iOS: Remove the iCade support
It has done nothing since 2017. iCade input came through iCadeReaderView,
a hidden UIKeyInput view that decoded the controller's keystrokes, and
f0ba66fcd6 ("add support for smart keyboard") commented out the code
that created it. Nothing has made one since, so the view controller's
iCadeEventDelegate methods and the ICadeTracker behind them were never
called.

iCade controllers present themselves to iOS as Bluetooth keyboards, so
their keys still arrive as hardware keyboard presses, which can be
mapped like any other keys. Modern controllers go through
GameController.framework anyway.

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

253 lines
6.4 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());
}
if ([[GCController controllers] count] > 0) {
[self setupController:[[GCController controllers] firstObject]];
}
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(){};