view src/libvterm/src/keyboard.c @ 31192:dcde141f2d1e v9.0.0930

patch 9.0.0930: cannot debug the Kitty keyboard protocol with TermDebug Commit: https://github.com/vim/vim/commit/63a2e360cca2c70ab0a85d14771d3259d4b3aafa Author: Bram Moolenaar <Bram@vim.org> Date: Wed Nov 23 20:20:18 2022 +0000 patch 9.0.0930: cannot debug the Kitty keyboard protocol with TermDebug Problem: Cannot debug the Kitty keyboard protocol with TermDebug. Solution: Add Kitty keyboard protocol support to the libvterm fork. Recognize the escape sequences that the protocol generates. Add the 'keyprotocol' option to allow the user to specify for which terminal what protocol is to be used, instead of hard-coding this. Add recognizing the kitty keyboard protocol status.
author Bram Moolenaar <Bram@vim.org>
date Wed, 23 Nov 2022 21:30:04 +0100
parents 0ecb16d5f86f
children
line wrap: on
line source

#include "vterm_internal.h"

#include <stdio.h>

#include "utf8.h"

// VIM: added
int vterm_is_modify_other_keys(VTerm *vt)
{
  return vt->state->mode.modify_other_keys;
}

// VIM: added
int vterm_is_kitty_keyboard(VTerm *vt)
{
  return vt->state->mode.kitty_keyboard;
}


void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod)
{
  // VIM: added modifyOtherKeys support
  if (vterm_is_modify_other_keys(vt) && mod != 0) {
    vterm_push_output_sprintf_ctrl(vt, C1_CSI, "27;%d;%d~", mod+1, c);
    return;
  }

  // VIM: added kitty keyboard protocol support
  if (vterm_is_kitty_keyboard(vt) && mod != 0) {
    vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod+1);
    return;
  }

  /* The shift modifier is never important for Unicode characters
   * apart from Space
   */
  if(c != ' ')
    mod &= ~VTERM_MOD_SHIFT;

  if(mod == 0) {
    // Normal text - ignore just shift
    char str[6];
    int seqlen = fill_utf8(c, str);
    vterm_push_output_bytes(vt, str, seqlen);
    return;
  }

  int needs_CSIu;
  switch(c) {
    /* Special Ctrl- letters that can't be represented elsewise */
    case 'i': case 'j': case 'm': case '[':
      needs_CSIu = 1;
      break;
    /* Ctrl-\ ] ^ _ don't need CSUu */
    case '\\': case ']': case '^': case '_':
      needs_CSIu = 0;
      break;
    /* Shift-space needs CSIu */
    case ' ':
      needs_CSIu = !!(mod & VTERM_MOD_SHIFT);
      break;
    /* All other characters needs CSIu except for letters a-z */
    default:
      needs_CSIu = (c < 'a' || c > 'z');
  }

  /* ALT we can just prefix with ESC; anything else requires CSI u */
  if(needs_CSIu && (mod & ~VTERM_MOD_ALT)) {
    vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod+1);
    return;
  }

  if(mod & VTERM_MOD_CTRL)
    c &= 0x1f;

  vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? ESC_S : "", c);
}

typedef struct {
  enum {
    KEYCODE_NONE,
    KEYCODE_LITERAL,
    KEYCODE_TAB,
    KEYCODE_ENTER,
    KEYCODE_SS3,
    KEYCODE_CSI,
    KEYCODE_CSI_CURSOR,
    KEYCODE_CSINUM,
    KEYCODE_KEYPAD,
  } type;
  char literal;
  int csinum;
} keycodes_s;

// Order here must be exactly the same as VTermKey enum!
static keycodes_s keycodes[] = {
  { KEYCODE_NONE,       0, 0 }, // NONE

  { KEYCODE_ENTER,      '\r', 0 }, // ENTER
  { KEYCODE_TAB,        '\t',  0 }, // TAB
  { KEYCODE_LITERAL,    '\x7f', 0 }, // BACKSPACE == ASCII DEL
  { KEYCODE_LITERAL,    '\x1b', 0 }, // ESCAPE

  { KEYCODE_CSI_CURSOR, 'A', 0 }, // UP
  { KEYCODE_CSI_CURSOR, 'B', 0 }, // DOWN
  { KEYCODE_CSI_CURSOR, 'D', 0 }, // LEFT
  { KEYCODE_CSI_CURSOR, 'C', 0 }, // RIGHT

  { KEYCODE_CSINUM, '~', 2 },  // INS
  { KEYCODE_CSINUM, '~', 3 },  // DEL
  { KEYCODE_CSI_CURSOR, 'H', 0 }, // HOME
  { KEYCODE_CSI_CURSOR, 'F', 0 }, // END
  { KEYCODE_CSINUM, '~', 5 },  // PAGEUP
  { KEYCODE_CSINUM, '~', 6 },  // PAGEDOWN
};

static keycodes_s keycodes_fn[] = {
  { KEYCODE_NONE,       0, 0 },   // F0 - shouldn't happen
  { KEYCODE_SS3,	'P', 0 }, // F1
  { KEYCODE_SS3,	'Q', 0 }, // F2
  { KEYCODE_SS3,	'R', 0 }, // F3
  { KEYCODE_SS3,	'S', 0 }, // F4
  { KEYCODE_CSINUM, '~', 15 }, // F5
  { KEYCODE_CSINUM, '~', 17 }, // F6
  { KEYCODE_CSINUM, '~', 18 }, // F7
  { KEYCODE_CSINUM, '~', 19 }, // F8
  { KEYCODE_CSINUM, '~', 20 }, // F9
  { KEYCODE_CSINUM, '~', 21 }, // F10
  { KEYCODE_CSINUM, '~', 23 }, // F11
  { KEYCODE_CSINUM, '~', 24 }, // F12
};

