You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
3.9 KiB
143 lines
3.9 KiB
|
6 days ago
|
import Foundation
|
||
|
|
import os.log
|
||
|
|
import SystemExtensions
|
||
|
|
|
||
|
|
class TinyGPUDriverLoadingStateMachine {
|
||
|
|
enum State { case unloaded, activating, needsApproval, activated, activationError }
|
||
|
|
}
|
||
|
|
|
||
|
|
class TinyGPUViewModel: NSObject {
|
||
|
|
|
||
|
|
@Published private var state: TinyGPUDriverLoadingStateMachine.State = .unloaded
|
||
|
|
|
||
|
|
override init() {
|
||
|
|
super.init()
|
||
|
|
refreshInitialDextState()
|
||
|
|
}
|
||
|
|
|
||
|
|
private func refreshInitialDextState() {
|
||
|
|
#if os(macOS)
|
||
|
|
Task.detached { [dextIdentifier] in
|
||
|
|
let newState = Self.queryDextState(bundleID: dextIdentifier)
|
||
|
|
await MainActor.run { self.state = newState }
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#if os(macOS)
|
||
|
|
private static func queryDextState(bundleID: String) -> TinyGPUDriverLoadingStateMachine.State {
|
||
|
|
let tool = "/usr/bin/systemextensionsctl"
|
||
|
|
let p = Process()
|
||
|
|
p.executableURL = URL(fileURLWithPath: tool)
|
||
|
|
p.arguments = ["list"]
|
||
|
|
|
||
|
|
let pipe = Pipe()
|
||
|
|
p.standardOutput = pipe
|
||
|
|
p.standardError = Pipe()
|
||
|
|
|
||
|
|
do {
|
||
|
|
try p.run()
|
||
|
|
p.waitUntilExit()
|
||
|
|
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||
|
|
guard let output = String(data: data, encoding: .utf8) else { return .unloaded }
|
||
|
|
|
||
|
|
// Look for our bundle id line
|
||
|
|
if let line = output.split(separator: "\n").first(where: { $0.contains(bundleID) }) {
|
||
|
|
if line.contains("[activated enabled]") { return .activated }
|
||
|
|
if line.contains("[activated waiting for user]") { return .needsApproval }
|
||
|
|
if line.contains("terminated waiting to uninstall") { return .unloaded }
|
||
|
|
return .activating
|
||
|
|
} else {
|
||
|
|
return .unloaded
|
||
|
|
}
|
||
|
|
} catch {
|
||
|
|
return .unloaded
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
private let dextIdentifier: String = "org.tinygrad.tinygpu.edriver"
|
||
|
|
|
||
|
|
public var dextLoadingState: String {
|
||
|
|
switch state {
|
||
|
|
case .unloaded:
|
||
|
|
return "TinyGPUDriver isn't loaded."
|
||
|
|
case .activating:
|
||
|
|
return "Activating TinyGPUDriver, please wait."
|
||
|
|
case .needsApproval:
|
||
|
|
return "Please follow the prompt to approve TinyGPUDriver."
|
||
|
|
case .activated:
|
||
|
|
return "TinyGPUDriver has been activated and is ready to use. You can close the installer."
|
||
|
|
case .activationError:
|
||
|
|
return "TinyGPUDriver has experienced an error during activation.\nPlease check the logs to find the error."
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
extension TinyGPUViewModel: ObservableObject {
|
||
|
|
|
||
|
|
#if os(macOS)
|
||
|
|
func activateMyDext() {
|
||
|
|
activateExtension(dextIdentifier)
|
||
|
|
}
|
||
|
|
|
||
|
|
func deactivateMyDext() {
|
||
|
|
deactivateExtension(dextIdentifier)
|
||
|
|
}
|
||
|
|
|
||
|
|
func activateExtension(_ dextIdentifier: String) {
|
||
|
|
|
||
|
|
let request = OSSystemExtensionRequest
|
||
|
|
.activationRequest(forExtensionWithIdentifier: dextIdentifier,
|
||
|
|
queue: .main)
|
||
|
|
request.delegate = self
|
||
|
|
OSSystemExtensionManager.shared.submitRequest(request)
|
||
|
|
|
||
|
|
self.state = .activating
|
||
|
|
}
|
||
|
|
|
||
|
|
func deactivateExtension(_ dextIdentifier: String) {
|
||
|
|
|
||
|
|
let request = OSSystemExtensionRequest.deactivationRequest(forExtensionWithIdentifier: dextIdentifier, queue: .main)
|
||
|
|
request.delegate = self
|
||
|
|
OSSystemExtensionManager.shared.submitRequest(request)
|
||
|
|
|
||
|
|
self.state = .unloaded
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#if os(macOS)
|
||
|
|
extension TinyGPUViewModel: OSSystemExtensionRequestDelegate {
|
||
|
|
|
||
|
|
func request(
|
||
|
|
_ request: OSSystemExtensionRequest,
|
||
|
|
actionForReplacingExtension existing: OSSystemExtensionProperties,
|
||
|
|
withExtension ext: OSSystemExtensionProperties) -> OSSystemExtensionRequest.ReplacementAction {
|
||
|
|
|
||
|
|
var replacementAction: OSSystemExtensionRequest.ReplacementAction
|
||
|
|
|
||
|
|
os_log("sysex actionForReplacingExtension: %@ %@", existing, ext)
|
||
|
|
|
||
|
|
replacementAction = .replace
|
||
|
|
self.state = .activating
|
||
|
|
return replacementAction
|
||
|
|
}
|
||
|
|
|
||
|
|
func requestNeedsUserApproval(_ request: OSSystemExtensionRequest) {
|
||
|
|
os_log("sysex requestNeedsUserApproval")
|
||
|
|
self.state = .needsApproval
|
||
|
|
}
|
||
|
|
|
||
|
|
func request(_ request: OSSystemExtensionRequest, didFinishWithResult result: OSSystemExtensionRequest.Result) {
|
||
|
|
os_log("sysex didFinishWithResult: %d", result.rawValue)
|
||
|
|
self.state = .activated
|
||
|
|
}
|
||
|
|
|
||
|
|
func request(_ request: OSSystemExtensionRequest, didFailWithError error: Error) {
|
||
|
|
os_log("sysex didFailWithError: %@", error.localizedDescription)
|
||
|
|
self.state = .activationError
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|