Last active
September 1, 2024 04:11
-
-
Save unxed/d979fe069039fe075c18eb0218b1f8f5 to your computer and use it in GitHub Desktop.
Windows-compatible kitty keyboard protocol implementation; Public Domain
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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