diff options
Diffstat (limited to 'boxes.c')
-rw-r--r-- | boxes.c | 286 |
1 files changed, 210 insertions, 76 deletions
@@ -309,7 +309,6 @@ struct key | |||
309 | // TODO - Add more shift variations, plus Ctrl & Alt variations when needed. | 309 | // TODO - Add more shift variations, plus Ctrl & Alt variations when needed. |
310 | // TODO - tmux messes with the shift function keys somehow. | 310 | // TODO - tmux messes with the shift function keys somehow. |
311 | // TODO - Add other miscelany that does not use an escape sequence. | 311 | // TODO - Add other miscelany that does not use an escape sequence. |
312 | // TODO - maybe worth writing a proper CSI parse instead. Would be useful for terminal size and mouse reports. | ||
313 | 312 | ||
314 | // This is sorted by type, though there is some overlap. | 313 | // This is sorted by type, though there is some overlap. |
315 | // Human typing speeds wont need binary searching speeds on this small table. | 314 | // Human typing speeds wont need binary searching speeds on this small table. |
@@ -353,64 +352,67 @@ struct key keys[] = | |||
353 | {"\x9B", "CSI"}, // CSI The eight bit encoding of "Esc [". | 352 | {"\x9B", "CSI"}, // CSI The eight bit encoding of "Esc [". |
354 | 353 | ||
355 | // "Usual" xterm CSI sequences, with ";1" omitted for no modifiers. | 354 | // "Usual" xterm CSI sequences, with ";1" omitted for no modifiers. |
356 | {"\x1B[1~", "Home"}, // Duplicate, think I've seen this somewhere. | 355 | // Even though we have a proper CSI parser, these should still be in this table. |
357 | {"\x1B[2~", "Ins"}, | 356 | // Coz we would need a table anyway in the CSI parser, so might as well keep them with the others. |
358 | {"\x1B[3~", "Del"}, | 357 | // Also, less code, no need to have a separate scanner for that other table. |
359 | {"\x1B[4~", "End"}, // Duplicate, think I've seen this somewhere. | 358 | {"\x9B\x31~", "Home"}, // Duplicate, think I've seen this somewhere. |
360 | {"\x1B[5~", "PgUp"}, | 359 | {"\x9B\x32~", "Ins"}, |
361 | {"\x1B[6~", "PgDn"}, | 360 | {"\x9B\x33~", "Del"}, |
362 | {"\x1B[7~", "Home"}, | 361 | {"\x9B\x34~", "End"}, // Duplicate, think I've seen this somewhere. |
363 | {"\x1B[8~", "End"}, | 362 | {"\x9B\x35~", "PgUp"}, |
364 | {"\x1B[11~", "F1"}, | 363 | {"\x9B\x36~", "PgDn"}, |
365 | {"\x1B[12~", "F2"}, | 364 | {"\x9B\x37~", "Home"}, |
366 | {"\x1B[13~", "F3"}, | 365 | {"\x9B\x38~", "End"}, |
367 | {"\x1B[14~", "F4"}, | 366 | {"\x9B\x31\x31~", "F1"}, |
368 | {"\x1B[15~", "F5"}, | 367 | {"\x9B\x31\x32~", "F2"}, |
369 | {"\x1B[17~", "F6"}, | 368 | {"\x9B\x31\x33~", "F3"}, |
370 | {"\x1B[18~", "F7"}, | 369 | {"\x9B\x31\x34~", "F4"}, |
371 | {"\x1B[19~", "F8"}, | 370 | {"\x9B\x31\x35~", "F5"}, |
372 | {"\x1B[20~", "F9"}, | 371 | {"\x9B\x31\x37~", "F6"}, |
373 | {"\x1B[21~", "F10"}, | 372 | {"\x9B\x31\x38~", "F7"}, |
374 | {"\x1B[23~", "F11"}, | 373 | {"\x9B\x31\x39~", "F8"}, |
375 | {"\x1B[24~", "F12"}, | 374 | {"\x9B\x32\x30~", "F9"}, |
375 | {"\x9B\x32\x31~", "F10"}, | ||
376 | {"\x9B\x32\x33~", "F11"}, | ||
377 | {"\x9B\x32\x34~", "F12"}, | ||
376 | 378 | ||
377 | // As above, ";2" means shift modifier. | 379 | // As above, ";2" means shift modifier. |
378 | {"\x1B[1;2~", "Shift Home"}, | 380 | {"\x9B\x31;2~", "Shift Home"}, |
379 | {"\x1B[2;2~", "Shift Ins"}, | 381 | {"\x9B\x32;2~", "Shift Ins"}, |
380 | {"\x1B[3;2~", "Shift Del"}, | 382 | {"\x9B\x33;2~", "Shift Del"}, |
381 | {"\x1B[4;2~", "Shift End"}, | 383 | {"\x9B\x34;2~", "Shift End"}, |
382 | {"\x1B[5;2~", "Shift PgUp"}, | 384 | {"\x9B\x35;2~", "Shift PgUp"}, |
383 | {"\x1B[6;2~", "Shift PgDn"}, | 385 | {"\x9B\x36;2~", "Shift PgDn"}, |
384 | {"\x1B[7;2~", "Shift Home"}, | 386 | {"\x9B\x37;2~", "Shift Home"}, |
385 | {"\x1B[8;2~", "Shift End"}, | 387 | {"\x9B\x38;2~", "Shift End"}, |
386 | {"\x1B[11;2~", "Shift F1"}, | 388 | {"\x9B\x31\x31;2~", "Shift F1"}, |
387 | {"\x1B[12;2~", "Shift F2"}, | 389 | {"\x9B\x31\x32;2~", "Shift F2"}, |
388 | {"\x1B[13;2~", "Shift F3"}, | 390 | {"\x9B\x31\x33;2~", "Shift F3"}, |
389 | {"\x1B[14;2~", "Shift F4"}, | 391 | {"\x9B\x31\x34;2~", "Shift F4"}, |
390 | {"\x1B[15;2~", "Shift F5"}, | 392 | {"\x9B\x31\x35;2~", "Shift F5"}, |
391 | {"\x1B[17;2~", "Shift F6"}, | 393 | {"\x9B\x31\x37;2~", "Shift F6"}, |
392 | {"\x1B[18;2~", "Shift F7"}, | 394 | {"\x9B\x31\x38;2~", "Shift F7"}, |
393 | {"\x1B[19;2~", "Shift F8"}, | 395 | {"\x9B\x31\x39;2~", "Shift F8"}, |
394 | {"\x1B[20;2~", "Shift F9"}, | 396 | {"\x9B\x32\x30;2~", "Shift F9"}, |
395 | {"\x1B[21;2~", "Shift F10"}, | 397 | {"\x9B\x32\x31;2~", "Shift F10"}, |
396 | {"\x1B[23;2~", "Shift F11"}, | 398 | {"\x9B\x32\x33;2~", "Shift F11"}, |
397 | {"\x1B[24;2~", "Shift F12"}, | 399 | {"\x9B\x32\x34;2~", "Shift F12"}, |
398 | 400 | ||
399 | // "Normal" Some terminals are special, and it seems they only have four function keys. | 401 | // "Normal" Some terminals are special, and it seems they only have four function keys. |
400 | {"\x1B[A", "Up"}, | 402 | {"\x9B\x41", "Up"}, |
401 | {"\x1B[B", "Down"}, | 403 | {"\x9B\x42", "Down"}, |
402 | {"\x1B[C", "Right"}, | 404 | {"\x9B\x43", "Right"}, |
403 | {"\x1B[D", "Left"}, | 405 | {"\x9B\x44", "Left"}, |
404 | {"\x1B[F", "End"}, | 406 | {"\x9B\x46", "End"}, |
405 | {"\x1B[H", "Home"}, | 407 | {"\x9BH", "Home"}, |
406 | {"\x1B[P", "F1"}, | 408 | {"\x9BP", "F1"}, |
407 | {"\x1B[Q", "F2"}, | 409 | {"\x9BQ", "F2"}, |
408 | {"\x1B[R", "F3"}, | 410 | {"\x9BR", "F3"}, |
409 | {"\x1B[S", "F4"}, | 411 | {"\x9BS", "F4"}, |
410 | {"\x1B[1;2P", "Shift F1"}, | 412 | {"\x9B\x31;2P", "Shift F1"}, |
411 | {"\x1B[1;2Q", "Shift F2"}, | 413 | {"\x9B\x31;2Q", "Shift F2"}, |
412 | {"\x1B[1;2R", "Shift F3"}, | 414 | {"\x9B\x31;2R", "Shift F3"}, |
413 | {"\x1B[1;2S", "Shift F4"}, | 415 | {"\x9B\x31;2S", "Shift F4"}, |
414 | 416 | ||
415 | // "Application" Esc O is known as SS3 | 417 | // "Application" Esc O is known as SS3 |
416 | {"\x1BOA", "Up"}, | 418 | {"\x1BOA", "Up"}, |
@@ -429,13 +431,13 @@ struct key keys[] = | |||
429 | {"\x1BOS", "F4"}, | 431 | {"\x1BOS", "F4"}, |
430 | {"\x1BOT", "F5"}, | 432 | {"\x1BOT", "F5"}, |
431 | // These two conflict with the above four function key variations. | 433 | // These two conflict with the above four function key variations. |
432 | {"\x1B[R", "F6"}, | 434 | {"\x9BR", "F6"}, |
433 | {"\x1B[S", "F7"}, | 435 | {"\x9BS", "F7"}, |
434 | {"\x1B[T", "F8"}, | 436 | {"\x9BT", "F8"}, |
435 | {"\x1B[U", "F9"}, | 437 | {"\x9BU", "F9"}, |
436 | {"\x1B[V", "F10"}, | 438 | {"\x9BV", "F10"}, |
437 | {"\x1B[W", "F11"}, | 439 | {"\x9BW", "F11"}, |
438 | {"\x1B[X", "F12"}, | 440 | {"\x9BX", "F12"}, |
439 | 441 | ||
440 | // Can't remember, but saw them somewhere. | 442 | // Can't remember, but saw them somewhere. |
441 | {"\x1BO1;2P", "Shift F1"}, | 443 | {"\x1BO1;2P", "Shift F1"}, |
@@ -493,6 +495,7 @@ typedef struct _view view; | |||
493 | 495 | ||
494 | typedef void (*boxFunction) (box *box); | 496 | typedef void (*boxFunction) (box *box); |
495 | typedef void (*eventHandler) (view *view); | 497 | typedef void (*eventHandler) (view *view); |
498 | typedef char * (*CSIhandler) (long extra, int *code, int count); | ||
496 | 499 | ||
497 | struct function | 500 | struct function |
498 | { | 501 | { |
@@ -1819,23 +1822,46 @@ Less and more have the "ZZ" command, but nothing else seems to have multi ordina | |||
1819 | Some editors have a shortcut command concept. The smallest unique first part of each command will match, as well as anything longer. | 1822 | Some editors have a shortcut command concept. The smallest unique first part of each command will match, as well as anything longer. |
1820 | A further complication is if we are not implementing some commands that might change what is "shortest unique prefix". | 1823 | A further complication is if we are not implementing some commands that might change what is "shortest unique prefix". |
1821 | 1824 | ||
1822 | The response from a terminal size check command includes a prefix, a suffix, with the data in the middle. | ||
1823 | send "\x1B[s\x1B[999C\x1B[999B\x1B[6n\x1B[u" | ||
1824 | Which breaks down to - save cursor position, down 999, right 999, request cursor position (DSR), restore cursor position. | ||
1825 | response is "\x1B[ ; R", where the spaces are replaced by digits LINES and COLUMNS respectively. | 1825 | response is "\x1B[ ; R", where the spaces are replaced by digits LINES and COLUMNS respectively. |
1826 | And just to screw things up - {"\x1B[1;2R", "Shift F3"}, though no one's going to have a 1 x 2 terminal. | ||
1827 | |||
1828 | Mouse events are likely similar. | ||
1829 | */ | 1826 | */ |
1827 | |||
1828 | |||
1829 | char *termSize(long extra, int *params, int count) | ||
1830 | { | ||
1831 | int r = params[0], c = params[1]; | ||
1832 | |||
1833 | // TODO - Deal with defaults, though perhaps this wont ever send defaults? | ||
1834 | // The heuristic below ignores defaults. | ||
1835 | // Check it's not an F3 key variation, coz some of them use the same CSI function code. | ||
1836 | // This is a heuristic, we are checking against an unusable terminal size. | ||
1837 | // TODO - Double check what the maximum F3 variations can be. | ||
1838 | if ((2 == count) && (8 < r) && (8 < c)) | ||
1839 | { | ||
1840 | // TODO - We got a valid terminal size response, do something with it. | ||
1841 | } | ||
1842 | |||
1843 | return NULL; | ||
1844 | } | ||
1845 | |||
1846 | struct CSI | ||
1847 | { | ||
1848 | char *code; | ||
1849 | CSIhandler func; | ||
1850 | }; | ||
1851 | |||
1852 | struct CSI CSI_terminators[] = | ||
1853 | { | ||
1854 | {"R", termSize}, | ||
1855 | {NULL, NULL} | ||
1856 | }; | ||
1857 | |||
1830 | void editLine(long extra, void (*lineChar)(long extra, char *buffer), struct keyCommand * (*lineCommand)(long extra, char *command)) | 1858 | void editLine(long extra, void (*lineChar)(long extra, char *buffer), struct keyCommand * (*lineCommand)(long extra, char *command)) |
1831 | { | 1859 | { |
1832 | struct pollfd pollfds[1]; | 1860 | struct pollfd pollfds[1]; |
1833 | // Get the initial command set. | 1861 | // Get the initial command set. |
1834 | struct keyCommand *ourKeys = lineCommand(extra, ""); | 1862 | struct keyCommand *ourKeys = lineCommand(extra, ""); |
1835 | char buffer[20]; | 1863 | char buffer[20], command[20], csFinal[8]; |
1836 | char command[20]; | 1864 | int csi = 0, csCount = 0, csIndex = 0, csParams[8], index = 0, pollcount = 1; |
1837 | int pollcount = 1; | ||
1838 | int index = 0; | ||
1839 | // TODO - multiline editLine is an advanced feature. Editing boxes just moves the editLine up and down. | 1865 | // TODO - multiline editLine is an advanced feature. Editing boxes just moves the editLine up and down. |
1840 | // uint16_t h = 1; | 1866 | // uint16_t h = 1; |
1841 | // TODO - should check if it's at the top of the box, then grow it down instead of up if so. | 1867 | // TODO - should check if it's at the top of the box, then grow it down instead of up if so. |
@@ -1850,6 +1876,14 @@ void editLine(long extra, void (*lineChar)(long extra, char *buffer), struct key | |||
1850 | int j, p; | 1876 | int j, p; |
1851 | char *found = NULL; | 1877 | char *found = NULL; |
1852 | 1878 | ||
1879 | if (0 == index) | ||
1880 | { | ||
1881 | buffer[0] = 0; | ||
1882 | csi = 0; | ||
1883 | csCount = 0; | ||
1884 | csIndex = 0; | ||
1885 | } | ||
1886 | |||
1853 | // Apparently it's more portable to reset this each time. | 1887 | // Apparently it's more portable to reset this each time. |
1854 | memset(pollfds, 0, pollcount * sizeof(struct pollfd)); | 1888 | memset(pollfds, 0, pollcount * sizeof(struct pollfd)); |
1855 | pollfds[0].events = POLLIN; | 1889 | pollfds[0].events = POLLIN; |
@@ -1868,7 +1902,6 @@ void editLine(long extra, void (*lineChar)(long extra, char *buffer), struct key | |||
1868 | // After a short delay to check, this is a real Escape key, not part of an escape sequence, so deal with it. | 1902 | // After a short delay to check, this is a real Escape key, not part of an escape sequence, so deal with it. |
1869 | strcpy(command, "^["); | 1903 | strcpy(command, "^["); |
1870 | index = 0; | 1904 | index = 0; |
1871 | buffer[0] = 0; | ||
1872 | } | 1905 | } |
1873 | // TODO - Send a timer event to lineCommand(). This wont be a precise timed event, but don't think we need one. | 1906 | // TODO - Send a timer event to lineCommand(). This wont be a precise timed event, but don't think we need one. |
1874 | } | 1907 | } |
@@ -1905,13 +1938,29 @@ void editLine(long extra, void (*lineChar)(long extra, char *buffer), struct key | |||
1905 | fprintf(stderr, "(%x) %c, ", (int) buffer[j], buffer[j]); | 1938 | fprintf(stderr, "(%x) %c, ", (int) buffer[j], buffer[j]); |
1906 | fflush(stderr); | 1939 | fflush(stderr); |
1907 | index = 0; | 1940 | index = 0; |
1908 | buffer[0] = 0; | ||
1909 | } | 1941 | } |
1910 | else buffer[index] = 0; | 1942 | else buffer[index] = 0; |
1911 | } | 1943 | } |
1912 | } | 1944 | } |
1913 | } | 1945 | } |
1914 | 1946 | ||
1947 | // Check if it's a CSI before we check for the known key sequences. | ||
1948 | if ('\x9B' == buffer[0]) | ||
1949 | csi = 1; | ||
1950 | if (('\x1B' == buffer[0]) && ('[' == buffer[1])) | ||
1951 | csi = 2; | ||
1952 | if (('\xC2' == buffer[0]) && ('\x9B' == buffer[1])) | ||
1953 | csi = 2; | ||
1954 | if (2 == csi) | ||
1955 | { | ||
1956 | buffer[0] = '\x9B'; | ||
1957 | for (j = 1; buffer[j]; j++) | ||
1958 | buffer[j] = buffer[j + 1]; | ||
1959 | index--; | ||
1960 | csi = 1; | ||
1961 | } | ||
1962 | |||
1963 | // Check for known key sequences. | ||
1915 | // For a real timeout checked Esc, buffer is now empty, so this for loop wont find it anyway. | 1964 | // For a real timeout checked Esc, buffer is now empty, so this for loop wont find it anyway. |
1916 | // 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. | 1965 | // 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. |
1917 | for (j = 0; keys[j].code; j++) // Search for multibyte keys and control keys. | 1966 | for (j = 0; keys[j].code; j++) // Search for multibyte keys and control keys. |
@@ -1920,11 +1969,94 @@ void editLine(long extra, void (*lineChar)(long extra, char *buffer), struct key | |||
1920 | { | 1969 | { |
1921 | strcat(command, keys[j].name); | 1970 | strcat(command, keys[j].name); |
1922 | index = 0; | 1971 | index = 0; |
1923 | buffer[0] = 0; | 1972 | csi = 0; |
1924 | break; | 1973 | break; |
1925 | } | 1974 | } |
1926 | } | 1975 | } |
1927 | 1976 | ||
1977 | // Find out if it's a CSI sequence that's not in the known key sequences. | ||
1978 | if (csi) | ||
1979 | { | ||
1980 | /* ECMA-048 section 5.2 defines this, and is unreadable. | ||
1981 | General CSI format - CSI [private] n1 ; n2 [extra] final | ||
1982 | 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. | ||
1983 | n1 0x30 to 0x3f [01234567890:;<=>?] ASCII digits forming a "number" | ||
1984 | 0x3a [:] used for floats, not expecting any. Could also be used as some other sort of inter digit separator. | ||
1985 | 0x3b [;] separates the parameters | ||
1986 | extra 0x20 to 0x2f [ !"#$%&'()*+,-./] Can be multiple, likely isn't. | ||
1987 | final 0x40 to 0x7e "@A .. Z[\]^_`a .. z{|}~" it's private if 0x70 to 0x7e [p ..z{|}~] | ||
1988 | Though the "private" ~ is used for key codes. | ||
1989 | We also have SS3 "\x1BO" for other keys, but that's not a CSI. | ||
1990 | C0 controls, DEL (0x7f), or high characters are undefined. | ||
1991 | TODO So abort the current CSI and start from scratch. | ||
1992 | */ | ||
1993 | |||
1994 | char *t; | ||
1995 | |||
1996 | csIndex = 1; | ||
1997 | csFinal[0] = 0; | ||
1998 | // Unspecified params default to a value that is command dependant. | ||
1999 | // However, they will never be negative, so we can use -1 to flag a default value. | ||
2000 | for (j = 0; j < sizeof(csParams); j++) | ||
2001 | csParams[j] = -1; | ||
2002 | |||
2003 | if ('M' == buffer[1]) | ||
2004 | { | ||
2005 | // 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. | ||
2006 | } | ||
2007 | else | ||
2008 | { | ||
2009 | // TODO - Using rindex here, coz index is a variable in this scope. Should rename index. | ||
2010 | // Check for the private bit. | ||
2011 | if (rindex("<=>?", buffer[1])) | ||
2012 | { | ||
2013 | csFinal[0] = buffer[1]; | ||
2014 | csFinal[1] = 0; | ||
2015 | csIndex++; | ||
2016 | } | ||
2017 | |||
2018 | // Decode parameters. | ||
2019 | j = csIndex; | ||
2020 | do | ||
2021 | { | ||
2022 | // So we know when we get to the end of parameter space. | ||
2023 | t = rindex("01234567890:;<=>?", buffer[j + 1]); | ||
2024 | // See if we passed a paremeter. | ||
2025 | if ((';' == buffer[j]) || (NULL == t)) | ||
2026 | { | ||
2027 | buffer[j] = 0; | ||
2028 | // Empty parameters are default parameters, so only deal with non defaults. | ||
2029 | if (';' != buffer[csIndex] || (NULL == t)) | ||
2030 | { | ||
2031 | // TODO - Might be ":" in the number somewhere, but we are not expecting any in anything we do. | ||
2032 | csParams[csCount] = atoi(&buffer[csIndex]); | ||
2033 | } | ||
2034 | csCount++; | ||
2035 | csIndex = j + 1; | ||
2036 | } | ||
2037 | j++; | ||
2038 | } | ||
2039 | while (t); | ||
2040 | |||
2041 | // Get the final command sequence, and search for it. | ||
2042 | strcat(csFinal, &buffer[csIndex]); | ||
2043 | for (j = 0; CSI_terminators[j].code; j++) | ||
2044 | { | ||
2045 | if (strcmp(CSI_terminators[j].code, csFinal) == 0) | ||
2046 | { | ||
2047 | t = CSI_terminators[j].func(extra, csParams, csCount); | ||
2048 | if (t) | ||
2049 | strcpy(command, t); | ||
2050 | break; | ||
2051 | } | ||
2052 | } | ||
2053 | } | ||
2054 | |||
2055 | csi = 0; | ||
2056 | // Wether or not it's a CSI we understand, it's been handled either here or in the key sequence scanning above. | ||
2057 | index = 0; | ||
2058 | } | ||
2059 | |||
1928 | // See if it's an ordinary key, | 2060 | // See if it's an ordinary key, |
1929 | if ((1 == index) && isprint(buffer[0])) | 2061 | if ((1 == index) && isprint(buffer[0])) |
1930 | { | 2062 | { |
@@ -1945,7 +2077,6 @@ void editLine(long extra, void (*lineChar)(long extra, char *buffer), struct key | |||
1945 | else lineChar(extra, buffer); | 2077 | else lineChar(extra, buffer); |
1946 | } | 2078 | } |
1947 | index = 0; | 2079 | index = 0; |
1948 | buffer[0] = 0; | ||
1949 | } | 2080 | } |
1950 | 2081 | ||
1951 | if (command[0]) // Search for a bound key. | 2082 | if (command[0]) // Search for a bound key. |
@@ -2685,6 +2816,9 @@ void boxes_main(void) | |||
2685 | tcsetattr(0, TCSANOW, &termio); | 2816 | tcsetattr(0, TCSANOW, &termio); |
2686 | 2817 | ||
2687 | // TODO - set up a handler for SIGWINCH to find out when the terminal has been resized. | 2818 | // TODO - set up a handler for SIGWINCH to find out when the terminal has been resized. |
2819 | // TODO - send "\x1B[s\x1B[999C\x1B[999B\x1B[6n\x1B[u" | ||
2820 | // Which breaks down to - save cursor position, down 999, right 999, request cursor position (DSR), restore cursor position. | ||
2821 | // Already got the stub for dealing with the response. | ||
2688 | terminal_size(&W, &H); | 2822 | terminal_size(&W, &H); |
2689 | if (toys.optflags & FLAG_w) | 2823 | if (toys.optflags & FLAG_w) |
2690 | W = TT.w; | 2824 | W = TT.w; |