diff options
Diffstat (limited to '')
| -rw-r--r-- | boxes.c | 420 | ||||
| -rw-r--r-- | handlekeys.c | 419 | ||||
| -rw-r--r-- | handlekeys.h | 7 |
3 files changed, 432 insertions, 414 deletions
| @@ -33,12 +33,11 @@ config BOXES | |||
| 33 | */ | 33 | */ |
| 34 | 34 | ||
| 35 | #include "toys.h" | 35 | #include "toys.h" |
| 36 | #include "lib/handlekeys.h" | ||
| 36 | 37 | ||
| 37 | GLOBALS( | 38 | GLOBALS( |
| 38 | char *mode; | 39 | char *mode; |
| 39 | long h, w; | 40 | long h, w; |
| 40 | // TODO - actually, these should be globals in the library, and leave this buffer alone. | ||
| 41 | int stillRunning, overWriteMode; | ||
| 42 | ) | 41 | ) |
| 43 | 42 | ||
| 44 | #define TT this.boxes | 43 | #define TT this.boxes |
| @@ -296,181 +295,7 @@ sized morsels? | |||
| 296 | 295 | ||
| 297 | */ | 296 | */ |
| 298 | 297 | ||
| 299 | struct key | ||
| 300 | { | ||
| 301 | char *code; | ||
| 302 | char *name; | ||
| 303 | }; | ||
| 304 | |||
| 305 | // This table includes some variations I have found on some terminals, and the MC "Esc digit" versions. | ||
| 306 | // http://rtfm.etla.org/xterm/ctlseq.html has a useful guide. | ||
| 307 | // TODO - Don't think I got all the linux console variations. | ||
| 308 | // TODO - Add more shift variations, plus Ctrl & Alt variations when needed. | ||
| 309 | // TODO - tmux messes with the shift function keys somehow. | ||
| 310 | // TODO - Add other miscelany that does not use an escape sequence. | ||
| 311 | |||
| 312 | // This is sorted by type, though there is some overlap. | ||
| 313 | // Human typing speeds wont need binary searching speeds on this small table. | ||
| 314 | // So simple wins out over speed, and sorting by terminal type wins the simple test. | ||
| 315 | struct key keys[] = | ||
| 316 | { | ||
| 317 | // Control characters. | ||
| 318 | // {"\x00", "^@"}, // NUL Commented out coz it's the C string terminator, and may confuse things. | ||
| 319 | {"\x01", "^A"}, // SOH Apparently sometimes sent as Home | ||
| 320 | {"\x02", "^B"}, // STX | ||
| 321 | {"\x03", "^C"}, // ETX SIGINT Emacs and vi. | ||
| 322 | {"\x04", "^D"}, // EOT EOF Emacs, joe, and nano. | ||
| 323 | {"\x05", "^E"}, // ENQ Apparently sometimes sent as End | ||
| 324 | {"\x06", "^F"}, // ACK | ||
| 325 | {"\x07", "^G"}, // BEL | ||
| 326 | {"\x08", "Del"}, // BS Delete key, usually. | ||
| 327 | {"\x09", "Tab"}, // HT Tab key. | ||
| 328 | {"\x0A", "Return"}, // LF Return key. Roxterm at least is translating both Ctrl-J and Ctrl-M into this. | ||
| 329 | {"\x0B", "^K"}, // VT | ||
| 330 | {"\x0C", "^L"}, // FF | ||
| 331 | {"\x0D", "^M"}, // CR Other Return key, usually. | ||
| 332 | {"\x0E", "^N"}, // SO | ||
| 333 | {"\x0F", "^O"}, // SI DISCARD | ||
| 334 | {"\x10", "^P"}, // DLE | ||
| 335 | {"\x11", "^Q"}, // DC1 SIGCONT Vi, and made up commands in MC, which seem to work anyway. | ||
| 336 | {"\x12", "^R"}, // DC2 | ||
| 337 | {"\x13", "^S"}, // DC3 SIGSTOP can't be caught. Emacs and vi, so much for "can't be caught". | ||
| 338 | {"\x14", "^T"}, // DC4 SIGINFO STATUS | ||
| 339 | {"\x15", "^U"}, // NAK KILL character | ||
| 340 | {"\x16", "^V"}, // SYN LNEXT | ||
| 341 | {"\x17", "^W"}, // ETB WERASE | ||
| 342 | {"\x18", "^X"}, // CAN KILL character | ||
| 343 | {"\x19", "^Y"}, // EM DSUSP SIGTSTP | ||
| 344 | {"\x1A", "^Z"}, // SUB SIGTSTP | ||
| 345 | // {"\x1B", "^["}, // ESC Esc key. Commented out coz it's the ANSI start byte in the below multibyte keys. Handled in the code with a timeout. | ||
| 346 | {"\x1C", "^\\"}, // FS SIGQUIT Some say ^D is SIGQUIT, but my tests say it's this. | ||
| 347 | {"\x1D", "^]"}, // GS | ||
| 348 | {"\x1E", "^^"}, // RS | ||
| 349 | {"\x1F", "^_"}, // US | ||
| 350 | {"\x7F", "BS"}, // Backspace key, usually. Ctrl-? perhaps? | ||
| 351 | // {"\x9B", "CSI"}, // CSI The eight bit encoding of "Esc [". Commented out for the same reason Esc is. | ||
| 352 | |||
| 353 | // "Usual" xterm CSI sequences, with ";1" omitted for no modifiers. | ||
| 354 | // Even though we have a proper CSI parser, these should still be in this table. | ||
| 355 | // Coz we would need a table anyway in the CSI parser, so might as well keep them with the others. | ||
| 356 | // Also, less code, no need to have a separate scanner for that other table. | ||
| 357 | {"\x9B\x31~", "Home"}, // Duplicate, think I've seen this somewhere. | ||
| 358 | {"\x9B\x32~", "Ins"}, | ||
| 359 | {"\x9B\x33~", "Del"}, | ||
| 360 | {"\x9B\x34~", "End"}, // Duplicate, think I've seen this somewhere. | ||
| 361 | {"\x9B\x35~", "PgUp"}, | ||
| 362 | {"\x9B\x36~", "PgDn"}, | ||
| 363 | {"\x9B\x37~", "Home"}, | ||
| 364 | {"\x9B\x38~", "End"}, | ||
| 365 | {"\x9B\x31\x31~", "F1"}, | ||
| 366 | {"\x9B\x31\x32~", "F2"}, | ||
| 367 | {"\x9B\x31\x33~", "F3"}, | ||
| 368 | {"\x9B\x31\x34~", "F4"}, | ||
| 369 | {"\x9B\x31\x35~", "F5"}, | ||
| 370 | {"\x9B\x31\x37~", "F6"}, | ||
| 371 | {"\x9B\x31\x38~", "F7"}, | ||
| 372 | {"\x9B\x31\x39~", "F8"}, | ||
| 373 | {"\x9B\x32\x30~", "F9"}, | ||
| 374 | {"\x9B\x32\x31~", "F10"}, | ||
| 375 | {"\x9B\x32\x33~", "F11"}, | ||
| 376 | {"\x9B\x32\x34~", "F12"}, | ||
| 377 | |||
| 378 | // As above, ";2" means shift modifier. | ||
| 379 | {"\x9B\x31;2~", "Shift Home"}, | ||
| 380 | {"\x9B\x32;2~", "Shift Ins"}, | ||
| 381 | {"\x9B\x33;2~", "Shift Del"}, | ||
| 382 | {"\x9B\x34;2~", "Shift End"}, | ||
| 383 | {"\x9B\x35;2~", "Shift PgUp"}, | ||
| 384 | {"\x9B\x36;2~", "Shift PgDn"}, | ||
| 385 | {"\x9B\x37;2~", "Shift Home"}, | ||
| 386 | {"\x9B\x38;2~", "Shift End"}, | ||
| 387 | {"\x9B\x31\x31;2~", "Shift F1"}, | ||
| 388 | {"\x9B\x31\x32;2~", "Shift F2"}, | ||
| 389 | {"\x9B\x31\x33;2~", "Shift F3"}, | ||
| 390 | {"\x9B\x31\x34;2~", "Shift F4"}, | ||
| 391 | {"\x9B\x31\x35;2~", "Shift F5"}, | ||
| 392 | {"\x9B\x31\x37;2~", "Shift F6"}, | ||
| 393 | {"\x9B\x31\x38;2~", "Shift F7"}, | ||
| 394 | {"\x9B\x31\x39;2~", "Shift F8"}, | ||
| 395 | {"\x9B\x32\x30;2~", "Shift F9"}, | ||
| 396 | {"\x9B\x32\x31;2~", "Shift F10"}, | ||
| 397 | {"\x9B\x32\x33;2~", "Shift F11"}, | ||
| 398 | {"\x9B\x32\x34;2~", "Shift F12"}, | ||
| 399 | |||
| 400 | // "Normal" Some terminals are special, and it seems they only have four function keys. | ||
| 401 | {"\x9B\x41", "Up"}, | ||
| 402 | {"\x9B\x42", "Down"}, | ||
| 403 | {"\x9B\x43", "Right"}, | ||
| 404 | {"\x9B\x44", "Left"}, | ||
| 405 | {"\x9B\x46", "End"}, | ||
| 406 | {"\x9BH", "Home"}, | ||
| 407 | {"\x9BP", "F1"}, | ||
| 408 | {"\x9BQ", "F2"}, | ||
| 409 | {"\x9BR", "F3"}, | ||
| 410 | {"\x9BS", "F4"}, | ||
| 411 | {"\x9B\x31;2P", "Shift F1"}, | ||
| 412 | {"\x9B\x31;2Q", "Shift F2"}, | ||
| 413 | {"\x9B\x31;2R", "Shift F3"}, | ||
| 414 | {"\x9B\x31;2S", "Shift F4"}, | ||
| 415 | |||
| 416 | // "Application" Esc O is known as SS3 | ||
| 417 | {"\x1BOA", "Up"}, | ||
| 418 | {"\x1BOB", "Down"}, | ||
| 419 | {"\x1BOC", "Right"}, | ||
| 420 | {"\x1BOD", "Left"}, | ||
| 421 | {"\x1BOF", "End"}, | ||
| 422 | {"\x1BOH", "Home"}, | ||
| 423 | {"\x1BOn", "Del"}, | ||
| 424 | {"\x1BOp", "Ins"}, | ||
| 425 | {"\x1BOq", "End"}, | ||
| 426 | {"\x1BOw", "Home"}, | ||
| 427 | {"\x1BOP", "F1"}, | ||
| 428 | {"\x1BOO", "F2"}, | ||
| 429 | {"\x1BOR", "F3"}, | ||
| 430 | {"\x1BOS", "F4"}, | ||
| 431 | {"\x1BOT", "F5"}, | ||
| 432 | // These two conflict with the above four function key variations. | ||
| 433 | {"\x9BR", "F6"}, | ||
| 434 | {"\x9BS", "F7"}, | ||
| 435 | {"\x9BT", "F8"}, | ||
| 436 | {"\x9BU", "F9"}, | ||
| 437 | {"\x9BV", "F10"}, | ||
| 438 | {"\x9BW", "F11"}, | ||
| 439 | {"\x9BX", "F12"}, | ||
| 440 | |||
| 441 | // Can't remember, but saw them somewhere. | ||
| 442 | {"\x1BO1;2P", "Shift F1"}, | ||
| 443 | {"\x1BO1;2Q", "Shift F2"}, | ||
| 444 | {"\x1BO1;2R", "Shift F3"}, | ||
| 445 | {"\x1BO1;2S", "Shift F4"}, | ||
| 446 | |||
| 447 | // MC "Esc digit" specials. | ||
| 448 | // NOTE - The MC Esc variations might not be such a good idea, other programs want the Esc key for other things. | ||
| 449 | // Notably seems that "Esc somekey" is used in place of "Alt somekey" AKA "Meta somekey" coz apparently some OSes swallow those. | ||
| 450 | // Conversely, some terminals send "Esc somekey" when you do "Alt somekey". | ||
| 451 | // MC Esc variants might be used on Macs for other things? | ||
| 452 | {"\x1B\x31", "F1"}, | ||
| 453 | {"\x1B\x32", "F2"}, | ||
| 454 | {"\x1B\x33", "F3"}, | ||
| 455 | {"\x1B\x34", "F4"}, | ||
| 456 | {"\x1B\x35", "F5"}, | ||
| 457 | {"\x1B\x36", "F6"}, | ||
| 458 | {"\x1B\x37", "F7"}, | ||
| 459 | {"\x1B\x38", "F8"}, | ||
| 460 | {"\x1B\x39", "F9"}, | ||
| 461 | {"\x1B\x30", "F10"}, | ||
| 462 | |||
| 463 | /* TODO - Rob says - | ||
| 464 | ...you don't need a NULL terminator for | ||
| 465 | an array, you can do sizeof(table)/sizeof(*table). Divide the size of | ||
| 466 | the table (in bytes) by the size of a member of the table (in bytes) to | ||
| 467 | get the number of entries. | ||
| 468 | |||
| 469 | I should try that trick. Seems to work, let's do that everywhere. | ||
| 470 | */ | ||
| 471 | 298 | ||
| 472 | {NULL, NULL} | ||
| 473 | }; | ||
| 474 | 299 | ||
| 475 | char *borderChars[][6] = | 300 | char *borderChars[][6] = |
| 476 | { | 301 | { |
| @@ -653,6 +478,7 @@ void drawBox(box *box); | |||
| 653 | #define BOX_HSPLIT 1 // Marks if it's a horizontally or vertically split. | 478 | #define BOX_HSPLIT 1 // Marks if it's a horizontally or vertically split. |
| 654 | #define BOX_BORDER 2 // Mark if it has a border, often full screen boxes wont. | 479 | #define BOX_BORDER 2 // Mark if it has a border, often full screen boxes wont. |
| 655 | 480 | ||
| 481 | static int overWriteMode; | ||
| 656 | static box *rootBox; // Parent of the rest of the boxes, or the only box. Always a full screen. | 482 | static box *rootBox; // Parent of the rest of the boxes, or the only box. Always a full screen. |
| 657 | static box *currentBox; | 483 | static box *currentBox; |
| 658 | static view *commandLine; | 484 | static view *commandLine; |
| @@ -1709,7 +1535,7 @@ void deleteChar(view *view) | |||
| 1709 | // Only if there IS a next line. | 1535 | // Only if there IS a next line. |
| 1710 | if (&(view->content->lines) != view->line->next) | 1536 | if (&(view->content->lines) != view->line->next) |
| 1711 | { | 1537 | { |
| 1712 | mooshStrings(view->line, view->line->next->line, view->iX, 1, !TT.overWriteMode); | 1538 | mooshStrings(view->line, view->line->next->line, view->iX, 1, !overWriteMode); |
| 1713 | view->line->next->line = NULL; | 1539 | view->line->next->line = NULL; |
| 1714 | freeLine(view->content, view->line->next); | 1540 | freeLine(view->content, view->line->next); |
| 1715 | // TODO - should check if we are on the last page, then deal with scrolling. | 1541 | // TODO - should check if we are on the last page, then deal with scrolling. |
| @@ -1718,7 +1544,7 @@ void deleteChar(view *view) | |||
| 1718 | } | 1544 | } |
| 1719 | } | 1545 | } |
| 1720 | else | 1546 | else |
| 1721 | mooshStrings(view->line, NULL, view->iX, 1, !TT.overWriteMode); | 1547 | mooshStrings(view->line, NULL, view->iX, 1, !overWriteMode); |
| 1722 | } | 1548 | } |
| 1723 | 1549 | ||
| 1724 | void backSpaceChar(view *view) | 1550 | void backSpaceChar(view *view) |
| @@ -1775,7 +1601,7 @@ void executeLine(view *view) | |||
| 1775 | 1601 | ||
| 1776 | void quit(view *view) | 1602 | void quit(view *view) |
| 1777 | { | 1603 | { |
| 1778 | TT.stillRunning = 0; | 1604 | handle_keys_quit(); |
| 1779 | } | 1605 | } |
| 1780 | 1606 | ||
| 1781 | void nop(view *view) | 1607 | void nop(view *view) |
| @@ -1862,7 +1688,7 @@ static int handleKeySequence(long extra, char *sequence) | |||
| 1862 | { | 1688 | { |
| 1863 | // TODO - Should check for tabs to, and insert them. | 1689 | // TODO - Should check for tabs to, and insert them. |
| 1864 | // Though better off having a function for that? | 1690 | // Though better off having a function for that? |
| 1865 | mooshStrings(view->line, sequence, view->iX, 0, !TT.overWriteMode); | 1691 | mooshStrings(view->line, sequence, view->iX, 0, !overWriteMode); |
| 1866 | view->oW = formatLine(view, view->line->line, &(view->output)); | 1692 | view->oW = formatLine(view, view->line->line, &(view->output)); |
| 1867 | moveCursorRelative(view, strlen(sequence), 0, 0, 0); | 1693 | moveCursorRelative(view, strlen(sequence), 0, 0, 0); |
| 1868 | updateLine(view); | 1694 | updateLine(view); |
| @@ -1873,240 +1699,6 @@ static int handleKeySequence(long extra, char *sequence) | |||
| 1873 | } | 1699 | } |
| 1874 | 1700 | ||
| 1875 | 1701 | ||
| 1876 | static volatile sig_atomic_t sigWinch; | ||
| 1877 | |||
| 1878 | static void handleSignals(int signo) | ||
| 1879 | { | ||
| 1880 | sigWinch = 1; | ||
| 1881 | } | ||
| 1882 | |||
| 1883 | // Basically this is the main loop. | ||
| 1884 | |||
| 1885 | // TODO - Unhandled complications - | ||
| 1886 | // Less and more have the "ZZ" command, but nothing else seems to have multi ordinary character commands. | ||
| 1887 | |||
| 1888 | void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence), void (*handle_CSI)(long extra, char *command, int *params, int count)) | ||
| 1889 | { | ||
| 1890 | fd_set selectFds; | ||
| 1891 | struct timespec timeout; | ||
| 1892 | struct sigaction sigAction, oldSigAction; | ||
| 1893 | sigset_t signalMask; | ||
| 1894 | char buffer[20], sequence[20]; | ||
| 1895 | int buffIndex = 0; | ||
| 1896 | |||
| 1897 | buffer[0] = 0; | ||
| 1898 | sequence[0] = 0; | ||
| 1899 | |||
| 1900 | // Terminals send the SIGWINCH signal when they resize. | ||
| 1901 | memset(&sigAction, 0, sizeof(sigAction)); | ||
| 1902 | sigAction.sa_handler = handleSignals; | ||
| 1903 | sigAction.sa_flags = SA_RESTART;// Useless if we are using poll. | ||
| 1904 | if (sigaction(SIGWINCH, &sigAction, &oldSigAction)) perror_exit("can't set signal handler SIGWINCH"); | ||
| 1905 | sigemptyset(&signalMask); | ||
| 1906 | sigaddset(&signalMask, SIGWINCH); | ||
| 1907 | |||
| 1908 | // TODO - OS buffered keys might be a problem, but we can't do the usual timestamp filter for now. | ||
| 1909 | TT.stillRunning = 1; | ||
| 1910 | while (TT.stillRunning) | ||
| 1911 | { | ||
| 1912 | int j, p, csi = 0; | ||
| 1913 | |||
| 1914 | // Apparently it's more portable to reset these each time. | ||
| 1915 | FD_ZERO(&selectFds); | ||
| 1916 | FD_SET(0, &selectFds); | ||
| 1917 | timeout.tv_sec = 0; timeout.tv_nsec = 100000000; // One tenth of a second. | ||
| 1918 | |||
| 1919 | // TODO - A bit unstable at the moment, something makes it go into a horrid CPU eating edit line flicker mode sometimes. And / or vi mode can crash on exit (stack smash). | ||
| 1920 | // This might be fixed now. | ||
| 1921 | |||
| 1922 | // We got a "terminal size changed" signal, ask the terminal how big it is now. | ||
| 1923 | if (sigWinch) | ||
| 1924 | { | ||
| 1925 | // Send - save cursor position, down 999, right 999, request cursor position, restore cursor position. | ||
| 1926 | fputs("\x1B[s\x1B[999C\x1B[999B\x1B[6n\x1B[u", stdout); | ||
| 1927 | fflush(stdout); | ||
| 1928 | sigWinch = 0; | ||
| 1929 | } | ||
| 1930 | |||
| 1931 | // TODO - Should only ask for a time out after we get an Escape, or the user requested time ticks. | ||
| 1932 | // I wanted to use poll, but that would mean using ppoll, which is Linux only, and involves defining swear words to get it. | ||
| 1933 | p = pselect(0 + 1, &selectFds, NULL, NULL, &timeout, &signalMask); | ||
| 1934 | if (0 > p) | ||
| 1935 | { | ||
| 1936 | if (EINTR == errno) | ||
| 1937 | continue; | ||
| 1938 | perror_exit("poll"); | ||
| 1939 | } | ||
| 1940 | else if (0 == p) // A timeout, trigger a time event. | ||
| 1941 | { | ||
| 1942 | if ((0 == buffer[1]) && ('\x1B' == buffer[0])) | ||
| 1943 | { | ||
| 1944 | // After a short delay to check, this is a real Escape key, not part of an escape sequence, so deal with it. | ||
| 1945 | // TODO - so far the only uses of this have the escape at the start, but maybe a strcat is needed instead later? | ||
| 1946 | strcpy(sequence, "^["); | ||
| 1947 | buffer[0] = buffIndex = 0; | ||
| 1948 | } | ||
| 1949 | // TODO - Call some sort of timer tick callback. This wont be a precise timed event, but don't think we need one. | ||
| 1950 | } | ||
| 1951 | else if ((0 < p) && FD_ISSET(0, &selectFds)) | ||
| 1952 | { | ||
| 1953 | // I am assuming that we get the input atomically, each multibyte key fits neatly into one read. | ||
| 1954 | // If that's not true (which is entirely likely), then we have to get complicated with circular buffers and stuff, or just one byte at a time. | ||
| 1955 | j = read(0, &buffer[buffIndex], sizeof(buffer) - (buffIndex + 1)); | ||
| 1956 | if (j < 0) // An error happened. | ||
| 1957 | { | ||
| 1958 | // For now, just ignore errors. | ||
| 1959 | fprintf(stderr, "input error on %d\n", p); | ||
| 1960 | fflush(stderr); | ||
| 1961 | } | ||
| 1962 | else if (j == 0) // End of file. | ||
| 1963 | { | ||
| 1964 | TT.stillRunning = 0; | ||
| 1965 | fprintf(stderr, "EOF\n"); | ||
| 1966 | for (j = 0; buffer[j + 1]; j++) | ||
| 1967 | fprintf(stderr, "(%x), ", (int) buffer[j]); | ||
| 1968 | fflush(stderr); | ||
| 1969 | } | ||
| 1970 | else | ||
| 1971 | { | ||
| 1972 | buffIndex += j; | ||
| 1973 | if (sizeof(buffer) < (buffIndex + 1)) // Ran out of buffer. | ||
| 1974 | { | ||
| 1975 | fprintf(stderr, "Full buffer - %s -> %s\n", buffer, sequence); | ||
| 1976 | for (j = 0; buffer[j + 1]; j++) | ||
| 1977 | fprintf(stderr, "(%x) %c, ", (int) buffer[j], buffer[j]); | ||
| 1978 | fflush(stderr); | ||
| 1979 | buffIndex = 0; | ||
| 1980 | } | ||
| 1981 | buffer[buffIndex] = 0; | ||
| 1982 | } | ||
| 1983 | } | ||
| 1984 | |||
| 1985 | // Check if it's a CSI before we check for the known key sequences. | ||
| 1986 | if ('\x9B' == buffer[0]) | ||
| 1987 | csi = 1; | ||
| 1988 | if (('\x1B' == buffer[0]) && ('[' == buffer[1])) | ||
| 1989 | csi = 2; | ||
| 1990 | if (('\xC2' == buffer[0]) && ('\x9B' == buffer[1])) | ||
| 1991 | csi = 2; | ||
| 1992 | if (2 == csi) | ||
| 1993 | { | ||
| 1994 | buffer[0] = '\x9B'; | ||
| 1995 | for (j = 1; buffer[j]; j++) | ||
| 1996 | buffer[j] = buffer[j + 1]; | ||
| 1997 | buffIndex--; | ||
| 1998 | csi = 1; | ||
| 1999 | } | ||
| 2000 | |||
| 2001 | // Check for known key sequences. | ||
| 2002 | // For a real timeout checked Esc, buffer is now empty, so this for loop wont find it anyway. | ||
| 2003 | // While it's true we could avoid it by checking, the user already had to wait for a time out, and this loop wont take THAT long. | ||
| 2004 | for (j = 0; keys[j].code; j++) // Search for multibyte keys and control keys. | ||
| 2005 | { | ||
| 2006 | if (strcmp(keys[j].code, buffer) == 0) | ||
| 2007 | { | ||
| 2008 | strcat(sequence, keys[j].name); | ||
| 2009 | buffer[0] = buffIndex = 0; | ||
| 2010 | csi = 0; | ||
| 2011 | break; | ||
| 2012 | } | ||
| 2013 | } | ||
| 2014 | |||
| 2015 | // Find out if it's a CSI sequence that's not in the known key sequences. | ||
| 2016 | if (csi) | ||
| 2017 | { | ||
| 2018 | /* ECMA-048 section 5.2 defines this, and is unreadable. | ||
| 2019 | General CSI format - CSI [private] n1 ; n2 [extra] final | ||
| 2020 | private 0x3c to 0x3f "<=>?" if first byte is one of these, this is a private command, if it's one of the other n1 ones, it's not private. | ||
| 2021 | n1 0x30 to 0x3f "01234567890:;<=>?" ASCII digits forming a "number" | ||
| 2022 | 0x3a [:] used for floats, not expecting any. Could also be used as some other sort of inter digit separator. | ||
| 2023 | 0x3b [;] separates the parameters | ||
| 2024 | extra 0x20 to 0x2f [ !"#$%&'()*+,-./] Can be multiple, likely isn't. | ||
| 2025 | final 0x40 to 0x7e "@A .. Z[\]^_`a .. z{|}~" it's private if 0x70 to 0x7e "p .. z{|}~" | ||
| 2026 | Though the "private" ~ is used for key codes. | ||
| 2027 | We also have SS3 "\x1BO" for other keys, but that's not a CSI. | ||
| 2028 | C0 controls, DEL (0x7f), or high characters are undefined. | ||
| 2029 | TODO So abort the current CSI and start from scratch. | ||
| 2030 | */ | ||
| 2031 | |||
| 2032 | char *t, csFinal[8]; | ||
| 2033 | int csIndex = 1, csParams[8]; | ||
| 2034 | |||
| 2035 | csFinal[0] = 0; | ||
| 2036 | p = 0; | ||
| 2037 | // Unspecified params default to a value that is command dependant. | ||
| 2038 | // However, they will never be negative, so we can use -1 to flag a default value. | ||
| 2039 | for (j = 0; j < (sizeof(csParams) / sizeof(*csParams)); j++) | ||
| 2040 | csParams[j] = -1; | ||
| 2041 | |||
| 2042 | if ('M' == buffer[1]) | ||
| 2043 | { | ||
| 2044 | // TODO - We have a mouse report, which is CSI M ..., where the rest is binary encoded, more or less. Not fitting into the CSI format. | ||
| 2045 | } | ||
| 2046 | else | ||
| 2047 | { | ||
| 2048 | // Check for the private bit. | ||
| 2049 | if (index("<=>?", buffer[1])) | ||
| 2050 | { | ||
| 2051 | csFinal[0] = buffer[1]; | ||
| 2052 | csFinal[1] = 0; | ||
| 2053 | csIndex++; | ||
| 2054 | } | ||
| 2055 | |||
| 2056 | // Decode parameters. | ||
| 2057 | j = csIndex; | ||
| 2058 | do | ||
| 2059 | { | ||
| 2060 | // So we know when we get to the end of parameter space. | ||
| 2061 | t = index("01234567890:;<=>?", buffer[j + 1]); | ||
| 2062 | // See if we passed a paremeter. | ||
| 2063 | if ((';' == buffer[j]) || (!t)) | ||
| 2064 | { | ||
| 2065 | // Only stomp on the ; if it's really the ;. | ||
| 2066 | if (t) | ||
| 2067 | buffer[j] = 0; | ||
| 2068 | // Empty parameters are default parameters, so only deal with non defaults. | ||
| 2069 | if (';' != buffer[csIndex] || (!t)) | ||
| 2070 | { | ||
| 2071 | // TODO - Might be ":" in the number somewhere, but we are not expecting any in anything we do. | ||
| 2072 | csParams[p] = atoi(&buffer[csIndex]); | ||
| 2073 | } | ||
| 2074 | p++; | ||
| 2075 | csIndex = j + 1; | ||
| 2076 | } | ||
| 2077 | j++; | ||
| 2078 | } | ||
| 2079 | while (t); | ||
| 2080 | |||
| 2081 | // Get the final command sequence, and pass it to the callback. | ||
| 2082 | strcat(csFinal, &buffer[csIndex]); | ||
| 2083 | if (handle_CSI) | ||
| 2084 | handle_CSI(extra, csFinal, csParams, p); | ||
| 2085 | } | ||
| 2086 | |||
| 2087 | csi = 0; | ||
| 2088 | // Wether or not it's a CSI we understand, it's been handled either here or in the key sequence scanning above. | ||
| 2089 | buffer[0] = buffIndex = 0; | ||
| 2090 | } | ||
| 2091 | |||
| 2092 | // Pass the result to the callback. | ||
| 2093 | if ((handle_sequence) && (sequence[0] || buffer[0])) | ||
| 2094 | { | ||
| 2095 | char b[strlen(sequence) + strlen(buffer) + 1]; | ||
| 2096 | |||
| 2097 | sprintf(b, "%s%s", sequence, buffer); | ||
| 2098 | if (handle_sequence(extra, b)) | ||
| 2099 | { | ||
| 2100 | sequence[0] = 0; | ||
| 2101 | buffer[0] = buffIndex = 0; | ||
| 2102 | } | ||
| 2103 | } | ||
| 2104 | } | ||
| 2105 | |||
| 2106 | sigaction(SIGWINCH, &oldSigAction, NULL); | ||
| 2107 | } | ||
| 2108 | |||
| 2109 | |||
| 2110 | // The default command to function mappings, with help text. Any editor that does not have it's own commands can use these for keystroke binding and such. | 1702 | // The default command to function mappings, with help text. Any editor that does not have it's own commands can use these for keystroke binding and such. |
| 2111 | // Though most of the editors have their own variation. Maybe just use the joe one as default, it uses short names at least. | 1703 | // Though most of the editors have their own variation. Maybe just use the joe one as default, it uses short names at least. |
| 2112 | struct function simpleEditCommands[] = | 1704 | struct function simpleEditCommands[] = |
diff --git a/handlekeys.c b/handlekeys.c new file mode 100644 index 0000000..70ecfd7 --- /dev/null +++ b/handlekeys.c | |||
| @@ -0,0 +1,419 @@ | |||
| 1 | /* handlekeys.c - Generic terminal input handler. | ||
| 2 | * | ||
| 3 | * Copyright 2012 David Seikel <won_fang@yahoo.com.au> | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include "toys.h" | ||
| 7 | #include "handlekeys.h" | ||
| 8 | |||
| 9 | struct key | ||
| 10 | { | ||
| 11 | char *code; | ||
| 12 | char *name; | ||
| 13 | }; | ||
| 14 | |||
| 15 | // This table includes some variations I have found on some terminals, and the MC "Esc digit" versions. | ||
| 16 | // http://rtfm.etla.org/xterm/ctlseq.html has a useful guide. | ||
| 17 | // TODO - Don't think I got all the linux console variations. | ||
| 18 | // TODO - Add more shift variations, plus Ctrl & Alt variations when needed. | ||
| 19 | // TODO - tmux messes with the shift function keys somehow. | ||
| 20 | // TODO - Add other miscelany that does not use an escape sequence. | ||
| 21 | |||
| 22 | // This is sorted by type, though there is some overlap. | ||
| 23 | // Human typing speeds wont need binary searching speeds on this small table. | ||
| 24 | // So simple wins out over speed, and sorting by terminal type wins the simple test. | ||
| 25 | static struct key keys[] = | ||
| 26 | { | ||
| 27 | // Control characters. | ||
| 28 | // {"\x00", "^@"}, // NUL Commented out coz it's the C string terminator, and may confuse things. | ||
| 29 | {"\x01", "^A"}, // SOH Apparently sometimes sent as Home | ||
| 30 | {"\x02", "^B"}, // STX | ||
| 31 | {"\x03", "^C"}, // ETX SIGINT Emacs and vi. | ||
| 32 | {"\x04", "^D"}, // EOT EOF Emacs, joe, and nano. | ||
| 33 | {"\x05", "^E"}, // ENQ Apparently sometimes sent as End | ||
| 34 | {"\x06", "^F"}, // ACK | ||
| 35 | {"\x07", "^G"}, // BEL | ||
| 36 | {"\x08", "Del"}, // BS Delete key, usually. | ||
| 37 | {"\x09", "Tab"}, // HT Tab key. | ||
| 38 | {"\x0A", "Return"}, // LF Return key. Roxterm at least is translating both Ctrl-J and Ctrl-M into this. | ||
| 39 | {"\x0B", "^K"}, // VT | ||
| 40 | {"\x0C", "^L"}, // FF | ||
| 41 | {"\x0D", "^M"}, // CR Other Return key, usually. | ||
| 42 | {"\x0E", "^N"}, // SO | ||
| 43 | {"\x0F", "^O"}, // SI DISCARD | ||
| 44 | {"\x10", "^P"}, // DLE | ||
| 45 | {"\x11", "^Q"}, // DC1 SIGCONT Vi, and made up commands in MC, which seem to work anyway. | ||
| 46 | {"\x12", "^R"}, // DC2 | ||
| 47 | {"\x13", "^S"}, // DC3 SIGSTOP can't be caught. Emacs and vi, so much for "can't be caught". | ||
| 48 | {"\x14", "^T"}, // DC4 SIGINFO STATUS | ||
| 49 | {"\x15", "^U"}, // NAK KILL character | ||
| 50 | {"\x16", "^V"}, // SYN LNEXT | ||
| 51 | {"\x17", "^W"}, // ETB WERASE | ||
| 52 | {"\x18", "^X"}, // CAN KILL character | ||
| 53 | {"\x19", "^Y"}, // EM DSUSP SIGTSTP | ||
| 54 | {"\x1A", "^Z"}, // SUB SIGTSTP | ||
| 55 | // {"\x1B", "^["}, // ESC Esc key. Commented out coz it's the ANSI start byte in the below multibyte keys. Handled in the code with a timeout. | ||
| 56 | {"\x1C", "^\\"}, // FS SIGQUIT Some say ^D is SIGQUIT, but my tests say it's this. | ||
| 57 | {"\x1D", "^]"}, // GS | ||
| 58 | {"\x1E", "^^"}, // RS | ||
| 59 | {"\x1F", "^_"}, // US | ||
| 60 | {"\x7F", "BS"}, // Backspace key, usually. Ctrl-? perhaps? | ||
| 61 | // {"\x9B", "CSI"}, // CSI The eight bit encoding of "Esc [". Commented out for the same reason Esc is. | ||
| 62 | |||
| 63 | // "Usual" xterm CSI sequences, with ";1" omitted for no modifiers. | ||
| 64 | // Even though we have a proper CSI parser, these should still be in this table. | ||
| 65 | // Coz we would need a table anyway in the CSI parser, so might as well keep them with the others. | ||
| 66 | // Also, less code, no need to have a separate scanner for that other table. | ||
| 67 | {"\x9B\x31~", "Home"}, // Duplicate, think I've seen this somewhere. | ||
| 68 | {"\x9B\x32~", "Ins"}, | ||
| 69 | {"\x9B\x33~", "Del"}, | ||
| 70 | {"\x9B\x34~", "End"}, // Duplicate, think I've seen this somewhere. | ||
| 71 | {"\x9B\x35~", "PgUp"}, | ||
| 72 | {"\x9B\x36~", "PgDn"}, | ||
| 73 | {"\x9B\x37~", "Home"}, | ||
| 74 | {"\x9B\x38~", "End"}, | ||
| 75 | {"\x9B\x31\x31~", "F1"}, | ||
| 76 | {"\x9B\x31\x32~", "F2"}, | ||
| 77 | {"\x9B\x31\x33~", "F3"}, | ||
| 78 | {"\x9B\x31\x34~", "F4"}, | ||
| 79 | {"\x9B\x31\x35~", "F5"}, | ||
| 80 | {"\x9B\x31\x37~", "F6"}, | ||
| 81 | {"\x9B\x31\x38~", "F7"}, | ||
| 82 | {"\x9B\x31\x39~", "F8"}, | ||
| 83 | {"\x9B\x32\x30~", "F9"}, | ||
| 84 | {"\x9B\x32\x31~", "F10"}, | ||
| 85 | {"\x9B\x32\x33~", "F11"}, | ||
| 86 | {"\x9B\x32\x34~", "F12"}, | ||
| 87 | |||
| 88 | // As above, ";2" means shift modifier. | ||
| 89 | {"\x9B\x31;2~", "Shift Home"}, | ||
| 90 | {"\x9B\x32;2~", "Shift Ins"}, | ||
| 91 | {"\x9B\x33;2~", "Shift Del"}, | ||
| 92 | {"\x9B\x34;2~", "Shift End"}, | ||
| 93 | {"\x9B\x35;2~", "Shift PgUp"}, | ||
| 94 | {"\x9B\x36;2~", "Shift PgDn"}, | ||
| 95 | {"\x9B\x37;2~", "Shift Home"}, | ||
| 96 | {"\x9B\x38;2~", "Shift End"}, | ||
| 97 | {"\x9B\x31\x31;2~", "Shift F1"}, | ||
| 98 | {"\x9B\x31\x32;2~", "Shift F2"}, | ||
| 99 | {"\x9B\x31\x33;2~", "Shift F3"}, | ||
| 100 | {"\x9B\x31\x34;2~", "Shift F4"}, | ||
| 101 | {"\x9B\x31\x35;2~", "Shift F5"}, | ||
| 102 | {"\x9B\x31\x37;2~", "Shift F6"}, | ||
| 103 | {"\x9B\x31\x38;2~", "Shift F7"}, | ||
| 104 | {"\x9B\x31\x39;2~", "Shift F8"}, | ||
| 105 | {"\x9B\x32\x30;2~", "Shift F9"}, | ||
| 106 | {"\x9B\x32\x31;2~", "Shift F10"}, | ||
| 107 | {"\x9B\x32\x33;2~", "Shift F11"}, | ||
| 108 | {"\x9B\x32\x34;2~", "Shift F12"}, | ||
| 109 | |||
| 110 | // "Normal" Some terminals are special, and it seems they only have four function keys. | ||
| 111 | {"\x9B\x41", "Up"}, | ||
| 112 | {"\x9B\x42", "Down"}, | ||
| 113 | {"\x9B\x43", "Right"}, | ||
| 114 | {"\x9B\x44", "Left"}, | ||
| 115 | {"\x9B\x46", "End"}, | ||
| 116 | {"\x9BH", "Home"}, | ||
| 117 | {"\x9BP", "F1"}, | ||
| 118 | {"\x9BQ", "F2"}, | ||
| 119 | {"\x9BR", "F3"}, | ||
| 120 | {"\x9BS", "F4"}, | ||
| 121 | {"\x9B\x31;2P", "Shift F1"}, | ||
| 122 | {"\x9B\x31;2Q", "Shift F2"}, | ||
| 123 | {"\x9B\x31;2R", "Shift F3"}, | ||
| 124 | {"\x9B\x31;2S", "Shift F4"}, | ||
| 125 | |||
| 126 | // "Application" Esc O is known as SS3 | ||
| 127 | {"\x1BOA", "Up"}, | ||
| 128 | {"\x1BOB", "Down"}, | ||
| 129 | {"\x1BOC", "Right"}, | ||
| 130 | {"\x1BOD", "Left"}, | ||
| 131 | {"\x1BOF", "End"}, | ||
| 132 | {"\x1BOH", "Home"}, | ||
| 133 | {"\x1BOn", "Del"}, | ||
| 134 | {"\x1BOp", "Ins"}, | ||
| 135 | {"\x1BOq", "End"}, | ||
| 136 | {"\x1BOw", "Home"}, | ||
| 137 | {"\x1BOP", "F1"}, | ||
| 138 | {"\x1BOO", "F2"}, | ||
| 139 | {"\x1BOR", "F3"}, | ||
| 140 | {"\x1BOS", "F4"}, | ||
| 141 | {"\x1BOT", "F5"}, | ||
| 142 | // These two conflict with the above four function key variations. | ||
| 143 | {"\x9BR", "F6"}, | ||
| 144 | {"\x9BS", "F7"}, | ||
| 145 | {"\x9BT", "F8"}, | ||
| 146 | {"\x9BU", "F9"}, | ||
| 147 | {"\x9BV", "F10"}, | ||
| 148 | {"\x9BW", "F11"}, | ||
| 149 | {"\x9BX", "F12"}, | ||
| 150 | |||
| 151 | // Can't remember, but saw them somewhere. | ||
| 152 | {"\x1BO1;2P", "Shift F1"}, | ||
| 153 | {"\x1BO1;2Q", "Shift F2"}, | ||
| 154 | {"\x1BO1;2R", "Shift F3"}, | ||
| 155 | {"\x1BO1;2S", "Shift F4"}, | ||
| 156 | |||
| 157 | // MC "Esc digit" specials. | ||
| 158 | // NOTE - The MC Esc variations might not be such a good idea, other programs want the Esc key for other things. | ||
| 159 | // Notably seems that "Esc somekey" is used in place of "Alt somekey" AKA "Meta somekey" coz apparently some OSes swallow those. | ||
| 160 | // Conversely, some terminals send "Esc somekey" when you do "Alt somekey". | ||
| 161 | // MC Esc variants might be used on Macs for other things? | ||
| 162 | {"\x1B\x31", "F1"}, | ||
| 163 | {"\x1B\x32", "F2"}, | ||
| 164 | {"\x1B\x33", "F3"}, | ||
| 165 | {"\x1B\x34", "F4"}, | ||
| 166 | {"\x1B\x35", "F5"}, | ||
| 167 | {"\x1B\x36", "F6"}, | ||
| 168 | {"\x1B\x37", "F7"}, | ||
| 169 | {"\x1B\x38", "F8"}, | ||
| 170 | {"\x1B\x39", "F9"}, | ||
| 171 | {"\x1B\x30", "F10"}, | ||
| 172 | |||
| 173 | /* TODO - Rob says - | ||
| 174 | ...you don't need a NULL terminator for | ||
| 175 | an array, you can do sizeof(table)/sizeof(*table). Divide the size of | ||
| 176 | the table (in bytes) by the size of a member of the table (in bytes) to | ||
| 177 | get the number of entries. | ||
| 178 | |||
| 179 | I should try that trick. Seems to work, let's do that everywhere. | ||
| 180 | */ | ||
| 181 | |||
| 182 | {NULL, NULL} | ||
| 183 | }; | ||
| 184 | |||
| 185 | static volatile sig_atomic_t sigWinch; | ||
| 186 | static int stillRunning; | ||
| 187 | |||
| 188 | static void handleSignals(int signo) | ||
| 189 | { | ||
| 190 | sigWinch = 1; | ||
| 191 | } | ||
| 192 | |||
| 193 | // TODO - Unhandled complications - | ||
| 194 | // Less and more have the "ZZ" command, but nothing else seems to have multi ordinary character commands. | ||
| 195 | void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence), void (*handle_CSI)(long extra, char *command, int *params, int count)) | ||
| 196 | { | ||
| 197 | fd_set selectFds; | ||
| 198 | struct timespec timeout; | ||
| 199 | struct sigaction sigAction, oldSigAction; | ||
| 200 | sigset_t signalMask; | ||
| 201 | char buffer[20], sequence[20]; | ||
| 202 | int buffIndex = 0; | ||
| 203 | |||
| 204 | buffer[0] = 0; | ||
| 205 | sequence[0] = 0; | ||
| 206 | |||
| 207 | // Terminals send the SIGWINCH signal when they resize. | ||
| 208 | memset(&sigAction, 0, sizeof(sigAction)); | ||
| 209 | sigAction.sa_handler = handleSignals; | ||
| 210 | sigAction.sa_flags = SA_RESTART;// Useless if we are using poll. | ||
| 211 | if (sigaction(SIGWINCH, &sigAction, &oldSigAction)) perror_exit("can't set signal handler SIGWINCH"); | ||
| 212 | sigemptyset(&signalMask); | ||
| 213 | sigaddset(&signalMask, SIGWINCH); | ||
| 214 | |||
| 215 | // TODO - OS buffered keys might be a problem, but we can't do the usual timestamp filter for now. | ||
| 216 | stillRunning = 1; | ||
| 217 | while (stillRunning) | ||
| 218 | { | ||
| 219 | int j, p, csi = 0; | ||
| 220 | |||
| 221 | // Apparently it's more portable to reset these each time. | ||
| 222 | FD_ZERO(&selectFds); | ||
| 223 | FD_SET(0, &selectFds); | ||
| 224 | timeout.tv_sec = 0; timeout.tv_nsec = 100000000; // One tenth of a second. | ||
| 225 | |||
| 226 | // TODO - A bit unstable at the moment, something makes it go into a horrid CPU eating edit line flicker mode sometimes. And / or vi mode can crash on exit (stack smash). | ||
| 227 | // This might be fixed now. | ||
| 228 | |||
| 229 | // We got a "terminal size changed" signal, ask the terminal how big it is now. | ||
| 230 | if (sigWinch) | ||
| 231 | { | ||
| 232 | // Send - save cursor position, down 999, right 999, request cursor position, restore cursor position. | ||
| 233 | fputs("\x1B[s\x1B[999C\x1B[999B\x1B[6n\x1B[u", stdout); | ||
| 234 | fflush(stdout); | ||
| 235 | sigWinch = 0; | ||
| 236 | } | ||
| 237 | |||
| 238 | // TODO - Should only ask for a time out after we get an Escape, or the user requested time ticks. | ||
| 239 | // I wanted to use poll, but that would mean using ppoll, which is Linux only, and involves defining swear words to get it. | ||
| 240 | p = pselect(0 + 1, &selectFds, NULL, NULL, &timeout, &signalMask); | ||
| 241 | if (0 > p) | ||
| 242 | { | ||
| 243 | if (EINTR == errno) | ||
| 244 | continue; | ||
| 245 | perror_exit("poll"); | ||
| 246 | } | ||
| 247 | else if (0 == p) // A timeout, trigger a time event. | ||
| 248 | { | ||
| 249 | if ((0 == buffer[1]) && ('\x1B' == buffer[0])) | ||
| 250 | { | ||
| 251 | // After a short delay to check, this is a real Escape key, not part of an escape sequence, so deal with it. | ||
| 252 | // TODO - so far the only uses of this have the escape at the start, but maybe a strcat is needed instead later? | ||
| 253 | strcpy(sequence, "^["); | ||
| 254 | buffer[0] = buffIndex = 0; | ||
| 255 | } | ||
| 256 | // TODO - Call some sort of timer tick callback. This wont be a precise timed event, but don't think we need one. | ||
| 257 | } | ||
| 258 | else if ((0 < p) && FD_ISSET(0, &selectFds)) | ||
| 259 | { | ||
| 260 | // I am assuming that we get the input atomically, each multibyte key fits neatly into one read. | ||
| 261 | // If that's not true (which is entirely likely), then we have to get complicated with circular buffers and stuff, or just one byte at a time. | ||
| 262 | j = read(0, &buffer[buffIndex], sizeof(buffer) - (buffIndex + 1)); | ||
| 263 | if (j < 0) // An error happened. | ||
| 264 | { | ||
| 265 | // For now, just ignore errors. | ||
| 266 | fprintf(stderr, "input error on %d\n", p); | ||
| 267 | fflush(stderr); | ||
| 268 | } | ||
| 269 | else if (j == 0) // End of file. | ||
| 270 | { | ||
| 271 | stillRunning = 0; | ||
| 272 | fprintf(stderr, "EOF\n"); | ||
| 273 | for (j = 0; buffer[j + 1]; j++) | ||
| 274 | fprintf(stderr, "(%x), ", (int) buffer[j]); | ||
| 275 | fflush(stderr); | ||
| 276 | } | ||
| 277 | else | ||
| 278 | { | ||
| 279 | buffIndex += j; | ||
| 280 | if (sizeof(buffer) < (buffIndex + 1)) // Ran out of buffer. | ||
| 281 | { | ||
| 282 | fprintf(stderr, "Full buffer - %s -> %s\n", buffer, sequence); | ||
| 283 | for (j = 0; buffer[j + 1]; j++) | ||
| 284 | fprintf(stderr, "(%x) %c, ", (int) buffer[j], buffer[j]); | ||
| 285 | fflush(stderr); | ||
| 286 | buffIndex = 0; | ||
| 287 | } | ||
| 288 | buffer[buffIndex] = 0; | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | // Check if it's a CSI before we check for the known key sequences. | ||
| 293 | if ('\x9B' == buffer[0]) | ||
| 294 | csi = 1; | ||
| 295 | if (('\x1B' == buffer[0]) && ('[' == buffer[1])) | ||
| 296 | csi = 2; | ||
| 297 | if (('\xC2' == buffer[0]) && ('\x9B' == buffer[1])) | ||
| 298 | csi = 2; | ||
| 299 | if (2 == csi) | ||
| 300 | { | ||
| 301 | buffer[0] = '\x9B'; | ||
| 302 | for (j = 1; buffer[j]; j++) | ||
| 303 | buffer[j] = buffer[j + 1]; | ||
| 304 | buffIndex--; | ||
| 305 | csi = 1; | ||
| 306 | } | ||
| 307 | |||
| 308 | // Check for known key sequences. | ||
| 309 | // For a real timeout checked Esc, buffer is now empty, so this for loop wont find it anyway. | ||
| 310 | // While it's true we could avoid it by checking, the user already had to wait for a time out, and this loop wont take THAT long. | ||
| 311 | for (j = 0; keys[j].code; j++) // Search for multibyte keys and control keys. | ||
| 312 | { | ||
| 313 | if (strcmp(keys[j].code, buffer) == 0) | ||
| 314 | { | ||
| 315 | strcat(sequence, keys[j].name); | ||
| 316 | buffer[0] = buffIndex = 0; | ||
| 317 | csi = 0; | ||
| 318 | break; | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | // Find out if it's a CSI sequence that's not in the known key sequences. | ||
| 323 | if (csi) | ||
| 324 | { | ||
| 325 | /* ECMA-048 section 5.2 defines this, and is unreadable. | ||
| 326 | General CSI format - CSI [private] n1 ; n2 [extra] final | ||
| 327 | private 0x3c to 0x3f "<=>?" if first byte is one of these, this is a private command, if it's one of the other n1 ones, it's not private. | ||
| 328 | n1 0x30 to 0x3f "01234567890:;<=>?" ASCII digits forming a "number" | ||
| 329 | 0x3a [:] used for floats, not expecting any. Could also be used as some other sort of inter digit separator. | ||
| 330 | 0x3b [;] separates the parameters | ||
| 331 | extra 0x20 to 0x2f [ !"#$%&'()*+,-./] Can be multiple, likely isn't. | ||
| 332 | final 0x40 to 0x7e "@A .. Z[\]^_`a .. z{|}~" it's private if 0x70 to 0x7e "p .. z{|}~" | ||
| 333 | Though the "private" ~ is used for key codes. | ||
| 334 | We also have SS3 "\x1BO" for other keys, but that's not a CSI. | ||
| 335 | C0 controls, DEL (0x7f), or high characters are undefined. | ||
| 336 | TODO So abort the current CSI and start from scratch. | ||
| 337 | */ | ||
| 338 | |||
| 339 | char *t, csFinal[8]; | ||
| 340 | int csIndex = 1, csParams[8]; | ||
| 341 | |||
| 342 | csFinal[0] = 0; | ||
| 343 | p = 0; | ||
| 344 | // Unspecified params default to a value that is command dependant. | ||
| 345 | // However, they will never be negative, so we can use -1 to flag a default value. | ||
| 346 | for (j = 0; j < (sizeof(csParams) / sizeof(*csParams)); j++) | ||
| 347 | csParams[j] = -1; | ||
| 348 | |||
| 349 | if ('M' == buffer[1]) | ||
| 350 | { | ||
| 351 | // TODO - We have a mouse report, which is CSI M ..., where the rest is binary encoded, more or less. Not fitting into the CSI format. | ||
| 352 | } | ||
| 353 | else | ||
| 354 | { | ||
| 355 | // Check for the private bit. | ||
| 356 | if (index("<=>?", buffer[1])) | ||
| 357 | { | ||
| 358 | csFinal[0] = buffer[1]; | ||
| 359 | csFinal[1] = 0; | ||
| 360 | csIndex++; | ||
| 361 | } | ||
| 362 | |||
| 363 | // Decode parameters. | ||
| 364 | j = csIndex; | ||
| 365 | do | ||
| 366 | { | ||
| 367 | // So we know when we get to the end of parameter space. | ||
| 368 | t = index("01234567890:;<=>?", buffer[j + 1]); | ||
| 369 | // See if we passed a paremeter. | ||
| 370 | if ((';' == buffer[j]) || (!t)) | ||
| 371 | { | ||
| 372 | // Only stomp on the ; if it's really the ;. | ||
| 373 | if (t) | ||
| 374 | buffer[j] = 0; | ||
| 375 | // Empty parameters are default parameters, so only deal with non defaults. | ||
| 376 | if (';' != buffer[csIndex] || (!t)) | ||
| 377 | { | ||
| 378 | // TODO - Might be ":" in the number somewhere, but we are not expecting any in anything we do. | ||
| 379 | csParams[p] = atoi(&buffer[csIndex]); | ||
| 380 | } | ||
| 381 | p++; | ||
| 382 | csIndex = j + 1; | ||
| 383 | } | ||
| 384 | j++; | ||
| 385 | } | ||
| 386 | while (t); | ||
| 387 | |||
| 388 | // Get the final command sequence, and pass it to the callback. | ||
| 389 | strcat(csFinal, &buffer[csIndex]); | ||
| 390 | if (handle_CSI) | ||
| 391 | handle_CSI(extra, csFinal, csParams, p); | ||
| 392 | } | ||
| 393 | |||
| 394 | csi = 0; | ||
| 395 | // Wether or not it's a CSI we understand, it's been handled either here or in the key sequence scanning above. | ||
| 396 | buffer[0] = buffIndex = 0; | ||
| 397 | } | ||
| 398 | |||
| 399 | // Pass the result to the callback. | ||
| 400 | if ((handle_sequence) && (sequence[0] || buffer[0])) | ||
| 401 | { | ||
| 402 | char b[strlen(sequence) + strlen(buffer) + 1]; | ||
| 403 | |||
| 404 | sprintf(b, "%s%s", sequence, buffer); | ||
| 405 | if (handle_sequence(extra, b)) | ||
| 406 | { | ||
| 407 | sequence[0] = 0; | ||
| 408 | buffer[0] = buffIndex = 0; | ||
| 409 | } | ||
| 410 | } | ||
| 411 | } | ||
| 412 | |||
| 413 | sigaction(SIGWINCH, &oldSigAction, NULL); | ||
| 414 | } | ||
| 415 | |||
| 416 | void handle_keys_quit() | ||
| 417 | { | ||
| 418 | stillRunning = 0; | ||
| 419 | } | ||
diff --git a/handlekeys.h b/handlekeys.h new file mode 100644 index 0000000..c12c290 --- /dev/null +++ b/handlekeys.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | /* handlekeys.h - Generic terminal input handler. | ||
| 2 | * | ||
| 3 | * Copyright 2012 David Seikel <won_fang@yahoo.com.au> | ||
| 4 | */ | ||
| 5 | |||
| 6 | void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence), void (*handle_CSI)(long extra, char *command, int *params, int count)); | ||
| 7 | void handle_keys_quit(); | ||
