Skip to content

Instantly share code, notes, and snippets.

@unxed
Last active September 1, 2024 04:11
Show Gist options
  • Save unxed/d979fe069039fe075c18eb0218b1f8f5 to your computer and use it in GitHub Desktop.
Save unxed/d979fe069039fe075c18eb0218b1f8f5 to your computer and use it in GitHub Desktop.
Windows-compatible kitty keyboard protocol implementation; Public Domain
if (_kitty_kb_flags) {
// References:
// https://sw.kovidgoyal.net/kitty/keyboard-protocol/
// https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
char buffer[64] = {0};
const bool ctrl = (KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED)) != 0;
const bool alt = (KeyEvent.dwControlKeyState & (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED)) != 0;
const bool shift = (KeyEvent.dwControlKeyState & (SHIFT_PRESSED)) != 0;
int keycode = towlower(KeyEvent.uChar.UnicodeChar);
int base = 0;
if ((KeyEvent.wVirtualKeyCode >= 'A') && (KeyEvent.wVirtualKeyCode <= 'Z')) {
base = towlower(KeyEvent.wVirtualKeyCode);
if (base == keycode) { base = 0; }
}
if (base && !keycode) { keycode = base; }
int shifted = 0;
if (shift || (KeyEvent.uChar.UnicodeChar && iswupper(KeyEvent.uChar.UnicodeChar))) {
shifted = KeyEvent.uChar.UnicodeChar;
}
int modifiers = 0;
if (shift) modifiers |= 1;
if (alt) modifiers |= 2;
if (ctrl) modifiers |= 4;
if (KeyEvent.dwControlKeyState & CAPSLOCK_ON) modifiers |= 64;
if (KeyEvent.dwControlKeyState & NUMLOCK_ON) modifiers |= 128;
modifiers += 1; // as spec requres
char suffix = 'u';
// apply modifications for special keys
switch (KeyEvent.wVirtualKeyCode) {
case VK_ESCAPE: keycode = 27; break;
case VK_RETURN: keycode = 13; break;
case VK_TAB: keycode = 9; break;
case VK_BACK: keycode = 127; break;
case VK_INSERT: keycode = 2; suffix = '~'; break;
case VK_DELETE: keycode = 3; suffix = '~'; break;
case VK_LEFT: keycode = 1; suffix = 'D'; break;
case VK_RIGHT: keycode = 1; suffix = 'C'; break;
case VK_UP: keycode = 1; suffix = 'A'; break;
case VK_DOWN: keycode = 1; suffix = 'B'; break;
case VK_PRIOR: keycode = 5; suffix = '~'; break;
case VK_NEXT: keycode = 6; suffix = '~'; break;
case VK_HOME: keycode = 1; suffix = 'H'; break;
case VK_END: keycode = 1; suffix = 'F'; break;
case VK_F1: keycode = 11; suffix = '~'; break;
case VK_F2: keycode = 12; suffix = '~'; break;
case VK_F3: keycode = 13; suffix = '~'; break;
case VK_F4: keycode = 14; suffix = '~'; break;
case VK_F5: keycode = 15; suffix = '~'; break;
case VK_F6: keycode = 17; suffix = '~'; break;
case VK_F7: keycode = 18; suffix = '~'; break;
case VK_F8: keycode = 19; suffix = '~'; break;
case VK_F9: keycode = 20; suffix = '~'; break;
case VK_F10: keycode = 21; suffix = '~'; break;
case VK_F11: keycode = 23; suffix = '~'; break;
case VK_F12: keycode = 24; suffix = '~'; break;
case VK_MENU:
{
if (KeyEvent.dwControlKeyState & ENHANCED_KEY) {
// right
keycode = 57449; suffix = 'u';
} else {
// left
keycode = 57443; suffix = 'u';
}
break;
}
case VK_CONTROL:
{
if ((KeyEvent.dwControlKeyState & ENHANCED_KEY)) {
// right
keycode = 57448; suffix = 'u';
} else {
// left
keycode = 57442; suffix = 'u';
}
break;
}
case VK_SHIFT:
{
if (KeyEvent.wVirtualScanCode == RIGHT_SHIFT_VSC) {
// right
keycode = 57447; suffix = 'u';
} else {
// left
keycode = 57441; suffix = 'u';
}
break;
}
}
// avoid sending base char if it is equal to keycode
if (base == keycode) { base = 0; }
int flags = _kitty_kb_flags;
if (!(flags & 8)) { // "Report all keys as escape codes" disabled
// do not sent modifiers themselfs
if (!keycode && (modifiers > 1)) {
return "";
}
}
// Generate ESC sequence, format:
// CSI unicode-key-code:shifted-key:base-layout-key ; modifiers:event-type ; text-as-codepoints u
int len = 0;
len += snprintf(buffer + len, sizeof(buffer) - len, "\x1B[");
// Part 1
// We are not able to generate proper sequence for this key for now, sorry
if (!keycode) { return ""; }
len += snprintf(buffer + len, sizeof(buffer) - len, "%i", keycode);
if ((flags & 4) && (shifted || base)) { // "report alternative keys" enabled
len += snprintf(buffer + len, sizeof(buffer) - len, ":");
if (shifted) {
len += snprintf(buffer + len, sizeof(buffer) - len, "%i", shifted);
}
if (base) {
len += snprintf(buffer + len, sizeof(buffer) - len, ":%i", base);
}
}
// Part 2
if ((modifiers > 1) || ((flags & 2) && !KeyEvent.bKeyDown)) {
len += snprintf(buffer + len, sizeof(buffer) - len, ";");
len += snprintf(buffer + len, sizeof(buffer) - len, "%i", modifiers);
if ((flags & 2) && !KeyEvent.bKeyDown) {
len += snprintf(buffer + len, sizeof(buffer) - len, ":%i", 3);
}
}
// Part 3
if ((flags & 16) && KeyEvent.uChar.UnicodeChar) {
if (!((modifiers > 1) || ((flags & 2) && !KeyEvent.bKeyDown))) {
// If no part 2, lets add ";"
len += snprintf(buffer + len, sizeof(buffer) - len, ";");
}
len += snprintf(buffer + len, sizeof(buffer) - len, ";%i", KeyEvent.uChar.UnicodeChar);
}
// Finally
len += snprintf(buffer + len, sizeof(buffer) - len, "%c", suffix);
if (!(flags & 8) && KeyEvent.uChar.UnicodeChar && !alt && !ctrl) {
// "Report all keys as escape codes" disabled
// just send text
len = snprintf(buffer, sizeof(buffer), "%lc", KeyEvent.uChar.UnicodeChar);
}
return buffer;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment