diff options
-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(); | ||