Skip to content

Add Kitty Keyboard Protocol (KKP) Input Support#9

Merged
jserv merged 1 commit intojserv:mainfrom
PinkNekoFist:main
Dec 9, 2025
Merged

Add Kitty Keyboard Protocol (KKP) Input Support#9
jserv merged 1 commit intojserv:mainfrom
PinkNekoFist:main

Conversation

@PinkNekoFist
Copy link
Collaborator

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

  • Implemented a parser for KKP escape sequences
  • Added support for key press / key release events
  • Correctly mapped shifted keys and modifier states
  • Integrated KKP events into the existing input system
  • Verified working behavior under Kitty on X11/Xwayland

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:

  • X11
  • Xwayland
    (using linux_display_server x11 in kitty.conf also confirms this)

This seems to be a Kitty-specific limitation or bug under Wayland.

Comment on lines 864 to 867
if (write(STDOUT_FILENO, query, 4) != 4) {
perror("write");
return 1;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Wrong return value on error.
It returns true (protocol supported) on failure.

Copy link
Owner

@jserv jserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Owner

@jserv jserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 886 to 888
if (strstr(buffer, "\033[?") != NULL && buffer[nread - 1] == 'u') {
return true;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if extra data after 'u'? What if partial response? More robust parsing would help.

Copy link
Owner

@jserv jserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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));

Copy link
Owner

@jserv jserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@jserv
Copy link
Owner

jserv commented Dec 5, 2025

@cubic-dev-ai Track the following:

  1. Fix event_type / modifiers parsing order in CSI u handler
  2. Reset csi_u_params at sequence start
  3. Handle CSI u completion directly, not via ascii_key('u') routing
  4. Remove dead code (current_sub_parm, unused struct fields)

Repository owner deleted a comment from cubic-dev-ai bot Dec 5, 2025
cubic-dev-ai[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

Repository owner deleted a comment from cubic-dev-ai bot Dec 5, 2025
cubic-dev-ai[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

Comment on lines 395 to 411
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;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Owner

@jserv jserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
  }

jserv

This comment was marked as resolved.

Repository owner deleted a comment from cubic-dev-ai bot Dec 6, 2025
cubic-dev-ai[bot]

This comment was marked as outdated.

Repository owner deleted a comment from cubic-dev-ai bot Dec 6, 2025
@PinkNekoFist
Copy link
Collaborator Author

I have never used git rebase command before, I think I have to check the details of usage first.

cubic-dev-ai[bot]

This comment was marked as resolved.

@jserv
Copy link
Owner

jserv commented Dec 6, 2025

I have never used git rebase command before, I think I have to check the details of usage first.

Check this video.

Copy link
Owner

@jserv jserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check https://cbea.ms/git-commit/ carefully and enforce the rules.

You should express the insights for KKP and compatibilities.

Copy link
Owner

@jserv jserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update 'USAGE.md' as the statement "Ctrl is difficult to capture in terminal environments" is no longer valid.

Also, check mouse control.

cubic-dev-ai[bot]

This comment was marked as resolved.

Repository owner deleted a comment from cubic-dev-ai bot Dec 8, 2025
Repository owner deleted a comment from cubic-dev-ai bot Dec 8, 2025
cubic-dev-ai[bot]

This comment was marked as resolved.

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).
@jserv jserv merged commit e264ed6 into jserv:main Dec 9, 2025
7 checks passed
@jserv
Copy link
Owner

jserv commented Dec 9, 2025

Thank @PinkNekoFist for contributing!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants