1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
diff --git a/Sources/bar/Models/AppDelegate.swift b/Sources/bar/Models/AppDelegate.swift
index cefdfc0..5db4946 100644
--- a/Sources/bar/Models/AppDelegate.swift
+++ b/Sources/bar/Models/AppDelegate.swift
@@ -6,15 +6,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
private var windows: [BarWindow] = []
private var blocks: [Block] = Config.blocks.map(\.resolved)
private var tokens: [UnsafeMutableRawPointer] = []
+ private var screenObserver: NSObjectProtocol?
func applicationDidFinishLaunching(_ notification: Notification) {
- for screen in NSScreen.screens {
- let frame = Screen.barFrame(screen, position: Config.position, height: Config.height)
- let window = BarWindow(contentRect: frame)
- window.contentView = NSHostingView(rootView: BarView(blocks: blocks))
- window.orderFrontRegardless()
- windows.append(window)
- }
+ buildWindows()
// Register a listener to hide the bar
tokens.append(
@@ -41,11 +36,24 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
// Start the block's auto refresh
for block in blocks { block.start() }
+
+ // Observe screen changes — recreate windows when displays
+ // are added or removed.
+ screenObserver = NotificationCenter.default.addObserver(
+ forName: NSApplication.didChangeScreenParametersNotification,
+ object: nil,
+ queue: .main
+ ) { [weak self] _ in
+ self?.rebuildWindows()
+ }
}
func applicationWillTerminate(_ notification: Notification) {
for block in blocks { block.stop() }
for token in tokens { Notifier.cancel(token) }
+ if let observer = screenObserver {
+ NotificationCenter.default.removeObserver(observer)
+ }
}
/// Order out all bar windows
@@ -68,6 +76,25 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
}
}
+ /// Create a bar window for each connected screen.
+ @MainActor private func buildWindows() {
+ for screen in NSScreen.screens {
+ let frame = Screen.barFrame(screen, position: Config.position, height: Config.height)
+ let window = BarWindow(contentRect: frame)
+ window.contentView = NSHostingView(rootView: BarView(blocks: blocks))
+ window.orderFrontRegardless()
+ windows.append(window)
+ }
+ }
+
+ /// Remove all existing windows and create new ones for the current
+ /// set of screens — called when a display is connected or disconnected.
+ @MainActor private func rebuildWindows() {
+ for window in windows { window.orderOut(nil) }
+ windows.removeAll()
+ buildWindows()
+ }
+
/// 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.
|