// // MSG_DesktopApp.swift // MSG Desktop // // Created by Chad Nelson on 3/8/25. // import SwiftUI import AppKit import Foundation func runShellCommandAsync(command: String, outputText: Binding?, showButton: Binding?) { DispatchQueue.global(qos: .background).async { let task = Process() let pipe = Pipe() task.standardOutput = pipe task.standardError = pipe task.arguments = ["-c", command] task.launchPath = "/bin/zsh" task.standardInput = nil task.environment = [ "GUILE_LOAD_PATH": "/opt/homebrew/share/guile/site/3.0:/usr/local/share/guile/site/3.0/", "GUILE_LOAD_COMPILED_PATH": "/opt/homebrew/lib/guile/3.0/site-ccache:/usr/local/lib/guile/3.0/site-ccache/", "GUILE_SYSTEM_EXTENSIONS_PATH": "/opt/homebrew/lib/guile/3.0/extensions:/usr/local/lib/guile/3.0/extensions/", "PATH": "$PATH:/opt/homebrew/bin:/usr/bin:/usr/local/bin:/bin" ] task.launch() let fileHandle = pipe.fileHandleForReading fileHandle.waitForDataInBackgroundAndNotify() var observer: NSObjectProtocol? observer = NotificationCenter.default.addObserver(forName: .NSFileHandleDataAvailable, object: fileHandle, queue: nil) { _ in let data = fileHandle.availableData if let output = String(data: data, encoding: .utf8), !output.isEmpty { DispatchQueue.main.async { if let outputText = outputText { outputText.wrappedValue += output } } } fileHandle.waitForDataInBackgroundAndNotify() } task.waitUntilExit() if let observer = observer { NotificationCenter.default.removeObserver(observer) } if showButton != nil { showButton?.wrappedValue = false; } } } class AppDelegate: NSObject, NSApplicationDelegate { func applicationWillTerminate(_ notification: Notification) { runShellCommandAsync(command: "msg machine stop", outputText: nil, showButton: nil) runShellCommandAsync(command: "kill $(ps aux | grep '[g]uile' | awk '{print $2}')", outputText: nil, showButton: nil) runShellCommandAsync(command: "kill $(ps aux | grep '[q]emu' | awk '{print $2}')", outputText: nil, showButton: nil) } func saveAppState() { print("App is terminating. Saving state...") UserDefaults.standard.set(Date(), forKey: "lastClosed") } var statusBarItem: NSStatusItem! func applicationDidFinishLaunching(_ notification: Notification) { statusBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) if let button = statusBarItem.button { button.title = "MSG" button.toolTip = "MSG App" } let menu = NSMenu() menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApp), keyEquivalent: "q")) statusBarItem.menu = menu } @objc func quitApp() { let alert = NSAlert() alert.messageText = "Are you sure you want to quit?" alert.informativeText = "Do you want to quit the app and exit?" alert.alertStyle = .warning alert.addButton(withTitle: "Quit") alert.addButton(withTitle: "Cancel") let response = alert.runModal() if response == .alertFirstButtonReturn { NSApplication.shared.terminate(nil) } } } class AppState: ObservableObject { @Published var output: String = "Please select an option \n" static let shared = AppState() } @main struct MSG_DesktopApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } }