Created
April 8, 2023 08:07
-
-
Save unxed/8649a6223e28312eb0df9ac11660b1d8 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
size_t TTYInputSequenceParser::TryParseAsKittyEscapeSequence(const char *s, size_t l) | |
{ | |
// kovidgoyal's kitty keyboard protocol (progressive enhancement flags 15) support | |
// CSI [ XXX : XXX : XXX ; XXX : XXX [u~ABCDEFHPQRS] | |
// some parts sometimes ommitted, see docs | |
// https://sw.kovidgoyal.net/kitty/keyboard-protocol/ | |
// todo: enhanced key flag now set for essential keys only, should be set for more ones | |
// todo: add more keys. all needed by far2l seem to be here, but kitty supports much more | |
#define KITTY_MOD_SHIFT 1 | |
#define KITTY_MOD_ALT 2 | |
#define KITTY_MOD_CONTROL 4 | |
#define KITTY_MOD_CAPSLOCK 64 | |
#define KITTY_MOD_NUMLOCK 128 | |
#define KITTY_EVT_KEYUP 3 | |
/** 32 is enough without "text-as-code points" mode, but should be increased if this mode is enabled */ | |
const int max_kitty_esc_size = 32; | |
/** first_limit should be set to 3 if "text-as-code points" mode is on */ | |
/** also second_limit should be increased to maximum # of code points per key in "text-as-code points" mode */ | |
const char first_limit = 2; | |
const char second_limit = 3; | |
int params[first_limit][second_limit] = {0}; | |
int first_count = 0; | |
int second_count = 0; | |
bool end_found = 0; | |
size_t i; | |
for (i = 1;; i++) { | |
if (i >= l) { | |
return LIKELY(l < max_kitty_esc_size) ? TTY_PARSED_WANTMORE : TTY_PARSED_BADSEQUENCE; | |
} | |
if (s[i] == ';') { | |
second_count = 0; | |
first_count++; | |
if (first_count >= first_limit) { return TTY_PARSED_BADSEQUENCE; } | |
} else if (s[i] == ':') { | |
second_count++; | |
if (second_count >= second_limit) { return TTY_PARSED_BADSEQUENCE; } | |
} else if (!isdigit(s[i])) { | |
end_found = true; | |
break; | |
} else { // digit | |
params[first_count][second_count] = atoi(&s[i]); | |
for (;;i++) { if (!isdigit(s[i]) || (i >= l)) break; } | |
i--; | |
} | |
} | |
// check for correct sequence ending | |
end_found = end_found && ( | |
(s[i] == 'u') || (s[i] == '~') || | |
(s[i] == 'A') || (s[i] == 'B') || | |
(s[i] == 'C') || (s[i] == 'D') || | |
(s[i] == 'E') || (s[i] == 'F') || | |
(s[i] == 'H') || (s[i] == 'P') || | |
(s[i] == 'Q') || | |
(s[i] == 'R') || // "R" is still vaild here in old kitty versions | |
(s[i] == 'S') | |
); | |
if (!end_found) { | |
return TTY_PARSED_BADSEQUENCE; | |
} | |
/* | |
fprintf(stderr, "%i %i %i %i %i \n", params[0][0], params[0][1], params[0][2], params[0][3], params[0][4]); | |
fprintf(stderr, "%i %i %i %i %i \n", params[1][0], params[1][1], params[1][2], params[1][3], params[1][4]); | |
fprintf(stderr, "%i %i %i %i %i \n", params[2][0], params[2][1], params[2][2], params[2][3], params[2][4]); | |
fprintf(stderr, "%i %i\n", first_count, second_count); | |
*/ | |
int event_type = params[1][1]; | |
int modif_state = params[1][0]; | |
INPUT_RECORD ir = {0}; | |
ir.EventType = KEY_EVENT; | |
if (modif_state) { | |
modif_state -= 1; | |
if (modif_state & KITTY_MOD_SHIFT) { ir.Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED; } | |
if (modif_state & KITTY_MOD_ALT) { ir.Event.KeyEvent.dwControlKeyState |= LEFT_ALT_PRESSED; } | |
if (modif_state & KITTY_MOD_CONTROL) { ir.Event.KeyEvent.dwControlKeyState |= | |
right_ctrl_down ? RIGHT_CTRL_PRESSED : LEFT_CTRL_PRESSED; } | |
if (modif_state & KITTY_MOD_CAPSLOCK) { ir.Event.KeyEvent.dwControlKeyState |= CAPSLOCK_ON; } | |
if (modif_state & KITTY_MOD_NUMLOCK) { ir.Event.KeyEvent.dwControlKeyState |= NUMLOCK_ON; } | |
} | |
int base_char = params[0][2] ? params[0][2] : params[0][0]; | |
if (base_char <= UCHAR_MAX && isalpha(base_char)) { | |
ir.Event.KeyEvent.wVirtualKeyCode = (base_char - 'a') + 0x41; | |
} | |
if (base_char <= UCHAR_MAX && isdigit(base_char)) { | |
ir.Event.KeyEvent.wVirtualKeyCode = (base_char - '0') + 0x30; | |
} | |
switch (base_char) { | |
case '`' : ir.Event.KeyEvent.wVirtualKeyCode = VK_OEM_3; break; | |
case '-' : ir.Event.KeyEvent.wVirtualKeyCode = VK_OEM_MINUS; break; | |
case '=' : ir.Event.KeyEvent.wVirtualKeyCode = VK_OEM_PLUS; break; | |
case '[' : ir.Event.KeyEvent.wVirtualKeyCode = VK_OEM_4; break; | |
case ']' : ir.Event.KeyEvent.wVirtualKeyCode = VK_OEM_6; break; | |
case '\\' : ir.Event.KeyEvent.wVirtualKeyCode = VK_OEM_5; break; | |
case ';' : ir.Event.KeyEvent.wVirtualKeyCode = VK_OEM_1; break; | |
case '\'' : ir.Event.KeyEvent.wVirtualKeyCode = VK_OEM_7; break; | |
case ',' : ir.Event.KeyEvent.wVirtualKeyCode = VK_OEM_COMMA; break; | |
case '.' : ir.Event.KeyEvent.wVirtualKeyCode = VK_OEM_PERIOD; break; | |
case '/' : ir.Event.KeyEvent.wVirtualKeyCode = VK_OEM_2; break; | |
case 9 : ir.Event.KeyEvent.wVirtualKeyCode = VK_TAB; break; | |
case 27 : ir.Event.KeyEvent.wVirtualKeyCode = VK_ESCAPE; break; | |
case 13 : if (s[i] == '~') { | |
ir.Event.KeyEvent.wVirtualKeyCode = VK_F3; | |
} else { | |
ir.Event.KeyEvent.wVirtualKeyCode = VK_RETURN; | |
} | |
break; | |
case 127 : ir.Event.KeyEvent.wVirtualKeyCode = VK_BACK; break; | |
case 2 : if (s[i] == '~') ir.Event.KeyEvent.wVirtualKeyCode = VK_INSERT; break; | |
case 3 : if (s[i] == '~') ir.Event.KeyEvent.wVirtualKeyCode = VK_DELETE; break; | |
case 5 : if (s[i] == '~') { ir.Event.KeyEvent.wVirtualKeyCode = VK_PRIOR; | |
ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY; } break; | |
case 6 : if (s[i] == '~') { ir.Event.KeyEvent.wVirtualKeyCode = VK_NEXT; | |
ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY; } break; | |
case 15 : if (s[i] == '~') ir.Event.KeyEvent.wVirtualKeyCode = VK_F5; break; | |
case 17 : if (s[i] == '~') ir.Event.KeyEvent.wVirtualKeyCode = VK_F6; break; | |
case 18 : if (s[i] == '~') ir.Event.KeyEvent.wVirtualKeyCode = VK_F7; break; | |
case 19 : if (s[i] == '~') ir.Event.KeyEvent.wVirtualKeyCode = VK_F8; break; | |
case 20 : if (s[i] == '~') ir.Event.KeyEvent.wVirtualKeyCode = VK_F9; break; | |
case 21 : if (s[i] == '~') ir.Event.KeyEvent.wVirtualKeyCode = VK_F10; break; | |
case 23 : if (s[i] == '~') ir.Event.KeyEvent.wVirtualKeyCode = VK_F11; break; | |
case 24 : if (s[i] == '~') ir.Event.KeyEvent.wVirtualKeyCode = VK_F12; break; | |
case 57399 : case 57425 : ir.Event.KeyEvent.wVirtualKeyCode = VK_NUMPAD0; break; | |
case 57400 : case 57424 : ir.Event.KeyEvent.wVirtualKeyCode = VK_NUMPAD1; break; | |
case 57401 : case 57420 : ir.Event.KeyEvent.wVirtualKeyCode = VK_NUMPAD2; break; | |
case 57402 : case 57422 : ir.Event.KeyEvent.wVirtualKeyCode = VK_NUMPAD3; break; | |
case 57403 : case 57417 : ir.Event.KeyEvent.wVirtualKeyCode = VK_NUMPAD4; break; | |
case 57404 : ir.Event.KeyEvent.wVirtualKeyCode = VK_NUMPAD5; break; | |
case 57405 : case 57418 : ir.Event.KeyEvent.wVirtualKeyCode = VK_NUMPAD6; break; | |
case 57406 : case 57423 : ir.Event.KeyEvent.wVirtualKeyCode = VK_NUMPAD7; break; | |
case 57407 : case 57419 : ir.Event.KeyEvent.wVirtualKeyCode = VK_NUMPAD8; break; | |
case 57408 : case 57421 : ir.Event.KeyEvent.wVirtualKeyCode = VK_NUMPAD9; break; | |
case 57409 : case 57426 : ir.Event.KeyEvent.wVirtualKeyCode = VK_DECIMAL; break; | |
case 57410 : ir.Event.KeyEvent.wVirtualKeyCode = VK_DIVIDE; break; | |
case 57411 : ir.Event.KeyEvent.wVirtualKeyCode = VK_MULTIPLY; break; | |
case 57412 : ir.Event.KeyEvent.wVirtualKeyCode = VK_SUBTRACT; break; | |
case 57413 : ir.Event.KeyEvent.wVirtualKeyCode = VK_ADD; break; | |
case 57414 : ir.Event.KeyEvent.wVirtualKeyCode = VK_RETURN; break; | |
case 57444 : ir.Event.KeyEvent.wVirtualKeyCode = VK_LWIN; break; | |
case 57450 : ir.Event.KeyEvent.wVirtualKeyCode = VK_RWIN; break; | |
case 57363 : ir.Event.KeyEvent.wVirtualKeyCode = VK_APPS; break; | |
case 57448 : ir.Event.KeyEvent.wVirtualKeyCode = VK_CONTROL; | |
if (event_type != KITTY_EVT_KEYUP) { | |
right_ctrl_down = 1; | |
ir.Event.KeyEvent.dwControlKeyState |= RIGHT_CTRL_PRESSED; | |
} else { | |
right_ctrl_down = 0; | |
ir.Event.KeyEvent.dwControlKeyState &= ~RIGHT_CTRL_PRESSED; | |
} | |
ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY; | |
break; | |
case 57442 : ir.Event.KeyEvent.wVirtualKeyCode = VK_CONTROL; | |
if (event_type != KITTY_EVT_KEYUP) { | |
ir.Event.KeyEvent.dwControlKeyState |= LEFT_CTRL_PRESSED; | |
} else { | |
ir.Event.KeyEvent.dwControlKeyState &= ~LEFT_CTRL_PRESSED; | |
} | |
break; | |
case 57443 : ir.Event.KeyEvent.wVirtualKeyCode = VK_MENU; | |
if (event_type != KITTY_EVT_KEYUP) { | |
ir.Event.KeyEvent.dwControlKeyState |= LEFT_ALT_PRESSED; | |
} else { | |
ir.Event.KeyEvent.dwControlKeyState &= ~LEFT_ALT_PRESSED; | |
} | |
break; | |
case 57449 : ir.Event.KeyEvent.wVirtualKeyCode = VK_MENU; | |
if (event_type != KITTY_EVT_KEYUP) { | |
ir.Event.KeyEvent.dwControlKeyState |= RIGHT_ALT_PRESSED; | |
} else { | |
ir.Event.KeyEvent.dwControlKeyState &= ~RIGHT_ALT_PRESSED; | |
} | |
ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY; | |
break; | |
case 57441 : ir.Event.KeyEvent.wVirtualKeyCode = VK_SHIFT; | |
// todo: add LEFT_SHIFT_PRESSED / RIGHT_SHIFT_PRESSED | |
// see https://github.com/microsoft/terminal/issues/337 | |
if (event_type != KITTY_EVT_KEYUP) { | |
ir.Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED; | |
} else { | |
ir.Event.KeyEvent.dwControlKeyState &= ~SHIFT_PRESSED; | |
} | |
break; | |
case 57447 : ir.Event.KeyEvent.wVirtualKeyCode = VK_SHIFT; | |
// todo: add LEFT_SHIFT_PRESSED / RIGHT_SHIFT_PRESSED | |
// see https://github.com/microsoft/terminal/issues/337 | |
if (event_type != KITTY_EVT_KEYUP) { | |
ir.Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED; | |
} else { | |
ir.Event.KeyEvent.dwControlKeyState &= ~SHIFT_PRESSED; | |
} | |
ir.Event.KeyEvent.wVirtualScanCode = RIGHT_SHIFT_VSC; | |
break; | |
case 57360 : ir.Event.KeyEvent.wVirtualKeyCode = VK_NUMLOCK; break; | |
case 57358 : ir.Event.KeyEvent.wVirtualKeyCode = VK_CAPITAL; break; | |
} | |
switch (s[i]) { | |
case 'A': ir.Event.KeyEvent.wVirtualKeyCode = VK_UP; | |
ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY; break; | |
case 'B': ir.Event.KeyEvent.wVirtualKeyCode = VK_DOWN; | |
ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY; break; | |
case 'C': ir.Event.KeyEvent.wVirtualKeyCode = VK_RIGHT; | |
ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY; break; | |
case 'D': ir.Event.KeyEvent.wVirtualKeyCode = VK_LEFT; | |
ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY; break; | |
case 'E': ir.Event.KeyEvent.wVirtualKeyCode = VK_NUMPAD5; break; | |
case 'H': ir.Event.KeyEvent.wVirtualKeyCode = VK_HOME; | |
ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY; break; | |
case 'F': ir.Event.KeyEvent.wVirtualKeyCode = VK_END; | |
ir.Event.KeyEvent.dwControlKeyState |= ENHANCED_KEY; break; | |
case 'P': ir.Event.KeyEvent.wVirtualKeyCode = VK_F1; break; | |
case 'Q': ir.Event.KeyEvent.wVirtualKeyCode = VK_F2; break; | |
case 'R': ir.Event.KeyEvent.wVirtualKeyCode = VK_F3; break; | |
case 'S': ir.Event.KeyEvent.wVirtualKeyCode = VK_F4; break; | |
} | |
if (ir.Event.KeyEvent.wVirtualScanCode == 0) { | |
ir.Event.KeyEvent.wVirtualScanCode = | |
WINPORT(MapVirtualKey)(ir.Event.KeyEvent.wVirtualKeyCode, MAPVK_VK_TO_VSC); | |
} | |
ir.Event.KeyEvent.uChar.UnicodeChar = params[0][1] ? params[0][1] : params[0][0]; | |
if ( | |
(ir.Event.KeyEvent.uChar.UnicodeChar < 32) || | |
(ir.Event.KeyEvent.uChar.UnicodeChar == 127) || | |
((ir.Event.KeyEvent.uChar.UnicodeChar >= 57358) && (ir.Event.KeyEvent.uChar.UnicodeChar <= 57454)) | |
) { | |
// those are special values, should not be used as unicode char | |
ir.Event.KeyEvent.uChar.UnicodeChar = 0; | |
} | |
if ((modif_state & KITTY_MOD_CAPSLOCK) && !(modif_state & KITTY_MOD_SHIFT)) { | |
// it's weird, but kitty can not give us uppercase utf8 in caps lock mode | |
// ("text-as-codepoints" mode should solve it, but it is not working for cyrillic chars for unknown reason) | |
ir.Event.KeyEvent.uChar.UnicodeChar = towupper(ir.Event.KeyEvent.uChar.UnicodeChar); | |
} | |
ir.Event.KeyEvent.bKeyDown = (event_type != KITTY_EVT_KEYUP) ? 1 : 0; | |
ir.Event.KeyEvent.wRepeatCount = 0; | |
_ir_pending.emplace_back(ir); | |
return i+1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment