Skip to content

Instantly share code, notes, and snippets.

@rockorager
Last active December 6, 2025 23:27
Show Gist options
  • Select an option

  • Save rockorager/e695fb2924d36b2bcf1fff4a3704bd83 to your computer and use it in GitHub Desktop.

Select an option

Save rockorager/e695fb2924d36b2bcf1fff4a3704bd83 to your computer and use it in GitHub Desktop.

Private Mode for In-Band Window Resize Notifications

Terminal emulators typically receive window resize events by installing a signal handler for the SIGWINCH signal. Handling of these signals can create challenges due to their inherently racy properties. Resize events must be synchronized with other application state in a safe manner.

Standard control sequences exist to query the current terminal size from the terminal and receive an in-band control sequence with the window size. However, this system requires polling - which is not ideal. Usually, SIGWINCH handling is preferred.

This specification defines a single new Private Mode which enables automatic reporting of in-band text area resize events -

  • 2048 - Enable/disable reports for text area size in both characters and pixels

Note

When using ReadConsoleInput on Windows, resize notifications are already delivered in band.

Detection and Enabling

Detection is performed with a standard DECRQM query:

CSI ? 2048 $ p

To which the terminal will respond with a DECRPM response:

CSI ? 2048 ; Ps $ y

A Ps value of 0 or 4 means the mode is not supported. Reference

The reports can be enabled using DECSET or DECRST control sequences:

CSI ? 2048 h to enable the mode. CSI ? 2048 l to disable the mode.

Reports

The format of the response / notification is:

CSI 48 ; height_chars ; width_chars ; height_pix ; width_pix t

If a terminal is not capable of reporting pixel sizes, it must report them as 0. A terminal MUST report pixel sizes if it is capable of reporting them.

Any field MAY contain sub-parameters, separated by colons (':', ASCII 0x3A). If a client does not understand these, it MUST ignore subparameters. Currently, no subparameters are defined.

Note

The reported size MUST be the text area size. Text area does not include any padding the terminal applies to the window

Implementation Notes

When first enabled, the terminal MUST send a report of the current size.

If the mode is already enabled, the terminal MUST immediately report the current size if an attempt is made to enable the feature.

This specification does not dictate any sort of throttling or limiting the frequency with which reports can be sent.

The terminal MUST NOT send notifications until the internal resize is complete. That is, the terminal must be prepared for the TTY and application to behave at the new size prior to sending the sequence.

Important

The reported area MUST be the text area size. Font size changes can also affect the text area size

Example

An application queries the terminal for support:

A: \x1b[?2048$p

The terminal responds with:

T: \x1b[?2048;2y

The mode is supported but not currently enabled. The application enables the mode.

A: \x1b[?2048h

The terminal turns the mode on, and gives an immediate report of the window size.

T: \x1b[48;24;80;240;1600t

After some time, the user changes the window size. The terminal sends a new size report.

T: \x1b[48;48;80;480;1600t

@craigbarnes
Copy link

The reports can be enabled using SM or RM control sequences:

CSI ? Ps h to enable the mode. CSI ? Ps l to disable the mode.

Small nit: these sequences are DECSET/DECRST (there's no ? in the ECMA-48 SM/RM sequences).

@rockorager
Copy link
Author

Good catch! Updated.

@rockorager
Copy link
Author

I've updated the spec to be a single mode which delivers both characters and pixel sizes in one response, after an IRC discussion with dnkl.

@joshka
Copy link

joshka commented Jun 3, 2025

Is there any tracking of which terminal emulators support this?

@rockorager
Copy link
Author

Is there any tracking of which terminal emulators support this?

No but I will add that. Right now I know foot, ghostty, iterm2, and kitty all have support

@jquast
Copy link

jquast commented Nov 3, 2025

By my analysis the following 33 terminals tested, 4 support in-band resize notification, https://ucs-detect.readthedocs.io/results.html#dec-private-modes-support

  • ghostty
  • iTerm2
  • kitty
  • foot

And I have implemented it in my python TUI library, blessed, documented here https://blessed.readthedocs.io/en/latest/measuring.html#resizing

Because I feel strongly that this is needed, because SIGWINCH, and, its carrying protocol features/options (telnet, ssh) sometimes cannot reliably transmit about window size changes, I wrote previously in this demonstration program https://github.com/jquast/blessed/blob/master/bin/resize.py

A strange problem: programs that perform screen addressing incorrectly
determine the screen margins. Calls to reset(1) do not resolve the
issue.

This may often happen because the transport is incapable of communicating
the terminal size, such as over a serial line.

@ismail-yilmaz
Copy link

ismail-yilmaz commented Nov 6, 2025

Hi,

Bobcat will also start supporting this mode starting from v0.9.8 (the release is imminent).

The mode is recently implemented in the underlying terminal library (Upp::TerminalCtrl).

And it is already enabled in AUR package.

@ldemailly
Copy link

filled microsoft/terminal#19618 to request implementation of this for windows terminal where it'd be most useful

@j4james
Copy link

j4james commented Dec 5, 2025

Small nit: these sequences are DECSET/DECRST (there's no ? in the ECMA-48 SM/RM sequences).

Nit++: These sequences are actually SM and RM. ECMA-48 just refers to them as private modes in the documentation for the Set Mode and Reset Mode controls. Even DEC didn't refer to them as anything other than SM and RM. The only places you're likely to find "decset" mentioned in the DEC documentation is in reference to their DECset publishing software.

The origin of the DECSET and DECRST names is actually the old xterm source code from the 1980s. It needed a way to dispatch the two types of modes separately, and the constants originally used for that were SET/RST for the standard modes, and DECSET/DECRST for the private modes (at the time there were just a handful of modes, most of which came from DEC).

At some point somebody decided to create some documentation for xterm, and went through the source code compiling a list of the escapes sequences that it supported. They didn't know the official names for anything (back then most people would not have had access to the ANSI or DEC documentation), so they just copied those constants directly from the source.

So the original xterm ctlseq list had entries looking something like this:

ESC [ Ps h      Mode Set (SET)
ESC [ Ps l      Mode Reset (RST)
ESC [ ? Ps h    DEC Private Mode Set (DECSET)
ESC [ ? Ps l    DEC Private Mode Reset (DECRST)

Over time the ANSI mode controls were corrected with the official SM/RM acronyms, but the private mode controls just kept the original constants that were extracted from the source code. It's unfortunate, because they're not just technically incorrect, but also misleading (those private modes were never exclusively DEC, and these days the majority are probably not DEC modes).

Not that it really matters, but I thought some people might find the history interesting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment