#14199

DECRQM reports alt screen modes 47/1047/1049 independently of the active screen

Openjcollieopened 1mo ago

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$y

Actual Behavior

Mode 47 reports set while the primary screen is showing:

^[[?47;1$y

Reproduction Steps

  1. Open Ghostty.
  2. 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
  1. 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/A

Ghostty 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        : kqueue

OS 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/A

Additional 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

3
jcollie1mo ago

Thanks for the xterm reference!

korikhin1mo ago

Hi @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=false

I 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.

korikhin1mo ago

Done. #14362

@jcollie