Skip to content

macos: honor window-save-state=never for restorable windows - #14506

Merged
bo2themax merged 3 commits into
ghostty-org:mainfrom
BarutSRB:fix-macos-window-save-state-never
Oct 2, 2026
Merged

bo2themax merged 3 commits into
ghostty-org:mainfrom
BarutSRB:fix-macos-window-save-state-never

Conversation

@BarutSRB

@BarutSRB BarutSRB commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

With window-save-state = never, normal terminal windows are still marked isRestorable. Ghostty disables saving through NSQuitAlwaysKeepsWindows, but AppKit continued taking persistent-UI window snapshots in my reproduction on macOS 27.2 (26B5091g).

Make TerminalController.windowDidLoad() also honor never when setting window.isRestorable, and only assign the restoration class and identifier when that flag is enabled. This adds no work to move, resize, or rendering callbacks.

For newly created windows, default and always retain the existing behavior, including the exclusion of windows launched with a custom command. The follow-up commit also covers windows that are already open: on config reload, each terminal window re-syncs its restoration state through the same path used at window creation. Windows started with a custom command stay excluded.

Reproduction and measurements

I reproduced intermittent roughly 500 ms stalls while repeatedly focusing left/right between several Ghostty windows and other applications in OmniWM's Niri layout. window-save-state = never was set before restarting Ghostty.

The issue reproduced on 1.3.1 and an unmodified build of 76895d97b. In the latter, paired process samples showed NSPersistentUIWindowSnapshotter waiting through SLSConnectionSynchronizeSLSCATransaction, alongside Ghostty's main thread waiting on the connection lock under SLSConnectionSetLastSLSCATransaction.

Comparing unmodified and patched builds of the same commit over two approximately 20-second navigation captures:

Measurement Unmodified Patched
Focus presses 113 129
Successful Ghostty AX frame-write attempts 2,155 2,995
Ghostty AX frame-write attempts around 500 ms 5 0
Slowest Ghostty AX frame-write attempt 539.8 ms 22.5 ms
Ghostty main-thread samples in the CA/SkyLight connection-lock wait 2,205 / 12,544 0 / 12,173

The persistent-UI snapshot worker was absent from the patched sample, and navigation feels noticeably smoother. These are aggregate samples from one before/after pair with different input counts, not a controlled end-to-end latency benchmark. Both Ghostty builds used ReleaseLocal with the same ReleaseFast core, while OmniWM remained the same instrumented Debug process.

Validation

  • macos/build.nu --action test: 275 passed, 1 skipped, 0 failed.
  • New macos/Tests/Terminal/TerminalControllerRestorationTests.swift: reload flips restoration on an open window between never, default and always; custom-command windows stay non-restorable across reloads; surface-level config changes leave it alone.
  • macos/build.nu --configuration ReleaseLocal: passed.
  • Strict SwiftLint on both changed files: passed.
  • Manual navigation reproduction with the patched build: noticeably smoother, with the results above.

AI disclosure

I used OpenAI Codex to assist with diagnosis, implement the two-line change, run verification, analyze the captures, and draft this description. I reproduced the issue and tested the patched app interactively.

Normal terminal windows remain eligible for native AppKit restoration
when window-save-state is never, allowing background window snapshots
to contend with window updates.

Apply the setting when creating a terminal window and assign its
restoration class and identifier only when restoration is enabled.
@BarutSRB
BarutSRB requested a review from a team as a code owner October 1, 2026 20:02
@ghostty-bot ghostty-bot Bot added the os/macos label Oct 1, 2026
@BarutSRB

BarutSRB commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

@BarutSRB
BarutSRB force-pushed the fix-macos-window-save-state-never branch from c86bd50 to 1360297 Compare October 1, 2026 20:35
@bo2themax

Copy link
Copy Markdown
Member

it does not update existing windows when configuration is reloaded.

Is there a reason why it's not updated after reload?

draft this description

Please be cautious about this in the future, the PR description should be written by HOOMAN.

@BarutSRB

BarutSRB commented Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

@bo2themax Hey, sorry for the non HOOMAN 😂 PR description, had so many scattered notes for it that I just fed it to organize it as I was on a break and used my phone.

As for not updating exsisting windows, you are right, pushing soon the follow up commit.

Existing terminal windows retained their creation-time restoration
policy after configuration changes.

Refresh that policy on global config reload and share setup with window
creation, preserving the custom-command exclusion.

@bo2themax bo2themax left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One suggestion, also the tests seems unnecessary and potentially flaky. Accessing UI stuff in the UnitTest is not that reliable.

Comment thread macos/Sources/Features/Terminal/TerminalController.swift Outdated
Let isRestorable control preservation while assigning the restoration
class and identifier consistently for every terminal window.

Remove the restoration test fixture that creates AppKit windows and
broadcasts configuration changes through the shared test host.
@BarutSRB

BarutSRB commented Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

Thank you ♥️

@bo2themax
bo2themax merged commit f523504 into ghostty-org:main Oct 2, 2026
@github-actions github-actions Bot added this to the 1.4.0 milestone Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants