Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions Sources/DefiMacOS/MacOSPlatform+Snapshot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ extension SnapshotEngine {
) -> DesktopSnapshot {
let snapshotStartedAt = ProcessInfo.processInfo.systemUptime
let explicitlyDestroyedWindowIDs = consumeExplicitlyDestroyedWindows()
let frontmostProcessID = onMain {
_ in NSWorkspace.shared.frontmostApplication
}?.processIdentifier
let tracesWindowTopology = windowTopologyEventPending
let capturedTopologyRequiresFullSnapshot =
windowTopologyRequiresFullSnapshot
Expand Down Expand Up @@ -648,7 +645,13 @@ extension SnapshotEngine {
"frame commit observed settled=\(settledCommitLatenciesMS.count) deferred=\(deferredMismatchCount) max_latency_ms=\(maximumSettledLatencyMS, format: .fixed(precision: 2))"
)
}
let focusedWindowID = focusedWindowID(in: windows)
let frontmostProcessID = onMain {
_ in NSWorkspace.shared.frontmostApplication
}?.processIdentifier
let focusedWindowID = focusedWindowID(
in: windows,
frontmostProcessID: frontmostProcessID
)
lastNativeFocusedWindowID = focusedWindowID
verifiedNativeFocusedWindowID = focusedWindowID
if let focusedWindowID,
Expand Down
4 changes: 2 additions & 2 deletions Sources/DefiMacOS/SnapshotEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -849,9 +849,9 @@ extension SnapshotEngine {
}

func focusedWindowID(
in windows: [Window]
in windows: [Window],
frontmostProcessID: pid_t?
) -> WindowID? {
let frontmostProcessID = onMain { _ in NSWorkspace.shared.frontmostApplication }?.processIdentifier
let system = AXUIElementCreateSystemWide()
let focusedApplication: CFTypeRef? = AXMessagingTimeoutAccess.shared
.withTimeout(
Expand Down
11 changes: 4 additions & 7 deletions Sources/DefiRuntime/WindowReconciliation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,12 @@ public func discoverWindow(
else {
throw ReducerError.unknownWorkspace(workspaceID)
}
// Policy: a managed window spawning into the active workspace of its
// monitor always inserts after the focused column and takes focus - the
// native focused-window event can legitimately lag window creation, so it
// alone must not gate this.
// Policy: a managed window spawned by the frontmost application follows
// its assigned workspace and takes focus. The native focused-window event
// can legitimately lag window creation, so it alone must not gate this.
let followsFocus =
followFocusIntent
|| (isFrontmostAppSpawn
&& workspaceID == state.monitors[monitorIndex].activeWorkspace
&& !window.floating)
|| (isFrontmostAppSpawn && !window.floating)

if window.floating && !window.forceTiling {
state.monitors[monitorIndex].workspaces[workspaceIndex].floatingWindows.append(window.id)
Expand Down
3 changes: 3 additions & 0 deletions Tests/DefiRuntimeTests/BackgroundWindowDiscoveryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ struct BackgroundWindowDiscoveryTests {
appID: "editor",
title: "Selected",
frame: Rect(x: 0, y: 0, width: 600, height: 800),
processID: 7,
monitorID: monitorID
)
let backgroundWindow = Window(
id: WindowID(rawValue: 2),
appID: "proxy",
title: "Background",
frame: Rect(x: 600, y: 0, width: 600, height: 800),
processID: 42,
monitorID: monitorID
)
try discoverWindow(
Expand All @@ -39,6 +41,7 @@ struct BackgroundWindowDiscoveryTests {
[selectedWindow, backgroundWindow],
config: config,
nativeFocusedWindowID: selectedWindow.id,
frontmostProcessID: 7,
state: &state
)

Expand Down
49 changes: 49 additions & 0 deletions Tests/DefiRuntimeTests/PlacementPreferencesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,55 @@ import Testing
struct PlacementPreferencesTests {
private let monitorID = MonitorID(rawValue: 1)

@Test
func `Frontmost application spawn follows persisted workspace`() throws {
let web = WorkspaceID(rawValue: "web")
let config = Config(workspaces: WorkspacesConfig(names: ["dev", web.rawValue]))
var state = RuntimeState(config: config)
state.attachMonitor(monitorID)
let selected = Window(
id: WindowID(rawValue: 1),
appID: "com.example.Editor",
title: "Editor",
frame: Rect(x: 0, y: 0, width: 600, height: 800),
processID: 7,
monitorID: monitorID
)
let launched = Window(
id: WindowID(rawValue: 2),
appID: "com.example.Chat",
title: "Chat",
frame: Rect(x: 600, y: 0, width: 600, height: 800),
processID: 42,
monitorID: monitorID
)
let preferences = PlacementPreferences(
applications: [
"com.example.chat": WindowPlacementPreference(
workspaceID: web,
monitorID: monitorID
)
]
)
try discoverWindow(
selected,
decision: RuleDecision(followFocus: true),
isNativelyFocused: true,
state: &state
)

reconcileWindows(
[selected, launched],
config: config,
placementPreferences: preferences,
frontmostProcessID: 42,
state: &state
)

#expect(state.monitors[0].activeWorkspace == web)
#expect(state.selectedWindowID(on: monitorID) == launched.id)
}

@Test
func `Reconcile restores persisted application workspace`() throws {
let config = Config(workspaces: WorkspacesConfig(names: ["dev", "web"]))
Expand Down