Add Kitty Keyboard Protocol (KKP) Input Support#9
Conversation
| if (write(STDOUT_FILENO, query, 4) != 4) { | ||
| perror("write"); | ||
| return 1; | ||
| } |
There was a problem hiding this comment.
Bug: Wrong return value on error.
It returns true (protocol supported) on failure.
jserv
left a comment
There was a problem hiding this comment.
Lines 676-720 (STATE_CSI) and 722-796 (STATE_CSI_U) are nearly identical. Only difference is ':' sub-parameter handling. Consider:
- Single state with conditional ':' handling
- Or extract common logic into helper function
jserv
left a comment
There was a problem hiding this comment.
Inconsistent fire key handling.
Standard path (line 286-287):
if (ch == ' ' || ch == 'f' || ch == 'F' || ch == 'i' || ch == 'I')
doom_key = DOOM_KEY_CTRL;KKP path (lines 267-272):
} else if (input->csi_u_params.keycode == 57442 ||
input->csi_u_params.keycode == ' ' ||
input->csi_u_params.keycode == 'f' ||
...Duplicated logic - extract to helper.
| if (strstr(buffer, "\033[?") != NULL && buffer[nread - 1] == 'u') { | ||
| return true; | ||
| } |
There was a problem hiding this comment.
What if extra data after 'u'? What if partial response? More robust parsing would help.
jserv
left a comment
There was a problem hiding this comment.
The csi_u_params struct is never cleared between key events. This means:
- Stale keycode, modifiers, or event_type from previous sequences may bleed into new parsing
- This can cause incorrect key mapping or false Ctrl+C detection
Fix: Reset csi_u_params at start of CSI sequence parsing:
input->state = STATE_CSI_U;
memset(&input->csi_u_params, 0, sizeof(input->csi_u_params));
jserv
left a comment
There was a problem hiding this comment.
kitty_keyboard_protocol_supported() has multiple return paths with inconsistent handling:
- ready == -1 calls perror() but then falls through to return false at line 893
- The double return false at lines 891 and 893 is redundant
} else {
return false; // Line 891
}
return false; // Line 893 - unreachable or redundant|
@cubic-dev-ai Track the following:
|
| bool already_held = is_key_held(input, doom_key); | ||
| bool is_new_keypress = false; | ||
|
|
||
| /* handle kitty release event */ | ||
| if (input->kitty_keyboard_protocol_active) { | ||
| if (input->csi_u_params.event_type == 1) { | ||
| doom_key_down(doom_key); | ||
| } else if (input->csi_u_params.event_type == 3) { | ||
| doom_key_up(doom_key); | ||
| } | ||
| /* Event Type 2 (Key Repeat) is explicitly ignored. | ||
| * Reason: Game engine handles continuous movement by tracking | ||
| * the key state (active/inactive) between the 'down' and 'up' | ||
| * events. | ||
| */ | ||
| return; | ||
| } |
There was a problem hiding this comment.
Problem: If Kitty ever sends CSI A/B/C/D (instead of CSI u) when KKP is active, the key gets stuck down forever. Per KKP spec with mode 11, arrow keys SHOULD be CSI u sequences, so this might be dead code.
Either:
- Remove the KKP block from csi_key() entirely, OR
- Add sched_key_release() as fallback
There was a problem hiding this comment.
I tested it using kitten show-key -m kitty, and observed the following output when pressing and releasing the Up arrow key:
UP PRESS
CSI A
UP RELEASE
CSI 1 ; 1 : 3 A
This shows:
- UP key press produces the legacy-style shorthand sequence
CSI A(ESC [ A). - UP key release produces the extended format
CSI 1 ; 1 : 3 A, which includes the key code, state (press/release), and modifier fields defined by the Kitty protocol.
I then tested the same scenario with a minimal C program that enables the protocol by sending \033[>11u and simply reads raw data from stdin:
#include <stdio.h>
#include <unistd.h>
int main() {
printf("\033[>11u"); // Enable report-all-keys-as-escape-codes
fflush(stdout);
char buf[256];
while (1) {
int n = read(0, buf, sizeof(buf));
if (n > 0) {
fwrite(buf, 1, n, stdout); // Print raw bytes for inspection
fflush(stdout);
}
}
}The escape sequences received by this program were identical to the output from kitten show-key.
In the KKP, the flag 0b1000 corresponds to “Report all keys as escape codes”, which instructs the terminal to send every key event as an escape sequence (CSI/SS3), instead of using traditional single-byte input for basic keys.
According to KKP spec, the flag 0b001: With this flag turned on, all key events that do not generate text are represented in one of the following two forms:
CSI number; modifier u
CSI 1; modifier [~ABCDEFHPQS]
I think current action is correct.
jserv
left a comment
There was a problem hiding this comment.
Both ascii_key() (lines 258-277) and csi_key() (lines 395-410) have nearly identical:
if (input->csi_u_params.event_type == 1) {
doom_key_down(doom_key);
} else if (input->csi_u_params.event_type == 3) {
doom_key_up(doom_key);
}Extract to helper function:
static void handle_kkp_event(int doom_key, int event_type) {
if (event_type == 1) doom_key_down(doom_key);
else if (event_type == 3) doom_key_up(doom_key);
}|
I have never used |
Check this video. |
jserv
left a comment
There was a problem hiding this comment.
Check https://cbea.ms/git-commit/ carefully and enforce the rules.
You should express the insights for KKP and compatibilities.
jserv
left a comment
There was a problem hiding this comment.
Update 'USAGE.md' as the statement "Ctrl is difficult to capture in terminal environments" is no longer valid.
Also, check mouse control.
The current input layer is constrained by the limitations of traditional terminal escape sequences. A major pain point is the reliable detection of modifier keys (particularly Ctrl), which often collide with TTY control characters or are intercepted before reaching the application. This makes consistent in-game controls difficult to implement. Integrate the Kitty Keyboard Protocol (KKP) to provide a modern, explicit, and unambiguous mechanism for key events. This allows for full modifier state detection, distinct key press/release semantics, and prevents TTY-level character mangling. Implementation Insights: - Runtime Detection: KKP support is verified via a \033[?u query at startup. If the terminal does not respond within 200ms, the system gracefully falls back to legacy parsing. - Extended Parser: The input subsystem now handles CSI u sequences with colon-delimited sub-parameters to map extended key events to internal DOOM keycodes. - Wayland Compatibility: While the protocol mode is set to 11 to request release events, the implementation explicitly handles event type 1 (press) and type 3 (release) to account for inconsistent behavior in some environments (e.g., Kitty on Wayland).
|
Thank @PinkNekoFist for contributing! |
This PR adds full parsing and handling for the Kitty Keyboard Protocol (KKP), enabling reliable movement, firing, and modifier-key detection (including Ctrl) when running under Kitty.
Summary of Changes
Known Issues / Platform Notes
While testing, I discovered that Kitty under native Wayland does not emit event type 2, which is required for some parts of the protocol.
The exact same behavior does work under:
(using
linux_display_server x11inkitty.confalso confirms this)This seems to be a Kitty-specific limitation or bug under Wayland.