diff options
author | David Walter Seikel | 2014-01-27 20:31:37 +1000 |
---|---|---|
committer | David Walter Seikel | 2014-01-27 20:31:37 +1000 |
commit | a940c2bac8ef25eb58a68e76331e33165a264255 (patch) | |
tree | 9f2b3277ce648b3363dd0bbe2bc1e5287eff7ace | |
parent | Fix up and change some MC edit keys. (diff) | |
download | boxes-a940c2bac8ef25eb58a68e76331e33165a264255.zip boxes-a940c2bac8ef25eb58a68e76331e33165a264255.tar.gz boxes-a940c2bac8ef25eb58a68e76331e33165a264255.tar.bz2 boxes-a940c2bac8ef25eb58a68e76331e33165a264255.tar.xz |
Major rejig of the main loop.
Simpler.
Fixed up control key combinations.
Probably fucked up vi some how.
-rw-r--r-- | boxes.c | 266 |
1 files changed, 114 insertions, 152 deletions
@@ -303,6 +303,7 @@ struct key | |||
303 | // Conversely, some terminals send "Esc somekey" when you do "Alt somekey". | 303 | // Conversely, some terminals send "Esc somekey" when you do "Alt somekey". |
304 | // Those MC Esc variants might be used on Macs for other things? | 304 | // Those MC Esc variants might be used on Macs for other things? |
305 | // TODO - Don't think I got all the linux console variations. | 305 | // TODO - Don't think I got all the linux console variations. |
306 | // TODO - tmux messes with the shift function keys somehow. | ||
306 | // TODO - Add more shift variations, plus Ctrl & Alt variations. | 307 | // TODO - Add more shift variations, plus Ctrl & Alt variations. |
307 | // TODO - Add other miscelany that does not use an escape sequence. Including mouse events. | 308 | // TODO - Add other miscelany that does not use an escape sequence. Including mouse events. |
308 | // TODO - Perhaps sort this for quicker searching, OR to say which terminal is which, though there is some overlap. | 309 | // TODO - Perhaps sort this for quicker searching, OR to say which terminal is which, though there is some overlap. |
@@ -380,40 +381,38 @@ struct key keys[] = | |||
380 | {"\x1B[23;2~", "Shift F11"}, | 381 | {"\x1B[23;2~", "Shift F11"}, |
381 | {"\x1B[24;2~", "Shift F12"}, | 382 | {"\x1B[24;2~", "Shift F12"}, |
382 | 383 | ||
383 | // These are here for documentation, but some are mapped to particular key names. | 384 | // {"\x00", "^@"}, // NUL Commented out coz it's the C string terminator, and may confuse things. |
384 | // The commented out control keys are handled in editLine(), or just used via "^X". | 385 | {"\x01", "^A"}, // SOH |
385 | // {"\x00", "^@"}, // NUL | 386 | {"\x02", "^B"}, // STX |
386 | // {"\x01", "^A"}, // SOH | 387 | {"\x03", "^C"}, // ETX SIGTERM |
387 | // {"\x02", "^B"}, // STX | 388 | {"\x04", "^D"}, // EOT |
388 | // {"\x03", "^C"}, // ETX | 389 | {"\x05", "^E"}, // ENQ |
389 | // {"\x04", "^D"}, // EOT | 390 | {"\x06", "^F"}, // ACK |
390 | // {"\x05", "^E"}, // ENQ | 391 | {"\x07", "^G"}, // BEL |
391 | // {"\x06", "^F"}, // ACK | ||
392 | // {"\x07", "^G"}, // BEL | ||
393 | {"\x08", "Del"}, // BS Delete key, usually. | 392 | {"\x08", "Del"}, // BS Delete key, usually. |
394 | {"\x09", "Tab"}, // HT Tab key. | 393 | {"\x09", "Tab"}, // HT Tab key. |
395 | {"\x0A", "Return"}, // LF Return key. Roxterm at least is translating both Ctrl-J and Ctrl-M into this. | 394 | {"\x0A", "Return"}, // LF Return key. Roxterm at least is translating both Ctrl-J and Ctrl-M into this. |
396 | // {"\x0B", "^K"}, // VT | 395 | {"\x0B", "^K"}, // VT |
397 | // {"\x0C", "^L"}, // FF | 396 | {"\x0C", "^L"}, // FF |
398 | // {"\x0D", "^M"}, // CR | 397 | {"\x0D", "^M"}, // CR Other Return key, usually. |
399 | // {"\x0E", "^N"}, // SO | 398 | {"\x0E", "^N"}, // SO |
400 | // {"\x0F", "^O"}, // SI | 399 | {"\x0F", "^O"}, // SI |
401 | // {"\x10", "^P"}, // DLE | 400 | {"\x10", "^P"}, // DLE |
402 | // {"\x11", "^Q"}, // DC1 | 401 | {"\x11", "^Q"}, // DC1 |
403 | // {"\x12", "^R"}, // DC2 | 402 | {"\x12", "^R"}, // DC2 |
404 | // {"\x13", "^S"}, // DC3 | 403 | {"\x13", "^S"}, // DC3 |
405 | // {"\x14", "^T"}, // DC4 | 404 | {"\x14", "^T"}, // DC4 |
406 | // {"\x15", "^U"}, // NAK | 405 | {"\x15", "^U"}, // NAK |
407 | // {"\x16", "^V"}, // SYN | 406 | {"\x16", "^V"}, // SYN |
408 | // {"\x17", "^W"}, // ETB | 407 | {"\x17", "^W"}, // ETB |
409 | // {"\x18", "^X"}, // CAN | 408 | {"\x18", "^X"}, // CAN |
410 | // {"\x19", "^Y"}, // EM | 409 | {"\x19", "^Y"}, // EM |
411 | // {"\x1A", "^Z"}, // SUB | 410 | {"\x1A", "^Z"}, // SUB |
412 | // {"\x1B", "Esc"}, // ESC Esc key. Commented out coz it's the ANSI start byte in the above multibyte keys. Handled in the code with a timeout. | 411 | {"\x1B", "^["}, // ESC Esc key. Commented out coz it's the ANSI start byte in the above multibyte keys. Handled in the code with a timeout. |
413 | // {"\x1C", "^\\"}, // FS | 412 | {"\x1C", "^\\"}, // FS |
414 | // {"\x1D", "^]"}, // GS | 413 | {"\x1D", "^]"}, // GS |
415 | // {"\x1E", "^^"}, // RS | 414 | {"\x1E", "^^"}, // RS |
416 | // {"\x1F", "^_"}, // US | 415 | {"\x1F", "^_"}, // US |
417 | {"\x7f", "BS"}, // Backspace key, usually. Ctrl-? perhaps? | 416 | {"\x7f", "BS"}, // Backspace key, usually. Ctrl-? perhaps? |
418 | {NULL, NULL} | 417 | {NULL, NULL} |
419 | }; | 418 | }; |
@@ -1729,66 +1728,9 @@ void nop(box *box, event *event) | |||
1729 | // 'tis a nop, don't actually do anything. | 1728 | // 'tis a nop, don't actually do anything. |
1730 | } | 1729 | } |
1731 | 1730 | ||
1732 | #define BUFFER_LEN 16 | ||
1733 | |||
1734 | 1731 | ||
1735 | int handleKey(view *view, int i, char *keyName, char *buffer) | ||
1736 | { | ||
1737 | // This is using the currentBox instead of view, coz the command line keys are part of the box context now, not separate. | ||
1738 | struct keyCommand *keys = currentBox->view->content->context->modes[currentBox->view->mode].keys; | ||
1739 | int k, len = strlen(keyName), found = 0, doZero = 1; | ||
1740 | |||
1741 | for (k = 0; keys[k].key; k++) | ||
1742 | { | ||
1743 | if (strncmp(keys[k].key, keyName, len) == 0) | ||
1744 | { | ||
1745 | if ('\0' != keys[k].key[len]) | ||
1746 | { // Found only a partial key. | ||
1747 | if (('^' == keyName[0]) && (1 != len)) // Want to let actual ^ characters through unmolested. | ||
1748 | { // And it's a control key combo, so keep accumulating them. | ||
1749 | // Note this wont just keep accumulating, coz the moment it no longer matches any key combos, it fails to be found and falls through. | ||
1750 | found = 1; | ||
1751 | i++; | ||
1752 | doZero = 0; | ||
1753 | break; | ||
1754 | } | ||
1755 | else // It's really an ordinary key, but we can break early at least. | ||
1756 | break; | ||
1757 | } | ||
1758 | else // We have matched the entire key name, so do it. | ||
1759 | { | ||
1760 | found = 1; | ||
1761 | doCommand(view->content->context->commands, keys[k].command, view, NULL); | ||
1762 | } | ||
1763 | break; | ||
1764 | } | ||
1765 | } | ||
1766 | if (!found) // No bound key, or partial control key combo, add input to the current view. | ||
1767 | { | ||
1768 | // TODO - Should check for tabs to, and insert them. | ||
1769 | // Though better off having a function for that? | ||
1770 | if ((i == 0) && (isprint(buffer[0]))) | ||
1771 | { | ||
1772 | mooshStrings(view->line, buffer, view->iX, 0, !TT.overWriteMode); | ||
1773 | view->oW = formatLine(view, view->line->line, &(view->output)); | ||
1774 | moveCursorRelative(view, strlen(buffer), 0, 0, 0); | ||
1775 | } | ||
1776 | else | ||
1777 | { | ||
1778 | // TODO - Should bitch on the status line instead. | ||
1779 | fprintf(stderr, "Key is %s\n", keyName); | ||
1780 | fflush(stderr); | ||
1781 | } | ||
1782 | } | ||
1783 | if (doZero) | ||
1784 | { | ||
1785 | i = 0; | ||
1786 | buffer[0] = '\0'; | ||
1787 | } | ||
1788 | |||
1789 | return i; | ||
1790 | } | ||
1791 | 1732 | ||
1733 | #define BUFFER_LEN 16 | ||
1792 | 1734 | ||
1793 | // Basically this is the main loop. | 1735 | // Basically this is the main loop. |
1794 | 1736 | ||
@@ -1800,14 +1742,16 @@ void editLine(view *view, int16_t X, int16_t Y, int16_t W, int16_t H) | |||
1800 | { | 1742 | { |
1801 | struct termios termio, oldtermio; | 1743 | struct termios termio, oldtermio; |
1802 | struct pollfd pollfds[1]; | 1744 | struct pollfd pollfds[1]; |
1803 | char buffer[BUFFER_LEN]; | 1745 | char buffer[BUFFER_LEN + 1]; |
1746 | char command[BUFFER_LEN + 1]; | ||
1804 | int pollcount = 1; | 1747 | int pollcount = 1; |
1805 | int i = 0; | 1748 | int i = 0; |
1806 | // TODO - multiline editLine is an advanced feature. Editing boxes just moves the editLine up and down. | 1749 | // TODO - multiline editLine is an advanced feature. Editing boxes just moves the editLine up and down. |
1807 | // uint16_t h = 1; | 1750 | // uint16_t h = 1; |
1808 | // TODO - should check if it's at the top of the box, then grow it down instead of up if so. | 1751 | // TODO - should check if it's at the top of the box, then grow it down instead of up if so. |
1809 | 1752 | ||
1810 | buffer[0] = '\0'; | 1753 | buffer[0] = 0; |
1754 | command[0] = 0; | ||
1811 | 1755 | ||
1812 | if (view->box) | 1756 | if (view->box) |
1813 | sizeViewToBox(view->box, X, Y, W, H); | 1757 | sizeViewToBox(view->box, X, Y, W, H); |
@@ -1845,6 +1789,8 @@ void editLine(view *view, int16_t X, int16_t Y, int16_t W, int16_t H) | |||
1845 | // TODO - We can reuse one or two of these to have less of them. | 1789 | // TODO - We can reuse one or two of these to have less of them. |
1846 | int j = 0, p, ret, y, len; | 1790 | int j = 0, p, ret, y, len; |
1847 | 1791 | ||
1792 | // Coz things might change out from under us, find the current view. | ||
1793 | // TODO - see if I can get this lot out of here. | ||
1848 | if (commandMode) | 1794 | if (commandMode) |
1849 | view = commandLine; | 1795 | view = commandLine; |
1850 | else | 1796 | else |
@@ -1861,6 +1807,7 @@ void editLine(view *view, int16_t X, int16_t Y, int16_t W, int16_t H) | |||
1861 | pollfds[0].events = POLLIN; | 1807 | pollfds[0].events = POLLIN; |
1862 | pollfds[0].fd = 0; | 1808 | pollfds[0].fd = 0; |
1863 | 1809 | ||
1810 | // TODO - Should only ask for a time out after we get an Escape. | ||
1864 | p = poll(pollfds, pollcount, 100); // Timeout of one tenth of a second (100). | 1811 | p = poll(pollfds, pollcount, 100); // Timeout of one tenth of a second (100). |
1865 | if (0 > p) perror_exit("poll"); | 1812 | if (0 > p) perror_exit("poll"); |
1866 | if (0 == p) // A timeout, trigger a time event. | 1813 | if (0 == p) // A timeout, trigger a time event. |
@@ -1868,24 +1815,21 @@ void editLine(view *view, int16_t X, int16_t Y, int16_t W, int16_t H) | |||
1868 | if ((1 == i) && ('\x1B' == buffer[0])) | 1815 | if ((1 == i) && ('\x1B' == buffer[0])) |
1869 | { | 1816 | { |
1870 | // After a short delay to check, this is a real Escape key, not part of an escape sequence, so deal with it. | 1817 | // After a short delay to check, this is a real Escape key, not part of an escape sequence, so deal with it. |
1871 | strcpy(buffer, "^["); | 1818 | strcpy(command, "^["); |
1872 | i = 1; | 1819 | i = 0; |
1873 | i = handleKey(view, i, buffer, buffer); | 1820 | buffer[0] = 0; |
1874 | continue; | ||
1875 | } | ||
1876 | else | ||
1877 | { | ||
1878 | // TODO - Send a timer event somewhere. | ||
1879 | // This wont be a precise timed event, but don't think we need one. | ||
1880 | continue; | ||
1881 | } | 1821 | } |
1822 | // TODO - Send a timer event somewhere. This wont be a precise timed event, but don't think we need one. | ||
1882 | } | 1823 | } |
1883 | for (p--; 0 <= p; p--) | 1824 | |
1825 | while (0 < p) | ||
1884 | { | 1826 | { |
1827 | p--; | ||
1885 | if (pollfds[p].revents & POLLIN) | 1828 | if (pollfds[p].revents & POLLIN) |
1886 | { | 1829 | { |
1887 | ret = read(pollfds[p].fd, &buffer[i], 1); | 1830 | // I am assuming that we get the input atomically, each multibyte key fits neatly into one read. |
1888 | buffer[i + 1] = '\0'; | 1831 | // 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. |
1832 | ret = read(pollfds[p].fd, &buffer[i], BUFFER_LEN - i); | ||
1889 | if (ret < 0) // An error happened. | 1833 | if (ret < 0) // An error happened. |
1890 | { | 1834 | { |
1891 | // For now, just ignore errors. | 1835 | // For now, just ignore errors. |
@@ -1896,66 +1840,84 @@ void editLine(view *view, int16_t X, int16_t Y, int16_t W, int16_t H) | |||
1896 | { | 1840 | { |
1897 | fprintf(stderr, "EOF\n"); | 1841 | fprintf(stderr, "EOF\n"); |
1898 | fflush(stderr); | 1842 | fflush(stderr); |
1899 | break; | ||
1900 | } | ||
1901 | else if (BUFFER_LEN == i + 1) // Ran out of buffer. | ||
1902 | { | ||
1903 | fprintf(stderr, "Full buffer -%s\n", buffer); | ||
1904 | for (j = 0; buffer[j + 1]; j++) | ||
1905 | fprintf(stderr, "(%x) %c, ", (int) buffer[j], buffer[j]); | ||
1906 | fflush(stderr); | ||
1907 | i = 0; | ||
1908 | } | 1843 | } |
1909 | else | 1844 | else |
1910 | { | 1845 | { |
1911 | char *keyName = NULL; | 1846 | i += ret; |
1912 | 1847 | if (BUFFER_LEN <= i) // Ran out of buffer. | |
1913 | if (('\x1B' == buffer[i]) && (0 != i)) // An unrecognised escape sequence, start again. | ||
1914 | // TODO - it might be a reply from a query we sent, like asking for the terminal size or cursor position. Which apparently is the same thing. | ||
1915 | { | 1848 | { |
1916 | // TODO - Should bitch on the status line instead. | 1849 | fprintf(stderr, "Full buffer - %s -> %s\n", buffer, command); |
1917 | fprintf(stderr, "Unknown escape sequence "); | ||
1918 | for (j = 0; buffer[j + 1]; j++) | 1850 | for (j = 0; buffer[j + 1]; j++) |
1919 | fprintf(stderr, "(%x) %c, ", (int) buffer[j], buffer[j]); | 1851 | fprintf(stderr, "(%x) %c, ", (int) buffer[j], buffer[j]); |
1920 | fprintf(stderr, "\n"); | ||
1921 | fflush(stderr); | 1852 | fflush(stderr); |
1922 | buffer[0] = '\x1B'; | 1853 | i = 0; |
1923 | i = 1; | 1854 | buffer[0] = 0; |
1924 | continue; | ||
1925 | } | ||
1926 | |||
1927 | for (j = 0; keys[j].code; j++) // Search for multibyte keys and some control keys. | ||
1928 | { | ||
1929 | if (strcmp(keys[j].code, buffer) == 0) | ||
1930 | { | ||
1931 | keyName = keys[j].name; | ||
1932 | break; | ||
1933 | } | ||
1934 | } | 1855 | } |
1935 | // See if it's an ordinary key, | ||
1936 | if ((NULL == keyName) && (0 == i) && isprint(buffer[0])) | ||
1937 | keyName = buffer; | ||
1938 | // Check for control keys, but not those that have already been identified, or ESC. | ||
1939 | if ((NULL == keyName) && iscntrl(buffer[i]) && ('\x1B' != buffer[i])) | ||
1940 | { | ||
1941 | // Convert to "^X" format. | ||
1942 | buffer[i + 1] = buffer[i] + '@'; | ||
1943 | buffer[i++] = '^'; | ||
1944 | buffer[i + 1] = '\0'; | ||
1945 | keyName=buffer; | ||
1946 | } | ||
1947 | // See if it's already accumulating a control key combo. | ||
1948 | if ('^' == buffer[0]) | ||
1949 | keyName = buffer; | ||
1950 | // For now we will assume that control keys could be the start of multi key combinations. | ||
1951 | // TODO - If the view->context HAS on event handler, use it, otherwise look up the specific event handler in the context modes ourselves? | ||
1952 | if (keyName) // Search for a bound key. | ||
1953 | i = handleKey(view, i, keyName, buffer); | ||
1954 | else | 1856 | else |
1955 | i++; | 1857 | buffer[i] = 0; |
1956 | } | 1858 | } |
1957 | } | 1859 | } |
1958 | } | 1860 | } |
1861 | |||
1862 | // TODO - think vi got screwed up now. sigh | ||
1863 | |||
1864 | // For a real timeout checked Esc, buffer is now empty, so this for loop wont find it anyway. | ||
1865 | // 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. | ||
1866 | for (j = 0; keys[j].code; j++) // Search for multibyte keys and some control keys. | ||
1867 | { | ||
1868 | if (strcmp(keys[j].code, buffer) == 0) | ||
1869 | { | ||
1870 | strcat(command, keys[j].name); | ||
1871 | i = 0; | ||
1872 | buffer[0] = 0; | ||
1873 | break; | ||
1874 | } | ||
1875 | } | ||
1876 | |||
1877 | // See if it's an ordinary key, | ||
1878 | if ((1 == i) && isprint(buffer[0])) | ||
1879 | { | ||
1880 | // If there's an outstanding command, add this to the end of it. | ||
1881 | if (command[0]) | ||
1882 | strcat(command, buffer); | ||
1883 | else | ||
1884 | { | ||
1885 | // TODO - Should check for tabs to, and insert them. | ||
1886 | // Though better off having a function for that? | ||
1887 | // TODO - see if I can get these out of here. Some sort of pushCharacter(buffer, blob) that is passed in. | ||
1888 | mooshStrings(view->line, buffer, view->iX, 0, !TT.overWriteMode); | ||
1889 | view->oW = formatLine(view, view->line->line, &(view->output)); | ||
1890 | moveCursorRelative(view, strlen(buffer), 0, 0, 0); | ||
1891 | } | ||
1892 | i = 0; | ||
1893 | buffer[0] = 0; | ||
1894 | } | ||
1895 | |||
1896 | // TODO - If the view->context has on event handler, use it, otherwise look up the specific event handler in the context modes ourselves. | ||
1897 | if (command[0]) // Search for a bound key. | ||
1898 | { | ||
1899 | if (BUFFER_LEN <= strlen(command)) | ||
1900 | { | ||
1901 | fprintf(stderr, "Full command buffer - %s \n", command); | ||
1902 | fflush(stderr); | ||
1903 | command[0] = 0; | ||
1904 | } | ||
1905 | |||
1906 | // This is using the currentBox instead of view, coz the command line keys are part of the box context now, not separate. | ||
1907 | // More importantly, the currentBox may change due to a command. | ||
1908 | struct keyCommand *ourKeys = currentBox->view->content->context->modes[currentBox->view->mode].keys; | ||
1909 | |||
1910 | for (j = 0; ourKeys[j].key; j++) | ||
1911 | { | ||
1912 | if (strcmp(ourKeys[j].key, command) == 0) | ||
1913 | { | ||
1914 | doCommand(view->content->context->commands, ourKeys[j].command, view, NULL); | ||
1915 | command[0] = 0; | ||
1916 | break; | ||
1917 | } | ||
1918 | } | ||
1919 | } | ||
1920 | |||
1959 | } | 1921 | } |
1960 | 1922 | ||
1961 | // Restore the old terminal settings. | 1923 | // Restore the old terminal settings. |