Supports AirPlaying games to external displays
This commit is contained in:
parent
7ad5df1949
commit
32e7c1f93e
@ -1 +1 @@
|
|||||||
Subproject commit c9ac6b5f4f50e9ae4b5fa7f8b420f240003df127
|
Subproject commit 4b5084c75149dc0fc7b4abb4e6ab4f41df025ecd
|
||||||
@ -183,6 +183,7 @@
|
|||||||
D54F710429E89DFC009C069A /* NotificationName+Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54F710329E89DFC009C069A /* NotificationName+Settings.swift */; };
|
D54F710429E89DFC009C069A /* NotificationName+Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54F710329E89DFC009C069A /* NotificationName+Settings.swift */; };
|
||||||
D55C468F29E761C000EA6DE9 /* AnyFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55C468E29E761C000EA6DE9 /* AnyFeature.swift */; };
|
D55C468F29E761C000EA6DE9 /* AnyFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55C468E29E761C000EA6DE9 /* AnyFeature.swift */; };
|
||||||
D55C469129E7631000EA6DE9 /* AnyOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55C469029E7631000EA6DE9 /* AnyOption.swift */; };
|
D55C469129E7631000EA6DE9 /* AnyOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55C469029E7631000EA6DE9 /* AnyOption.swift */; };
|
||||||
|
D560BD8629EDC45600289847 /* ExternalDisplaySceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D560BD8529EDC45600289847 /* ExternalDisplaySceneDelegate.swift */; };
|
||||||
D57D795629F300E100BB2CF8 /* CustomTintColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A817AF29DF4E6E00904AFE /* CustomTintColor.swift */; };
|
D57D795629F300E100BB2CF8 /* CustomTintColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A817AF29DF4E6E00904AFE /* CustomTintColor.swift */; };
|
||||||
D57D795F29F315F700BB2CF8 /* FeatureDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54A4BB229E4D27E004C7D57 /* FeatureDetailView.swift */; };
|
D57D795F29F315F700BB2CF8 /* FeatureDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54A4BB229E4D27E004C7D57 /* FeatureDetailView.swift */; };
|
||||||
D57D796029F315F700BB2CF8 /* ExperimentalFeaturesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9C00229DDED6D00A8D610 /* ExperimentalFeaturesView.swift */; };
|
D57D796029F315F700BB2CF8 /* ExperimentalFeaturesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A9C00229DDED6D00A8D610 /* ExperimentalFeaturesView.swift */; };
|
||||||
@ -437,6 +438,7 @@
|
|||||||
D54F710329E89DFC009C069A /* NotificationName+Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationName+Settings.swift"; sourceTree = "<group>"; };
|
D54F710329E89DFC009C069A /* NotificationName+Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NotificationName+Settings.swift"; sourceTree = "<group>"; };
|
||||||
D55C468E29E761C000EA6DE9 /* AnyFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyFeature.swift; sourceTree = "<group>"; };
|
D55C468E29E761C000EA6DE9 /* AnyFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyFeature.swift; sourceTree = "<group>"; };
|
||||||
D55C469029E7631000EA6DE9 /* AnyOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyOption.swift; sourceTree = "<group>"; };
|
D55C469029E7631000EA6DE9 /* AnyOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyOption.swift; sourceTree = "<group>"; };
|
||||||
|
D560BD8529EDC45600289847 /* ExternalDisplaySceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalDisplaySceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
D586496F297734280081477E /* CheatMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatMetadata.swift; sourceTree = "<group>"; };
|
D586496F297734280081477E /* CheatMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatMetadata.swift; sourceTree = "<group>"; };
|
||||||
D586497129774ABD0081477E /* CheatBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatBase.swift; sourceTree = "<group>"; };
|
D586497129774ABD0081477E /* CheatBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatBase.swift; sourceTree = "<group>"; };
|
||||||
D5864977297756CE0081477E /* CheatBaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatBaseView.swift; sourceTree = "<group>"; };
|
D5864977297756CE0081477E /* CheatBaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatBaseView.swift; sourceTree = "<group>"; };
|
||||||
@ -950,8 +952,8 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BFFA71DC1AAC406100EE9DD1 /* AppDelegate.swift */,
|
BFFA71DC1AAC406100EE9DD1 /* AppDelegate.swift */,
|
||||||
D5A98CE1284EF14B00E023E5 /* SceneDelegate.swift */,
|
|
||||||
BFFA71E01AAC406100EE9DD1 /* Main.storyboard */,
|
BFFA71E01AAC406100EE9DD1 /* Main.storyboard */,
|
||||||
|
D502F82129EF05F800ACCDA1 /* Scenes */,
|
||||||
BFFC46211D59848000AF2CC6 /* Launch */,
|
BFFC46211D59848000AF2CC6 /* Launch */,
|
||||||
BF46894D1AAC469800A2586D /* Game Selection */,
|
BF46894D1AAC469800A2586D /* Game Selection */,
|
||||||
BFFB709D1AF99ACA00DE56FE /* Emulation */,
|
BFFB709D1AF99ACA00DE56FE /* Emulation */,
|
||||||
@ -1012,6 +1014,15 @@
|
|||||||
path = Launch;
|
path = Launch;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
D502F82129EF05F800ACCDA1 /* Scenes */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
D5A98CE1284EF14B00E023E5 /* SceneDelegate.swift */,
|
||||||
|
D560BD8529EDC45600289847 /* ExternalDisplaySceneDelegate.swift */,
|
||||||
|
);
|
||||||
|
path = Scenes;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
D517F6BB29E737F5000D14D0 /* Types */ = {
|
D517F6BB29E737F5000D14D0 /* Types */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -1505,6 +1516,7 @@
|
|||||||
BF6BF3131EB7E47F008E83CD /* ImportOption.swift in Sources */,
|
BF6BF3131EB7E47F008E83CD /* ImportOption.swift in Sources */,
|
||||||
BFEE943D23F2180200CDA07D /* Delta4ToDelta5.xcmappingmodel in Sources */,
|
BFEE943D23F2180200CDA07D /* Delta4ToDelta5.xcmappingmodel in Sources */,
|
||||||
BF31878B1D489AAA00BD020D /* CheatValidator.swift in Sources */,
|
BF31878B1D489AAA00BD020D /* CheatValidator.swift in Sources */,
|
||||||
|
D560BD8629EDC45600289847 /* ExternalDisplaySceneDelegate.swift in Sources */,
|
||||||
BFFC46201D59823500AF2CC6 /* InitialGamesStoryboardSegue.swift in Sources */,
|
BFFC46201D59823500AF2CC6 /* InitialGamesStoryboardSegue.swift in Sources */,
|
||||||
BF15AF841F54B43B009B6AAB /* ActionInput.swift in Sources */,
|
BF15AF841F54B43B009B6AAB /* ActionInput.swift in Sources */,
|
||||||
BF4828841F9027B600028B97 /* Delta.xcdatamodeld in Sources */,
|
BF4828841F9027B600028B97 /* Delta.xcdatamodeld in Sources */,
|
||||||
|
|||||||
@ -121,7 +121,17 @@ extension AppDelegate
|
|||||||
{
|
{
|
||||||
// Called when a new scene session is being created.
|
// Called when a new scene session is being created.
|
||||||
// Use this method to select a configuration to create the new scene with.
|
// Use this method to select a configuration to create the new scene with.
|
||||||
return UISceneConfiguration(name: "Main", sessionRole: connectingSceneSession.role)
|
|
||||||
|
if connectingSceneSession.role == .windowExternalDisplay
|
||||||
|
{
|
||||||
|
// External Display
|
||||||
|
return UISceneConfiguration(name: "External Display", sessionRole: connectingSceneSession.role)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Default Scene
|
||||||
|
return UISceneConfiguration(name: "Main", sessionRole: connectingSceneSession.role)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>)
|
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>)
|
||||||
|
|||||||
@ -212,6 +212,9 @@ class GameViewController: DeltaCore.GameViewController
|
|||||||
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.emulationDidQuit(with:)), name: EmulatorCore.emulationDidQuitNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.emulationDidQuit(with:)), name: EmulatorCore.emulationDidQuitNotification, object: nil)
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.didEnableJIT(with:)), name: ServerManager.didEnableJITNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.didEnableJIT(with:)), name: ServerManager.didEnableJITNotification, object: nil)
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.sceneWillConnect(with:)), name: UIScene.willConnectNotification, object: nil)
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.sceneDidDisconnect(with:)), name: UIScene.didDisconnectNotification, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit
|
deinit
|
||||||
@ -679,8 +682,45 @@ private extension GameViewController
|
|||||||
self.controllerView.controllerSkin = touchControllerSkin
|
self.controllerView.controllerSkin = touchControllerSkin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.updateExternalDisplay()
|
||||||
|
|
||||||
self.view.setNeedsLayout()
|
self.view.setNeedsLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateGameViews()
|
||||||
|
{
|
||||||
|
if UIApplication.shared.isExternalDisplayConnected
|
||||||
|
{
|
||||||
|
// AirPlaying, hide all (non-touch) screens.
|
||||||
|
|
||||||
|
if let traits = self.controllerView.controllerSkinTraits, let screens = self.controllerView.controllerSkin?.screens(for: traits)
|
||||||
|
{
|
||||||
|
for (screen, gameView) in zip(screens, self.gameViews)
|
||||||
|
{
|
||||||
|
gameView.isEnabled = screen.isTouchScreen
|
||||||
|
gameView.isHidden = !screen.isTouchScreen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Either self.controllerView.controllerSkin is `nil`, or it doesn't support these traits.
|
||||||
|
// Most likely this system only has 1 screen, so just hide self.gameView.
|
||||||
|
|
||||||
|
self.gameView.isEnabled = false
|
||||||
|
self.gameView.isHidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Not AirPlaying, show all screens.
|
||||||
|
|
||||||
|
for gameView in self.gameViews
|
||||||
|
{
|
||||||
|
gameView.isEnabled = true
|
||||||
|
gameView.isHidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - Game Saves -
|
//MARK: - Game Saves -
|
||||||
@ -1072,12 +1112,60 @@ extension GameViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension GameViewController
|
||||||
|
{
|
||||||
|
func connectExternalDisplay(for scene: ExternalDisplayScene)
|
||||||
|
{
|
||||||
|
// We need to receive gameViewController(_:didUpdateGameViews) callback.
|
||||||
|
scene.gameViewController.delegate = self
|
||||||
|
|
||||||
|
self.updateGameViews()
|
||||||
|
self.updateExternalDisplay()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateExternalDisplay()
|
||||||
|
{
|
||||||
|
guard let scene = UIApplication.shared.externalDisplayScene else { return }
|
||||||
|
|
||||||
|
if scene.game?.fileURL != self.game?.fileURL
|
||||||
|
{
|
||||||
|
scene.game = self.game
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateExternalDisplayGameViews()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateExternalDisplayGameViews()
|
||||||
|
{
|
||||||
|
guard let scene = UIApplication.shared.externalDisplayScene, let emulatorCore = self.emulatorCore else { return }
|
||||||
|
|
||||||
|
for gameView in scene.gameViewController.gameViews
|
||||||
|
{
|
||||||
|
emulatorCore.add(gameView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func disconnectExternalDisplay(for scene: ExternalDisplayScene)
|
||||||
|
{
|
||||||
|
scene.gameViewController.delegate = nil
|
||||||
|
|
||||||
|
for gameView in scene.gameViewController.gameViews
|
||||||
|
{
|
||||||
|
self.emulatorCore?.remove(gameView)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateGameViews()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//MARK: - GameViewControllerDelegate -
|
//MARK: - GameViewControllerDelegate -
|
||||||
/// GameViewControllerDelegate
|
/// GameViewControllerDelegate
|
||||||
extension GameViewController: GameViewControllerDelegate
|
extension GameViewController: GameViewControllerDelegate
|
||||||
{
|
{
|
||||||
func gameViewController(_ gameViewController: DeltaCore.GameViewController, handleMenuInputFrom gameController: GameController)
|
func gameViewController(_ gameViewController: DeltaCore.GameViewController, handleMenuInputFrom gameController: GameController)
|
||||||
{
|
{
|
||||||
|
guard gameViewController == self else { return }
|
||||||
|
|
||||||
if let pausingGameController = self.pausingGameController
|
if let pausingGameController = self.pausingGameController
|
||||||
{
|
{
|
||||||
guard pausingGameController == gameController else { return }
|
guard pausingGameController == gameController else { return }
|
||||||
@ -1103,6 +1191,8 @@ extension GameViewController: GameViewControllerDelegate
|
|||||||
|
|
||||||
func gameViewControllerShouldResumeEmulation(_ gameViewController: DeltaCore.GameViewController) -> Bool
|
func gameViewControllerShouldResumeEmulation(_ gameViewController: DeltaCore.GameViewController) -> Bool
|
||||||
{
|
{
|
||||||
|
guard gameViewController == self else { return false }
|
||||||
|
|
||||||
var result = false
|
var result = false
|
||||||
|
|
||||||
rst_dispatch_sync_on_main_thread {
|
rst_dispatch_sync_on_main_thread {
|
||||||
@ -1111,6 +1201,20 @@ extension GameViewController: GameViewControllerDelegate
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func gameViewController(_ gameViewController: DeltaCore.GameViewController, didUpdateGameViews gameViews: [GameView])
|
||||||
|
{
|
||||||
|
// gameViewController could be `self` or ExternalDisplayScene.gameViewController.
|
||||||
|
|
||||||
|
if gameViewController == self
|
||||||
|
{
|
||||||
|
self.updateGameViews()
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.updateExternalDisplayGameViews()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension GameViewController
|
private extension GameViewController
|
||||||
@ -1364,6 +1468,18 @@ private extension GameViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func sceneWillConnect(with notification: Notification)
|
||||||
|
{
|
||||||
|
guard let scene = notification.object as? ExternalDisplayScene else { return }
|
||||||
|
self.connectExternalDisplay(for: scene)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func sceneDidDisconnect(with notification: Notification)
|
||||||
|
{
|
||||||
|
guard let scene = notification.object as? ExternalDisplayScene else { return }
|
||||||
|
self.disconnectExternalDisplay(for: scene)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension UserDefaults
|
private extension UserDefaults
|
||||||
|
|||||||
85
Delta/Scenes/ExternalDisplaySceneDelegate.swift
Normal file
85
Delta/Scenes/ExternalDisplaySceneDelegate.swift
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
//
|
||||||
|
// ExternalDisplaySceneDelegate.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 4/17/23.
|
||||||
|
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
extension UIApplication
|
||||||
|
{
|
||||||
|
var isExternalDisplayConnected: Bool {
|
||||||
|
let scene = UIApplication.shared.connectedScenes.first { $0.session.role == .windowExternalDisplay }
|
||||||
|
return scene != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var externalDisplayScene: ExternalDisplayScene? {
|
||||||
|
let scene = UIApplication.shared.connectedScenes.compactMap({ $0 as? ExternalDisplayScene }).first(where: { $0.session.role == .windowExternalDisplay })
|
||||||
|
return scene
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExternalDisplayScene: UIWindowScene
|
||||||
|
{
|
||||||
|
let gameViewController = DeltaCore.GameViewController()
|
||||||
|
|
||||||
|
var game: GameProtocol? {
|
||||||
|
get { self.gameViewController.game }
|
||||||
|
set { self.gameViewController.game = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExternalDisplaySceneDelegate: UIResponder, UIWindowSceneDelegate
|
||||||
|
{
|
||||||
|
var window: UIWindow?
|
||||||
|
|
||||||
|
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
|
||||||
|
{
|
||||||
|
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
|
||||||
|
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
|
||||||
|
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
||||||
|
guard let windowScene = scene as? UIWindowScene, let externalDisplayScene = scene as? ExternalDisplayScene else { return }
|
||||||
|
|
||||||
|
self.window = GameWindow(windowScene: windowScene)
|
||||||
|
self.window?.tintColor = .deltaPurple
|
||||||
|
self.window?.rootViewController = externalDisplayScene.gameViewController
|
||||||
|
self.window?.makeKeyAndVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneDidDisconnect(_ scene: UIScene)
|
||||||
|
{
|
||||||
|
// Called as the scene is being released by the system.
|
||||||
|
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
||||||
|
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
||||||
|
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneDidBecomeActive(_ scene: UIScene)
|
||||||
|
{
|
||||||
|
// Called when the scene has moved from an inactive state to an active state.
|
||||||
|
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneWillResignActive(_ scene: UIScene)
|
||||||
|
{
|
||||||
|
// Called when the scene will move from an active state to an inactive state.
|
||||||
|
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneWillEnterForeground(_ scene: UIScene)
|
||||||
|
{
|
||||||
|
// Called as the scene transitions from the background to the foreground.
|
||||||
|
// Use this method to undo the changes made on entering the background.
|
||||||
|
}
|
||||||
|
|
||||||
|
func sceneDidEnterBackground(_ scene: UIScene)
|
||||||
|
{
|
||||||
|
// Called as the scene transitions from the foreground to the background.
|
||||||
|
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
||||||
|
// to restore the scene back to its current state.
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -222,6 +222,17 @@
|
|||||||
<string>Main</string>
|
<string>Main</string>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
|
<key>UIWindowSceneSessionRoleExternalDisplay</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>UISceneClassName</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).ExternalDisplayScene</string>
|
||||||
|
<key>UISceneConfigurationName</key>
|
||||||
|
<string>External Display</string>
|
||||||
|
<key>UISceneDelegateClassName</key>
|
||||||
|
<string>$(PRODUCT_MODULE_NAME).ExternalDisplaySceneDelegate</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>UIBackgroundModes</key>
|
<key>UIBackgroundModes</key>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user