1// 2// DisplayManager.m 3// native 4// 5// Created by xieyi on 2019/6/9. 6// 7 8#import "DisplayManager.h" 9#import "ViewController.h" 10#import "AppDelegate.h" 11#include "Common/System/Display.h" 12#include "Common/System/System.h" 13#include "Common/System/NativeApp.h" 14#include "Core/System.h" 15#import <AVFoundation/AVFoundation.h> 16 17#define IS_IPAD() ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) 18 19@interface DisplayManager () 20 21@property BOOL listenerActive; 22@property (atomic, retain) NSMutableArray<UIScreen *> *extDisplays; 23@property CGRect originalFrame; 24@property CGRect originalBounds; 25@property CGAffineTransform originalTransform; 26 27- (void)updateScreen:(UIScreen *)screen; 28 29@end 30 31@implementation DisplayManager 32 33- (instancetype)init 34{ 35 self = [super init]; 36 if (self) { 37 [self setListenerActive:NO]; 38 [self setExtDisplays:[[NSMutableArray<UIScreen *> alloc] init]]; 39 } 40 return self; 41} 42 43+ (DisplayManager *)shared { 44 static DisplayManager *sharedInstance = nil; 45 static dispatch_once_t onceToken; 46 dispatch_once(&onceToken, ^{ 47 sharedInstance = [[DisplayManager alloc] init]; 48 }); 49 return sharedInstance; 50} 51 52- (void)setupDisplayListener { 53 // Disable external display by default 54 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"enable_external_display"] == NO) { 55 return; 56 } 57 if ([self listenerActive]) { 58 NSLog(@"setupDisplayListener already called"); 59 return; 60 } 61 NSLog(@"Setting up display manager"); 62 [self setMainScreen:[UIScreen mainScreen]]; 63 UIWindow *gameWindow = [(AppDelegate *)[[UIApplication sharedApplication] delegate] window]; 64 [self setOriginalFrame: [gameWindow frame]]; 65 [self setOriginalBounds:[gameWindow bounds]]; 66 [self setOriginalTransform:[gameWindow transform]]; 67 // Display connected 68 [[NSNotificationCenter defaultCenter] addObserverForName:UIScreenDidConnectNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull notification) { 69 UIScreen *screen = (UIScreen *) notification.object; 70 NSLog(@"New display connected: %@", [screen debugDescription]); 71 [[self extDisplays] addObject:screen]; 72 // Do not switch to second connected display 73 if ([self mainScreen] != [UIScreen mainScreen]) { 74 return; 75 } 76 // Ignore mute switch when connected to external display 77 NSError *error = nil; 78 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error]; 79 [self updateScreen:screen]; 80 }]; 81 // Display disconnected 82 [[NSNotificationCenter defaultCenter] addObserverForName:UIScreenDidDisconnectNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull notification) { 83 UIScreen *screen = (UIScreen *) notification.object; 84 NSLog(@"Display disconnected: %@", [screen debugDescription]); 85 if ([[self extDisplays] containsObject:screen]) { 86 [[self extDisplays] removeObject:screen]; 87 } 88 if ([[self extDisplays] count] > 0) { 89 UIScreen *newScreen = [[self extDisplays] lastObject]; 90 [self updateScreen:newScreen]; 91 } else { 92 NSError *error = nil; 93 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&error]; 94 [self updateScreen:[UIScreen mainScreen]]; 95 } 96 }]; 97 [self setListenerActive:YES]; 98} 99 100- (void)updateScreen:(UIScreen *)screen { 101 [self setMainScreen:screen]; 102 UIWindow *gameWindow = [(AppDelegate *)[[UIApplication sharedApplication] delegate] window]; 103 // Hide before moving window to external display, otherwise iPhone won't switch to it 104 [gameWindow setHidden:YES]; 105 [gameWindow setScreen:screen]; 106 // Set optimal resolution 107 // Dispatch later to prevent "no window is preset" error 108 dispatch_async(dispatch_get_main_queue(), ^{ 109 if (screen != [UIScreen mainScreen]) { 110 NSUInteger count = [[screen availableModes] count]; 111 UIScreenMode* mode = [screen availableModes][count - 1]; 112 [screen setCurrentMode:mode]; 113 mode = [screen currentMode]; 114 // Fix overscan 115 // TODO: Hacky solution. Screen is still scaled even if UIScreenOverscanCompensationNone is set. 116 [screen setOverscanCompensation:UIScreenOverscanCompensationNone]; 117 CGSize fullSize = mode.size; 118 UIEdgeInsets insets = [screen overscanCompensationInsets]; 119 fullSize.width -= insets.left + insets.right; 120 fullSize.height -= insets.top + insets.bottom; 121 [gameWindow setFrame:CGRectMake(insets.left, insets.top, fullSize.width, fullSize.height)]; 122 [gameWindow setBounds:CGRectMake(0, 0, fullSize.width, fullSize.height)]; 123 [self updateResolution:screen]; 124 [gameWindow setTransform:CGAffineTransformMakeScale(mode.size.width / fullSize.width, mode.size.height / fullSize.height)]; 125 } else { 126 [gameWindow setTransform:[self originalTransform]]; 127 [gameWindow setFrame:[self originalFrame]]; 128 [gameWindow setBounds:[self originalBounds]]; 129 [self updateResolution:screen]; 130 } 131 [gameWindow setHidden:NO]; 132 }); 133} 134 135- (void)updateResolution:(UIScreen *)screen { 136 float scale = screen.scale; 137 138 if ([screen respondsToSelector:@selector(nativeScale)]) { 139 scale = screen.nativeScale; 140 } 141 142 CGSize size = screen.applicationFrame.size; 143 144 if (size.height > size.width) { 145 float h = size.height; 146 size.height = size.width; 147 size.width = h; 148 } 149 150 if (screen == [UIScreen mainScreen]) { 151 g_dpi = (IS_IPAD() ? 200.0f : 150.0f) * scale; 152 } else { 153 float diagonal = sqrt(size.height * size.height + size.width * size.width); 154 g_dpi = diagonal * scale * 0.1f; 155 } 156 g_dpi_scale_x = 240.0f / g_dpi; 157 g_dpi_scale_y = 240.0f / g_dpi; 158 g_dpi_scale_real_x = g_dpi_scale_x; 159 g_dpi_scale_real_y = g_dpi_scale_y; 160 pixel_xres = size.width * scale; 161 pixel_yres = size.height * scale; 162 163 dp_xres = pixel_xres * g_dpi_scale_x; 164 dp_yres = pixel_yres * g_dpi_scale_y; 165 166 pixel_in_dps_x = (float)pixel_xres / (float)dp_xres; 167 pixel_in_dps_y = (float)pixel_yres / (float)dp_yres; 168 169 [[sharedViewController view] setContentScaleFactor:scale]; 170 171 // PSP native resize 172 PSP_CoreParameter().pixelWidth = pixel_xres; 173 PSP_CoreParameter().pixelHeight = pixel_yres; 174 175 NativeResized(); 176 177 NSLog(@"Updated display resolution: (%d, %d) @%.1fx", pixel_xres, pixel_yres, scale); 178} 179 180@end 181