From c3bff1e28e30b879a05ac8d9318e2850cece1a95 Mon Sep 17 00:00:00 2001 From: Tommaso Negri Date: Mon, 10 Aug 2026 15:47:18 +0200 Subject: add listeners to hide or show all bars --- Sources/bar/Models/AppDelegate.swift | 37 +++++++++++++++++++++++++++++++++- Sources/bar/Models/Notifier.swift | 26 ++++-------------------- Sources/bar/bar.docc/manual-updates.md | 4 ++-- 3 files changed, 42 insertions(+), 25 deletions(-) (limited to 'Sources') diff --git a/Sources/bar/Models/AppDelegate.swift b/Sources/bar/Models/AppDelegate.swift index a7ecb5a..cefdfc0 100644 --- a/Sources/bar/Models/AppDelegate.swift +++ b/Sources/bar/Models/AppDelegate.swift @@ -16,15 +16,30 @@ final class AppDelegate: NSObject, NSApplicationDelegate { windows.append(window) } + // Register a listener to hide the bar + tokens.append( + Notifier.register("bar.hide") { [weak self] in + self?.hide() + } + ) + + // Register a listener to show the bar + tokens.append( + Notifier.register("bar.show") { [weak self] in + self?.show() + } + ) + // Register one listener per unique name; a single post fans out to every // block with that name (see Notifier). for name in Set(blocks.map(\.name)) { - let token = Notifier.register(name) { [weak self] in + let token = Notifier.register("bar.touch.\(name)") { [weak self] in self?.handleRefresh(name) } tokens.append(token) } + // Start the block's auto refresh for block in blocks { block.start() } } @@ -33,6 +48,26 @@ final class AppDelegate: NSObject, NSApplicationDelegate { for token in tokens { Notifier.cancel(token) } } + /// Order out all bar windows + nonisolated private func hide() { + Task { @MainActor [weak self] in + guard let self else { return } + for window in self.windows { + window.orderOut(nil) + } + } + } + + /// Order front all bar windows + nonisolated private func show() { + Task { @MainActor [weak self] in + guard let self else { return } + for window in self.windows { + window.orderFrontRegardless() + } + } + } + /// Called from a Darwin notification handler. Marked `nonisolated` because /// libnotify's delivery contract isn't statically visible to Swift, so we /// hop back to the main actor explicitly. diff --git a/Sources/bar/Models/Notifier.swift b/Sources/bar/Models/Notifier.swift index cdfe19e..24c4950 100644 --- a/Sources/bar/Models/Notifier.swift +++ b/Sources/bar/Models/Notifier.swift @@ -17,15 +17,7 @@ private final class NotifyBox { /// no client binary, using `notifyutil`: /// /// ``` -/// notifyutil -p -/// ``` -/// -/// Each category of events uses a different `prefix` -/// to avoid collisions. For example, block refresh -/// notifications use ``touchPrefix``: -/// -/// ``` -/// notifyutil -p bar.touch. +/// notifyutil -p bar.touch. /// ``` /// /// > Note: `notify.h` isn't part of Swift's Darwin module, @@ -35,18 +27,10 @@ private final class NotifyBox { /// > The callback fires on an arbitrary thread; handlers /// > must hop to the main actor themselves (see ``AppDelegate``). enum Notifier { - /// Prefix for block refresh notifications. - /// - /// Use `notifyutil -p bar.touch.` to trigger - /// a block update from the shell. - static let touchPrefix = "bar.touch." - /// Register `handler` to fire whenever the notification is posted. /// /// - Parameters: - /// - name: The notification name suffix. - /// - prefix: The full notification name will be `prefix + name`. - /// Defaults to ``touchPrefix``. + /// - name: The notification name. /// - handler: Closure to invoke when the notification fires. /// /// The returned token is a retained pointer used to keep the handler @@ -56,10 +40,8 @@ enum Notifier { @discardableResult static func register( _ name: String, - prefix: String = Notifier.touchPrefix, handler: @escaping @Sendable () -> Void ) -> UnsafeMutableRawPointer { - let fullName = prefix + name let ctx = Unmanaged.passRetained(NotifyBox(handler)).toOpaque() CFNotificationCenterAddObserver( @@ -70,7 +52,7 @@ enum Notifier { let box = Unmanaged.fromOpaque(observer).takeUnretainedValue() box.handler() }, - fullName as CFString, + name as CFString, nil, .deliverImmediately ) @@ -80,7 +62,7 @@ enum Notifier { /// Remove and release a registration. /// - /// Pass the token returned by ``Notifier/register(_:prefix:handler:)``. + /// Pass the token returned by ``Notifier/register(_:handler:)``. /// Optional: process exit clears all pending registrations anyway. static func cancel(_ token: UnsafeMutableRawPointer) { let center = CFNotificationCenterGetDarwinNotifyCenter() diff --git a/Sources/bar/bar.docc/manual-updates.md b/Sources/bar/bar.docc/manual-updates.md index 60520ca..c5157e5 100644 --- a/Sources/bar/bar.docc/manual-updates.md +++ b/Sources/bar/bar.docc/manual-updates.md @@ -10,10 +10,10 @@ This is done by posting a [Darwin notification](https://developer.apple.com/docu Use `notifyutil` (bundled with macOS) to post a notification: ```shell -notifyutil -p bar.touch. +notifyutil -p bar.touch. ``` -Replace `` with the block's name (e.g. `clock`, `battery`). +Replace `` with the block's name (e.g. `clock`, `battery`). If multiple blocks share the same name, a single notification refreshes them all. ## Use cases -- cgit v1.2.3