Skip to main content

Kitty keyboard protocol

AI-Generated Content
This page was generated with the assistance of AI and may contain inaccuracies. It is intended as a placeholder for future human verification. If you spot issues ahead of its initial review, please report them on GitHub!
Experimental

Kitty keyboard protocol is a progressive enhancement system for keyboard input in terminal emulators. It replaces the legacy, ambiguous keyboard encoding inherited from physical terminals with a well-defined, extensible scheme based on Unicode codepoints and CSI sequences.

Syntax​

CSI > flags u       Push current mode and enable flags (set keyboard mode)
CSI < u Pop keyboard mode (restore previous mode)
CSI ? u Query current keyboard mode flags
CSI number ; modifiers u Key event report
Formal syntax
push-mode    = 0x1b, "[", ">", flags, "u" ;
pop-mode     = 0x1b, "[", "<", [ number ], "u" ;
query-mode   = 0x1b, "[", "?", "u" ;
key-event    = 0x1b, "[", key-code, [ ":", shifted, [ ":", base ] ], [ ";", modifiers, [ ":", event-type ] ], [ ";", text ], "u" ;
flags        = digit, { digit } ;                 (* bitmask: 1|2|4|8|16 *)
key-code     = digit, { digit } ;                 (* Unicode codepoint *)
shifted      = digit, { digit } ;                 (* shifted key codepoint *)
base         = digit, { digit } ;                 (* base layout key codepoint *)
modifiers    = digit, { digit } ;                 (* modifier bitmask + 1 *)
event-type   = "1" | "2" | "3" ;                  (* 1=press, 2=repeat, 3=release *)
text         = codepoint, { ":", codepoint } ;    (* associated text codepoints *)
codepoint    = digit, { digit } ;
digit        = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
number       = digit, { digit } ;

Enhancement flags​

The flags parameter in the push sequence is a bitmask that selects which enhancements to activate. Multiple flags can be combined by ORing them together.

FlagValueEnhancement
Disambiguate1Disambiguate escape codes for special keys
Report event types2Report key press, repeat, and release events
Report alternate keys4Report shifted key and base layout key
All keys as escape codes8Report all key events as CSI u sequences
Report associated text16Include generated text in key event reports

Modifier encoding​

Modifiers are encoded as a bitmask with 1 added (following the xterm convention). The modifier value sent in key events is 1 + bitmask.

ModifierBit value
Shift1
Alt2
Ctrl4
Super8
Hyper16
Meta32
Caps Lock64
Num Lock128

Description​

