mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-12 17:19:28 +01:00
Merge pull request #12094 from imxieyi/master
External display support on iOS
This commit is contained in:
8 files changed
+241
-33
No files matched your search
@@ -735,6 +735,8 @@ elseif(IOS)
|
||||
ios/main.mm
|
||||
ios/AppDelegate.mm
|
||||
ios/AppDelegate.h
|
||||
ios/DisplayManager.h
|
||||
ios/DisplayManager.mm
|
||||
ios/ViewController.mm
|
||||
ios/ViewController.h
|
||||
ios/iOSCoreAudio.mm
|
||||
@@ -1986,6 +1988,7 @@ if(TargetBin)
|
||||
endif()
|
||||
|
||||
if(IOS)
|
||||
list(APPEND NativeAssets ios/Settings.bundle)
|
||||
add_executable(${TargetBin} MACOSX_BUNDLE ${ICON_PATH_ABS} ${NativeAssets} ${SHADER_FILES} ${FLASH0_FILES} ${LANG_FILES} ${NativeAppSource} "ios/Launch Screen.storyboard")
|
||||
else()
|
||||
add_executable(${TargetBin} MACOSX_BUNDLE ${ICON_PATH_ABS} ${NativeAssets} ${SHADER_FILES} ${FLASH0_FILES} ${LANG_FILES} ${NativeAppSource})
|
||||
@@ -2042,6 +2045,7 @@ if(IOS)
|
||||
set_target_properties(${TargetBin} PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/ios/PPSSPP-Info.plist"
|
||||
RESOURCE "ios/Launch Screen.storyboard"
|
||||
RESOURCE "ios/Settings.bundle"
|
||||
XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET}
|
||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "iPhone/iPad"
|
||||
XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
@property (strong, nonatomic) UIWindow *window;
|
||||
@property (strong, nonatomic) UIScreen *screen;
|
||||
|
||||
@property (strong, nonatomic) ViewController *viewController;
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// DisplayManager.h
|
||||
// native
|
||||
//
|
||||
// Created by xieyi on 2019/6/9.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface DisplayManager : NSObject
|
||||
|
||||
- (void)setupDisplayListener;
|
||||
- (void)updateResolution:(UIScreen *)screen;
|
||||
|
||||
@property (nonatomic, strong) UIScreen *mainScreen;
|
||||
|
||||
@property (class, readonly, strong) DisplayManager *shared;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,179 @@
|
||||
//
|
||||
// DisplayManager.m
|
||||
// native
|
||||
//
|
||||
// Created by xieyi on 2019/6/9.
|
||||
//
|
||||
|
||||
#import "DisplayManager.h"
|
||||
#import "ViewController.h"
|
||||
#import "AppDelegate.h"
|
||||
#include "base/display.h"
|
||||
#include "base/NativeApp.h"
|
||||
#include "Core/System.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#define IS_IPAD() ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
|
||||
|
||||
@interface DisplayManager ()
|
||||
|
||||
@property BOOL listenerActive;
|
||||
@property (atomic, retain) NSMutableArray<UIScreen *> *extDisplays;
|
||||
@property CGRect originalFrame;
|
||||
@property CGRect originalBounds;
|
||||
@property CGAffineTransform originalTransform;
|
||||
|
||||
- (void)updateScreen:(UIScreen *)screen;
|
||||
|
||||
@end
|
||||
|
||||
@implementation DisplayManager
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self setListenerActive:NO];
|
||||
[self setExtDisplays:[[NSMutableArray<UIScreen *> alloc] init]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (DisplayManager *)shared {
|
||||
static DisplayManager *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[DisplayManager alloc] init];
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (void)setupDisplayListener {
|
||||
// Disable external display by default
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"enable_external_display"] == NO) {
|
||||
return;
|
||||
}
|
||||
if ([self listenerActive]) {
|
||||
NSLog(@"setupDisplayListener already called");
|
||||
return;
|
||||
}
|
||||
NSLog(@"Setting up display manager");
|
||||
[self setMainScreen:[UIScreen mainScreen]];
|
||||
UIWindow *gameWindow = [(AppDelegate *)[[UIApplication sharedApplication] delegate] window];
|
||||
[self setOriginalFrame: [gameWindow frame]];
|
||||
[self setOriginalBounds:[gameWindow bounds]];
|
||||
[self setOriginalTransform:[gameWindow transform]];
|
||||
// Display connected
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:UIScreenDidConnectNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull notification) {
|
||||
UIScreen *screen = (UIScreen *) notification.object;
|
||||
NSLog(@"New display connected: %@", [screen debugDescription]);
|
||||
[[self extDisplays] addObject:screen];
|
||||
// Do not switch to second connected display
|
||||
if ([self mainScreen] != [UIScreen mainScreen]) {
|
||||
return;
|
||||
}
|
||||
// Ignore mute switch when connected to external display
|
||||
NSError *error = nil;
|
||||
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
|
||||
[self updateScreen:screen];
|
||||
}];
|
||||
// Display disconnected
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:UIScreenDidDisconnectNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull notification) {
|
||||
UIScreen *screen = (UIScreen *) notification.object;
|
||||
NSLog(@"Display disconnected: %@", [screen debugDescription]);
|
||||
if ([[self extDisplays] containsObject:screen]) {
|
||||
[[self extDisplays] removeObject:screen];
|
||||
}
|
||||
if ([[self extDisplays] count] > 0) {
|
||||
UIScreen *newScreen = [[self extDisplays] lastObject];
|
||||
[self updateScreen:newScreen];
|
||||
} else {
|
||||
NSError *error = nil;
|
||||
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&error];
|
||||
[self updateScreen:[UIScreen mainScreen]];
|
||||
}
|
||||
}];
|
||||
[self setListenerActive:YES];
|
||||
}
|
||||
|
||||
- (void)updateScreen:(UIScreen *)screen {
|
||||
[self setMainScreen:screen];
|
||||
UIWindow *gameWindow = [(AppDelegate *)[[UIApplication sharedApplication] delegate] window];
|
||||
// Hide before moving window to external display, otherwise iPhone won't switch to it
|
||||
[gameWindow setHidden:YES];
|
||||
[gameWindow setScreen:screen];
|
||||
// Set optimal resolution
|
||||
// Dispatch later to prevent "no window is preset" error
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (screen != [UIScreen mainScreen]) {
|
||||
NSUInteger count = [[screen availableModes] count];
|
||||
UIScreenMode* mode = [screen availableModes][count - 1];
|
||||
[screen setCurrentMode:mode];
|
||||
mode = [screen currentMode];
|
||||
// Fix overscan
|
||||
// TODO: Hacky solution. Screen is still scaled even if UIScreenOverscanCompensationNone is set.
|
||||
[screen setOverscanCompensation:UIScreenOverscanCompensationNone];
|
||||
CGSize fullSize = mode.size;
|
||||
UIEdgeInsets insets = [screen overscanCompensationInsets];
|
||||
fullSize.width -= insets.left + insets.right;
|
||||
fullSize.height -= insets.top + insets.bottom;
|
||||
[gameWindow setFrame:CGRectMake(insets.left, insets.top, fullSize.width, fullSize.height)];
|
||||
[gameWindow setBounds:CGRectMake(0, 0, fullSize.width, fullSize.height)];
|
||||
[self updateResolution:screen];
|
||||
[gameWindow setTransform:CGAffineTransformMakeScale(mode.size.width / fullSize.width, mode.size.height / fullSize.height)];
|
||||
} else {
|
||||
[gameWindow setTransform:[self originalTransform]];
|
||||
[gameWindow setFrame:[self originalFrame]];
|
||||
[gameWindow setBounds:[self originalBounds]];
|
||||
[self updateResolution:screen];
|
||||
}
|
||||
[gameWindow setHidden:NO];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)updateResolution:(UIScreen *)screen {
|
||||
float scale = screen.scale;
|
||||
|
||||
if ([screen respondsToSelector:@selector(nativeScale)]) {
|
||||
scale = screen.nativeScale;
|
||||
}
|
||||
|
||||
CGSize size = screen.applicationFrame.size;
|
||||
|
||||
if (size.height > size.width) {
|
||||
float h = size.height;
|
||||
size.height = size.width;
|
||||
size.width = h;
|
||||
}
|
||||
|
||||
if (screen == [UIScreen mainScreen]) {
|
||||
g_dpi = (IS_IPAD() ? 200.0f : 150.0f) * scale;
|
||||
} else {
|
||||
float diagonal = sqrt(size.height * size.height + size.width * size.width);
|
||||
g_dpi = diagonal * scale * 0.1f;
|
||||
}
|
||||
g_dpi_scale_x = 240.0f / g_dpi;
|
||||
g_dpi_scale_y = 240.0f / g_dpi;
|
||||
g_dpi_scale_real_x = g_dpi_scale_x;
|
||||
g_dpi_scale_real_y = g_dpi_scale_y;
|
||||
pixel_xres = size.width * scale;
|
||||
pixel_yres = size.height * scale;
|
||||
|
||||
dp_xres = pixel_xres * g_dpi_scale_x;
|
||||
dp_yres = pixel_yres * g_dpi_scale_y;
|
||||
|
||||
pixel_in_dps_x = (float)pixel_xres / (float)dp_xres;
|
||||
pixel_in_dps_y = (float)pixel_yres / (float)dp_yres;
|
||||
|
||||
[[sharedViewController view] setContentScaleFactor:scale];
|
||||
|
||||
// PSP native resize
|
||||
PSP_CoreParameter().pixelWidth = pixel_xres;
|
||||
PSP_CoreParameter().pixelHeight = pixel_yres;
|
||||
|
||||
NativeResized();
|
||||
|
||||
NSLog(@"Updated display resolution: (%d, %d) @%.1fx", pixel_xres, pixel_yres, scale);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>StringsTable</key>
|
||||
<string>Root</string>
|
||||
<key>PreferenceSpecifiers</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Type</key>
|
||||
<string>PSToggleSwitchSpecifier</string>
|
||||
<key>Title</key>
|
||||
<string>enable_external_display</string>
|
||||
<key>Key</key>
|
||||
<string>enable_external_display</string>
|
||||
<key>DefaultValue</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
+8
-32
@@ -5,7 +5,9 @@
|
||||
// Modified by xSacha
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "ViewController.h"
|
||||
#import "DisplayManager.h"
|
||||
#import "SubtleVolume.h"
|
||||
#import <GLKit/GLKit.h>
|
||||
#include <cassert>
|
||||
@@ -143,8 +145,10 @@ static GraphicsContext *graphicsContext;
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
[[DisplayManager shared] setupDisplayListener];
|
||||
|
||||
self.view.frame = [[UIScreen mainScreen] bounds];
|
||||
UIScreen* screen = [(AppDelegate*)[UIApplication sharedApplication].delegate screen];
|
||||
self.view.frame = [screen bounds];
|
||||
self.view.multipleTouchEnabled = YES;
|
||||
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
|
||||
|
||||
@@ -158,36 +162,7 @@ static GraphicsContext *graphicsContext;
|
||||
[EAGLContext setCurrentContext:self.context];
|
||||
self.preferredFramesPerSecond = 60;
|
||||
|
||||
// Might be useful for a speed boot, sacrificing resolution:
|
||||
// view.contentScaleFactor = 1.0;
|
||||
|
||||
float scale = [UIScreen mainScreen].scale;
|
||||
|
||||
if ([[UIScreen mainScreen] respondsToSelector:@selector(nativeScale)]) {
|
||||
scale = [UIScreen mainScreen].nativeScale;
|
||||
}
|
||||
|
||||
CGSize size = [[UIApplication sharedApplication].delegate window].frame.size;
|
||||
|
||||
if (size.height > size.width) {
|
||||
float h = size.height;
|
||||
size.height = size.width;
|
||||
size.width = h;
|
||||
}
|
||||
|
||||
g_dpi = (IS_IPAD() ? 200.0f : 150.0f) * scale;
|
||||
g_dpi_scale_x = 240.0f / g_dpi;
|
||||
g_dpi_scale_y = 240.0f / g_dpi;
|
||||
g_dpi_scale_real_x = g_dpi_scale_x;
|
||||
g_dpi_scale_real_y = g_dpi_scale_y;
|
||||
pixel_xres = size.width * scale;
|
||||
pixel_yres = size.height * scale;
|
||||
|
||||
dp_xres = pixel_xres * g_dpi_scale_x;
|
||||
dp_yres = pixel_yres * g_dpi_scale_y;
|
||||
|
||||
pixel_in_dps_x = (float)pixel_xres / (float)dp_xres;
|
||||
pixel_in_dps_y = (float)pixel_yres / (float)dp_yres;
|
||||
[[DisplayManager shared] updateResolution:[UIScreen mainScreen]];
|
||||
|
||||
graphicsContext = new IOSGraphicsContext();
|
||||
|
||||
@@ -238,7 +213,6 @@ static GraphicsContext *graphicsContext;
|
||||
time_update();
|
||||
}
|
||||
|
||||
threadStopped = true;
|
||||
|
||||
ILOG("Emulation thread shutting down\n");
|
||||
NativeShutdownGraphics();
|
||||
@@ -246,6 +220,8 @@ static GraphicsContext *graphicsContext;
|
||||
// Also ask the main thread to stop, so it doesn't hang waiting for a new frame.
|
||||
ILOG("Emulation thread stopping\n");
|
||||
graphicsContext->StopThread();
|
||||
|
||||
threadStopped = true;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -61,7 +61,10 @@ bool System_GetPropertyBool(SystemProperty prop) {
|
||||
|
||||
void System_SendMessage(const char *command, const char *parameter) {
|
||||
if (!strcmp(command, "finish")) {
|
||||
exit(0);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[sharedViewController shutdown];
|
||||
exit(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user