1 #include "termkey.h"
2 #include "termkey-internal.h"
3 
4 #include <stdio.h>
5 #include <string.h>
6 
7 // There are 64 codes 0x40 - 0x7F
8 static int keyinfo_initialised = 0;
9 static struct keyinfo ss3s[64];
10 static char ss3_kpalts[64];
11 
12 typedef struct {
13   TermKey *tk;
14   int saved_string_id;
15   char *saved_string;
16 } TermKeyCsi;
17 
18 typedef TermKeyResult CsiHandler(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args);
19 static CsiHandler *csi_handlers[64];
20 
21 /*
22  * Handler for CSI/SS3 cmd keys
23  */
24 
25 static struct keyinfo csi_ss3s[64];
26 
handle_csi_ss3_full(TermKey * tk,TermKeyKey * key,int cmd,long * arg,int args)27 static TermKeyResult handle_csi_ss3_full(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args)
28 {
29   if(args > 1 && arg[1] != -1)
30     key->modifiers = arg[1] - 1;
31   else
32     key->modifiers = 0;
33 
34   key->type = csi_ss3s[cmd - 0x40].type;
35   key->code.sym = csi_ss3s[cmd - 0x40].sym;
36   key->modifiers &= ~(csi_ss3s[cmd - 0x40].modifier_mask);
37   key->modifiers |= csi_ss3s[cmd - 0x40].modifier_set;
38 
39   if(key->code.sym == TERMKEY_SYM_UNKNOWN)
40     return TERMKEY_RES_NONE;
41 
42   return TERMKEY_RES_KEY;
43 }
44 
register_csi_ss3_full(TermKeyType type,TermKeySym sym,int modifier_set,int modifier_mask,unsigned char cmd)45 static void register_csi_ss3_full(TermKeyType type, TermKeySym sym, int modifier_set, int modifier_mask, unsigned char cmd)
46 {
47   if(cmd < 0x40 || cmd >= 0x80) {
48     return;
49   }
50 
51   csi_ss3s[cmd - 0x40].type = type;
52   csi_ss3s[cmd - 0x40].sym = sym;
53   csi_ss3s[cmd - 0x40].modifier_set = modifier_set;
54   csi_ss3s[cmd - 0x40].modifier_mask = modifier_mask;
55 
56   csi_handlers[cmd - 0x40] = &handle_csi_ss3_full;
57 }
58 
register_csi_ss3(TermKeyType type,TermKeySym sym,unsigned char cmd)59 static void register_csi_ss3(TermKeyType type, TermKeySym sym, unsigned char cmd)
60 {
61   register_csi_ss3_full(type, sym, 0, 0, cmd);
62 }
63 
64 /*
65  * Handler for SS3 keys with kpad alternate representations
66  */
67 
register_ss3kpalt(TermKeyType type,TermKeySym sym,unsigned char cmd,char kpalt)68 static void register_ss3kpalt(TermKeyType type, TermKeySym sym, unsigned char cmd, char kpalt)
69 {
70   if(cmd < 0x40 || cmd >= 0x80) {
71     return;
72   }
73 
74   ss3s[cmd - 0x40].type = type;
75   ss3s[cmd - 0x40].sym = sym;
76   ss3s[cmd - 0x40].modifier_set = 0;
77   ss3s[cmd - 0x40].modifier_mask = 0;
78   ss3_kpalts[cmd - 0x40] = kpalt;
79 }
80 
81 /*
82  * Handler for CSI number ~ function keys
83  */
84 
85 static struct keyinfo csifuncs[35]; /* This value must be increased if more CSI function keys are added */
86 #define NCSIFUNCS (sizeof(csifuncs)/sizeof(csifuncs[0]))
87 
handle_csifunc(TermKey * tk,TermKeyKey * key,int cmd,long * arg,int args)88 static TermKeyResult handle_csifunc(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args)
89 {
90   if(args > 1 && arg[1] != -1)
91     key->modifiers = arg[1] - 1;
92   else
93     key->modifiers = 0;
94 
95   key->type = TERMKEY_TYPE_KEYSYM;
96 
97   if(arg[0] == 27) {
98     int mod = key->modifiers;
99     (*tk->method.emit_codepoint)(tk, arg[2], key);
100     key->modifiers |= mod;
101   }
102   else if(arg[0] >= 0 && arg[0] < NCSIFUNCS) {
103     key->type = csifuncs[arg[0]].type;
104     key->code.sym = csifuncs[arg[0]].sym;
105     key->modifiers &= ~(csifuncs[arg[0]].modifier_mask);
106     key->modifiers |= csifuncs[arg[0]].modifier_set;
107   }
108   else
109     key->code.sym = TERMKEY_SYM_UNKNOWN;
110 
111   if(key->code.sym == TERMKEY_SYM_UNKNOWN) {
112 #ifdef DEBUG
113     fprintf(stderr, "CSI: Unknown function key %ld\n", arg[0]);
114 #endif
115     return TERMKEY_RES_NONE;
116   }
117 
118   return TERMKEY_RES_KEY;
119 }
120 
register_csifunc(TermKeyType type,TermKeySym sym,int number)121 static void register_csifunc(TermKeyType type, TermKeySym sym, int number)
122 {
123   if(number >= NCSIFUNCS) {
124     return;
125   }
126 
127   csifuncs[number].type = type;
128   csifuncs[number].sym = sym;
129   csifuncs[number].modifier_set = 0;
130   csifuncs[number].modifier_mask = 0;
131 
132   csi_handlers['~' - 0x40] = &handle_csifunc;
133 }
134 
135 /*
136  * Handler for CSI u extended Unicode keys
137  */
138 
handle_csi_u(TermKey * tk,TermKeyKey * key,int cmd,long * arg,int args)139 static TermKeyResult handle_csi_u(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args)
140 {
141   switch(cmd) {
142     case 'u': {
143       if(args > 1 && arg[1] != -1)
144         key->modifiers = arg[1] - 1;
145       else
146         key->modifiers = 0;
147 
148       int mod = key->modifiers;
149       key->type = TERMKEY_TYPE_KEYSYM;
150       (*tk->method.emit_codepoint)(tk, arg[0], key);
151       key->modifiers |= mod;
152 
153       return TERMKEY_RES_KEY;
154     }
155     default:
156       return TERMKEY_RES_NONE;
157   }
158 }
159 
160 /*
161  * Handler for CSI M / CSI m mouse events in SRG and rxvt encodings
162  * Note: This does not handle X10 encoding
163  */
164 
handle_csi_m(TermKey * tk,TermKeyKey * key,int cmd,long * arg,int args)165 static TermKeyResult handle_csi_m(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args)
166 {
167   int initial = cmd >> 8;
168   cmd &= 0xff;
169 
170   switch(cmd) {
171     case 'M':
172     case 'm':
173       break;
174     default:
175       return TERMKEY_RES_NONE;
176   }
177 
178   if(!initial && args >= 3) { // rxvt protocol
179     key->type = TERMKEY_TYPE_MOUSE;
180     key->code.mouse[0] = arg[0];
181 
182     key->modifiers     = (key->code.mouse[0] & 0x1c) >> 2;
183     key->code.mouse[0] &= ~0x1c;
184 
185     termkey_key_set_linecol(key, arg[1], arg[2]);
186 
187     return TERMKEY_RES_KEY;
188   }
189 
190   if(initial == '<' && args >= 3) { // SGR protocol
191     key->type = TERMKEY_TYPE_MOUSE;
192     key->code.mouse[0] = arg[0];
193 
194     key->modifiers     = (key->code.mouse[0] & 0x1c) >> 2;
195     key->code.mouse[0] &= ~0x1c;
196 
197     termkey_key_set_linecol(key, arg[1], arg[2]);
198 
199     if(cmd == 'm') // release
200       key->code.mouse[3] |= 0x80;
201 
202     return TERMKEY_RES_KEY;
203   }
204 
205   return TERMKEY_RES_NONE;
206 }
207 
termkey_interpret_mouse(TermKey * tk,const TermKeyKey * key,TermKeyMouseEvent * event,int * button,int * line,int * col)208 TermKeyResult termkey_interpret_mouse(TermKey *tk, const TermKeyKey *key, TermKeyMouseEvent *event, int *button, int *line, int *col)
209 {
210   if(key->type != TERMKEY_TYPE_MOUSE)
211     return TERMKEY_RES_NONE;
212 
213   if(button)
214     *button = 0;
215 
216   termkey_key_get_linecol(key, line, col);
217 
218   if(!event)
219     return TERMKEY_RES_KEY;
220 
221   int btn = 0;
222 
223   int code = key->code.mouse[0];
224 
225   int drag = code & 0x20;
226 
227   code &= ~0x3c;
228 
229   switch(code) {
230   case 0:
231   case 1:
232   case 2:
233     *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS;
234     btn = code + 1;
235     break;
236 
237   case 3:
238     *event = TERMKEY_MOUSE_RELEASE;
239     // no button hint
240     break;
241 
242   case 64:
243   case 65:
244     *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS;
245     btn = code + 4 - 64;
246     break;
247 
248   default:
249     *event = TERMKEY_MOUSE_UNKNOWN;
250   }
251 
252   if(button)
253     *button = btn;
254 
255   if(key->code.mouse[3] & 0x80)
256     *event = TERMKEY_MOUSE_RELEASE;
257 
258   return TERMKEY_RES_KEY;
259 }
260 
261 /*
262  * Handler for CSI ? R position reports
263  * A plain CSI R with no arguments is probably actually <F3>
264  */
265 
handle_csi_R(TermKey * tk,TermKeyKey * key,int cmd,long * arg,int args)266 static TermKeyResult handle_csi_R(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args)
267 {
268   switch(cmd) {
269     case 'R'|'?'<<8:
270       if(args < 2)
271         return TERMKEY_RES_NONE;
272 
273       key->type = TERMKEY_TYPE_POSITION;
274       termkey_key_set_linecol(key, arg[1], arg[0]);
275       return TERMKEY_RES_KEY;
276 
277     default:
278       return handle_csi_ss3_full(tk, key, cmd, arg, args);
279   }
280 }
281 
termkey_interpret_position(TermKey * tk,const TermKeyKey * key,int * line,int * col)282 TermKeyResult termkey_interpret_position(TermKey *tk, const TermKeyKey *key, int *line, int *col)
283 {
284   if(key->type != TERMKEY_TYPE_POSITION)
285     return TERMKEY_RES_NONE;
286 
287   termkey_key_get_linecol(key, line, col);
288 
289   return TERMKEY_RES_KEY;
290 }
291 
292 /*
293  * Handler for CSI $y mode status reports
294  */
295 
handle_csi_y(TermKey * tk,TermKeyKey * key,int cmd,long * arg,int args)296 static TermKeyResult handle_csi_y(TermKey *tk, TermKeyKey *key, int cmd, long *arg, int args)
297 {
298   switch(cmd) {
299     case 'y'|'$'<<16:
300     case 'y'|'$'<<16 | '?'<<8:
301       if(args < 2)
302         return TERMKEY_RES_NONE;
303 
304       key->type = TERMKEY_TYPE_MODEREPORT;
305       key->code.mouse[0] = (cmd >> 8);
306       key->code.mouse[1] = arg[0] >> 8;
307       key->code.mouse[2] = arg[0] & 0xff;
308       key->code.mouse[3] = arg[1];
309       return TERMKEY_RES_KEY;
310 
311     default:
312       return TERMKEY_RES_NONE;
313   }
314 }
315 
termkey_interpret_modereport(TermKey * tk,const TermKeyKey * key,int * initial,int * mode,int * value)316 TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, int *initial, int *mode, int *value)
317 {
318   if(key->type != TERMKEY_TYPE_MODEREPORT)
319     return TERMKEY_RES_NONE;
320 
321   if(initial)
322     *initial = key->code.mouse[0];
323 
324   if(mode)
325     *mode = (key->code.mouse[1] << 8) | key->code.mouse[2];
326 
327   if(value)
328     *value = key->code.mouse[3];
329 
330   return TERMKEY_RES_KEY;
331 }
332 
333 #define CHARAT(i) (tk->buffer[tk->buffstart + (i)])
334 
parse_csi(TermKey * tk,size_t introlen,size_t * csi_len,long args[],size_t * nargs,unsigned long * commandp)335 static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len, long args[], size_t *nargs, unsigned long *commandp)
336 {
337   size_t csi_end = introlen;
338 
339   while(csi_end < tk->buffcount) {
340     if(CHARAT(csi_end) >= 0x40 && CHARAT(csi_end) < 0x80)
341       break;
342     csi_end++;
343   }
344 
345   if(csi_end >= tk->buffcount)
346     return TERMKEY_RES_AGAIN;
347 
348   unsigned char cmd = CHARAT(csi_end);
349   *commandp = cmd;
350 
351   char present = 0;
352   int argi = 0;
353 
354   size_t p = introlen;
355 
356   // See if there is an initial byte
357   if(CHARAT(p) >= '<' && CHARAT(p) <= '?') {
358     *commandp |= (CHARAT(p) << 8);
359     p++;
360   }
361 
362   // Now attempt to parse out up number;number;... separated values
363   while(p < csi_end) {
364     unsigned char c = CHARAT(p);
365 
366     if(c >= '0' && c <= '9') {
367       if(!present) {
368         args[argi] = c - '0';
369         present = 1;
370       }
371       else {
372         args[argi] = (args[argi] * 10) + c - '0';
373       }
374     }
375     else if(c == ';') {
376       if(!present)
377         args[argi] = -1;
378       present = 0;
379       argi++;
380 
381       if(argi > 16)
382         break;
383     }
384     else if(c >= 0x20 && c <= 0x2f) {
385       *commandp |= c << 16;
386       break;
387     }
388 
389     p++;
390   }
391 
392   if(present)
393     argi++;
394 
395   *nargs = argi;
396   *csi_len = csi_end + 1;
397 
398   return TERMKEY_RES_KEY;
399 }
400 
termkey_interpret_csi(TermKey * tk,const TermKeyKey * key,long args[],size_t * nargs,unsigned long * cmd)401 TermKeyResult termkey_interpret_csi(TermKey *tk, const TermKeyKey *key, long args[], size_t *nargs, unsigned long *cmd)
402 {
403   size_t dummy;
404 
405   if(tk->hightide == 0)
406     return TERMKEY_RES_NONE;
407   if(key->type != TERMKEY_TYPE_UNKNOWN_CSI)
408     return TERMKEY_RES_NONE;
409 
410   return parse_csi(tk, 0, &dummy, args, nargs, cmd);
411 }
412 
register_keys(void)413 static int register_keys(void)
414 {
415   int i;
416 
417   for(i = 0; i < 64; i++) {
418     csi_ss3s[i].sym = TERMKEY_SYM_UNKNOWN;
419     ss3s[i].sym     = TERMKEY_SYM_UNKNOWN;
420     ss3_kpalts[i] = 0;
421   }
422 
423   for(i = 0; i < NCSIFUNCS; i++)
424     csifuncs[i].sym = TERMKEY_SYM_UNKNOWN;
425 
426   register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_UP,    'A');
427   register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DOWN,  'B');
428   register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_RIGHT, 'C');
429   register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_LEFT,  'D');
430   register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_BEGIN, 'E');
431   register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_END,   'F');
432   register_csi_ss3(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HOME,  'H');
433   register_csi_ss3(TERMKEY_TYPE_FUNCTION, 1, 'P');
434   register_csi_ss3(TERMKEY_TYPE_FUNCTION, 2, 'Q');
435   register_csi_ss3(TERMKEY_TYPE_FUNCTION, 3, 'R');
436   register_csi_ss3(TERMKEY_TYPE_FUNCTION, 4, 'S');
437 
438   register_csi_ss3_full(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_TAB, TERMKEY_KEYMOD_SHIFT, TERMKEY_KEYMOD_SHIFT, 'Z');
439 
440   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPENTER,  'M', 0);
441   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPEQUALS, 'X', '=');
442   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPMULT,   'j', '*');
443   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPPLUS,   'k', '+');
444   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPCOMMA,  'l', ',');
445   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPMINUS,  'm', '-');
446   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPPERIOD, 'n', '.');
447   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KPDIV,    'o', '/');
448   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP0,      'p', '0');
449   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP1,      'q', '1');
450   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP2,      'r', '2');
451   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP3,      's', '3');
452   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP4,      't', '4');
453   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP5,      'u', '5');
454   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP6,      'v', '6');
455   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP7,      'w', '7');
456   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP8,      'x', '8');
457   register_ss3kpalt(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_KP9,      'y', '9');
458 
459   register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_FIND,      1);
460   register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_INSERT,    2);
461   register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_DELETE,    3);
462   register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_SELECT,    4);
463   register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEUP,    5);
464   register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_PAGEDOWN,  6);
465   register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_HOME,      7);
466   register_csifunc(TERMKEY_TYPE_KEYSYM, TERMKEY_SYM_END,       8);
467 
468   register_csifunc(TERMKEY_TYPE_FUNCTION, 1,  11);
469   register_csifunc(TERMKEY_TYPE_FUNCTION, 2,  12);
470   register_csifunc(TERMKEY_TYPE_FUNCTION, 3,  13);
471   register_csifunc(TERMKEY_TYPE_FUNCTION, 4,  14);
472   register_csifunc(TERMKEY_TYPE_FUNCTION, 5,  15);
473   register_csifunc(TERMKEY_TYPE_FUNCTION, 6,  17);
474   register_csifunc(TERMKEY_TYPE_FUNCTION, 7,  18);
475   register_csifunc(TERMKEY_TYPE_FUNCTION, 8,  19);
476   register_csifunc(TERMKEY_TYPE_FUNCTION, 9,  20);
477   register_csifunc(TERMKEY_TYPE_FUNCTION, 10, 21);
478   register_csifunc(TERMKEY_TYPE_FUNCTION, 11, 23);
479   register_csifunc(TERMKEY_TYPE_FUNCTION, 12, 24);
480   register_csifunc(TERMKEY_TYPE_FUNCTION, 13, 25);
481   register_csifunc(TERMKEY_TYPE_FUNCTION, 14, 26);
482   register_csifunc(TERMKEY_TYPE_FUNCTION, 15, 28);
483   register_csifunc(TERMKEY_TYPE_FUNCTION, 16, 29);
484   register_csifunc(TERMKEY_TYPE_FUNCTION, 17, 31);
485   register_csifunc(TERMKEY_TYPE_FUNCTION, 18, 32);
486   register_csifunc(TERMKEY_TYPE_FUNCTION, 19, 33);
487   register_csifunc(TERMKEY_TYPE_FUNCTION, 20, 34);
488 
489   csi_handlers['u' - 0x40] = &handle_csi_u;
490 
491   csi_handlers['M' - 0x40] = &handle_csi_m;
492   csi_handlers['m' - 0x40] = &handle_csi_m;
493 
494   csi_handlers['R' - 0x40] = &handle_csi_R;
495 
496   csi_handlers['y' - 0x40] = &handle_csi_y;
497 
498   keyinfo_initialised = 1;
499   return 1;
500 }
501 
new_driver(TermKey * tk,const char * term)502 static void *new_driver(TermKey *tk, const char *term)
503 {
504   if(!keyinfo_initialised)
505     if(!register_keys())
506       return NULL;
507 
508   TermKeyCsi *csi = malloc(sizeof *csi);
509   if(!csi)
510     return NULL;
511 
512   csi->tk = tk;
513   csi->saved_string_id = 0;
514   csi->saved_string = NULL;
515 
516   return csi;
517 }
518 
free_driver(void * info)519 static void free_driver(void *info)
520 {
521   TermKeyCsi *csi = info;
522 
523   if(csi->saved_string)
524     free(csi->saved_string);
525 
526   free(csi);
527 }
528 
peekkey_csi(TermKey * tk,TermKeyCsi * csi,size_t introlen,TermKeyKey * key,int force,size_t * nbytep)529 static TermKeyResult peekkey_csi(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key, int force, size_t *nbytep)
530 {
531   size_t csi_len;
532   size_t args = 16;
533   long arg[16];
534   unsigned long cmd;
535 
536   TermKeyResult ret = parse_csi(tk, introlen, &csi_len, arg, &args, &cmd);
537 
538   if(ret == TERMKEY_RES_AGAIN) {
539     if(!force)
540       return TERMKEY_RES_AGAIN;
541 
542     (*tk->method.emit_codepoint)(tk, '[', key);
543     key->modifiers |= TERMKEY_KEYMOD_ALT;
544     *nbytep = introlen;
545     return TERMKEY_RES_KEY;
546   }
547 
548   if(cmd == 'M' && args < 3) { // Mouse in X10 encoding consumes the next 3 bytes also
549     tk->buffstart += csi_len;
550     tk->buffcount -= csi_len;
551 
552     TermKeyResult mouse_result = (*tk->method.peekkey_mouse)(tk, key, nbytep);
553 
554     tk->buffstart -= csi_len;
555     tk->buffcount += csi_len;
556 
557     if(mouse_result == TERMKEY_RES_KEY)
558       *nbytep += csi_len;
559 
560     return mouse_result;
561   }
562 
563   TermKeyResult result = TERMKEY_RES_NONE;
564 
565   // We know from the logic above that cmd must be >= 0x40 and < 0x80
566   if(csi_handlers[(cmd & 0xff) - 0x40])
567     result = (*csi_handlers[(cmd & 0xff) - 0x40])(tk, key, cmd, arg, args);
568 
569   if(result == TERMKEY_RES_NONE) {
570 #ifdef DEBUG
571     switch(args) {
572       case 1:
573         fprintf(stderr, "CSI: Unknown arg1=%ld cmd=%c\n", arg[0], (char)cmd);
574         break;
575       case 2:
576         fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld cmd=%c\n", arg[0], arg[1], (char)cmd);
577         break;
578       case 3:
579         fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld arg3=%ld cmd=%c\n", arg[0], arg[1], arg[2], (char)cmd);
580         break;
581       default:
582         fprintf(stderr, "CSI: Unknown arg1=%ld arg2=%ld arg3=%ld ... args=%d cmd=%c\n", arg[0], arg[1], arg[2], args, (char)cmd);
583         break;
584     }
585 #endif
586     key->type = TERMKEY_TYPE_UNKNOWN_CSI;
587     key->code.number = cmd;
588 
589     tk->hightide = csi_len - introlen;
590     *nbytep = introlen; // Do not yet eat the data bytes
591     return TERMKEY_RES_KEY;
592   }
593 
594   *nbytep = csi_len;
595   return result;
596 }
597 
peekkey_ss3(TermKey * tk,TermKeyCsi * csi,size_t introlen,TermKeyKey * key,int force,size_t * nbytep)598 static TermKeyResult peekkey_ss3(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key, int force, size_t *nbytep)
599 {
600   if(tk->buffcount < introlen + 1) {
601     if(!force)
602       return TERMKEY_RES_AGAIN;
603 
604     (*tk->method.emit_codepoint)(tk, 'O', key);
605     key->modifiers |= TERMKEY_KEYMOD_ALT;
606     *nbytep = tk->buffcount;
607     return TERMKEY_RES_KEY;
608   }
609 
610   unsigned char cmd = CHARAT(introlen);
611 
612   if(cmd < 0x40 || cmd >= 0x80)
613     return TERMKEY_RES_NONE;
614 
615   key->type = csi_ss3s[cmd - 0x40].type;
616   key->code.sym = csi_ss3s[cmd - 0x40].sym;
617   key->modifiers = csi_ss3s[cmd - 0x40].modifier_set;
618 
619   if(key->code.sym == TERMKEY_SYM_UNKNOWN) {
620     if(tk->flags & TERMKEY_FLAG_CONVERTKP && ss3_kpalts[cmd - 0x40]) {
621       key->type = TERMKEY_TYPE_UNICODE;
622       key->code.codepoint = ss3_kpalts[cmd - 0x40];
623       key->modifiers = 0;
624 
625       key->utf8[0] = key->code.codepoint;
626       key->utf8[1] = 0;
627     }
628     else {
629       key->type = ss3s[cmd - 0x40].type;
630       key->code.sym = ss3s[cmd - 0x40].sym;
631       key->modifiers = ss3s[cmd - 0x40].modifier_set;
632     }
633   }
634 
635   if(key->code.sym == TERMKEY_SYM_UNKNOWN) {
636 #ifdef DEBUG
637     fprintf(stderr, "CSI: Unknown SS3 %c (0x%02x)\n", (char)cmd, cmd);
638 #endif
639     return TERMKEY_RES_NONE;
640   }
641 
642   *nbytep = introlen + 1;
643 
644   return TERMKEY_RES_KEY;
645 }
646 
peekkey_ctrlstring(TermKey * tk,TermKeyCsi * csi,size_t introlen,TermKeyKey * key,int force,size_t * nbytep)647 static TermKeyResult peekkey_ctrlstring(TermKey *tk, TermKeyCsi *csi, size_t introlen, TermKeyKey *key, int force, size_t *nbytep)
648 {
649   size_t str_end = introlen;
650 
651   while(str_end < tk->buffcount) {
652     if(CHARAT(str_end) == 0x9c) // ST
653       break;
654     if(CHARAT(str_end) == 0x1b &&
655        (str_end + 1) < tk->buffcount &&
656        CHARAT(str_end+1) == 0x5c) // ESC-prefixed ST
657       break;
658 
659     str_end++;
660   }
661 
662   if(str_end >= tk->buffcount)
663     return TERMKEY_RES_AGAIN;
664 
665 #ifdef DEBUG
666   fprintf(stderr, "Found a control string: %*s",
667       str_end - introlen, tk->buffer + tk->buffstart + introlen);
668 #endif
669 
670   *nbytep = str_end + 1;
671   if(CHARAT(str_end) == 0x1b)
672     (*nbytep)++;
673 
674   if(csi->saved_string)
675     free(csi->saved_string);
676 
677   size_t len = str_end - introlen;
678 
679   csi->saved_string_id++;
680   csi->saved_string = malloc(len + 1);
681 
682   strncpy(csi->saved_string, (char *)tk->buffer + tk->buffstart + introlen, len);
683   csi->saved_string[len] = 0;
684 
685   key->type = (CHARAT(introlen-1) & 0x1f) == 0x10 ?
686     TERMKEY_TYPE_DCS : TERMKEY_TYPE_OSC;
687   key->code.number = csi->saved_string_id;
688   key->modifiers = 0;
689 
690   return TERMKEY_RES_KEY;
691 }
692 
peekkey(TermKey * tk,void * info,TermKeyKey * key,int force,size_t * nbytep)693 static TermKeyResult peekkey(TermKey *tk, void *info, TermKeyKey *key, int force, size_t *nbytep)
694 {
695   if(tk->buffcount == 0)
696     return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE;
697 
698   TermKeyCsi *csi = info;
699 
700   switch(CHARAT(0)) {
701     case 0x1b:
702       if(tk->buffcount < 2)
703         return TERMKEY_RES_NONE;
704 
705       switch(CHARAT(1)) {
706         case 0x4f: // ESC-prefixed SS3
707           return peekkey_ss3(tk, csi, 2, key, force, nbytep);
708 
709         case 0x50: // ESC-prefixed DCS
710         case 0x5d: // ESC-prefixed OSC
711           return peekkey_ctrlstring(tk, csi, 2, key, force, nbytep);
712 
713         case 0x5b: // ESC-prefixed CSI
714           return peekkey_csi(tk, csi, 2, key, force, nbytep);
715       }
716 
717       return TERMKEY_RES_NONE;
718 
719     case 0x8f: // SS3
720       return peekkey_ss3(tk, csi, 1, key, force, nbytep);
721 
722     case 0x90: // DCS
723     case 0x9d: // OSC
724       return peekkey_ctrlstring(tk, csi, 1, key, force, nbytep);
725 
726     case 0x9b: // CSI
727       return peekkey_csi(tk, csi, 1, key, force, nbytep);
728   }
729 
730   return TERMKEY_RES_NONE;
731 }
732 
733 struct TermKeyDriver termkey_driver_csi = {
734   .name        = "CSI",
735 
736   .new_driver  = new_driver,
737   .free_driver = free_driver,
738 
739   .peekkey = peekkey,
740 };
741 
termkey_interpret_string(TermKey * tk,const TermKeyKey * key,const char ** strp)742 TermKeyResult termkey_interpret_string(TermKey *tk, const TermKeyKey *key, const char **strp)
743 {
744   struct TermKeyDriverNode *p;
745   for(p = tk->drivers; p; p = p->next)
746     if(p->driver == &termkey_driver_csi)
747       break;
748 
749   if(!p)
750     return TERMKEY_RES_NONE;
751 
752   if(key->type != TERMKEY_TYPE_DCS &&
753      key->type != TERMKEY_TYPE_OSC)
754     return TERMKEY_RES_NONE;
755 
756   TermKeyCsi *csi = p->info;
757 
758   if(csi->saved_string_id != key->code.number)
759     return TERMKEY_RES_NONE;
760 
761   *strp = csi->saved_string;
762 
763   return TERMKEY_RES_KEY;
764 }
765