Discussed in https://github.com/ghostty-org/ghostty/discussions/14198
Originally posted by aymanbagabas September 9, 2026
Issue Description
Modes 47, 1047 and 1049 all control one thing: whether the alternate screen is
active. Ghostty stores an independent bit per mode, and setMode writes that bit
before dispatching the screen switch, so the bit and the actual screen can
disagree. DECRQM then reports the stale bit.
Concretely, entering the alternate screen with ?47h and leaving it with
?1049l leaves mode 47 reported as set while the primary screen is showing.
requestMode reads the raw bit (modes.get(mode) in
src/termio/stream_handler.zig), and nothing reconciles that bit against the
active screen, which is tracked separately as screens.active_key.
The practical effect is that an application cannot use DECRQM to learn whether the alternate screen is active. That matters for anything that inspects or restores terminal state: shells, editors, multiplexers and session managers. It also means the three modes can report three different answers to the same question.
For reference, xterm keeps no per-mode bit for these. It answers DECRQM for all
three from a single buffer flag, screen->whichBuf:
misc.c#L5610-L5618.
Expected Behavior
After leaving the alternate screen, querying mode 47 reports reset:
^[[?47;2$yActual Behavior
Mode 47 reports set while the primary screen is showing:
^[[?47;1$yReproduction Steps
- Open Ghostty.
- Enter the alternate screen with one mode and leave with another, then query the first mode:
printf '\033[?47h\033[?1049l\033[?47$p'
IFS= read -r -t 1 reply; printf '%s\n' "$reply" | cat -v- Observe the reply is
^[[?47;1$y(set), although the primary screen is showing. It should be^[[?47;2$y(reset).
The same applies to any mix of the three. For example, ?47h followed by
querying 1047 reports reset while the alternate screen is active.
Ghostty Logs
N/AGhostty Version
Ghostty 1.3.1
Version
- version: 1.3.1
- channel: stable
Build Config
- Zig version : 0.15.2
- build mode : .ReleaseFast
- app runtime : .none
- font engine : .coretext
- renderer : renderer.generic.Renderer(renderer.Metal)
- libxev : kqueueOS Version Information
macOS 26.6.2 (build 25G83)
(Linux only) Display Server
None
(Linux only) Desktop Environment/Window Manager
No response
Minimal Ghostty Configuration
N/AAdditional Relevant Information
There is a fix on this branch:
https://github.com/aymanbagabas/ghostty/tree/aymanbagabas/vt-altscreen-mode-report
It makes screens.active_key the single source of truth, so all three modes
report the screen that is actually active.
It also stops XTRESTORE applying DECSET side effects for these modes. Restoring
mode 1049 currently routes through setMode, so ?1049r erases the screen;
restoring should only switch buffers. xterm does that here:
charproc.c#L8588-L8600.
Setting ?1049h still erases unconditionally, which is unchanged.
A second, larger change is worth considering. ModeEntry currently assumes every
mode owns a bit in ModePacked. Letting an entry declare that it reflects
another mode's state would collapse these three onto one bit and one XTSAVE slot,
and would remove the reconciliation entirely. That renumbers the snapshot bit
registry, so it needs a snapshot format version bump and matching snapshot.ksy
changes, which is why the branch does not do it. Happy to take that route instead
if preferred.
I acknowledge that:
- I have reviewed the FAQ and confirm that my issue is NOT among them.
- I have searched the Ghostty documentation and confirm there doesn't exist a configuration option that solves my issue.
- I have searched the Ghostty repository (both open and closed Discussions and Issues) and confirm this is not a duplicate of an existing issue or discussion.
- I have checked the "Preview" tab on all text fields to ensure that everything looks right, and have wrapped all configuration and code in code blocks with a group of three backticks (
```) on separate lines.
Activity
3Hi @jcollie! I'll open a proper draft PR later (after I get some sleep), but a preliminary review is certainly possible right now. Please see the comparison. I intentionally left comments with the heading “REVIEW” directly in the source code to make the review easier. These comments contain links to the source code from xterm. Also these changes are compatible with the recent PR (#14317) and are preparatory work for #4502, which is where I originally started but then fell down the rabbit hole....
It builds and at least passes
zig build test -Demit-macos-app=false -Demit-xcframework=falseI hope someone will have time for a review. Cheers!
For the record. This is just an attempt to scratch the surface, and the implications are still to be assessed.
Done. #14362
Thanks for the xterm reference!