@@ -309,6 +309,276 @@ void TTYInputSequenceParser::ParseAPC(const char *s, size_t l)
309309 }
310310}
311311
312+ bool right_ctrl_down = 0 ;
313+
314+ size_t TTYInputSequenceParser::TryParseAsKittyEscapeSequence (const char *s, size_t l)
315+ {
316+ // kovidgoyal's kitty keyboard protocol (progressive enhancement flags 15) support
317+ // CSI [ XXX : XXX : XXX ; XXX : XXX [u~ABCDEFHPQRS]
318+ // some parts sometimes ommitted, see docs
319+ // https://sw.kovidgoyal.net/kitty/keyboard-protocol/
320+
321+ // todo: enhanced key flag now set for essential keys only, should be set for more ones
322+
323+ // todo: add more keys. all needed by far2l seem to be here, but kitty supports much more
324+
325+ #define KITTY_MOD_SHIFT 1
326+ #define KITTY_MOD_ALT 2
327+ #define KITTY_MOD_CONTROL 4
328+ #define KITTY_MOD_CAPSLOCK 64
329+ #define KITTY_MOD_NUMLOCK 128
330+ #define KITTY_EVT_KEYUP 3
331+
332+ /* * 32 is enough without "text-as-code points" mode, but should be increased if this mode is enabled */
333+ const int max_kitty_esc_size = 32 ;
334+
335+ /* * first_limit should be set to 3 if "text-as-code points" mode is on */
336+ /* * also second_limit should be increased to maximum # of code points per key in "text-as-code points" mode */
337+ const char first_limit = 2 ;
338+ const char second_limit = 3 ;
339+ int params[first_limit][second_limit] = {0 };
340+ int first_count = 0 ;
341+ int second_count = 0 ;
342+ bool end_found = 0 ;
343+ size_t i;
344+
345+ for (i = 1 ;; i++) {
346+ if (i >= l) {
347+ return LIKELY (l < max_kitty_esc_size) ? TTY_PARSED_WANTMORE : TTY_PARSED_BADSEQUENCE ;
348+ }
349+ if (s[i] == ' ;' ) {
350+ second_count = 0 ;
351+ first_count++;
352+ if (first_count >= first_limit) {
353+ return TTY_PARSED_BADSEQUENCE ;
354+ }
355+ } else if (s[i] == ' :' ) {
356+ second_count++;
357+ if (second_count >= second_limit) {
358+ return TTY_PARSED_BADSEQUENCE ;
359+ }
360+ } else if (!isdigit (s[i])) {
361+ end_found = true ;
362+ break ;
363+ } else { // digit
364+ params[first_count][second_count] = atoi (&s[i]);
365+ while (i < l && isdigit (s[i])) {
366+ ++i;
367+ }
368+ i--;
369+ }
370+ }
371+
372+ // check for correct sequence ending
373+ end_found = end_found && (
374+ (s[i] == ' u' ) || (s[i] == ' ~' ) ||
375+ (s[i] == ' A' ) || (s[i] == ' B' ) ||
376+ (s[i] == ' C' ) || (s[i] == ' D' ) ||
377+ (s[i] == ' E' ) || (s[i] == ' F' ) ||
378+ (s[i] == ' H' ) || (s[i] == ' P' ) ||
379+ (s[i] == ' Q' ) ||
380+ (s[i] == ' R' ) || // "R" is still vaild here in old kitty versions
381+ (s[i] == ' S' )
382+ );
383+
384+ if (!end_found) {
385+ return TTY_PARSED_BADSEQUENCE ;
386+ }
387+
388+ /*
389+ fprintf(stderr, "%i %i %i %i %i \n", params[0][0], params[0][1], params[0][2], params[0][3], params[0][4]);
390+ fprintf(stderr, "%i %i %i %i %i \n", params[1][0], params[1][1], params[1][2], params[1][3], params[1][4]);
391+ fprintf(stderr, "%i %i %i %i %i \n", params[2][0], params[2][1], params[2][2], params[2][3], params[2][4]);
392+ fprintf(stderr, "%i %i\n", first_count, second_count);
393+ */
394+
395+ int event_type = params[1 ][1 ];
396+ int modif_state = params[1 ][0 ];
397+
398+ INPUT_RECORD ir = {0 };
399+ ir.EventType = KEY_EVENT ;
400+
401+ if (modif_state) {
402+ modif_state -= 1 ;
403+
404+ if (modif_state & KITTY_MOD_SHIFT ) { ir.Event .KeyEvent .dwControlKeyState |= SHIFT_PRESSED ; }
405+ if (modif_state & KITTY_MOD_ALT ) { ir.Event .KeyEvent .dwControlKeyState |= LEFT_ALT_PRESSED ; }
406+ if (modif_state & KITTY_MOD_CONTROL ) { ir.Event .KeyEvent .dwControlKeyState |=
407+ right_ctrl_down ? RIGHT_CTRL_PRESSED : LEFT_CTRL_PRESSED ; }
408+ if (modif_state & KITTY_MOD_CAPSLOCK ) { ir.Event .KeyEvent .dwControlKeyState |= CAPSLOCK_ON ; }
409+ if (modif_state & KITTY_MOD_NUMLOCK ) { ir.Event .KeyEvent .dwControlKeyState |= NUMLOCK_ON ; }
410+ }
411+
412+ int base_char = params[0 ][2 ] ? params[0 ][2 ] : params[0 ][0 ];
413+ if (base_char <= UCHAR_MAX && isalpha (base_char)) {
414+ ir.Event .KeyEvent .wVirtualKeyCode = (base_char - ' a' ) + 0x41 ;
415+ }
416+ if (base_char <= UCHAR_MAX && isdigit (base_char)) {
417+ ir.Event .KeyEvent .wVirtualKeyCode = (base_char - ' 0' ) + 0x30 ;
418+ }
419+ switch (base_char) {
420+ case ' `' : ir.Event .KeyEvent .wVirtualKeyCode = VK_OEM_3 ; break ;
421+ case ' -' : ir.Event .KeyEvent .wVirtualKeyCode = VK_OEM_MINUS ; break ;
422+ case ' =' : ir.Event .KeyEvent .wVirtualKeyCode = VK_OEM_PLUS ; break ;
423+ case ' [' : ir.Event .KeyEvent .wVirtualKeyCode = VK_OEM_4 ; break ;
424+ case ' ]' : ir.Event .KeyEvent .wVirtualKeyCode = VK_OEM_6 ; break ;
425+ case ' \\ ' : ir.Event .KeyEvent .wVirtualKeyCode = VK_OEM_5 ; break ;
426+ case ' ;' : ir.Event .KeyEvent .wVirtualKeyCode = VK_OEM_1 ; break ;
427+ case ' \' ' : ir.Event .KeyEvent .wVirtualKeyCode = VK_OEM_7 ; break ;
428+ case ' ,' : ir.Event .KeyEvent .wVirtualKeyCode = VK_OEM_COMMA ; break ;
429+ case ' .' : ir.Event .KeyEvent .wVirtualKeyCode = VK_OEM_PERIOD ; break ;
430+ case ' /' : ir.Event .KeyEvent .wVirtualKeyCode = VK_OEM_2 ; break ;
431+ case 9 : ir.Event .KeyEvent .wVirtualKeyCode = VK_TAB ; break ;
432+ case 27 : ir.Event .KeyEvent .wVirtualKeyCode = VK_ESCAPE ; break ;
433+ case 13 : if (s[i] == ' ~' ) {
434+ ir.Event .KeyEvent .wVirtualKeyCode = VK_F3 ;
435+ } else {
436+ ir.Event .KeyEvent .wVirtualKeyCode = VK_RETURN ;
437+ }
438+ break ;
439+ case 127 : ir.Event .KeyEvent .wVirtualKeyCode = VK_BACK ; break ;
440+ case 2 : if (s[i] == ' ~' ) ir.Event .KeyEvent .wVirtualKeyCode = VK_INSERT ; break ;
441+ case 3 : if (s[i] == ' ~' ) ir.Event .KeyEvent .wVirtualKeyCode = VK_DELETE ; break ;
442+ case 5 : if (s[i] == ' ~' ) { ir.Event .KeyEvent .wVirtualKeyCode = VK_PRIOR ;
443+ ir.Event .KeyEvent .dwControlKeyState |= ENHANCED_KEY ; } break ;
444+ case 6 : if (s[i] == ' ~' ) { ir.Event .KeyEvent .wVirtualKeyCode = VK_NEXT ;
445+ ir.Event .KeyEvent .dwControlKeyState |= ENHANCED_KEY ; } break ;
446+ case 15 : if (s[i] == ' ~' ) ir.Event .KeyEvent .wVirtualKeyCode = VK_F5 ; break ;
447+ case 17 : if (s[i] == ' ~' ) ir.Event .KeyEvent .wVirtualKeyCode = VK_F6 ; break ;
448+ case 18 : if (s[i] == ' ~' ) ir.Event .KeyEvent .wVirtualKeyCode = VK_F7 ; break ;
449+ case 19 : if (s[i] == ' ~' ) ir.Event .KeyEvent .wVirtualKeyCode = VK_F8 ; break ;
450+ case 20 : if (s[i] == ' ~' ) ir.Event .KeyEvent .wVirtualKeyCode = VK_F9 ; break ;
451+ case 21 : if (s[i] == ' ~' ) ir.Event .KeyEvent .wVirtualKeyCode = VK_F10 ; break ;
452+ case 23 : if (s[i] == ' ~' ) ir.Event .KeyEvent .wVirtualKeyCode = VK_F11 ; break ;
453+ case 24 : if (s[i] == ' ~' ) ir.Event .KeyEvent .wVirtualKeyCode = VK_F12 ; break ;
454+ case 57399 : case 57425 : ir.Event .KeyEvent .wVirtualKeyCode = VK_NUMPAD0 ; break ;
455+ case 57400 : case 57424 : ir.Event .KeyEvent .wVirtualKeyCode = VK_NUMPAD1 ; break ;
456+ case 57401 : case 57420 : ir.Event .KeyEvent .wVirtualKeyCode = VK_NUMPAD2 ; break ;
457+ case 57402 : case 57422 : ir.Event .KeyEvent .wVirtualKeyCode = VK_NUMPAD3 ; break ;
458+ case 57403 : case 57417 : ir.Event .KeyEvent .wVirtualKeyCode = VK_NUMPAD4 ; break ;
459+ case 57404 : ir.Event .KeyEvent .wVirtualKeyCode = VK_NUMPAD5 ; break ;
460+ case 57405 : case 57418 : ir.Event .KeyEvent .wVirtualKeyCode = VK_NUMPAD6 ; break ;
461+ case 57406 : case 57423 : ir.Event .KeyEvent .wVirtualKeyCode = VK_NUMPAD7 ; break ;
462+ case 57407 : case 57419 : ir.Event .KeyEvent .wVirtualKeyCode = VK_NUMPAD8 ; break ;
463+ case 57408 : case 57421 : ir.Event .KeyEvent .wVirtualKeyCode = VK_NUMPAD9 ; break ;
464+ case 57409 : case 57426 : ir.Event .KeyEvent .wVirtualKeyCode = VK_DECIMAL ; break ;
465+
466+ case 57410 : ir.Event .KeyEvent .wVirtualKeyCode = VK_DIVIDE ; break ;
467+ case 57411 : ir.Event .KeyEvent .wVirtualKeyCode = VK_MULTIPLY ; break ;
468+ case 57412 : ir.Event .KeyEvent .wVirtualKeyCode = VK_SUBTRACT ; break ;
469+ case 57413 : ir.Event .KeyEvent .wVirtualKeyCode = VK_ADD ; break ;
470+ case 57414 : ir.Event .KeyEvent .wVirtualKeyCode = VK_RETURN ; break ;
471+
472+ case 57444 : ir.Event .KeyEvent .wVirtualKeyCode = VK_LWIN ; break ;
473+ case 57450 : ir.Event .KeyEvent .wVirtualKeyCode = VK_RWIN ; break ;
474+ case 57363 : ir.Event .KeyEvent .wVirtualKeyCode = VK_APPS ; break ;
475+
476+ case 57448 : ir.Event .KeyEvent .wVirtualKeyCode = VK_CONTROL ;
477+ if (event_type != KITTY_EVT_KEYUP ) {
478+ right_ctrl_down = 1 ;
479+ ir.Event .KeyEvent .dwControlKeyState |= RIGHT_CTRL_PRESSED ;
480+ } else {
481+ right_ctrl_down = 0 ;
482+ ir.Event .KeyEvent .dwControlKeyState &= ~RIGHT_CTRL_PRESSED ;
483+ }
484+ ir.Event .KeyEvent .dwControlKeyState |= ENHANCED_KEY ;
485+ break ;
486+ case 57442 : ir.Event .KeyEvent .wVirtualKeyCode = VK_CONTROL ;
487+ if (event_type != KITTY_EVT_KEYUP ) {
488+ ir.Event .KeyEvent .dwControlKeyState |= LEFT_CTRL_PRESSED ;
489+ } else {
490+ ir.Event .KeyEvent .dwControlKeyState &= ~LEFT_CTRL_PRESSED ;
491+ }
492+ break ;
493+ case 57443 : ir.Event .KeyEvent .wVirtualKeyCode = VK_MENU ;
494+ if (event_type != KITTY_EVT_KEYUP ) {
495+ ir.Event .KeyEvent .dwControlKeyState |= LEFT_ALT_PRESSED ;
496+ } else {
497+ ir.Event .KeyEvent .dwControlKeyState &= ~LEFT_ALT_PRESSED ;
498+ }
499+ break ;
500+ case 57449 : ir.Event .KeyEvent .wVirtualKeyCode = VK_MENU ;
501+ if (event_type != KITTY_EVT_KEYUP ) {
502+ ir.Event .KeyEvent .dwControlKeyState |= RIGHT_ALT_PRESSED ;
503+ } else {
504+ ir.Event .KeyEvent .dwControlKeyState &= ~RIGHT_ALT_PRESSED ;
505+ }
506+ ir.Event .KeyEvent .dwControlKeyState |= ENHANCED_KEY ;
507+ break ;
508+ case 57441 : ir.Event .KeyEvent .wVirtualKeyCode = VK_SHIFT ;
509+ // todo: add LEFT_SHIFT_PRESSED / RIGHT_SHIFT_PRESSED
510+ // see https://github.com/microsoft/terminal/issues/337
511+ if (event_type != KITTY_EVT_KEYUP ) {
512+ ir.Event .KeyEvent .dwControlKeyState |= SHIFT_PRESSED ;
513+ } else {
514+ ir.Event .KeyEvent .dwControlKeyState &= ~SHIFT_PRESSED ;
515+ }
516+ break ;
517+ case 57447 : ir.Event .KeyEvent .wVirtualKeyCode = VK_SHIFT ;
518+ // todo: add LEFT_SHIFT_PRESSED / RIGHT_SHIFT_PRESSED
519+ // see https://github.com/microsoft/terminal/issues/337
520+ if (event_type != KITTY_EVT_KEYUP ) {
521+ ir.Event .KeyEvent .dwControlKeyState |= SHIFT_PRESSED ;
522+ } else {
523+ ir.Event .KeyEvent .dwControlKeyState &= ~SHIFT_PRESSED ;
524+ }
525+ ir.Event .KeyEvent .wVirtualScanCode = RIGHT_SHIFT_VSC ;
526+ break ;
527+
528+ case 57360 : ir.Event .KeyEvent .wVirtualKeyCode = VK_NUMLOCK ; break ;
529+ case 57358 : ir.Event .KeyEvent .wVirtualKeyCode = VK_CAPITAL ; break ;
530+
531+ }
532+ switch (s[i]) {
533+ case ' A' : ir.Event .KeyEvent .wVirtualKeyCode = VK_UP ;
534+ ir.Event .KeyEvent .dwControlKeyState |= ENHANCED_KEY ; break ;
535+ case ' B' : ir.Event .KeyEvent .wVirtualKeyCode = VK_DOWN ;
536+ ir.Event .KeyEvent .dwControlKeyState |= ENHANCED_KEY ; break ;
537+ case ' C' : ir.Event .KeyEvent .wVirtualKeyCode = VK_RIGHT ;
538+ ir.Event .KeyEvent .dwControlKeyState |= ENHANCED_KEY ; break ;
539+ case ' D' : ir.Event .KeyEvent .wVirtualKeyCode = VK_LEFT ;
540+ ir.Event .KeyEvent .dwControlKeyState |= ENHANCED_KEY ; break ;
541+ case ' E' : ir.Event .KeyEvent .wVirtualKeyCode = VK_NUMPAD5 ; break ;
542+ case ' H' : ir.Event .KeyEvent .wVirtualKeyCode = VK_HOME ;
543+ ir.Event .KeyEvent .dwControlKeyState |= ENHANCED_KEY ; break ;
544+ case ' F' : ir.Event .KeyEvent .wVirtualKeyCode = VK_END ;
545+ ir.Event .KeyEvent .dwControlKeyState |= ENHANCED_KEY ; break ;
546+ case ' P' : ir.Event .KeyEvent .wVirtualKeyCode = VK_F1 ; break ;
547+ case ' Q' : ir.Event .KeyEvent .wVirtualKeyCode = VK_F2 ; break ;
548+ case ' R' : ir.Event .KeyEvent .wVirtualKeyCode = VK_F3 ; break ;
549+ case ' S' : ir.Event .KeyEvent .wVirtualKeyCode = VK_F4 ; break ;
550+ }
551+
552+ if (ir.Event .KeyEvent .wVirtualScanCode == 0 ) {
553+ ir.Event .KeyEvent .wVirtualScanCode =
554+ WINPORT (MapVirtualKey)(ir.Event .KeyEvent .wVirtualKeyCode , MAPVK_VK_TO_VSC );
555+ }
556+
557+ ir.Event .KeyEvent .uChar .UnicodeChar = params[0 ][1 ] ? params[0 ][1 ] : params[0 ][0 ];
558+ if (
559+ (ir.Event .KeyEvent .uChar .UnicodeChar < 32 ) ||
560+ (ir.Event .KeyEvent .uChar .UnicodeChar == 127 ) ||
561+ ((ir.Event .KeyEvent .uChar .UnicodeChar >= 57358 ) && (ir.Event .KeyEvent .uChar .UnicodeChar <= 57454 )) ||
562+ !(WCHAR_IS_VALID (ir.Event .KeyEvent .uChar .UnicodeChar ))
563+ ) {
564+ // those are special values, should not be used as unicode char
565+ ir.Event .KeyEvent .uChar .UnicodeChar = 0 ;
566+ }
567+ if ((modif_state & KITTY_MOD_CAPSLOCK ) && !(modif_state & KITTY_MOD_SHIFT )) {
568+ // it's weird, but kitty can not give us uppercase utf8 in caps lock mode
569+ // ("text-as-codepoints" mode should solve it, but it is not working for cyrillic chars for unknown reason)
570+ ir.Event .KeyEvent .uChar .UnicodeChar = towupper (ir.Event .KeyEvent .uChar .UnicodeChar );
571+ }
572+
573+ ir.Event .KeyEvent .bKeyDown = (event_type != KITTY_EVT_KEYUP ) ? 1 : 0 ;
574+
575+ ir.Event .KeyEvent .wRepeatCount = 0 ;
576+
577+ _ir_pending.emplace_back (ir);
578+
579+ return i+1 ;
580+ }
581+
312582size_t TTYInputSequenceParser::TryParseAsWinTermEscapeSequence (const char *s, size_t l)
313583{
314584 // check for nasty win32-input-mode sequence: as described in
@@ -359,6 +629,7 @@ size_t TTYInputSequenceParser::TryParseAsWinTermEscapeSequence(const char *s, si
359629
360630size_t TTYInputSequenceParser::ParseEscapeSequence (const char *s, size_t l)
361631{
632+
362633 if (l > 2 && s[0 ] == ' [' && s[2 ] == ' n' ) {
363634 return 3 ;
364635 }
@@ -390,6 +661,13 @@ size_t TTYInputSequenceParser::ParseEscapeSequence(const char *s, size_t l)
390661 if (r != 0 )
391662 return r;
392663
664+ if (l > 1 && s[0 ] == ' [' ) {
665+ r = TryParseAsKittyEscapeSequence (s, l);
666+ if (r != TTY_PARSED_BADSEQUENCE ) {
667+ return r;
668+ }
669+ }
670+
393671 if (l > 1 && s[0 ] == ' [' ) {
394672 r = TryParseAsWinTermEscapeSequence (s, l);
395673 if (r != TTY_PARSED_BADSEQUENCE )
0 commit comments