Skip to content
Permalink
Browse files

Merge pull request #12094 from imxieyi/master

External display support on iOS
  • Loading branch information...
unknownbrackets committed Jun 24, 2019
2 parents 79f0560 + b37553f commit a7defd725d374abb9fe9a25ce4f96f76160c4e60
@@ -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.
@@ -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 @@ - (void)subtleVolume:(SubtleVolume *)volumeView didChange:(CGFloat)value {

- (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 @@ - (void)viewDidLoad {
[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,14 +213,15 @@ - (void)viewDidLoad {
time_update();
}

threadStopped = true;

ILOG("Emulation thread shutting down\n");
NativeShutdownGraphics();

// 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;
});
}

@@ -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);
});
}
}

0 comments on commit a7defd7

Please sign in to comment.
You can’t perform that action at this time.