termkey.c (32130B)
1 #include <ctype.h> 2 #include <errno.h> 3 #include <stdio.h> 4 #include <string.h> 5 6 #include "nvim/macros_defs.h" 7 #include "nvim/mbyte.h" 8 #include "nvim/memory.h" 9 #include "nvim/tui/termkey/driver-csi.h" 10 #include "nvim/tui/termkey/driver-ti.h" 11 #include "nvim/tui/termkey/termkey-internal.h" 12 #include "nvim/tui/termkey/termkey.h" 13 #include "nvim/tui/termkey/termkey_defs.h" 14 15 #ifndef _WIN32 16 // Include these directly instead of <termios.h> which is system-dependent. #31704 17 # include <poll.h> // IWYU pragma: keep 18 # include <strings.h> // IWYU pragma: keep 19 # include <unistd.h> // IWYU pragma: keep 20 #else 21 # include <io.h> 22 #endif 23 24 #include "tui/termkey/termkey.c.generated.h" 25 26 #ifdef _MSC_VER 27 # define strcaseeq(a, b) (_stricmp(a, b) == 0) 28 #else 29 # define strcaseeq(a, b) (strcasecmp(a, b) == 0) 30 #endif 31 32 struct TermKeyDriver termkey_driver_ti = { 33 .name = "terminfo", 34 35 .new_driver = new_driver_ti, 36 .free_driver = free_driver_ti, 37 38 .start_driver = start_driver_ti, 39 .stop_driver = stop_driver_ti, 40 41 .peekkey = peekkey_ti, 42 }; 43 44 struct TermKeyDriver termkey_driver_csi = { 45 .name = "CSI", 46 47 .new_driver = new_driver_csi, 48 .free_driver = free_driver_csi, 49 50 .peekkey = peekkey_csi, 51 }; 52 53 static struct TermKeyDriver *drivers[] = { 54 &termkey_driver_ti, 55 &termkey_driver_csi, 56 NULL, 57 }; 58 59 static struct { 60 TermKeySym sym; 61 const char *name; 62 } keynames[] = { 63 { TERMKEY_SYM_NONE, "NONE" }, 64 { TERMKEY_SYM_BACKSPACE, "Backspace" }, 65 { TERMKEY_SYM_TAB, "Tab" }, 66 { TERMKEY_SYM_ENTER, "Enter" }, 67 { TERMKEY_SYM_ESCAPE, "Escape" }, 68 { TERMKEY_SYM_SPACE, "Space" }, 69 { TERMKEY_SYM_DEL, "DEL" }, 70 { TERMKEY_SYM_UP, "Up" }, 71 { TERMKEY_SYM_DOWN, "Down" }, 72 { TERMKEY_SYM_LEFT, "Left" }, 73 { TERMKEY_SYM_RIGHT, "Right" }, 74 { TERMKEY_SYM_BEGIN, "Begin" }, 75 { TERMKEY_SYM_FIND, "Find" }, 76 { TERMKEY_SYM_INSERT, "Insert" }, 77 { TERMKEY_SYM_DELETE, "Delete" }, 78 { TERMKEY_SYM_SELECT, "Select" }, 79 { TERMKEY_SYM_PAGEUP, "PageUp" }, 80 { TERMKEY_SYM_PAGEDOWN, "PageDown" }, 81 { TERMKEY_SYM_HOME, "Home" }, 82 { TERMKEY_SYM_END, "End" }, 83 { TERMKEY_SYM_CANCEL, "Cancel" }, 84 { TERMKEY_SYM_CLEAR, "Clear" }, 85 { TERMKEY_SYM_CLOSE, "Close" }, 86 { TERMKEY_SYM_COMMAND, "Command" }, 87 { TERMKEY_SYM_COPY, "Copy" }, 88 { TERMKEY_SYM_EXIT, "Exit" }, 89 { TERMKEY_SYM_HELP, "Help" }, 90 { TERMKEY_SYM_MARK, "Mark" }, 91 { TERMKEY_SYM_MESSAGE, "Message" }, 92 { TERMKEY_SYM_MOVE, "Move" }, 93 { TERMKEY_SYM_OPEN, "Open" }, 94 { TERMKEY_SYM_OPTIONS, "Options" }, 95 { TERMKEY_SYM_PRINT, "Print" }, 96 { TERMKEY_SYM_REDO, "Redo" }, 97 { TERMKEY_SYM_REFERENCE, "Reference" }, 98 { TERMKEY_SYM_REFRESH, "Refresh" }, 99 { TERMKEY_SYM_REPLACE, "Replace" }, 100 { TERMKEY_SYM_RESTART, "Restart" }, 101 { TERMKEY_SYM_RESUME, "Resume" }, 102 { TERMKEY_SYM_SAVE, "Save" }, 103 { TERMKEY_SYM_SUSPEND, "Suspend" }, 104 { TERMKEY_SYM_UNDO, "Undo" }, 105 { TERMKEY_SYM_KP0, "KP0" }, 106 { TERMKEY_SYM_KP1, "KP1" }, 107 { TERMKEY_SYM_KP2, "KP2" }, 108 { TERMKEY_SYM_KP3, "KP3" }, 109 { TERMKEY_SYM_KP4, "KP4" }, 110 { TERMKEY_SYM_KP5, "KP5" }, 111 { TERMKEY_SYM_KP6, "KP6" }, 112 { TERMKEY_SYM_KP7, "KP7" }, 113 { TERMKEY_SYM_KP8, "KP8" }, 114 { TERMKEY_SYM_KP9, "KP9" }, 115 { TERMKEY_SYM_KPENTER, "KPEnter" }, 116 { TERMKEY_SYM_KPPLUS, "KPPlus" }, 117 { TERMKEY_SYM_KPMINUS, "KPMinus" }, 118 { TERMKEY_SYM_KPMULT, "KPMult" }, 119 { TERMKEY_SYM_KPDIV, "KPDiv" }, 120 { TERMKEY_SYM_KPCOMMA, "KPComma" }, 121 { TERMKEY_SYM_KPPERIOD, "KPPeriod" }, 122 { TERMKEY_SYM_KPEQUALS, "KPEquals" }, 123 { 0, NULL }, 124 }; 125 126 // Mouse event names 127 static const char *evnames[] = { "Unknown", "Press", "Drag", "Release" }; 128 129 #define CHARAT(i) (tk->buffer[tk->buffstart + (i)]) 130 131 #ifdef DEBUG 132 // Some internal debugging functions 133 134 static void print_buffer(TermKey *tk) 135 { 136 int i; 137 for (i = 0; i < tk->buffcount && i < 20; i++) { 138 fprintf(stderr, "%02x ", CHARAT(i)); 139 } 140 if (tk->buffcount > 20) { 141 fprintf(stderr, "..."); 142 } 143 } 144 145 static void print_key(TermKey *tk, TermKeyKey *key) 146 { 147 switch (key->type) { 148 case TERMKEY_TYPE_UNICODE: 149 fprintf(stderr, "Unicode codepoint=U+%04lx utf8='%s'", key->code.codepoint, key->utf8); 150 break; 151 case TERMKEY_TYPE_FUNCTION: 152 fprintf(stderr, "Function F%d", key->code.number); 153 break; 154 case TERMKEY_TYPE_KEYSYM: 155 fprintf(stderr, "Keysym sym=%d(%s)", key->code.sym, termkey_get_keyname(tk, key->code.sym)); 156 break; 157 case TERMKEY_TYPE_MOUSE: { 158 TermKeyMouseEvent ev; 159 int button, line, col; 160 termkey_interpret_mouse(tk, key, &ev, &button, &line, &col); 161 fprintf(stderr, "Mouse ev=%d button=%d pos=(%d,%d)\n", ev, button, line, col); 162 } 163 break; 164 case TERMKEY_TYPE_POSITION: { 165 int line, col; 166 termkey_interpret_position(tk, key, &line, &col); 167 fprintf(stderr, "Position report pos=(%d,%d)\n", line, col); 168 } 169 break; 170 case TERMKEY_TYPE_MODEREPORT: { 171 int initial, mode, value; 172 termkey_interpret_modereport(tk, key, &initial, &mode, &value); 173 fprintf(stderr, "Mode report mode=%s %d val=%d\n", initial == '?' ? "DEC" : "ANSI", mode, 174 value); 175 } 176 break; 177 case TERMKEY_TYPE_DCS: 178 fprintf(stderr, "Device Control String"); 179 break; 180 case TERMKEY_TYPE_OSC: 181 fprintf(stderr, "Operating System Control"); 182 break; 183 case TERMKEY_TYPE_APC: 184 fprintf(stderr, "Application Program Command"); 185 break; 186 case TERMKEY_TYPE_UNKNOWN_CSI: 187 fprintf(stderr, "unknown CSI\n"); 188 break; 189 } 190 191 int m = key->modifiers; 192 fprintf(stderr, " mod=%s%s%s+%02x", 193 (m & TERMKEY_KEYMOD_CTRL ? "C" : ""), 194 (m & TERMKEY_KEYMOD_ALT ? "A" : ""), 195 (m & TERMKEY_KEYMOD_SHIFT ? "S" : ""), 196 m & ~(TERMKEY_KEYMOD_CTRL|TERMKEY_KEYMOD_ALT|TERMKEY_KEYMOD_SHIFT)); 197 } 198 199 static const char *res2str(TermKeyResult res) 200 { 201 static char errorbuffer[256]; 202 203 switch (res) { 204 case TERMKEY_RES_KEY: 205 return "TERMKEY_RES_KEY"; 206 case TERMKEY_RES_EOF: 207 return "TERMKEY_RES_EOF"; 208 case TERMKEY_RES_AGAIN: 209 return "TERMKEY_RES_AGAIN"; 210 case TERMKEY_RES_NONE: 211 return "TERMKEY_RES_NONE"; 212 case TERMKEY_RES_ERROR: 213 snprintf(errorbuffer, sizeof errorbuffer, "TERMKEY_RES_ERROR(errno=%d)\n", errno); 214 return (const char *)errorbuffer; 215 } 216 217 return "unknown"; 218 } 219 #endif 220 221 TermKeyResult termkey_interpret_string(TermKey *tk, const TermKeyKey *key, const char **strp) 222 { 223 struct TermKeyDriverNode *p; 224 for (p = tk->drivers; p; p = p->next) { 225 if (p->driver == &termkey_driver_csi) { 226 break; 227 } 228 } 229 230 if (!p) { 231 return TERMKEY_RES_NONE; 232 } 233 234 if (key->type != TERMKEY_TYPE_DCS 235 && key->type != TERMKEY_TYPE_OSC 236 && key->type != TERMKEY_TYPE_APC) { 237 return TERMKEY_RES_NONE; 238 } 239 240 TermKeyCsi *csi = p->info; 241 242 if (csi->saved_string_id != key->code.number) { 243 return TERMKEY_RES_NONE; 244 } 245 246 *strp = csi->saved_string; 247 248 return TERMKEY_RES_KEY; 249 } 250 251 /// Similar to snprintf(str, size, "%s", src) except it turns CamelCase into 252 /// space separated values 253 static int snprint_cameltospaces(char *str, size_t size, const char *src) 254 { 255 int prev_lower = 0; 256 size_t l = 0; 257 while (*src && l < size - 1) { 258 if (isupper(*src) && prev_lower) { 259 if (str) { 260 str[l++] = ' '; 261 } 262 if (l >= size - 1) { 263 break; 264 } 265 } 266 prev_lower = islower(*src); 267 str[l++] = (char)tolower(*src++); 268 } 269 str[l] = 0; 270 // For consistency with snprintf, return the number of bytes that would have 271 // been written, excluding '\0' 272 while (*src) { 273 if (isupper(*src) && prev_lower) { 274 l++; 275 } 276 prev_lower = islower(*src); 277 src++; l++; 278 } 279 return (int)l; 280 } 281 282 /// Similar to strcmp(str, strcamel, n) except that: 283 /// it compares CamelCase in strcamel with space separated values in str; 284 /// it takes char**s and updates them 285 /// n counts bytes of strcamel, not str 286 static int strpncmp_camel(const char **strp, const char **strcamelp, size_t n) 287 { 288 const char *str = *strp, *strcamel = *strcamelp; 289 int prev_lower = 0; 290 291 for (; (*str || *strcamel) && n; n--) { 292 char b = (char)tolower(*strcamel); 293 if (isupper(*strcamel) && prev_lower) { 294 if (*str != ' ') { 295 break; 296 } 297 str++; 298 if (*str != b) { 299 break; 300 } 301 } else if (*str != b) { 302 break; 303 } 304 305 prev_lower = islower(*strcamel); 306 307 str++; 308 strcamel++; 309 } 310 311 *strp = str; 312 *strcamelp = strcamel; 313 return *str - *strcamel; 314 } 315 316 static TermKey *termkey_alloc(void) 317 { 318 TermKey *tk = xmalloc(sizeof(TermKey)); 319 320 // Default all the object fields but don't allocate anything 321 322 tk->fd = -1; 323 tk->flags = 0; 324 tk->canonflags = 0; 325 326 tk->buffer = NULL; 327 tk->buffstart = 0; 328 tk->buffcount = 0; 329 tk->buffsize = 256; // bytes 330 tk->hightide = 0; 331 332 #ifdef HAVE_TERMIOS 333 tk->restore_termios_valid = 0; 334 #endif 335 336 tk->ti_getstr_hook = NULL; 337 tk->ti_getstr_hook_data = NULL; 338 339 tk->waittime = 50; // msec 340 341 tk->is_closed = 0; 342 tk->is_started = 0; 343 344 tk->nkeynames = 64; 345 tk->keynames = NULL; 346 347 for (int i = 0; i < 32; i++) { 348 tk->c0[i].sym = TERMKEY_SYM_NONE; 349 } 350 351 tk->drivers = NULL; 352 353 tk->method.emit_codepoint = &emit_codepoint; 354 tk->method.peekkey_simple = &peekkey_simple; 355 tk->method.peekkey_mouse = &peekkey_mouse; 356 357 return tk; 358 } 359 360 static int termkey_init(TermKey *tk, TerminfoEntry *term) 361 { 362 tk->buffer = xmalloc(tk->buffsize); 363 tk->keynames = xmalloc(sizeof(tk->keynames[0]) * (size_t)tk->nkeynames); 364 365 int i; 366 for (i = 0; i < tk->nkeynames; i++) { 367 tk->keynames[i] = NULL; 368 } 369 370 for (i = 0; keynames[i].name; i++) { 371 if (termkey_register_keyname(tk, keynames[i].sym, keynames[i].name) == -1) { 372 goto abort_free_keynames; 373 } 374 } 375 376 register_c0(tk, TERMKEY_SYM_TAB, 0x09, NULL); 377 register_c0(tk, TERMKEY_SYM_ENTER, 0x0d, NULL); 378 register_c0(tk, TERMKEY_SYM_ESCAPE, 0x1b, NULL); 379 380 struct TermKeyDriverNode *tail = NULL; 381 382 for (i = 0; drivers[i]; i++) { 383 void *info = (*drivers[i]->new_driver)(tk, term); 384 if (!info) { 385 continue; 386 } 387 388 #ifdef DEBUG 389 fprintf(stderr, "Loading the %s driver...\n", drivers[i]->name); 390 #endif 391 392 struct TermKeyDriverNode *thisdrv = xmalloc(sizeof(*thisdrv)); 393 if (!thisdrv) { 394 goto abort_free_drivers; 395 } 396 397 thisdrv->driver = drivers[i]; 398 thisdrv->info = info; 399 thisdrv->next = NULL; 400 401 if (!tail) { 402 tk->drivers = thisdrv; 403 } else { 404 tail->next = thisdrv; 405 } 406 407 tail = thisdrv; 408 409 #ifdef DEBUG 410 fprintf(stderr, "Loaded %s driver\n", drivers[i]->name); 411 #endif 412 } 413 414 if (!tk->drivers) { 415 errno = ENOENT; 416 goto abort_free_keynames; 417 } 418 419 return 1; 420 421 abort_free_drivers: 422 for (struct TermKeyDriverNode *p = tk->drivers; p;) { 423 (*p->driver->free_driver)(p->info); 424 struct TermKeyDriverNode *next = p->next; 425 xfree(p); 426 p = next; 427 } 428 429 abort_free_keynames: 430 xfree(tk->keynames); 431 xfree(tk->buffer); 432 433 return 0; 434 } 435 436 TermKey *termkey_new_abstract(TerminfoEntry *term, int flags) 437 { 438 TermKey *tk = termkey_alloc(); 439 if (!tk) { 440 return NULL; 441 } 442 443 tk->fd = -1; 444 445 termkey_set_flags(tk, flags); 446 447 if (!termkey_init(tk, term)) { 448 xfree(tk); 449 return NULL; 450 } 451 452 if (!(flags & TERMKEY_FLAG_NOSTART) && !termkey_start(tk)) { 453 goto abort; 454 } 455 456 return tk; 457 458 abort: 459 xfree(tk); 460 return NULL; 461 } 462 463 void termkey_free(TermKey *tk) 464 { 465 xfree(tk->buffer); tk->buffer = NULL; 466 xfree(tk->keynames); tk->keynames = NULL; 467 468 struct TermKeyDriverNode *p; 469 for (p = tk->drivers; p;) { 470 (*p->driver->free_driver)(p->info); 471 struct TermKeyDriverNode *next = p->next; 472 xfree(p); 473 p = next; 474 } 475 476 xfree(tk); 477 } 478 479 void termkey_destroy(TermKey *tk) 480 { 481 if (tk->is_started) { 482 termkey_stop(tk); 483 } 484 485 termkey_free(tk); 486 } 487 488 void termkey_hook_terminfo_getstr(TermKey *tk, TermKey_Terminfo_Getstr_Hook *hookfn, void *data) 489 { 490 tk->ti_getstr_hook = hookfn; 491 tk->ti_getstr_hook_data = data; 492 } 493 494 int termkey_start(TermKey *tk) 495 { 496 if (tk->is_started) { 497 return 1; 498 } 499 500 #ifdef HAVE_TERMIOS 501 if (tk->fd != -1 && !(tk->flags & TERMKEY_FLAG_NOTERMIOS)) { 502 struct termios termios; 503 if (tcgetattr(tk->fd, &termios) == 0) { 504 tk->restore_termios = termios; 505 tk->restore_termios_valid = 1; 506 507 termios.c_iflag &= (tcflag_t) ~(IXON|INLCR|ICRNL); 508 termios.c_lflag &= (tcflag_t) ~(ICANON|ECHO 509 # ifdef IEXTEN 510 | IEXTEN 511 # endif 512 ); 513 termios.c_cc[VMIN] = 1; 514 termios.c_cc[VTIME] = 0; 515 516 if (tk->flags & TERMKEY_FLAG_CTRLC) { 517 // want no signal keys at all, so just disable ISIG 518 termios.c_lflag &= (tcflag_t) ~ISIG; 519 } else { 520 // Disable Ctrl-\==VQUIT and Ctrl-D==VSUSP but leave Ctrl-C as SIGINT 521 termios.c_cc[VQUIT] = _POSIX_VDISABLE; 522 termios.c_cc[VSUSP] = _POSIX_VDISABLE; 523 // Some OSes have Ctrl-Y==VDSUSP 524 # ifdef VDSUSP 525 termios.c_cc[VDSUSP] = _POSIX_VDISABLE; 526 # endif 527 } 528 529 # ifdef DEBUG 530 fprintf(stderr, "Setting termios(3) flags\n"); 531 # endif 532 tcsetattr(tk->fd, TCSANOW, &termios); 533 } 534 } 535 #endif 536 537 struct TermKeyDriverNode *p; 538 for (p = tk->drivers; p; p = p->next) { 539 if (p->driver->start_driver) { 540 if (!(*p->driver->start_driver)(tk, p->info)) { 541 return 0; 542 } 543 } 544 } 545 546 #ifdef DEBUG 547 fprintf(stderr, "Drivers started; termkey instance %p is ready\n", tk); 548 #endif 549 550 tk->is_started = 1; 551 return 1; 552 } 553 554 int termkey_stop(TermKey *tk) 555 { 556 if (!tk->is_started) { 557 return 1; 558 } 559 560 struct TermKeyDriverNode *p; 561 for (p = tk->drivers; p; p = p->next) { 562 if (p->driver->stop_driver) { 563 (*p->driver->stop_driver)(tk, p->info); 564 } 565 } 566 567 #ifdef HAVE_TERMIOS 568 if (tk->restore_termios_valid) { 569 tcsetattr(tk->fd, TCSANOW, &tk->restore_termios); 570 } 571 #endif 572 573 tk->is_started = 0; 574 575 return 1; 576 } 577 578 void termkey_set_flags(TermKey *tk, int newflags) 579 { 580 tk->flags = newflags; 581 582 if (tk->flags & TERMKEY_FLAG_SPACESYMBOL) { 583 tk->canonflags |= TERMKEY_CANON_SPACESYMBOL; 584 } else { 585 tk->canonflags &= ~TERMKEY_CANON_SPACESYMBOL; 586 } 587 } 588 589 int termkey_get_canonflags(TermKey *tk) 590 { 591 return tk->canonflags; 592 } 593 594 void termkey_set_canonflags(TermKey *tk, int flags) 595 { 596 tk->canonflags = flags; 597 598 if (tk->canonflags & TERMKEY_CANON_SPACESYMBOL) { 599 tk->flags |= TERMKEY_FLAG_SPACESYMBOL; 600 } else { 601 tk->flags &= ~TERMKEY_FLAG_SPACESYMBOL; 602 } 603 } 604 605 size_t termkey_get_buffer_size(TermKey *tk) 606 { 607 return tk->buffsize; 608 } 609 610 int termkey_set_buffer_size(TermKey *tk, size_t size) 611 { 612 unsigned char *buffer = xrealloc(tk->buffer, size); 613 614 tk->buffer = buffer; 615 tk->buffsize = size; 616 617 return 1; 618 } 619 620 size_t termkey_get_buffer_remaining(TermKey *tk) 621 { 622 // Return the total number of free bytes in the buffer, because that's what 623 // is available to the user. 624 return tk->buffsize - tk->buffcount; 625 } 626 627 static void eat_bytes(TermKey *tk, size_t count) 628 { 629 if (count >= tk->buffcount) { 630 tk->buffstart = 0; 631 tk->buffcount = 0; 632 return; 633 } 634 635 tk->buffstart += count; 636 tk->buffcount -= count; 637 } 638 639 int fill_utf8(int codepoint, char *str) 640 { 641 int nbytes = utf_char2bytes(codepoint, str); 642 str[nbytes] = 0; 643 return nbytes; 644 } 645 646 static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp, size_t *nbytep) 647 { 648 unsigned nbytes; 649 650 unsigned char b0 = bytes[0]; 651 652 if (b0 < 0x80) { 653 // Single byte ASCII 654 *cp = b0; 655 *nbytep = 1; 656 return TERMKEY_RES_KEY; 657 } else if (b0 < 0xc0) { 658 // Starts with a continuation byte - that's not right 659 *cp = UNICODE_INVALID; 660 *nbytep = 1; 661 return TERMKEY_RES_KEY; 662 } else if (b0 < 0xe0) { 663 nbytes = 2; 664 *cp = b0 & 0x1f; 665 } else if (b0 < 0xf0) { 666 nbytes = 3; 667 *cp = b0 & 0x0f; 668 } else if (b0 < 0xf8) { 669 nbytes = 4; 670 *cp = b0 & 0x07; 671 } else if (b0 < 0xfc) { 672 nbytes = 5; 673 *cp = b0 & 0x03; 674 } else if (b0 < 0xfe) { 675 nbytes = 6; 676 *cp = b0 & 0x01; 677 } else { 678 *cp = UNICODE_INVALID; 679 *nbytep = 1; 680 return TERMKEY_RES_KEY; 681 } 682 683 for (unsigned b = 1; b < nbytes; b++) { 684 unsigned char cb; 685 686 if (b >= len) { 687 return TERMKEY_RES_AGAIN; 688 } 689 690 cb = bytes[b]; 691 if (cb < 0x80 || cb >= 0xc0) { 692 *cp = UNICODE_INVALID; 693 *nbytep = b; 694 return TERMKEY_RES_KEY; 695 } 696 697 *cp <<= 6; 698 *cp |= cb & 0x3f; 699 } 700 701 // Check for overlong sequences 702 if ((int)nbytes > utf_char2len(*cp)) { 703 *cp = UNICODE_INVALID; 704 } 705 706 // Check for UTF-16 surrogates or invalid *cps 707 if ((*cp >= 0xD800 && *cp <= 0xDFFF) 708 || *cp == 0xFFFE 709 || *cp == 0xFFFF) { 710 *cp = UNICODE_INVALID; 711 } 712 713 *nbytep = nbytes; 714 return TERMKEY_RES_KEY; 715 } 716 717 static void emit_codepoint(TermKey *tk, int codepoint, TermKeyKey *key) 718 { 719 if (codepoint == 0) { 720 // ASCII NUL = Ctrl-Space 721 key->type = TERMKEY_TYPE_KEYSYM; 722 key->code.sym = TERMKEY_SYM_SPACE; 723 key->modifiers = TERMKEY_KEYMOD_CTRL; 724 } else if (codepoint < 0x20 && !(tk->flags & TERMKEY_FLAG_KEEPC0)) { 725 // C0 range 726 key->code.codepoint = 0; 727 key->modifiers = 0; 728 729 if (!(tk->flags & TERMKEY_FLAG_NOINTERPRET) && tk->c0[codepoint].sym != TERMKEY_SYM_UNKNOWN) { 730 key->code.sym = tk->c0[codepoint].sym; 731 key->modifiers |= tk->c0[codepoint].modifier_set; 732 } 733 734 if (!key->code.sym) { 735 key->type = TERMKEY_TYPE_UNICODE; 736 // Generically modified Unicode ought not report the SHIFT state, or else 737 // we get into complications trying to report Shift-; vs : and so on... 738 // In order to be able to represent Ctrl-Shift-A as CTRL modified 739 // unicode A, we need to call Ctrl-A simply 'a', lowercase 740 if (codepoint + 0x40 >= 'A' && codepoint + 0x40 <= 'Z') { 741 // it's a letter - use lowercase instead 742 key->code.codepoint = codepoint + 0x60; 743 } else { 744 key->code.codepoint = codepoint + 0x40; 745 } 746 key->modifiers = TERMKEY_KEYMOD_CTRL; 747 } else { 748 key->type = TERMKEY_TYPE_KEYSYM; 749 } 750 } else if (codepoint == 0x7f && !(tk->flags & TERMKEY_FLAG_NOINTERPRET)) { 751 // ASCII DEL 752 key->type = TERMKEY_TYPE_KEYSYM; 753 key->code.sym = TERMKEY_SYM_DEL; 754 key->modifiers = 0; 755 } else if (codepoint > 0 && codepoint < 0x80) { 756 // ASCII lowbyte range 757 key->type = TERMKEY_TYPE_UNICODE; 758 key->code.codepoint = codepoint; 759 key->modifiers = 0; 760 } else if (codepoint >= 0x80 && codepoint < 0xa0) { 761 // UTF-8 never starts with a C1 byte. So we can be sure of these 762 key->type = TERMKEY_TYPE_UNICODE; 763 key->code.codepoint = codepoint - 0x40; 764 key->modifiers = TERMKEY_KEYMOD_CTRL|TERMKEY_KEYMOD_ALT; 765 } else { 766 // UTF-8 codepoint 767 key->type = TERMKEY_TYPE_UNICODE; 768 key->code.codepoint = codepoint; 769 key->modifiers = 0; 770 } 771 772 termkey_canonicalise(tk, key); 773 774 if (key->type == TERMKEY_TYPE_UNICODE) { 775 fill_utf8(key->code.codepoint, key->utf8); 776 } 777 } 778 779 void termkey_canonicalise(TermKey *tk, TermKeyKey *key) 780 { 781 int flags = tk->canonflags; 782 783 if (flags & TERMKEY_CANON_SPACESYMBOL) { 784 if (key->type == TERMKEY_TYPE_UNICODE && key->code.codepoint == 0x20) { 785 key->type = TERMKEY_TYPE_KEYSYM; 786 key->code.sym = TERMKEY_SYM_SPACE; 787 } 788 } else { 789 if (key->type == TERMKEY_TYPE_KEYSYM && key->code.sym == TERMKEY_SYM_SPACE) { 790 key->type = TERMKEY_TYPE_UNICODE; 791 key->code.codepoint = 0x20; 792 fill_utf8(key->code.codepoint, key->utf8); 793 } 794 } 795 796 if (flags & TERMKEY_CANON_DELBS) { 797 if (key->type == TERMKEY_TYPE_KEYSYM && key->code.sym == TERMKEY_SYM_DEL) { 798 key->code.sym = TERMKEY_SYM_BACKSPACE; 799 } 800 } 801 } 802 803 static TermKeyResult peekkey(TermKey *tk, TermKeyKey *key, int force, size_t *nbytep) 804 { 805 int again = 0; 806 807 if (!tk->is_started) { 808 errno = EINVAL; 809 return TERMKEY_RES_ERROR; 810 } 811 812 // Press is the default event type. 813 key->event = TERMKEY_EVENT_PRESS; 814 815 #ifdef DEBUG 816 fprintf(stderr, "getkey(force=%d): buffer ", force); 817 print_buffer(tk); 818 fprintf(stderr, "\n"); 819 #endif 820 821 if (tk->hightide) { 822 tk->buffstart += tk->hightide; 823 tk->buffcount -= tk->hightide; 824 tk->hightide = 0; 825 } 826 827 TermKeyResult ret; 828 struct TermKeyDriverNode *p; 829 for (p = tk->drivers; p; p = p->next) { 830 ret = (p->driver->peekkey)(tk, p->info, key, force, nbytep); 831 832 #ifdef DEBUG 833 fprintf(stderr, "Driver %s yields %s\n", p->driver->name, res2str(ret)); 834 #endif 835 836 switch (ret) { 837 case TERMKEY_RES_KEY: 838 #ifdef DEBUG 839 print_key(tk, key); fprintf(stderr, "\n"); 840 #endif 841 // Slide the data down to stop it running away 842 { 843 size_t halfsize = tk->buffsize / 2; 844 845 if (tk->buffstart > halfsize) { 846 memcpy(tk->buffer, tk->buffer + halfsize, halfsize); 847 tk->buffstart -= halfsize; 848 } 849 } 850 FALLTHROUGH; 851 case TERMKEY_RES_EOF: 852 case TERMKEY_RES_ERROR: 853 return ret; 854 855 case TERMKEY_RES_AGAIN: 856 if (!force) { 857 again = 1; 858 } 859 FALLTHROUGH; 860 case TERMKEY_RES_NONE: 861 break; 862 } 863 } 864 865 if (again) { 866 return TERMKEY_RES_AGAIN; 867 } 868 869 ret = peekkey_simple(tk, key, force, nbytep); 870 871 #ifdef DEBUG 872 fprintf(stderr, "getkey_simple(force=%d) yields %s\n", force, res2str(ret)); 873 if (ret == TERMKEY_RES_KEY) { 874 print_key(tk, key); fprintf(stderr, "\n"); 875 } 876 #endif 877 878 return ret; 879 } 880 881 static TermKeyResult peekkey_simple(TermKey *tk, TermKeyKey *key, int force, size_t *nbytep) 882 { 883 if (tk->buffcount == 0) { 884 return tk->is_closed ? TERMKEY_RES_EOF : TERMKEY_RES_NONE; 885 } 886 887 unsigned char b0 = CHARAT(0); 888 889 if (b0 == 0x1b) { 890 // Escape-prefixed value? Might therefore be Alt+key 891 if (tk->buffcount == 1) { 892 // This might be an <Esc> press, or it may want to be part of a longer 893 // sequence 894 if (!force) { 895 return TERMKEY_RES_AGAIN; 896 } 897 898 (*tk->method.emit_codepoint)(tk, b0, key); 899 *nbytep = 1; 900 return TERMKEY_RES_KEY; 901 } 902 903 // Try another key there 904 tk->buffstart++; 905 tk->buffcount--; 906 907 // Run the full driver 908 TermKeyResult metakey_result = peekkey(tk, key, force, nbytep); 909 910 tk->buffstart--; 911 tk->buffcount++; 912 913 switch (metakey_result) { 914 case TERMKEY_RES_KEY: 915 key->modifiers |= TERMKEY_KEYMOD_ALT; 916 (*nbytep)++; 917 break; 918 919 case TERMKEY_RES_NONE: 920 case TERMKEY_RES_EOF: 921 case TERMKEY_RES_AGAIN: 922 case TERMKEY_RES_ERROR: 923 break; 924 } 925 926 return metakey_result; 927 } else if (b0 < 0xa0) { 928 // Single byte C0, G0 or C1 - C1 is never UTF-8 initial byte 929 (*tk->method.emit_codepoint)(tk, b0, key); 930 *nbytep = 1; 931 return TERMKEY_RES_KEY; 932 } else if (tk->flags & TERMKEY_FLAG_UTF8) { 933 // Some UTF-8 934 int codepoint; 935 TermKeyResult res = parse_utf8(tk->buffer + tk->buffstart, tk->buffcount, &codepoint, nbytep); 936 937 if (res == TERMKEY_RES_AGAIN && force) { 938 // There weren't enough bytes for a complete UTF-8 sequence but caller 939 // demands an answer. About the best thing we can do here is eat as many 940 // bytes as we have, and emit a UNICODE_INVALID. If the remaining bytes 941 // arrive later, they'll be invalid too. 942 codepoint = UNICODE_INVALID; 943 *nbytep = tk->buffcount; 944 res = TERMKEY_RES_KEY; 945 } 946 947 key->type = TERMKEY_TYPE_UNICODE; 948 key->modifiers = 0; 949 (*tk->method.emit_codepoint)(tk, codepoint, key); 950 return res; 951 } else { 952 // Non UTF-8 case - just report the raw byte 953 key->type = TERMKEY_TYPE_UNICODE; 954 key->code.codepoint = b0; 955 key->modifiers = 0; 956 957 key->utf8[0] = (char)key->code.codepoint; 958 key->utf8[1] = 0; 959 960 *nbytep = 1; 961 962 return TERMKEY_RES_KEY; 963 } 964 } 965 966 static TermKeyResult peekkey_mouse(TermKey *tk, TermKeyKey *key, size_t *nbytep) 967 { 968 if (tk->buffcount < 3) { 969 return TERMKEY_RES_AGAIN; 970 } 971 972 key->type = TERMKEY_TYPE_MOUSE; 973 key->code.mouse[0] = (char)CHARAT(0) - 0x20; 974 key->code.mouse[1] = (char)CHARAT(1) - 0x20; 975 key->code.mouse[2] = (char)CHARAT(2) - 0x20; 976 key->code.mouse[3] = 0; 977 978 key->modifiers = (key->code.mouse[0] & 0x1c) >> 2; 979 key->code.mouse[0] &= ~0x1c; 980 981 *nbytep = 3; 982 return TERMKEY_RES_KEY; 983 } 984 985 TermKeyResult termkey_getkey(TermKey *tk, TermKeyKey *key) 986 { 987 size_t nbytes = 0; 988 TermKeyResult ret = peekkey(tk, key, 0, &nbytes); 989 990 if (ret == TERMKEY_RES_KEY) { 991 eat_bytes(tk, nbytes); 992 } 993 994 if (ret == TERMKEY_RES_AGAIN) { 995 // Call peekkey() again in force mode to obtain whatever it can 996 (void)peekkey(tk, key, 1, &nbytes); 997 } 998 // Don't eat it yet though 999 1000 return ret; 1001 } 1002 1003 TermKeyResult termkey_getkey_force(TermKey *tk, TermKeyKey *key) 1004 { 1005 size_t nbytes = 0; 1006 TermKeyResult ret = peekkey(tk, key, 1, &nbytes); 1007 1008 if (ret == TERMKEY_RES_KEY) { 1009 eat_bytes(tk, nbytes); 1010 } 1011 1012 return ret; 1013 } 1014 1015 size_t termkey_push_bytes(TermKey *tk, const char *bytes, size_t len) 1016 { 1017 if (tk->buffstart) { 1018 memmove(tk->buffer, tk->buffer + tk->buffstart, tk->buffcount); 1019 tk->buffstart = 0; 1020 } 1021 1022 // Not expecting it ever to be greater but doesn't hurt to handle that 1023 if (tk->buffcount >= tk->buffsize) { 1024 errno = ENOMEM; 1025 return (size_t)-1; 1026 } 1027 1028 if (len > tk->buffsize - tk->buffcount) { 1029 len = tk->buffsize - tk->buffcount; 1030 } 1031 1032 // memcpy(), not strncpy() in case of null bytes in input 1033 memcpy(tk->buffer + tk->buffcount, bytes, len); 1034 tk->buffcount += len; 1035 1036 return len; 1037 } 1038 1039 TermKeySym termkey_register_keyname(TermKey *tk, TermKeySym sym, const char *name) 1040 { 1041 if (!sym) { 1042 sym = tk->nkeynames; 1043 } 1044 1045 if (sym >= tk->nkeynames) { 1046 const char **new_keynames = xrealloc(tk->keynames, sizeof(new_keynames[0]) * ((size_t)sym + 1)); 1047 1048 tk->keynames = new_keynames; 1049 1050 // Fill in the hole 1051 for (int i = tk->nkeynames; i < sym; i++) { 1052 tk->keynames[i] = NULL; 1053 } 1054 1055 tk->nkeynames = sym + 1; 1056 } 1057 1058 tk->keynames[sym] = name; 1059 1060 return sym; 1061 } 1062 1063 const char *termkey_get_keyname(TermKey *tk, TermKeySym sym) 1064 { 1065 if (sym == TERMKEY_SYM_UNKNOWN) { 1066 return "UNKNOWN"; 1067 } 1068 1069 if (sym < tk->nkeynames) { 1070 return tk->keynames[sym]; 1071 } 1072 1073 return "UNKNOWN"; 1074 } 1075 1076 static const char *termkey_lookup_keyname_format(TermKey *tk, const char *str, TermKeySym *sym, 1077 TermKeyFormat format) 1078 { 1079 // We store an array, so we can't do better than a linear search. Doesn't 1080 // matter because user won't be calling this too often 1081 1082 for (*sym = 0; *sym < tk->nkeynames; (*sym)++) { 1083 const char *thiskey = tk->keynames[*sym]; 1084 if (!thiskey) { 1085 continue; 1086 } 1087 size_t len = strlen(thiskey); 1088 if (format & TERMKEY_FORMAT_LOWERSPACE) { 1089 const char *thisstr = str; 1090 if (strpncmp_camel(&thisstr, &thiskey, len) == 0) { 1091 return thisstr; 1092 } 1093 } else { 1094 if (strncmp(str, thiskey, len) == 0) { 1095 return (char *)str + len; 1096 } 1097 } 1098 } 1099 1100 return NULL; 1101 } 1102 1103 const char *termkey_lookup_keyname(TermKey *tk, const char *str, TermKeySym *sym) 1104 { 1105 return termkey_lookup_keyname_format(tk, str, sym, 0); 1106 } 1107 1108 static TermKeySym register_c0(TermKey *tk, TermKeySym sym, unsigned char ctrl, const char *name) 1109 { 1110 return register_c0_full(tk, sym, 0, 0, ctrl, name); 1111 } 1112 1113 static TermKeySym register_c0_full(TermKey *tk, TermKeySym sym, int modifier_set, int modifier_mask, 1114 unsigned char ctrl, const char *name) 1115 { 1116 if (ctrl >= 0x20) { 1117 errno = EINVAL; 1118 return -1; 1119 } 1120 1121 if (name) { 1122 sym = termkey_register_keyname(tk, sym, name); 1123 } 1124 1125 tk->c0[ctrl].sym = sym; 1126 tk->c0[ctrl].modifier_set = modifier_set; 1127 tk->c0[ctrl].modifier_mask = modifier_mask; 1128 1129 return sym; 1130 } 1131 1132 static struct modnames { 1133 const char *shift, *alt, *ctrl; 1134 } 1135 modnames[] = { 1136 { "S", "A", "C" }, // 0 1137 { "Shift", "Alt", "Ctrl" }, // LONGMOD 1138 { "S", "M", "C" }, // ALTISMETA 1139 { "Shift", "Meta", "Ctrl" }, // ALTISMETA+LONGMOD 1140 { "s", "a", "c" }, // LOWERMOD 1141 { "shift", "alt", "ctrl" }, // LOWERMOD+LONGMOD 1142 { "s", "m", "c" }, // LOWERMOD+ALTISMETA 1143 { "shift", "meta", "ctrl" }, // LOWERMOD+ALTISMETA+LONGMOD 1144 }; 1145 1146 size_t termkey_strfkey(TermKey *tk, char *buffer, size_t len, TermKeyKey *key, TermKeyFormat format) 1147 { 1148 size_t pos = 0; 1149 size_t l = 0; 1150 1151 struct modnames *mods = &modnames[!!(format & TERMKEY_FORMAT_LONGMOD) + 1152 !!(format & TERMKEY_FORMAT_ALTISMETA) * 2 + 1153 !!(format & TERMKEY_FORMAT_LOWERMOD) * 4]; 1154 1155 int wrapbracket = (format & TERMKEY_FORMAT_WRAPBRACKET) 1156 && (key->type != TERMKEY_TYPE_UNICODE || key->modifiers != 0); 1157 1158 char sep = (format & TERMKEY_FORMAT_SPACEMOD) ? ' ' : '-'; 1159 1160 if (format & TERMKEY_FORMAT_CARETCTRL 1161 && key->type == TERMKEY_TYPE_UNICODE 1162 && key->modifiers == TERMKEY_KEYMOD_CTRL) { 1163 long codepoint = key->code.codepoint; 1164 1165 // Handle some of the special cases first 1166 if (codepoint >= 'a' && codepoint <= 'z') { 1167 l = (size_t)snprintf(buffer + pos, len - pos, wrapbracket ? "<^%c>" : "^%c", 1168 (char)codepoint - 0x20); 1169 if (l <= 0) { 1170 return pos; 1171 } 1172 pos += l; 1173 return pos; 1174 } else if ((codepoint >= '@' && codepoint < 'A') 1175 || (codepoint > 'Z' && codepoint <= '_')) { 1176 l = (size_t)snprintf(buffer + pos, len - pos, wrapbracket ? "<^%c>" : "^%c", (char)codepoint); 1177 if (l <= 0) { 1178 return pos; 1179 } 1180 pos += l; 1181 return pos; 1182 } 1183 } 1184 1185 if (wrapbracket) { 1186 l = (size_t)snprintf(buffer + pos, len - pos, "<"); 1187 if (l <= 0) { 1188 return pos; 1189 } 1190 pos += l; 1191 } 1192 1193 if (key->modifiers & TERMKEY_KEYMOD_ALT) { 1194 l = (size_t)snprintf(buffer + pos, len - pos, "%s%c", mods->alt, sep); 1195 if (l <= 0) { 1196 return pos; 1197 } 1198 pos += l; 1199 } 1200 1201 if (key->modifiers & TERMKEY_KEYMOD_CTRL) { 1202 l = (size_t)snprintf(buffer + pos, len - pos, "%s%c", mods->ctrl, sep); 1203 if (l <= 0) { 1204 return pos; 1205 } 1206 pos += l; 1207 } 1208 1209 if (key->modifiers & TERMKEY_KEYMOD_SHIFT) { 1210 l = (size_t)snprintf(buffer + pos, len - pos, "%s%c", mods->shift, sep); 1211 if (l <= 0) { 1212 return pos; 1213 } 1214 pos += l; 1215 } 1216 1217 switch (key->type) { 1218 case TERMKEY_TYPE_UNICODE: 1219 if (!key->utf8[0]) { // In case of user-supplied key structures 1220 fill_utf8(key->code.codepoint, key->utf8); 1221 } 1222 l = (size_t)snprintf(buffer + pos, len - pos, "%s", key->utf8); 1223 break; 1224 case TERMKEY_TYPE_KEYSYM: { 1225 const char *name = termkey_get_keyname(tk, key->code.sym); 1226 if (format & TERMKEY_FORMAT_LOWERSPACE) { 1227 l = (size_t)snprint_cameltospaces(buffer + pos, len - pos, name); 1228 } else { 1229 l = (size_t)snprintf(buffer + pos, len - pos, "%s", name); 1230 } 1231 } 1232 break; 1233 case TERMKEY_TYPE_FUNCTION: 1234 l = (size_t)snprintf(buffer + pos, len - pos, "%c%d", 1235 (format & TERMKEY_FORMAT_LOWERSPACE ? 'f' : 'F'), key->code.number); 1236 break; 1237 case TERMKEY_TYPE_MOUSE: { 1238 TermKeyMouseEvent ev; 1239 int button; 1240 int line, col; 1241 termkey_interpret_mouse(tk, key, &ev, &button, &line, &col); 1242 1243 l = (size_t)snprintf(buffer + pos, len - pos, "Mouse%s(%d)", 1244 evnames[ev], button); 1245 1246 if (format & TERMKEY_FORMAT_MOUSE_POS) { 1247 if (l <= 0) { 1248 return pos; 1249 } 1250 pos += l; 1251 1252 l = (size_t)snprintf(buffer + pos, len - pos, " @ (%u,%u)", col, line); 1253 } 1254 } 1255 break; 1256 case TERMKEY_TYPE_POSITION: 1257 l = (size_t)snprintf(buffer + pos, len - pos, "Position"); 1258 break; 1259 case TERMKEY_TYPE_MODEREPORT: { 1260 int initial, mode, value; 1261 termkey_interpret_modereport(tk, key, &initial, &mode, &value); 1262 if (initial) { 1263 l = (size_t)snprintf(buffer + pos, len - pos, "Mode(%c%d=%d)", initial, mode, value); 1264 } else { 1265 l = (size_t)snprintf(buffer + pos, len - pos, "Mode(%d=%d)", mode, value); 1266 } 1267 } 1268 break; 1269 case TERMKEY_TYPE_DCS: 1270 l = (size_t)snprintf(buffer + pos, len - pos, "DCS"); 1271 break; 1272 case TERMKEY_TYPE_OSC: 1273 l = (size_t)snprintf(buffer + pos, len - pos, "OSC"); 1274 break; 1275 case TERMKEY_TYPE_APC: 1276 l = (size_t)snprintf(buffer + pos, len - pos, "APC"); 1277 break; 1278 case TERMKEY_TYPE_UNKNOWN_CSI: 1279 l = (size_t)snprintf(buffer + pos, len - pos, "CSI %c", key->code.number & 0xff); 1280 break; 1281 } 1282 1283 if (l <= 0) { 1284 return pos; 1285 } 1286 pos += l; 1287 1288 if (wrapbracket) { 1289 l = (size_t)snprintf(buffer + pos, len - pos, ">"); 1290 if (l <= 0) { 1291 return pos; 1292 } 1293 pos += l; 1294 } 1295 1296 return pos; 1297 }