fix: reload tint colors while running
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import AppKit
|
||||
import Darwin
|
||||
|
||||
@MainActor
|
||||
final class ColorSchemeObservation {
|
||||
private struct Watch: @unchecked Sendable {
|
||||
let source: any DispatchSourceFileSystemObject
|
||||
let device: dev_t
|
||||
let inode: ino_t
|
||||
}
|
||||
|
||||
private let directory: URL
|
||||
private let onChange: @MainActor () -> Void
|
||||
private var watches: [String: Watch] = [:]
|
||||
|
||||
init(directory: URL, onChange: @escaping @MainActor () -> Void) {
|
||||
self.directory = directory.standardizedFileURL
|
||||
self.onChange = onChange
|
||||
refreshWatches()
|
||||
}
|
||||
|
||||
deinit {
|
||||
for watch in watches.values { watch.source.cancel() }
|
||||
}
|
||||
|
||||
private func filesChanged() {
|
||||
refreshWatches()
|
||||
onChange()
|
||||
}
|
||||
|
||||
private func refreshWatches() {
|
||||
var ancestor = directory
|
||||
var isDirectory: ObjCBool = false
|
||||
while !FileManager.default.fileExists(atPath: ancestor.path, isDirectory: &isDirectory)
|
||||
|| !isDirectory.boolValue {
|
||||
let parent = ancestor.deletingLastPathComponent()
|
||||
guard parent.path != ancestor.path else { break }
|
||||
ancestor = parent
|
||||
}
|
||||
let urls = [ancestor, ancestor.deletingLastPathComponent(),
|
||||
directory.appendingPathComponent("config.toml"), directory.appendingPathComponent("theme.json")]
|
||||
var obsolete = Set(watches.keys)
|
||||
for url in urls {
|
||||
let path = url.path
|
||||
let descriptor = open(path, O_EVTONLY | O_CLOEXEC)
|
||||
guard descriptor >= 0 else { continue }
|
||||
var identity = stat()
|
||||
guard fstat(descriptor, &identity) == 0 else {
|
||||
close(descriptor)
|
||||
continue
|
||||
}
|
||||
obsolete.remove(path)
|
||||
if let existing = watches[path], existing.device == identity.st_dev, existing.inode == identity.st_ino {
|
||||
close(descriptor)
|
||||
continue
|
||||
}
|
||||
watches.removeValue(forKey: path)?.source.cancel()
|
||||
let source = DispatchSource.makeFileSystemObjectSource(fileDescriptor: descriptor,
|
||||
eventMask: [.write, .extend, .delete, .rename, .attrib, .revoke], queue: .main)
|
||||
source.setEventHandler { [weak self] in
|
||||
Task { @MainActor [weak self] in self?.filesChanged() }
|
||||
}
|
||||
source.setCancelHandler { close(descriptor) }
|
||||
watches[path] = Watch(source: source, device: identity.st_dev, inode: identity.st_ino)
|
||||
source.resume()
|
||||
}
|
||||
for path in obsolete { watches.removeValue(forKey: path)?.source.cancel() }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user