Skip to content

Kitty keyboard protocol

Status: Extension, originated by kitty and adopted by most current emulators.

The Kitty keyboard protocol replaces the legacy encoding with one that can represent every key, every modifier, key release and repeat events, and the text a key would produce. It is opt-in per application, layered on a stack so that nested programs do not disturb one another, and progressive: an application enables only the flags it needs.

Syntax

CSI > flags u        push a new set of flags on the stack
CSI < n u            pop n entries (default 1)
CSI = flags ; mode u set flags in the current entry without pushing
CSI ? u              query current flags
CSI ? flags u        reply to the query

flags is a bit mask:

Bit Name Effect
1 Disambiguate escape codes Keys that were ambiguous (Esc, Ctrl-letter, Alt-letter, Tab, Enter, Backspace) become CSI … u sequences
2 Report event types Add press/repeat/release as a sub-parameter
4 Report alternate keys Add the shifted key and base-layout key
8 Report all keys as escape codes Even plain printable keys become CSI … u
16 Report associated text Append the code points the key would have inserted

For CSI = flags ; mode u, mode is 1 to set the given flags, 2 to set the given flags without clearing others, and 3 to clear them.

Key encoding

CSI unicode-key-code : shifted-key : base-layout-key ; modifiers : event-type ; text-as-codepoints u

Sub-parameters are separated by : and may be omitted from the right; a key with no modifier and no event data is CSI 97 u for a.

The modifier value is one plus a mask:

Bit Modifier
1 Shift
2 Alt
4 Ctrl
8 Super
16 Hyper
32 Meta
64 Caps Lock
128 Num Lock

Event types are 1 press (default, may be omitted), 2 repeat, and 3 release; they are only sent when flag 2 is set.

Functional keys

Keys that already had a legacy encoding keep it, with the modifier and event sub-parameters attached in the same positions as the xterm form:

Key Encoding Note
Escape CSI 27 u Only with disambiguate; the bare ESC byte is no longer sent
Enter CSI 13 u With disambiguate when modified; plain Enter still sends CR unless flag 8 is set
Tab CSI 9 u As Enter
Backspace CSI 127 u As Enter
Insert CSI 2 ; mod ~ Legacy tilde form retained
Delete CSI 3 ; mod ~
Up, Down, Right, Left CSI 1 ; mod A … D CSI A when unmodified
Home, End CSI 1 ; mod H, CSI 1 ; mod F
F1–F4 CSI 1 ; mod P … S SS3 P unmodified
F5–F12 CSI 15 ; mod ~ … CSI 24 ; mod ~ Same numbers as xterm
Keypad, media, modifier keys CSI code ; mod u with code ≥ 57344 Private Use Area code points assigned by the protocol

The Private Use Area assignments start at U+E000 (57344) and cover Caps Lock, Scroll Lock, Num Lock, Print Screen, Pause, Menu, F13–F35, the numeric keypad, media keys, and left/right modifier keys. The full table is in the protocol document.

The stack

Each CSI > flags u pushes; each CSI < u pops. A program that pushes must pop before exiting, including on signal-driven exit, or the next program on the same PTY inherits an encoding it did not request. The terminal caps the stack depth and discards the oldest entry when exceeded, and resets the stack when the alternate screen is entered or left only in emulators that document doing so; do not rely on that.

The push/pop model exists so that a shell can enable disambiguation for its own line editor, run an editor that pushes its own flags, and get its flags back when the editor pops.

Detection

Send the query followed by a request that always answers, then read until the second reply arrives:

CSI ? u   CSI c

A terminal that supports the protocol replies CSI ? flags u before its primary device attributes report. A terminal that does not support it replies only to CSI c. This is the pattern kitty's own documentation recommends.

Interaction with legacy modes

  • Flag 1 alone leaves printable keys as plain text and keeps DECCKM and keypad modes effective.
  • Flag 8 overrides DECCKM: every key becomes CSI … u or the tilde form.
  • The protocol ignores modifyOtherKeys while any flags are active; kitty maps CSI > 4 ; 2 m to flag 1 for applications written for xterm.
  • Terminals that implement the protocol must still deliver bracketed paste and mouse events in their own encodings; the keyboard flags do not change them.

Ghostty, and therefore xterm+, has one important exception to the opt-in boundary: its default fixterms encoder reports ambiguous Ctrl-I, Ctrl-M, and Ctrl-[ combinations using CSI-u even while the Kitty flag value is zero. For example, Ctrl-I sends CSI 105 ; 5 u, while Tab remains 0x09. Seeing one of these sequences does not mean that an application enabled Kitty flags; query CSI ? u to determine the active protocol state. Traditional xterm collapses these pairs instead. xterm+ records this substantial default-input difference in docs/compatibility/drift.md.

Probe

tools/query '\033[?u\033[c'

A reply beginning ^[[?0u or ^[[?1u means the protocol is available. To watch encoded keys:

printf '\033[>1u'; cat -v; printf '\033[<u'

Press Escape; with the protocol active it prints ^[[27u rather than ^[.

Sources

Compatibility

Feature support by terminal. Imported claims have not been reverified; unknown is not unsupported.
Feature / stable ID Alacritty Apple Terminal ConPTY (conhost) Contour DomTerm foot Ghostty iTerm2 kitty Konsole mintty mlterm PuTTY Revenant RLogin st tmux rxvt-unicode VTE wayst WezTerm Windows Terminal xterm xterm.js
Kitty keyboard protocol
csi-u-kitty-keyboard
SupportedImported · unverified Unknown Unknown SupportedImported · unverified Unknown SupportedImported · unverified SupportedImported · unverified SupportedImported · unverified SupportedImported · unverified Unknown Unknown Unknown Unknown Unknown Unknown Unknown PartialImported · unverified Unknown Unknown Unknown SupportedImported · unverified UnknownImported · unverified UnknownImported · unverified UnknownImported · unverified
Kitty key report
csi-kitty-key-report
Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown
Kitty keyboard query
csi-kitty-keyboard-query
Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown
Kitty keyboard push flags
csi-kitty-keyboard-push-flags
Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown
Kitty keyboard pop flags
csi-kitty-keyboard-pop-flags
Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown
Kitty keyboard set flags
csi-kitty-keyboard-set-flags
Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown Unknown