Traditional terminal keyboard input suffers from fundamental ambiguities. For example, pressing Escape, Alt+[, and Ctrl+[ all produce the same byte (0x1b). The Enter key sends 0x0d, which is indistinguishable from Ctrl+M. These ambiguities exist because terminal input encoding was designed for physical serial terminals in the 1970s and was never updated.

The Kitty keyboard protocol solves this by introducing a stack-based mode system. Applications push a new keyboard mode with the desired enhancement flags onto a stack when they start, and pop it when they exit. This ensures clean restoration of the previous mode, even when multiple applications are nested (for example, running vim inside tmux inside a terminal).

Mode stack​

The protocol uses a push/pop model rather than a simple set/reset:

  • Push (CSI > flags u): Pushes the current mode onto the stack and activates the new flags. The terminal should support a stack depth of at least 8.
  • Pop (CSI < u): Pops the most recently pushed mode and restores it. An optional number parameter can pop multiple entries at once.
  • Query (CSI ? u): The terminal responds with CSI ? flags u indicating the currently active flags.

Key event encoding​

When the protocol is active, key events are reported as CSI number ; modifiers u where number is the Unicode codepoint of the key. For functional keys that do not have a Unicode codepoint, dedicated numbers from the Unicode Private Use Area are assigned.

Some common functional key mappings:

KeyCodepoint
Escape27
Enter13
Tab9
Backspace127
Insert57348
Delete57349
Home57350
End57351

The key event format can carry additional information depending on which enhancement flags are active. At its most complete, the format is:

CSI key_code : shifted_key : base_key ; modifiers : event_type ; text u

Examples​

# Enable disambiguate mode (flag 1)
printf '\e[>1u'

# Enable disambiguate + report event types (flags 1|2 = 3)
printf '\e[>3u'

# Enable all enhancements (flags 1|2|4|8|16 = 31)
printf '\e[>31u'

# Pop (restore previous mode)
printf '\e[<u'

# Pop two levels from the stack
printf '\e[<2u'

# Query current mode
printf '\e[?u'
# Terminal responds: ESC [ ? flags u

Specifications​

SpecificationSection
Kitty Keyboard Protocol—

Terminal support​

TerminalSupportVersionNotes
Terminal Emulators
Alacritty✓v0.13.0
Bobcat✗NoNo evidence of Kitty keyboard protocol implementation found in Bobcat codebase. Keyboard handling is delegated to TerminalCtrl library with only basic VT/PC function key styles supported.
contour✓v0.4.0.6245Implemented via CSIUENTER/CSIUQUERY/CSIUENHCE/CSIULEAVE at Functions.h:541-543, handled in Screen.cpp:3990-4016
foot✓1.10.2
Ghostty✓v1.0.0
iTerm2✓v20260216-nightlyFull progressive enhancement with CSI u, push/pop stack
Kitty✓YesFull progressive enhancement support
Konsole✗NoWin32-input-mode (9001) supported instead
mintty~NoOnly modifyOtherKeys (xterm) is implemented; kitty keyboard protocol (CSI = u) sequences not found
mlterm✗NoNot implemented; uses standard xterm modifyOtherKeys mode only
PuTTY✗No
Rio✓YesImplemented in commit 426e37d936 (2025-08-19) but not yet in any released version. Available only in 'nightly' tag. Supports CSI u sequences with progressive enhancement flags.
rxvt-unicode✗No
st✗No
terminology✗No
VT100✗No
VTE✗NoMode 2017 defined but not implemented
WezTerm✓YesRequires enable_kitty_keyboard config option
Windows Terminal✓YesFull implementation added in commit c81c66b (Feb 17, 2026). Includes Set/Query/Push/Pop operations at adaptDispatch.cpp:2073-2100
xterm✗No
xterm.js✓YesRequires vtExtensions.kittyKeyboard option, added after 6.0.0
Multiplexers
cy✓v1.11.0
GNU Screen✗No
tmux~3.2Supports extended keys protocol (CSI 27;mods;key~ and CSI key;mods u) similar to Kitty. Initial support in 3.2, revamped in 3.5 to support both mode 1 and mode 2
tuios✗NoTODO comment in internal/vt/key.go:32 explicitly states 'TODO: Support Kitty, CSI u, and XTerm modifyOtherKeys'
Zellij??

Flag 1: Disambiguate escape codes​

Disambiguate (flag 1) is the most fundamental enhancement. When enabled, keys that would normally produce ambiguous byte sequences instead send unambiguous CSI-based escape codes, eliminating the conflicts between control codes, modifier combinations, and special keys that have plagued terminal input for decades.

In legacy terminal input, many distinct key presses produce identical byte sequences:

Key pressLegacy byte(s)Conflict
Enter0x0dSame as Ctrl+M
Tab0x09Same as Ctrl+I
Backspace0x7f or 0x08Same as Ctrl+? or Ctrl+H
Escape0x1bSame as Alt+[ prefix
Ctrl+[0x1bSame as Escape

With disambiguate mode enabled, these keys send CSI u sequences based on their Unicode codepoints instead:

Key pressDisambiguated sequenceCodepoint
EnterCSI 13 uU+000D
TabCSI 9 uU+0009
BackspaceCSI 127 uU+007F
EscapeCSI 27 uU+001B

When modifiers are held, the modifier bitmask (plus 1) is included as a second parameter. For example, Ctrl+Enter sends CSI 13 ; 5 u (modifier bitmask 4 for Ctrl, plus 1 = 5).

Plain printable characters without modifiers (other than Shift) are still sent as their literal UTF-8 encoding. This flag only changes the encoding of keys that would otherwise be ambiguous.

Disambiguate mode specifically targets:

  • Functional keys that traditionally send C0 control codes: Enter, Tab, Backspace, Escape
  • Modified key combinations where Ctrl or Alt would create ambiguous sequences (e.g., Ctrl+A normally sends 0x01, but with this flag sends CSI 97 ; 5 u)
  • Keys that conflict with CSI/SS3 sequences: function keys, arrow keys, Home, End, etc., get standardized CSI u encoding when combined with modifiers
# Enable disambiguate mode
printf '\e[>1u'

# Now pressing Enter produces: ESC [ 1 3 u
# Instead of: 0x0d

# Pressing Ctrl+Enter produces: ESC [ 1 3 ; 5 u
# (13 = Enter codepoint, 5 = Ctrl modifier + 1)

# Pressing Shift+Tab produces: ESC [ 9 ; 2 u
# (9 = Tab codepoint, 2 = Shift modifier + 1)

# Regular typing (e.g., 'a') is unchanged: sends 0x61

# Restore previous mode
printf '\e[<u'

Flag 2: Report event types​

Report event types (flag 2) extends the protocol to report key release and repeat events in addition to the default key press events. The event type is encoded as a suffix on the modifier parameter, enabling applications to track the full lifecycle of each key press.

ValueTypeDescription
1PressKey was pressed down (default, may be omitted)
2RepeatKey is held down and auto-repeating
3ReleaseKey was released

Legacy terminal input only reports key press events. There is no mechanism to detect when a key is released or when an auto-repeat event occurs. This limits applications that need to track key state, such as games or applications that want to implement key-up triggered actions.

When flag 2 is enabled, the terminal appends an event type indicator to the modifier value using a colon separator. The format is modifiers:event_type. For press events, the event type of 1 may be omitted since it is the default. For a release event with no modifiers, the sequence would be CSI number ; 1:3 u (modifier value 1 meaning no modifiers, event type 3 for release).

Flag 2 is typically combined with flag 1 (disambiguate). When combined with flag 8 (all keys as escape codes), every key including plain printable characters will report press, repeat, and release events.

# Enable disambiguate + event types (flags 1|2 = 3)
printf '\e[>3u'

# Pressing and releasing Enter produces:
# Press: ESC [ 1 3 u (event type 1 omitted)
# Release: ESC [ 1 3 ; 1 : 3 u (modifier=1, event=release)

# Holding 'a' with Ctrl produces:
# Press: ESC [ 9 7 ; 5 u (Ctrl press, event type omitted)
# Repeat: ESC [ 9 7 ; 5 : 2 u (Ctrl repeat)
# Release: ESC [ 9 7 ; 5 : 3 u (Ctrl release)

# Restore previous mode
printf '\e[<u'

Flag 4: Report alternate keys​

Report alternate keys (flag 4) includes the shifted key value and the base layout key value in key event reports. This allows applications to understand both the physical key pressed and the character it would produce under the current keyboard layout and shift state.

FieldDescription
unicodeThe effective key codepoint for the current keyboard layout and modifier state
shiftedThe codepoint that would be produced if Shift were held (omitted if same as unicode)
baseThe codepoint of the key on the base (e.g., US English) keyboard layout (omitted if same as unicode)

On most keyboard layouts, a physical key can produce different characters depending on modifiers and the active layout. Without this flag, applications only receive the final resolved character and cannot determine which physical key was pressed or what the shifted variant would be.

When flag 4 is enabled, the terminal includes up to two additional codepoints in the key event, separated by colons after the primary key code. If a field is not applicable or is the same as the primary key code, it is left empty (but the colon separator is still present if a subsequent field is needed).

Use cases include games (bind movement to WASD physical key positions regardless of keyboard layout), keyboard shortcuts (define Ctrl+Z as "Ctrl + the key at the Z position" on any layout), and international input.

# Enable disambiguate + alternate keys (flags 1|4 = 5)
printf '\e[>5u'

# On a US layout, pressing Shift+2 (which produces '@'):
# ESC [ 6 4 : : 5 0 ; 2 u
# 64 = '@' (effective), shifted omitted, 50 = '2' (base), modifier 2 = Shift

# On a German layout, pressing the key at the 'z' position
# (which produces 'y' on German QWERTZ):
# ESC [ 1 2 1 : : 1 2 2 u
# 121 = 'y' (effective on German), base 122 = 'z' (US layout)

# Restore previous mode
printf '\e[<u'

Flag 8: All keys as escape codes​

All keys as escape codes (flag 8) makes every key event -- including plain, unmodified printable characters -- report as a CSI u escape sequence. Without this flag, unmodified printable keys (like typing a or 5) are still sent as their literal UTF-8 byte, even when other enhancement flags are active.

With flags 1 (disambiguate) alone, pressing a plain letter key like a still sends the byte 0x61 directly. The disambiguate flag only changes encoding for keys that are otherwise ambiguous. However, some applications need to treat every key event uniformly. Games, screen readers, and input method editors may benefit from a consistent event format for all keys.

Flag 8 implies the behavior of flag 1 (disambiguate). When combined with flag 2 (event types), every key including printable characters will report press, repeat, and release events.

note

Enabling flag 8 significantly increases the volume of escape sequence data sent to the application. Each key press that previously sent a single byte now sends a multi-byte escape sequence. Applications should only enable this flag when they genuinely need uniform event handling for all keys.

# Enable all keys as escape codes (flags 1|8 = 9)
printf '\e[>9u'

# Pressing 'a' produces: ESC [ 9 7 u
# (97 is the Unicode codepoint for 'a')

# Pressing 'A' (Shift+a) produces: ESC [ 9 7 ; 2 u
# (97 = 'a', modifier 2 = Shift)

# Pressing Space produces: ESC [ 3 2 u
# (32 is the Unicode codepoint for space)

# Enable all keys + event types (flags 1|2|8 = 11)
printf '\e[>11u'

# Pressing and releasing 'a':
# Press: ESC [ 9 7 u
# Release: ESC [ 9 7 ; 1 : 3 u

# Restore previous mode
printf '\e[<u'

Flag 16: Report associated text​

Report associated text (flag 16) includes the text that would have been generated by a key event in the escape sequence itself. The text is encoded as a colon-separated list of Unicode codepoints appended as a third parameter to the key event.

When other flags are active, the key event reports the physical key that was pressed (as a Unicode codepoint), but not necessarily the text that the key press would generate. In most cases these are the same, but they can differ:

  • Dead keys and compose sequences: Pressing a dead key followed by e produces an accented character. The key events report the individual key presses, but the associated text reports the composed result.
  • Input method editors (IMEs): An IME may consume multiple key presses and produce a single composed character or string.
  • Shift and layout transformations: Pressing Shift+2 on a US layout produces @. The key code reports 2 (the physical key), but the associated text reports @.
ScenarioKey codeAssociated text
Shift+2 on US layout50 (digit 2)64 (@ symbol)
Compose sequence resultLast key in sequenceComposed character
IME outputLast key pressedFull composed string
Function key (F1)57344(omitted, no text generated)

Flag 16 is typically combined with flags 1 and 8 to get associated text for all key events. The combination of all flags (CSI > 31 u) provides the most complete keyboard reporting available.

The associated text field occupies the third parameter position. If event types (flag 2) are active, the modifier parameter carries both the modifier bitmask and the event type (modifiers:event_type), followed by the text field:

CSI key_code ; modifiers:event_type ; text u
# Enable all enhancements (flags 1|2|4|8|16 = 31)
printf '\e[>31u'

# Pressing 'a' produces:
# ESC [ 9 7 ; 1 ; 9 7 u
# key=97 ('a'), modifiers=1 (none), text=97 ('a')

# Pressing Shift+2 on US layout (produces '@'):
# ESC [ 5 0 ; 2 ; 6 4 u
# key=50 ('2'), modifiers=2 (Shift), text=64 ('@')

# Pressing a dead key sequence (e.g., compose ' then e = e-acute):
# First key (dead accent): ESC [ 39 ; 1 u
# (no associated text -- dead key does not generate text alone)
# Second key (e): ESC [ 1 0 1 ; 1 ; 2 3 3 u
# key=101 ('e'), text=233 (U+00E9 'e-acute')

# Restore previous mode
printf '\e[<u'