Merge pull request #20926 from hrydgard/more-ios-fixes

iOS: Refactor the GL ViewController
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2025-10-27 20:06:53 +01:00
commit eeacc09fdc
11 files changed
+162 -55

No files matched your search

+1
View File
@@ -1349,6 +1349,7 @@ elseif(IOS AND NOT LIBRETRO)
ios/Controls.h
ios/Controls.mm
ios/ViewControllerCommon.h
ios/ViewControllerCommon.mm
ios/ViewController.mm
ios/ViewController.h
ios/ViewControllerMetal.mm
-2
View File
@@ -145,8 +145,6 @@ Bounds UIContext::GetLayoutBounds() const {
float top = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP);
float bottom = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM);
// ILOG("Insets: %f %f %f %f", left, right, top, bottom);
// Adjust left edge to compensate for cutouts (notches) if any.
bounds.x += left;
bounds.w -= (left + right);
+1 -1
View File
@@ -546,7 +546,7 @@ enum class MultiPartResult {
static MultiPartResult HandleMultipartPart(const http::ServerRequest &request, std::string boundary, const Path &uploadPath, ProgressTracker &progress) {
std::string firstBoundary = request.In()->ReadLine();
if (firstBoundary != "--" + boundary) {
WARN_LOG(Log::HTTP, "Bad boundary: Expected --%s but got %s", boundary.c_str());
WARN_LOG(Log::HTTP, "Bad boundary: Expected --%s but got %s", boundary.c_str(), firstBoundary.c_str());
return MultiPartResult::RequestError;
}
+2 -2
View File
@@ -495,8 +495,8 @@ void DrawFPS(UIContext *ctx, const Bounds &bounds) {
ctx->BindFontTexture();
ctx->Draw()->SetFontScale(0.7f, 0.7f);
ctx->Draw()->DrawText(ubuntu24, w.as_view(), bounds.x2() - 8, 20, 0xc0000000, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII);
ctx->Draw()->DrawText(ubuntu24, w.as_view(), bounds.x2() - 10, 19, 0xFF3fFF3f, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII);
ctx->Draw()->DrawText(ubuntu24, w.as_view(), bounds.x2() - 8, bounds.y + 10, 0xc0000000, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII);
ctx->Draw()->DrawText(ubuntu24, w.as_view(), bounds.x2() - 10, bounds.y + 8, 0xFF3fFF3f, ALIGN_TOPRIGHT | FLAG_DYNAMIC_ASCII);
ctx->Draw()->SetFontScale(1.0f, 1.0f);
ctx->Flush();
ctx->RebindTexture();
+4 -4
View File
@@ -1128,7 +1128,7 @@ GameBrowser *MainScreen::CreateBrowserTab(const Path &path, std::string_view tit
return gameBrowser;
}
UI::ViewGroup *MainScreen::CreateLogoView(UI::LayoutParams *layoutParams) {
UI::ViewGroup *MainScreen::CreateLogoView(bool portrait, UI::LayoutParams *layoutParams) {
using namespace UI;
AnchorLayout *logos = new AnchorLayout(layoutParams);
if (System_GetPropertyBool(SYSPROP_APP_GOLD)) {
@@ -1156,7 +1156,7 @@ UI::ViewGroup *MainScreen::CreateLogoView(UI::LayoutParams *layoutParams) {
ver->SetClip(false);
// Only allow copying the version if it looks like a git version string. 1.19 for example is not really necessary to be able to copy/paste.
if (strchr(PPSSPP_GIT_VERSION, '-')) {
if (!portrait && strchr(PPSSPP_GIT_VERSION, '-')) {
ver->OnClick.Add([](UI::EventParams &e) {
auto di = GetI18NCategory(I18NCat::DIALOG);
System_CopyStringToClipboard(PPSSPP_GIT_VERSION);
@@ -1298,7 +1298,7 @@ void MainScreen::CreateViews() {
if (vertical) {
LinearLayout *header = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
header->SetSpacing(5.0f);
header->Add(CreateLogoView(new LinearLayoutParams(WRAP_CONTENT, 80.0f, false)));
header->Add(CreateLogoView(true, new LinearLayoutParams(WRAP_CONTENT, 80.0f, false)));
LinearLayout *buttonGroup = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1.0f, UI::Gravity::G_VCENTER, UI::Margins(0,0,8,0)));
@@ -1317,7 +1317,7 @@ void MainScreen::CreateViews() {
ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins));
LinearLayout *rightColumnItems = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
rightColumnItems->SetSpacing(0.0f);
ViewGroup *logo = CreateLogoView(new LinearLayoutParams(FILL_PARENT, 80.0f, false));
ViewGroup *logo = CreateLogoView(false, new LinearLayoutParams(FILL_PARENT, 80.0f));
#if !defined(MOBILE_DEVICE)
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
ImageID icon(g_Config.UseFullScreen() ? "I_RESTORE" : "I_FULLSCREEN");
+1 -1
View File
@@ -136,7 +136,7 @@ protected:
void CreateViews() override;
void CreateRecentTab();
GameBrowser *CreateBrowserTab(const Path &path, std::string_view title, std::string_view howToTitle, std::string_view howToUri, BrowseFlags browseFlags, bool *bGridView, float *scrollPos);
UI::ViewGroup *CreateLogoView(UI::LayoutParams *layoutParams);
UI::ViewGroup *CreateLogoView(bool portrait, UI::LayoutParams *layoutParams);
void CreateMainButtons(UI::ViewGroup *parent, bool vertical);
void DrawBackground(UIContext &dc) override;
+6 -6
View File
@@ -108,13 +108,13 @@ bool InitController(GCController *controller) {
};
}
#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_HOME);
};
if (@available(iOS 14.0, *)) {
if ([extendedProfile respondsToSelector:@selector(buttonHome)] && extendedProfile.buttonHome != nil) {
extendedProfile.buttonHome.valueChangedHandler = ^(GCControllerButtonInput *button, float value, BOOL pressed) {
controllerButtonPressed(pressed, NKCODE_HOME);
};
}
}
#endif
extendedProfile.leftThumbstick.xAxis.valueChangedHandler = ^(GCControllerAxisInput *axis, float value) {
AxisInput axisInput;
+6 -1
View File
@@ -10,8 +10,13 @@
#include "ViewControllerCommon.h"
@interface PPSSPPViewControllerGL : GLKViewController <
@interface PPSSPPViewControllerGL : UIViewController <GLKViewDelegate,
iCadeEventDelegate, LocationHandlerDelegate, CameraFrameDelegate,
UIGestureRecognizerDelegate, UIKeyInput, PPSSPPViewController,
UIImagePickerControllerDelegate, UINavigationControllerDelegate>
// Public-ish control similar to GLKViewController
@property (nonatomic, assign) NSInteger preferredFramesPerSecond; // default 60
@end
+124 -35
View File
@@ -3,18 +3,20 @@
//
// Created by rock88
// Modified by xSacha
//
// Reworked by hrydgard
#import "AppDelegate.h"
#import "ViewController.h"
#import "DisplayManager.h"
#include "Controls.h"
#import "iOSCoreAudio.h"
#import "IAPManager.h"
#import <GLKit/GLKit.h>
#include <cassert>
#import "ViewController.h"
#import "Controls.h"
#import <QuartzCore/QuartzCore.h>
#include <cassert>
#include "Common/Net/Resolve.h"
#include "Common/UI/Screen.h"
#include "Common/GPU/thin3d.h"
@@ -109,9 +111,13 @@ id<PPSSPPViewController> sharedViewController;
NSString *imageFilename;
}
@property (nonatomic, strong) EAGLContext *glContext;
@property (nonatomic, strong) GLKView *glView;
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, assign) NSTimeInterval lastTimestamp;
@property (nonatomic, strong) EAGLContext* context;
//@property (nonatomic) iCadeReaderView* iCadeView;
@property (nonatomic) GCController *gameController __attribute__((weak_import));
@property (strong, nonatomic) CMMotionManager *motionManager;
@property (strong, nonatomic) NSOperationQueue *accelerometerQueue;
@@ -122,9 +128,11 @@ id<PPSSPPViewController> sharedViewController;
UIScreenEdgePanGestureRecognizer *mBackGestureRecognizer;
}
-(id) init {
self = [super init];
if (self) {
_preferredFramesPerSecond = 60; // default
sharedViewController = self;
g_iCadeTracker.InitKeyMap();
@@ -251,23 +259,45 @@ void GLRenderLoop(IOSGLESContext *graphicsContext) {
- (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.view addSubview:self.glView];
// 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:)];
if (@available(iOS 10.0, *)) {
self.displayLink.preferredFramesPerSecond = (NSInteger)self.preferredFramesPerSecond;
} else {
// older iOS: approximate with frameInterval
self.displayLink.frameInterval = MAX(1, (NSInteger)round(60.0 / self.preferredFramesPerSecond));
}
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
self.lastTimestamp = 0;
[[DisplayManager shared] setupDisplayListener];
UIScreen* screen = [(AppDelegate*)[UIApplication sharedApplication].delegate screen];
self.view.frame = [screen bounds];
self.view.multipleTouchEnabled = YES;
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
if (!self.context) {
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
}
GLKView* view = (GLKView *)self.view;
view.context = self.context;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
view.drawableStencilFormat = GLKViewDrawableStencilFormat8;
[EAGLContext setCurrentContext:self.context];
self.preferredFramesPerSecond = 60; // NOTE: We don't yet take advantage of 120hz screens
graphicsContext = new IOSGLESContext();
@@ -299,8 +329,79 @@ void GLRenderLoop(IOSGLESContext *graphicsContext) {
INFO_LOG(Log::G3D, "Done with viewDidLoad.");
}
- (void)handleSwipeFrom:(UIScreenEdgePanGestureRecognizer *)recognizer
{
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.glView.frame = self.view.bounds;
/*
// if you need to update viewport:
CGSize s = self.glView.bounds.size;
[EAGLContext setCurrentContext:self.glContext];
glViewport(0, 0, (GLsizei)round(s.width), (GLsizei)round(s.height));
*/
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Resume display link unless explicitly paused
INFO_LOG(Log::G3D, "viewWillAppear - resuming display link");
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// stop rendering while not visible
INFO_LOG(Log::G3D, "viewWillDisappear - pausing display link");
}
- (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;
if (self.displayLink) {
if (@available(iOS 10.0, *)) {
self.displayLink.preferredFramesPerSecond = (NSInteger)preferredFramesPerSecond;
} else {
self.displayLink.frameInterval = MAX(1, (NSInteger)round(60.0 / preferredFramesPerSecond));
}
}
}
- (void)displayLinkFired:(CADisplayLink *)dl {
// compute delta time
NSTimeInterval timestamp = dl.timestamp;
NSTimeInterval delta = 0;
if (self.lastTimestamp > 0) {
delta = timestamp - self.lastTimestamp;
} else {
delta = dl.duration; // fallback
}
self.lastTimestamp = timestamp;
// Ensure context is current before drawing
[EAGLContext setCurrentContext:self.glContext];
// Trigger GLKView draw
[self.glView display];
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
if (!renderLoopRunning) {
INFO_LOG(Log::G3D, "Ignoring drawInRect");
return;
}
if (sharedViewController) {
graphicsContext->ThreadFrame(true);
}
}
- (void)handleSwipeFrom:(UIScreenEdgePanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded) {
KeyInput key;
key.flags = KEY_DOWN | KEY_UP;
@@ -337,6 +438,8 @@ void GLRenderLoop(IOSGLESContext *graphicsContext) {
[[DisplayManager shared] updateResolution:[UIScreen mainScreen]];
INFO_LOG(Log::System, "didBecomeActive end");
self.displayLink.paused = NO;
}
- (void)willResignActive {
@@ -349,6 +452,8 @@ void GLRenderLoop(IOSGLESContext *graphicsContext) {
[self.motionManager stopAccelerometerUpdates];
}
INFO_LOG(Log::System, "willResignActive end");
self.displayLink.paused = YES;
}
- (void)shutdown
@@ -385,27 +490,11 @@ void GLRenderLoop(IOSGLESContext *graphicsContext) {
INFO_LOG(Log::System, "Done shutting down GL");
}
- (void)dealloc
{
INFO_LOG(Log::System, "dealloc GL");
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
if (!renderLoopRunning) {
INFO_LOG(Log::G3D, "Ignoring drawInRect");
return;
}
if (sharedViewController) {
graphicsContext->ThreadFrame(true);
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
g_touchTracker.Began(touches, self.view);
@@ -428,7 +517,7 @@ void GLRenderLoop(IOSGLESContext *graphicsContext) {
- (void)bindDefaultFBO
{
[(GLKView*)self.view bindDrawable];
[(GLKView*)self.glView bindDrawable];
}
- (void)buttonDown:(iCadeState)button
+2
View File
@@ -0,0 +1,2 @@
#import "ios/ViewControllerCommon.h"
+15 -3
View File
@@ -491,13 +491,25 @@ extern float g_safeInsetRight;
extern float g_safeInsetTop;
extern float g_safeInsetBottom;
static float BoostInset(float inset) {
if (inset > 0.0f) {
// If there's some inset, add a few pixels extra. Really needed on iPhone 12, at least.
inset += 4.0f;
}
return inset;
}
- (void)viewSafeAreaInsetsDidChange {
if (@available(iOS 11.0, *)) {
[super viewSafeAreaInsetsDidChange];
// 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;
g_safeInsetTop = self.view.safeAreaInsets.top;
g_safeInsetLeft = BoostInset(self.view.safeAreaInsets.left);
g_safeInsetRight = BoostInset(self.view.safeAreaInsets.right);
g_safeInsetTop = BoostInset(self.view.safeAreaInsets.top);
// TODO: In portrait mode, should probably use safeAreaInsets.bottom.
// However, in landscape mode, it's not really needed.
// g_safeInsetBottom = BoostInset(self.view.safeAreaInsets.bottom);
g_safeInsetBottom = 0.0f;
}
}