aboutsummaryrefslogtreecommitdiff
path: root/patches/bar-screen_hotplug-20260810-f6805c9.patch
blob: 1533b1587790eeeaae60cdf5ce9aa35bc319388a (plain) (blame)
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.