@@ -309,6 +309,276 @@ void TTYInputSequenceParser::ParseAPC(const char *s, size_t l)
309
309
}
310
310
}
311
311
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
+
312
582
size_t TTYInputSequenceParser::TryParseAsWinTermEscapeSequence (const char *s, size_t l)
313
583
{
314
584
// check for nasty win32-input-mode sequence: as described in
@@ -359,6 +629,7 @@ size_t TTYInputSequenceParser::TryParseAsWinTermEscapeSequence(const char *s, si
359
629
360
630
size_t TTYInputSequenceParser::ParseEscapeSequence (const char *s, size_t l)
361
631
{
632
+
362
633
if (l > 2 && s[0 ] == ' [' && s[2 ] == ' n' ) {
363
634
return 3 ;
364
635
}
@@ -390,6 +661,13 @@ size_t TTYInputSequenceParser::ParseEscapeSequence(const char *s, size_t l)
390
661
if (r != 0 )
391
662
return r;
392
663
664
+ if (l > 1 && s[0 ] == ' [' ) {
665
+ r = TryParseAsKittyEscapeSequence (s, l);
666
+ if (r != TTY_PARSED_BADSEQUENCE) {
667
+ return r;
668
+ }
669
+ }
670
+
393
671
if (l > 1 && s[0 ] == ' [' ) {
394
672
r = TryParseAsWinTermEscapeSequence (s, l);
395
673
if (r != TTY_PARSED_BADSEQUENCE)
0 commit comments