Skip to content

Commit

Permalink
Implement monitor-appkit-nsscreen-screens-id
Browse files Browse the repository at this point in the history
  • Loading branch information
nikitabobko committed Oct 14, 2024
1 parent 898331e commit 125e9b9
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 15 deletions.
2 changes: 2 additions & 0 deletions Sources/AppBundle/command/format.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ private enum FormatVar: Equatable {

enum MonitorFormatVar: String, Equatable {
case monitorId = "monitor-id"
case monitorAppKitNsScreenScreensId = "monitor-appkit-nsscreen-screens-id"
case monitorName = "monitor-name"
}
}
Expand Down Expand Up @@ -141,6 +142,7 @@ extension String {
case (.monitor(let m), .monitor(let f)):
return switch f {
case .monitorId: .success(m.monitorId.map { .int($0 + 1) } ?? .string("NULL-MONITOR-ID"))
case .monitorAppKitNsScreenScreensId: .success(.int(m.monitorAppKitNsScreenScreensId))
case .monitorName: .success(.string(m.name))
}
case (.app(let a), .app(let f)):
Expand Down
2 changes: 1 addition & 1 deletion Sources/AppBundle/focus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ struct FrozenFocus: AeroAny, Equatable {

var _focus: FrozenFocus = {
// It's fine to call *Inaccurate during startup
let monitor = focusedMonitorInaccurate ?? mainMonitor
let monitor = mainMonitor
return FrozenFocus(windowId: nil, workspaceName: monitor.activeWorkspace.name, monitorId: monitor.monitorId ?? 0)
}()

Expand Down
43 changes: 32 additions & 11 deletions Sources/AppBundle/model/Monitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import AppKit
import Common

private struct MonitorImpl {
let monitorAppKitNsScreenScreensId: Int
let name: String
let rect: Rect
let visibleRect: Rect
Expand All @@ -14,6 +15,8 @@ extension MonitorImpl: Monitor {

/// Use it instead of NSScreen because it can be mocked in tests
protocol Monitor: AeroAny {
/// The index in NSScreen.screens array. 1-based index
var monitorAppKitNsScreenScreensId: Int { get }
var name: String { get }
var rect: Rect { get }
var visibleRect: Rect { get }
Expand All @@ -23,13 +26,15 @@ protocol Monitor: AeroAny {

class LazyMonitor: Monitor {
private let screen: NSScreen
let monitorAppKitNsScreenScreensId: Int
let name: String
let width: CGFloat
let height: CGFloat
private var _rect: Rect?
private var _visibleRect: Rect?

init(_ screen: NSScreen) {
init(monitorAppKitNsScreenScreensId: Int, _ screen: NSScreen) {
self.monitorAppKitNsScreenScreensId = monitorAppKitNsScreenScreensId
self.name = screen.localizedName
self.width = screen.frame.width // Don't call rect because it would cause recursion during mainMonitor init
self.height = screen.frame.height // Don't call rect because it would cause recursion during mainMonitor init
Expand All @@ -45,8 +50,19 @@ class LazyMonitor: Monitor {
}
}

// Note to myself: Don't use NSScreen.main, it's garbage
// 1. The name is misleading, it's supposed to be called "focusedScreen"
// 2. It's inaccurate because NSScreen.main doesn't work correctly from NSWorkspace.didActivateApplicationNotification &
// kAXFocusedWindowChangedNotification callbacks.
private extension NSScreen {
var monitor: Monitor { MonitorImpl(name: localizedName, rect: rect, visibleRect: visibleRect) }
func toMonitor(monitorAppKitNsScreenScreensId: Int) -> Monitor {
MonitorImpl(
monitorAppKitNsScreenScreensId: monitorAppKitNsScreenScreensId,
name: localizedName,
rect: rect,
visibleRect: visibleRect
)
}

var isMainScreen: Bool {
frame.minX == 0 && frame.minY == 0
Expand All @@ -65,19 +81,24 @@ private extension NSScreen {
}

private let testMonitorRect = Rect(topLeftX: 0, topLeftY: 0, width: 1920, height: 1080)
private let testMonitor = MonitorImpl(name: "Test Monitor", rect: testMonitorRect, visibleRect: testMonitorRect)

/// It's inaccurate because NSScreen.main doesn't work correctly from NSWorkspace.didActivateApplicationNotification &
/// kAXFocusedWindowChangedNotification callbacks.
var focusedMonitorInaccurate: Monitor? {
isUnitTest ? testMonitor : NSScreen.main?.monitor
}
private let testMonitor = MonitorImpl(
monitorAppKitNsScreenScreensId: 1,
name: "Test Monitor",
rect: testMonitorRect,
visibleRect: testMonitorRect
)

var mainMonitor: Monitor {
isUnitTest ? testMonitor : LazyMonitor(NSScreen.screens.singleOrNil(where: \.isMainScreen)!)
if isUnitTest { return testMonitor }
let elem = NSScreen.screens.withIndex.singleOrNil(where: \.value.isMainScreen)!
return LazyMonitor(monitorAppKitNsScreenScreensId: elem.index + 1, elem.value)
}

var monitors: [Monitor] { isUnitTest ? [testMonitor] : NSScreen.screens.map(\.monitor) }
var monitors: [Monitor] {
isUnitTest
? [testMonitor]
: NSScreen.screens.enumerated().map { $0.element.toMonitor(monitorAppKitNsScreenScreensId: $0.offset + 1) }
}

var sortedMonitors: [Monitor] {
monitors.sorted(using: [SelectorComparator(selector: \.rect.minX), SelectorComparator(selector: \.rect.minY)])
Expand Down
2 changes: 2 additions & 0 deletions Sources/AppBundle/model/MonitorEx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ extension Monitor {
)
}

/// todo make 1-based
/// 0-based index
var monitorId: Int? {
let sorted = sortedMonitors
let origin = self.rect.topLeftCorner
Expand Down
3 changes: 2 additions & 1 deletion docs/aerospace-list-monitors.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ If not specified, the default `<output-format>` is: +

The following variables can be used inside `<output-format>`:

%{monitor-id}:: Number. Sequential number of the belonging monitor
%{monitor-id}:: 1-based Number. Sequential number of the belonging monitor
%{monitor-appkit-nsscreen-screens-id}:: 1-based index of the belonging monitor in `NSScreen.screens` array. Useful for integration with other tools that might be using `NSScreen.screens` ordering (like sketchybar).
%{monitor-name}:: String. Name of the belonging monitor

%{right-padding}:: A special variable which expands with a minimum number of spaces required to form a right padding in the appropriate column
Expand Down
3 changes: 2 additions & 1 deletion docs/aerospace-list-windows.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ The following variables can be used inside `<output-format>`:

%{workspace}:: String. Name of the belonging workspace

%{monitor-id}:: Number. Sequential number of the belonging monitor
%{monitor-id}:: 1-based Number. Sequential number of the belonging monitor.
%{monitor-appkit-nsscreen-screens-id}:: 1-based index of the belonging monitor in `NSScreen.screens` array. Useful for integration with other tools that might be using `NSScreen.screens` ordering (like sketchybar).
%{monitor-name}:: String. Name of the belonging monitor

%{right-padding}:: A special variable which expands with a minimum number of spaces required to form a right padding in the appropriate column
Expand Down
3 changes: 2 additions & 1 deletion docs/aerospace-list-workspaces.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ The following variables can be used inside `<output-format>`:

%{workspace}:: String. Name of the belonging workspace

%{monitor-id}:: Number. Sequential number of the belonging monitor
%{monitor-id}:: 1-based Number. Sequential number of the belonging monitor
%{monitor-appkit-nsscreen-screens-id}:: 1-based Number. Sequential number of the belonging monitor in `NSScreen.screens`. Useful for integration with other tools that might be using `NSScreen.screens` ordering (like sketchybar).
%{monitor-name}:: String. Name of the belonging monitor

%{right-padding}:: A special variable which expands with a minimum number of spaces required to form a right padding in the appropriate column
Expand Down

0 comments on commit 125e9b9

Please sign in to comment.