Kitty Keyboard Protocol
The Kitty Keyboard Protocol provides enhanced keyboard input capabilities.
It allows your application to distinguish between some special kinds of keys, between key press, repeat, and release events, and provides improved support for modifiers and special characters.
The Kitty keyboard protocol is known to be supported by many popular terminals: Kitty, alacritty, foot, ghostty, iTerm2, rio, WezTerm.
On terminals that do not support the protocol, the
enable_kitty_keyboard() context manager gracefully does
nothing, and keyboard input continues to work normally, application code does
not need to change.
Overview
This protocol is designed mainly to resolve ambiguity, such as those related to
the Escape key, application keys, Control keys, and Alt+Key sequences. For
example, Ctrl+I and TAB key both send \t, but, with disambiguation mode
enabled, these are detected separately with unique sequences and names,
KEY_TAB and KEY_CTRL_I.
The protocol is automatically detected and enabled when you use the
enable_kitty_keyboard() context manager. If the terminal
doesn’t support it, your code continues to work normally using standard
keyboard input.
Unlike standard keyboard input where name is None
for plain characters, the Kitty protocol synthesizes names for all ASCII
alphanumeric and punctuation keys. See Key Name Synthesis for
details.
Getting Started
Here’s a simple example showing key press, repeat, and release events:
1#!/usr/bin/env python3
2from blessed import Terminal
3
4term = Terminal()
5
6print("Press and hold keys to see raw kitty keystrokes and their names (press 'q' to quit)")
7with term.enable_kitty_keyboard(report_events=True, report_all_keys=True, report_alternates=True):
8 print("kitty keyboard state:", term.get_kitty_keyboard_state())
9 with term.raw():
10 while True:
11 key = term.inkey()
12
13 kind = ("pressed" if key.pressed
14 else "repeated" if key.repeated
15 else "released")
16 if key.pressed and key.value == 'q':
17 break
18 print(
19 f"name={
20 key.key_name} value={
21 key.key_value} kind={kind}, sequence={
22 key!r}",
23 end="\r\n")
In this example, pressing and holding a key will show “pressed” once, then multiple “repeating” messages while held, and finally “released” when you let go. On terminals that don’t support the Kitty protocol, you’ll only see “pressed” events.
Event Types
When report_events=True is enabled, the Keystroke class provides
three properties to distinguish between key event types:
pressed-Trueif this is a key press eventrepeated-Trueif this is a key repeat event (repeat events issued by OS when held down)released-Trueif this is a key release event
These properties are only meaningful when Kitty keyboard protocol is enabled
with report_events=True. Without the protocol, all keystrokes will have
pressed=True and repeated=False, released=False.
The name attribute also includes event type suffixes:
KEY_CTRL_J for press, KEY_CTRL_J_REPEATED for repeat, and
KEY_CTRL_J_RELEASED for release events.
For press/release tracking (e.g., key maps), use key_name
instead – it returns the same name regardless of event type (KEY_CTRL_J
for press, repeat, and release). Similarly, key_value
returns the character even for release events, where value
returns ''.
Note: To distinguish press, repeat, and release for plain text keys like
a or 5, you must also enable report_all_keys=True. Without it, only
release events are encoded with the protocol – press and repeat arrive as plain
characters with no event-type information.
Example use case - detect only initial key presses and ignore repeats:
from blessed import Terminal
term = Terminal()
with term.enable_kitty_keyboard(report_events=True):
with term.cbreak():
while True:
key = term.inkey()
# Only respond to initial keypress, ignoring repeat and release
if key.pressed:
if key == 'q':
break
print(f"Key {key!r} pressed")
Protocol Features
The enable_kitty_keyboard() context manager accepts any
combination of the following feature flags as keyword arguments of type bool:
- disambiguate
Enables disambiguated escape codes. With this enabled, pressing the Escape keys, control, and some application keys produce distinct sequences that more correctly identify the user’s keypress. For example, Ctrl+I and Tab can be distinguished as
KEY_CTRL_IandKEY_TAB.- report_events
Reports key repeat and release events in addition to key press events. This allows detecting when keys are held down or released.
- report_alternates
Reports both the shifted and base layout keys for keyboard shortcuts. Useful for handling shortcuts consistently across different keyboard layouts (e.g., matching both Ctrl+Shift+Equal and Ctrl+Plus as the same shortcut).
- report_all_keys
Reports all keys as escape codes, including normal text keys that would normally be sent as plain characters.
- report_text
Reports the associated text with key events (requires
report_all_keys). This provides the actual character that would be typed alongside the key code.
Basic usage:
with term.enable_kitty_keyboard(disambiguate=True, report_events=True):
# Your code here
pass
Feel free to try the demonstration program, keymatrix.py to experiment with combining any or all possible kitty protocol features using Shift+F1 through Shift+F5.
Key Name Synthesis
The name is synthesized for all ASCII alphanumeric keys, punctuation,
and symbols while in kitty keyboard mode.
Some example Alphanumeric keys:
KEY_A, KEY_Z, KEY_0, KEY_9
KEY_CTRL_A, KEY_ALT_5, KEY_CTRL_ALT_SHIFT_M
Punctuation and symbols use descriptive names:
KEY_SPACE, KEY_PERIOD, KEY_COMMA, KEY_MINUS, KEY_EQUALS
KEY_LEFT_SQUARE_BRACKET, KEY_RIGHT_SQUARE_BRACKET
KEY_SLASH, KEY_BACKSLASH, KEY_SEMICOLON, KEY_APOSTROPHE
KEY_GRAVE_ACCENT, KEY_TILDE, KEY_EXCLAMATION_MARK
KEY_AT, KEY_HASH, KEY_DOLLAR, KEY_PERCENT, KEY_CARET
KEY_AMPERSAND, KEY_ASTERISK, KEY_PLUS, KEY_PIPE
KEY_LEFT_PARENTHESIS, KEY_RIGHT_PARENTHESIS
KEY_LEFT_CURLY_BRACKET, KEY_RIGHT_CURLY_BRACKET
KEY_LESS_THAN, KEY_GREATER_THAN, KEY_QUESTION_MARK
KEY_DOUBLE_QUOTE, KEY_COLON, KEY_UNDERSCORE
Modifiers and event-type suffixes combine as expected:
KEY_ALT_LEFT_SQUARE_BRACKET
KEY_CTRL_PERIOD
KEY_SPACE_RELEASED
KEY_ALT_MINUS_REPEATED
Shifted Key Reporting
The Kitty protocol reports the base key and modifiers separately rather
than the resulting character. On a US keyboard layout, pressing Shift+3
produces #, but the terminal reports codepoint 51 (digit 3) with the
Shift modifier. This means:
The name is
KEY_SHIFT_3, notKEY_HASHThe
valueis the actual character#
Symbol names like KEY_HASH, KEY_AMPERSAND, and KEY_TILDE are
still reachable when a terminal sends the symbol codepoint directly, but
on most layouts Shift+digit combinations will produce names based on the
base digit key.
Layout-Independent Key Handling
By default, the protocol reports whichever key the current keyboard layout maps to a given position. This means the keys of a physical position on the keyboard will produce different codepoints on different layouts, QWERTY, Dvorak, AZERTY, or many non-US layouts.
The report_alternates flag solves this by requesting the terminal to
also report the base layout key – the key identity independent of the
active layout. When report_alternates=True, blessed uses the base
layout key for name synthesis, so a shortcut like KEY_CTRL_Z works
regardless of whether the user’s layout places Z in the QWERTY
position or elsewhere.
with term.enable_kitty_keyboard(report_alternates=True):
with term.cbreak():
key = term.inkey()
if key.name == 'KEY_CTRL_Z':
undo()
This is especially useful for applications that bind shortcuts to physical key positions (like Ctrl+Z for undo) rather than to specific characters.
Compatibility
You can optionally check for protocol support:
from blessed import Terminal
term = Terminal()
state = term.get_kitty_keyboard_state()
if state is not None:
print("Kitty keyboard protocol is supported")
else:
print("No kitty protocol support.")
This check is not necessary but may be useful in some cases.
Timeout
The timeout parameter of enable_kitty_keyboard() controls
how long to await negotiation, in seconds.
When negotiating, both DA1 and Kitty protocol status request sequences are
transmitted, making it possible to automatically detect protocol support in
almost all cases, but terminals that support neither DA1 or Kitty protocol
(“dumb” terminals) will block forever unless timeout is set.
See Also
Keyboard Input - Basic keyboard input handling
Terminal.enable_kitty_keyboard()- Enable Kitty protocol featuresTerminal.get_kitty_keyboard_state()- Query current protocol stateKeystroke.pressed- Check if key was pressedKeystroke.repeated- Check if key is repeatingKeystroke.released- Check if key was releasedKeystroke.key_name- Key identity without event-type suffixKeystroke.key_value- Character for the key, even for release events