static keycodes_s keycodes_kp[] = {
  { KEYCODE_KEYPAD, '0', 'p' }, // KP_0
  { KEYCODE_KEYPAD, '1', 'q' }, // KP_1
  { KEYCODE_KEYPAD, '2', 'r' }, // KP_2
  { KEYCODE_KEYPAD, '3', 's' }, // KP_3
  { KEYCODE_KEYPAD, '4', 't' }, // KP_4
  { KEYCODE_KEYPAD, '5', 'u' }, // KP_5
  { KEYCODE_KEYPAD, '6', 'v' }, // KP_6
  { KEYCODE_KEYPAD, '7', 'w' }, // KP_7
  { KEYCODE_KEYPAD, '8', 'x' }, // KP_8
  { KEYCODE_KEYPAD, '9', 'y' }, // KP_9
  { KEYCODE_KEYPAD, '*', 'j' }, // KP_MULT
  { KEYCODE_KEYPAD, '+', 'k' }, // KP_PLUS
  { KEYCODE_KEYPAD, ',', 'l' }, // KP_COMMA
  { KEYCODE_KEYPAD, '-', 'm' }, // KP_MINUS
  { KEYCODE_KEYPAD, '.', 'n' }, // KP_PERIOD
  { KEYCODE_KEYPAD, '/', 'o' }, // KP_DIVIDE
  { KEYCODE_KEYPAD, '\n', 'M' }, // KP_ENTER
  { KEYCODE_KEYPAD, '=', 'X' }, // KP_EQUAL
};

void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
{
  if(key == VTERM_KEY_NONE)
    return;

  keycodes_s k;
  if(key < VTERM_KEY_FUNCTION_0) {
    if(key >= sizeof(keycodes)/sizeof(keycodes[0]))
      return;
    k = keycodes[key];
  }
  else if(key >= VTERM_KEY_FUNCTION_0 && key <= VTERM_KEY_FUNCTION_MAX) {
    if((key - VTERM_KEY_FUNCTION_0) >= sizeof(keycodes_fn)/sizeof(keycodes_fn[0]))
      return;
    k = keycodes_fn[key - VTERM_KEY_FUNCTION_0];
  }
  else if(key >= VTERM_KEY_KP_0) {
    if((key - VTERM_KEY_KP_0) >= sizeof(keycodes_kp)/sizeof(keycodes_kp[0]))
      return;
    k = keycodes_kp[key - VTERM_KEY_KP_0];
  }

  switch(k.type) {
  case KEYCODE_NONE:
    break;

  case KEYCODE_TAB:
    if (vterm_is_kitty_keyboard(vt) && mod != 0)
      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "9;%du", mod+1);
    /* Shift-Tab is CSI Z but plain Tab is 0x09 */
    else if(mod == VTERM_MOD_SHIFT)
      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "Z");
    else if(mod & VTERM_MOD_SHIFT)
      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%dZ", mod+1);
    else
      goto case_LITERAL;
    break;

  case KEYCODE_ENTER:
    /* Enter is CRLF in newline mode, but just LF in linefeed */
    if(vt->state->mode.newline)
      vterm_push_output_sprintf(vt, "\r\n");
    else
      goto case_LITERAL;
    break;

  case KEYCODE_LITERAL: case_LITERAL:
    if (vterm_is_modify_other_keys(vt) && mod != 0)
      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "27;%d;%d~", mod+1, k.literal);
    else if (vterm_is_kitty_keyboard(vt) && mod == 0 && k.literal == '\x1b')
      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%du", k.literal);
    else if ((vterm_is_kitty_keyboard(vt) && mod != 0)
	|| (mod & (VTERM_MOD_SHIFT|VTERM_MOD_CTRL)))
      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", k.literal, mod+1);
    else
      vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? ESC_S "%c" : "%c", k.literal);
    break;

  case KEYCODE_SS3: case_SS3:
    if(mod == 0)
      vterm_push_output_sprintf_ctrl(vt, C1_SS3, "%c", k.literal);
    else
      goto case_CSI;
    break;

  case KEYCODE_CSI: case_CSI:
    if(mod == 0)
      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%c", k.literal);
    else
      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%d%c", mod + 1, k.literal);
    break;

  case KEYCODE_CSINUM:
    if(mod == 0)
      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d%c", k.csinum, k.literal);
    else
      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%d%c", k.csinum, mod + 1, k.literal);
    break;

  case KEYCODE_CSI_CURSOR:
    if(vt->state->mode.cursor)
      goto case_SS3;
    else
      goto case_CSI;

  case KEYCODE_KEYPAD:
    if(vt->state->mode.keypad) {
      k.literal = k.csinum;
      goto case_SS3;
    }
    else
      goto case_LITERAL;
  }
}

void vterm_keyboard_start_paste(VTerm *vt)
{
  if(vt->state->mode.bracketpaste)
    vterm_push_output_sprintf_ctrl(vt, C1_CSI, "200~");
}

void vterm_keyboard_end_paste(VTerm *vt)
{
  if(vt->state->mode.bracketpaste)
    vterm_push_output_sprintf_ctrl(vt, C1_CSI, "201~");
}