input.c revision 5b78a3aa7741c3f44b676ccffa765cecee1cbd4c
1#include "vterm_internal.h"
2
3#include <stdio.h>
4
5#include "utf8.h"
6
7void vterm_input_push_char(VTerm *vt, VTermModifier mod, uint32_t c)
8{
9  /* The shift modifier is never important for Unicode characters
10   * apart from Space
11   */
12  if(c != ' ')
13    mod &= ~VTERM_MOD_SHIFT;
14  /* However, since Shift-Space is too easy to mistype accidentally, remove
15   * shift if it's the only modifier
16   */
17  else if(mod == VTERM_MOD_SHIFT)
18    mod = 0;
19
20  if(mod == 0) {
21    // Normal text - ignore just shift
22    char str[6];
23    int seqlen = fill_utf8(c, str);
24    vterm_push_output_bytes(vt, str, seqlen);
25    return;
26  }
27
28  int needs_CSIu;
29  switch(c) {
30    /* Special Ctrl- letters that can't be represented elsewise */
31    case 'h': case 'i': case 'j': case 'm': case '[':
32      needs_CSIu = 1;
33      break;
34    /* Ctrl-\ ] ^ _ don't need CSUu */
35    case '\\': case ']': case '^': case '_':
36      needs_CSIu = 0;
37      break;
38    /* All other characters needs CSIu except for letters a-z */
39    default:
40      needs_CSIu = (c < 'a' || c > 'z');
41  }
42
43  /* ALT we can just prefix with ESC; anything else requires CSI u */
44  if(needs_CSIu && (mod & ~VTERM_MOD_ALT)) {
45    vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod+1);
46    return;
47  }
48
49  if(mod & VTERM_MOD_CTRL)
50    c &= 0x1f;
51
52  vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? "\e" : "", c);
53}
54
55typedef struct {
56  enum {
57    KEYCODE_NONE,
58    KEYCODE_LITERAL,
59    KEYCODE_TAB,
60    KEYCODE_ENTER,
61    KEYCODE_SS3,
62    KEYCODE_CSI,
63    KEYCODE_CSI_CURSOR,
64    KEYCODE_CSINUM,
65    KEYCODE_KEYPAD,
66  } type;
67  char literal;
68  int csinum;
69} keycodes_s;
70
71static keycodes_s keycodes[] = {
72  { KEYCODE_NONE }, // NONE
73
74  { KEYCODE_ENTER,   '\r'   }, // ENTER
75  { KEYCODE_TAB,     '\t'   }, // TAB
76  { KEYCODE_LITERAL, '\x7f' }, // BACKSPACE == ASCII DEL
77  { KEYCODE_LITERAL, '\e'   }, // ESCAPE
78
79  { KEYCODE_CSI_CURSOR, 'A' }, // UP
80  { KEYCODE_CSI_CURSOR, 'B' }, // DOWN
81  { KEYCODE_CSI_CURSOR, 'D' }, // LEFT
82  { KEYCODE_CSI_CURSOR, 'C' }, // RIGHT
83
84  { KEYCODE_CSINUM, '~', 2 },  // INS
85  { KEYCODE_CSINUM, '~', 3 },  // DEL
86  { KEYCODE_CSI_CURSOR, 'H' }, // HOME
87  { KEYCODE_CSI_CURSOR, 'F' }, // END
88  { KEYCODE_CSINUM, '~', 5 },  // PAGEUP
89  { KEYCODE_CSINUM, '~', 6 },  // PAGEDOWN
90};
91
92static keycodes_s keycodes_fn[] = {
93  { KEYCODE_NONE },            // F0 - shouldn't happen
94  { KEYCODE_CSI_CURSOR, 'P' }, // F1
95  { KEYCODE_CSI_CURSOR, 'Q' }, // F2
96  { KEYCODE_CSI_CURSOR, 'R' }, // F3
97  { KEYCODE_CSI_CURSOR, 'S' }, // F4
98  { KEYCODE_CSINUM, '~', 15 }, // F5
99  { KEYCODE_CSINUM, '~', 17 }, // F6
100  { KEYCODE_CSINUM, '~', 18 }, // F7
101  { KEYCODE_CSINUM, '~', 19 }, // F8
102  { KEYCODE_CSINUM, '~', 20 }, // F9
103  { KEYCODE_CSINUM, '~', 21 }, // F10
104  { KEYCODE_CSINUM, '~', 23 }, // F11
105  { KEYCODE_CSINUM, '~', 24 }, // F12
106};
107
108static keycodes_s keycodes_kp[] = {
109  { KEYCODE_KEYPAD, '0', 'p' }, // KP_0
110  { KEYCODE_KEYPAD, '1', 'q' }, // KP_1
111  { KEYCODE_KEYPAD, '2', 'r' }, // KP_2
112  { KEYCODE_KEYPAD, '3', 's' }, // KP_3
113  { KEYCODE_KEYPAD, '4', 't' }, // KP_4
114  { KEYCODE_KEYPAD, '5', 'u' }, // KP_5
115  { KEYCODE_KEYPAD, '6', 'v' }, // KP_6
116  { KEYCODE_KEYPAD, '7', 'w' }, // KP_7
117  { KEYCODE_KEYPAD, '8', 'x' }, // KP_8
118  { KEYCODE_KEYPAD, '9', 'y' }, // KP_9
119  { KEYCODE_KEYPAD, '*', 'j' }, // KP_MULT
120  { KEYCODE_KEYPAD, '+', 'k' }, // KP_PLUS
121  { KEYCODE_KEYPAD, ',', 'l' }, // KP_COMMA
122  { KEYCODE_KEYPAD, '-', 'm' }, // KP_MINUS
123  { KEYCODE_KEYPAD, '.', 'n' }, // KP_PERIOD
124  { KEYCODE_KEYPAD, '/', 'o' }, // KP_DIVIDE
125  { KEYCODE_KEYPAD, '\n', 'M' }, // KP_ENTER
126  { KEYCODE_KEYPAD, '=', 'X' }, // KP_EQUAL
127};
128
129void vterm_input_push_key(VTerm *vt, VTermModifier mod, VTermKey key)
130{
131  /* Since Shift-Enter and Shift-Backspace are too easy to mistype
132   * accidentally, remove shift if it's the only modifier
133   */
134  if((key == VTERM_KEY_ENTER || key == VTERM_KEY_BACKSPACE) && mod == VTERM_MOD_SHIFT)
135    mod = 0;
136
137  if(key == VTERM_KEY_NONE)
138    return;
139
140  keycodes_s k;
141  if(key < VTERM_KEY_FUNCTION_0) {
142    if(key >= sizeof(keycodes)/sizeof(keycodes[0]))
143      return;
144    k = keycodes[key];
145  }
146  else if(key >= VTERM_KEY_FUNCTION_0 && key <= VTERM_KEY_FUNCTION_MAX) {
147    if((key - VTERM_KEY_FUNCTION_0) >= sizeof(keycodes_fn)/sizeof(keycodes_fn[0]))
148      return;
149    k = keycodes_fn[key - VTERM_KEY_FUNCTION_0];
150  }
151  else if(key >= VTERM_KEY_KP_0) {
152    if((key - VTERM_KEY_KP_0) >= sizeof(keycodes_kp)/sizeof(keycodes_kp[0]))
153      return;
154    k = keycodes_kp[key - VTERM_KEY_KP_0];
155  }
156
157  switch(k.type) {
158  case KEYCODE_NONE:
159    break;
160
161  case KEYCODE_TAB:
162    /* Shift-Tab is CSI Z but plain Tab is 0x09 */
163    if(mod == VTERM_MOD_SHIFT)
164      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "Z");
165    else if(mod & VTERM_MOD_SHIFT)
166      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%dZ", mod+1);
167    else
168      goto case_LITERAL;
169    break;
170
171  case KEYCODE_ENTER:
172    /* Enter is CRLF in newline mode, but just LF in linefeed */
173    if(vt->state->mode.newline)
174      vterm_push_output_sprintf(vt, "\r\n");
175    else
176      goto case_LITERAL;
177    break;
178
179  case KEYCODE_LITERAL: case_LITERAL:
180    if(mod & (VTERM_MOD_SHIFT|VTERM_MOD_CTRL))
181      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", k.literal, mod+1);
182    else
183      vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? "\e%c" : "%c", k.literal);
184    break;
185
186  case KEYCODE_SS3: case_SS3:
187    if(mod == 0)
188      vterm_push_output_sprintf_ctrl(vt, C1_SS3, "%c", k.literal);
189    else
190      goto case_CSI;
191    break;
192
193  case KEYCODE_CSI: case_CSI:
194    if(mod == 0)
195      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%c", k.literal);
196    else
197      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%d%c", mod + 1, k.literal);
198    break;
199
200  case KEYCODE_CSINUM:
201    if(mod == 0)
202      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d%c", k.csinum, k.literal);
203    else
204      vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%d%c", k.csinum, mod + 1, k.literal);
205    break;
206
207  case KEYCODE_CSI_CURSOR:
208    if(vt->state->mode.cursor)
209      goto case_SS3;
210    else
211      goto case_CSI;
212
213  case KEYCODE_KEYPAD:
214    if(vt->state->mode.keypad) {
215      k.literal = k.csinum;
216      goto case_SS3;
217    }
218    else
219      goto case_LITERAL;
220  }
221}
222