aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDavid Walter Seikel2014-01-30 19:10:24 +1000
committerDavid Walter Seikel2014-01-30 19:10:24 +1000
commit3fe934b59ff383e6d0369fdd7e1e7202dfb422ea (patch)
tree54eb4b953c41fe6528c26c0a9c21792aedaae923
parentI have no idea how my email address has been wrong for two years. lol (diff)
downloadboxes-3fe934b59ff383e6d0369fdd7e1e7202dfb422ea.zip
boxes-3fe934b59ff383e6d0369fdd7e1e7202dfb422ea.tar.gz
boxes-3fe934b59ff383e6d0369fdd7e1e7202dfb422ea.tar.bz2
boxes-3fe934b59ff383e6d0369fdd7e1e7202dfb422ea.tar.xz
Move handle_keys and friends into it's own file, for the library.
-rw-r--r--boxes.c420
-rw-r--r--handlekeys.c419
-rw-r--r--handlekeys.h7
3 files changed, 432 insertions, 414 deletions
diff --git a/boxes.c b/boxes.c
index f3c35d9..bc7b6b9 100644
--- a/boxes.c
+++ b/boxes.c
@@ -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
37GLOBALS( 38GLOBALS(
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
299struct 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.
315struct 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
465an array, you can do sizeof(table)/sizeof(*table). Divide the size of
466the table (in bytes) by the size of a member of the table (in bytes) to
467get the number of entries.
468
469I should try that trick. Seems to work, let's do that everywhere.
470*/
471 298
472 {NULL, NULL}
473};
474 299
475char *borderChars[][6] = 300char *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
481static int overWriteMode;
656static box *rootBox; // Parent of the rest of the boxes, or the only box. Always a full screen. 482static box *rootBox; // Parent of the rest of the boxes, or the only box. Always a full screen.
657static box *currentBox; 483static box *currentBox;
658static view *commandLine; 484static 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
1724void backSpaceChar(view *view) 1550void backSpaceChar(view *view)
@@ -1775,7 +1601,7 @@ void executeLine(view *view)
1775 1601
1776void quit(view *view) 1602void quit(view *view)
1777{ 1603{
1778 TT.stillRunning = 0; 1604 handle_keys_quit();
1779} 1605}
1780 1606
1781void nop(view *view) 1607void 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
1876static volatile sig_atomic_t sigWinch;
1877
1878static 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
1888void 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.
2029TODO 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.
2112struct function simpleEditCommands[] = 1704struct 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
9struct 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.
25static 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
175an array, you can do sizeof(table)/sizeof(*table). Divide the size of
176the table (in bytes) by the size of a member of the table (in bytes) to
177get the number of entries.
178
179I should try that trick. Seems to work, let's do that everywhere.
180*/
181
182 {NULL, NULL}
183};
184
185static volatile sig_atomic_t sigWinch;
186static int stillRunning;
187
188static 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.
195void 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.
336TODO 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
416void 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
6void handle_keys(long extra, int (*handle_sequence)(long extra, char *sequence), void (*handle_CSI)(long extra, char *command, int *params, int count));
7void handle_keys_quit();