From 999f00aafaa5df2910082e45aefd15a34864bb1a Mon Sep 17 00:00:00 2001 From: Christophe Fergeau Date: Fri, 10 Nov 2023 11:15:11 +0100 Subject: [PATCH] gui: Watch vm state to terminate when it's stopped The GUI code in virtualization_view.m is notified when there was a guest initiated shutdown ('guestDidStopVirtualMachine') and when there was a virtualization error ('didStopWithError'). When calling Stop(), the VM is forcefully stopped (similar to pulling the plug on real hardware). This action is neither a guest initiated shutdown, nor a virtualization error, so the GUI code does not catch it. This means after calling vm.Stop(), the GUI main loop will keep running, and the application using Code-Hex/vz will never exit. This commit fixes this by adding an observer for VM state changes, and by calling 'terminate' when the VM state becomes 'stopped' or 'error'. This fixes https://github.com/Code-Hex/vz/issues/150 --- virtualization_view.m | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/virtualization_view.m b/virtualization_view.m index 9fbf6d1..7e20faf 100644 --- a/virtualization_view.m +++ b/virtualization_view.m @@ -165,11 +165,30 @@ - (instancetype)init @end +API_AVAILABLE(macos(12.0)) +@interface VMStateObserver : NSObject +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; +@end + +@implementation VMStateObserver +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; +{ + if ([keyPath isEqualToString:@"state"]) { + int newState = (int)[change[NSKeyValueChangeNewKey] integerValue]; + if (newState == VZVirtualMachineStateStopped || newState == VZVirtualMachineStateError) { + [NSApp performSelectorOnMainThread:@selector(terminate:) withObject:context waitUntilDone:NO]; + [object removeObserver:self forKeyPath:@"state"]; + } + } +} +@end + @implementation AppDelegate { VZVirtualMachine *_virtualMachine; VZVirtualMachineView *_virtualMachineView; CGFloat _windowWidth; CGFloat _windowHeight; + VMStateObserver *_observer; } - (instancetype)initWithVirtualMachine:(VZVirtualMachine *)virtualMachine @@ -179,6 +198,11 @@ - (instancetype)initWithVirtualMachine:(VZVirtualMachine *)virtualMachine self = [super init]; _virtualMachine = virtualMachine; [_virtualMachine setDelegate:self]; + _observer = [[VMStateObserver alloc] init]; + [virtualMachine addObserver:_observer + forKeyPath:@"state" + options:NSKeyValueObservingOptionNew + context:(void *)self]; // Setup virtual machine view configs VZVirtualMachineView *view = [[[VZVirtualMachineView alloc] init] autorelease];