aboutsummaryrefslogtreecommitdiff
path: root/Sources/phbar/Models/Notifier.swift
diff options
context:
space:
mode:
authorTommaso Negri <me@tommasongr.com>2026-07-27 16:18:34 +0200
committerTommaso Negri <me@tommasongr.com>2026-07-27 16:18:34 +0200
commit535b42a7b8304340182ca1367aa42f047eb06bff (patch)
tree88e15512f874a00886fc7b97861feb5e92c6aaf5 /Sources/phbar/Models/Notifier.swift
parent6d165c74a5a6b471c155b12ab15ef833d6b2aa36 (diff)
downloadbar-535b42a7b8304340182ca1367aa42f047eb06bff.tar.gz
bar-535b42a7b8304340182ca1367aa42f047eb06bff.zip
scaffold basic structure
Diffstat (limited to 'Sources/phbar/Models/Notifier.swift')
-rw-r--r--Sources/phbar/Models/Notifier.swift55
1 files changed, 55 insertions, 0 deletions
diff --git a/Sources/phbar/Models/Notifier.swift b/Sources/phbar/Models/Notifier.swift
new file mode 100644
index 0000000..1124cd4
--- /dev/null
+++ b/Sources/phbar/Models/Notifier.swift
@@ -0,0 +1,55 @@
+import Foundation
+
+// Darwin notifications via CoreFoundation — the macOS-native, signal-like way
+// to trigger a refresh from the shell with no client binary:
+//
+// notifyutil -p <prefix><name>
+//
+// e.g. notifyutil -p house.panini.phbar.touch.workspaces
+//
+// (notify.h isn't part of Swift's Darwin module, so we go through
+// CFNotificationCenter, which talks to the same subsystem.)
+//
+// The callback fires on an arbitrary thread; handlers must hop to the main
+// actor themselves (see AppDelegate.handleRefresh).
+
+/// Retains the handler for the lifetime of the registration.
+private final class NotifyBox {
+ let handler: @Sendable () -> Void
+ init(_ handler: @escaping @Sendable () -> Void) { self.handler = handler }
+}
+
+enum Notifier {
+ static let prefix = "house.panini.phbar.touch."
+
+ /// Register `handler` to fire whenever the notification is posted.
+ @discardableResult
+ static func register(_ name: String, handler: @escaping @Sendable () -> Void)
+ -> UnsafeMutableRawPointer
+ {
+ let fullName = prefix + name
+ let ctx = Unmanaged.passRetained(NotifyBox(handler)).toOpaque()
+
+ CFNotificationCenterAddObserver(
+ CFNotificationCenterGetDarwinNotifyCenter(),
+ ctx,
+ { _, observer, _, _, _ in
+ guard let observer else { return }
+ let box = Unmanaged<NotifyBox>.fromOpaque(observer).takeUnretainedValue()
+ box.handler()
+ },
+ fullName as CFString,
+ nil,
+ .deliverImmediately
+ )
+
+ return ctx
+ }
+
+ /// Remove and release a registration. (Optional: process exit clears all.)
+ static func cancel(_ token: UnsafeMutableRawPointer) {
+ let center = CFNotificationCenterGetDarwinNotifyCenter()
+ CFNotificationCenterRemoveEveryObserver(center, token)
+ Unmanaged<NotifyBox>.fromOpaque(token).release()
+ }
+}