home(iOS): support multiple sessions

This commit is contained in:
osy 2024-02-21 10:56:26 -08:00
parent 8c88fd9aa7
commit 689367a9c3
6 changed files with 48 additions and 29 deletions

View File

@ -75,24 +75,6 @@ public extension VMDisplayViewController {
func enterLive() {
UIApplication.shared.isIdleTimerDisabled = disableIdleTimer
}
private func suspend() {
// dummy function for selector
}
func terminateApplication() {
DispatchQueue.main.async { [self] in
// animate to home screen
let app = UIApplication.shared
app.performSelector(onMainThread: #selector(suspend), with: nil, waitUntilDone: true)
// wait 2 seconds while app is going background
Thread.sleep(forTimeInterval: 2)
// exit app when app is in background
exit(0);
}
}
}
// MARK: Toolbar hiding

View File

@ -19,15 +19,23 @@ import SwiftUI
extension UTMData {
func run(vm: VMData, options: UTMVirtualMachineStartOptions = []) {
#if WITH_SOLO_VM
guard VMSessionState.allActiveSessions.count == 0 else {
logger.error("Session already started")
return
}
#endif
guard let wrapped = vm.wrapped else {
return
}
let session = VMSessionState(for: wrapped as! (any UTMSpiceVirtualMachine))
session.start()
if let session = VMSessionState.allActiveSessions.values.first(where: { $0.vm.id == wrapped.id }) {
session.showWindow()
} else if vm.state == .stopped {
let session = VMSessionState(for: wrapped as! (any UTMSpiceVirtualMachine))
session.start()
} else {
showErrorAlert(message: NSLocalizedString("This virtual machine is already running. In order to run it from this device, you must stop it first.", comment: "UTMDataExtension"))
}
}
func stop(vm: VMData) {
@ -37,6 +45,7 @@ extension UTMData {
if wrapped.registryEntry.isSuspended {
wrapped.requestVmDeleteState()
}
wrapped.requestVmStop()
}
func close(vm: VMData) {

View File

@ -425,10 +425,14 @@ extension VMSessionState {
logger.warning("Error starting audio session: \(error.localizedDescription)")
}
Self.allActiveSessions[id] = self
NotificationCenter.default.post(name: .vmSessionCreated, object: nil, userInfo: ["Session": self])
showWindow()
vm.requestVmStart(options: options)
}
func showWindow() {
NotificationCenter.default.post(name: .vmSessionCreated, object: nil, userInfo: ["Session": self])
}
@objc private func suspend() {
// dummy function for selector
}
@ -442,7 +446,9 @@ extension VMSessionState {
}
// tell other screens to shut down
Self.allActiveSessions.removeValue(forKey: id)
NotificationCenter.default.post(name: .vmSessionEnded, object: nil, userInfo: ["Session": self])
closeWindows()
#if WITH_SOLO_VM
// animate to home screen
let app = UIApplication.shared
app.performSelector(onMainThread: #selector(suspend), with: nil, waitUntilDone: true)
@ -452,8 +458,13 @@ extension VMSessionState {
// exit app when app is in background
exit(0)
#endif
}
func closeWindows() {
NotificationCenter.default.post(name: .vmSessionEnded, object: nil, userInfo: ["Session": self])
}
func powerDown() {
Task {
try? await vm.deleteSnapshot(name: nil)

View File

@ -43,7 +43,11 @@ struct UTMApp: App {
.environmentObject(data)
.onReceive(vmSessionCreatedNotification) { output in
let newSession = output.userInfo!["Session"] as! VMSessionState
openWindow(value: newSession.newWindow())
if let window = newSession.windows.first {
openWindow(value: window)
} else {
openWindow(value: newSession.newWindow())
}
}
.onReceive(vmSessionEndedNotification) { output in
let endedSession = output.userInfo!["Session"] as! VMSessionState
@ -58,10 +62,12 @@ struct UTMApp: App {
WindowGroup(for: VMSessionState.GlobalWindowID.self) { $globalID in
if let globalID = globalID, let session = VMSessionState.allActiveSessions[globalID.sessionID] {
VMWindowView(id: globalID.windowID).environmentObject(session)
#if WITH_SOLO_VM
.onAppear {
// currently we only support one session, so close the home window
dismissWindow(id: "home")
}
#endif
}
}
.windowResizability(.contentMinSize)

View File

@ -91,6 +91,9 @@ final class UTMRemoteSpiceVirtualMachine: UTMSpiceVirtualMachine {
}
didSet {
if state == .stopped {
virtualMachineDidStop()
}
delegate?.virtualMachine(self, didTransitionToState: state)
}
}
@ -278,6 +281,10 @@ extension UTMRemoteSpiceVirtualMachine {
try? await server.sendPackageFile(for: id, relativePathComponents: [kUTMBundleScreenshotFilename], data: data)
}
}
private func virtualMachineDidStop() {
ioService = nil
}
}
extension UTMRemoteSpiceVirtualMachine {

View File

@ -4406,6 +4406,7 @@
ENABLE_PREVIEWS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"WITH_JIT=1",
"WITH_SOLO_VM=1",
"WITH_USB=1",
"$(inherited)",
);
@ -4420,7 +4421,7 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "WITH_JIT WITH_USB $(inherited)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "WITH_JIT WITH_SOLO_VM WITH_USB $(inherited)";
SWIFT_OBJC_BRIDGING_HEADER = "Services/Swift-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@ -4440,6 +4441,7 @@
ENABLE_PREVIEWS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"WITH_JIT=1",
"WITH_SOLO_VM=1",
"WITH_USB=1",
"$(inherited)",
);
@ -4454,7 +4456,7 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "WITH_JIT WITH_USB $(inherited)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "WITH_JIT WITH_SOLO_VM WITH_USB $(inherited)";
SWIFT_OBJC_BRIDGING_HEADER = "Services/Swift-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,7";
@ -4761,6 +4763,7 @@
ENABLE_PREVIEWS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"WITH_QEMU_TCI=1",
"WITH_SOLO_VM=1",
"$(inherited)",
);
INFOPLIST_FILE = Platform/iOS/Info.plist;
@ -4776,7 +4779,7 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "WITH_QEMU_TCI $(inherited)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "WITH_QEMU_TCI WITH_SOLO_VM $(inherited)";
SWIFT_OBJC_BRIDGING_HEADER = "Services/Swift-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
@ -4796,6 +4799,7 @@
ENABLE_PREVIEWS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"WITH_QEMU_TCI=1",
"WITH_SOLO_VM=1",
"$(inherited)",
);
INFOPLIST_FILE = Platform/iOS/Info.plist;
@ -4811,7 +4815,7 @@
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "WITH_QEMU_TCI $(inherited)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "WITH_QEMU_TCI WITH_SOLO_VM $(inherited)";
SWIFT_OBJC_BRIDGING_HEADER = "Services/Swift-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,7";