aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDavid Walter Seikel2014-01-28 18:20:26 +1000
committerDavid Walter Seikel2014-01-28 18:20:26 +1000
commit1d1e3820e3b09c447ba18e47b50c22d741285347 (patch)
tree9d706cc54b99798aae2074bb9650254df321da05
parentMore vi fixes after the re organisation. (diff)
downloadboxes-1d1e3820e3b09c447ba18e47b50c22d741285347.zip
boxes-1d1e3820e3b09c447ba18e47b50c22d741285347.tar.gz
boxes-1d1e3820e3b09c447ba18e47b50c22d741285347.tar.bz2
boxes-1d1e3820e3b09c447ba18e47b50c22d741285347.tar.xz
Hack up editLine to make it more generic, and remove the dependency on box and view.
-rw-r--r--boxes.c245
1 files changed, 141 insertions, 104 deletions
diff --git a/boxes.c b/boxes.c
index 5152c20..b68782d 100644
--- a/boxes.c
+++ b/boxes.c
@@ -1741,15 +1741,57 @@ void nop(box *box, event *event)
1741} 1741}
1742 1742
1743 1743
1744static struct keyCommand *lineLoop(long extra)
1745{
1746 struct _view *view = (struct _view *) extra; // Though we pretty much stomp on this straight away.
1747 int y, len;
1748
1749 // Coz things might change out from under us, find the current view.
1750 if (commandMode) view = commandLine;
1751 else view = currentBox->view;
1752 // Draw the prompt and the current line.
1753 y = view->Y + (view->cY - view->offsetY);
1754 len = strlen(view->prompt);
1755 drawLine(y, view->X, view->X + view->W, "", " ", view->prompt, "", 0);
1756 drawContentLine(view, y, view->X + len, view->X + view->W, "", " ", view->line->line, "", 1);
1757 // Move the cursor.
1758 printf("\x1B[%d;%dH", y + 1, view->X + len + (view->cX - view->offsetX) + 1);
1759 fflush(stdout);
1760
1761 // This is using the currentBox instead of view, coz the command line keys are part of the box context now, not separate.
1762 // More importantly, the currentBox may change due to a command.
1763 return currentBox->view->content->context->modes[currentBox->view->mode].keys;
1764}
1765
1766static void lineChar(long extra, char *buffer)
1767{
1768 struct _view *view = (struct _view *) extra; // Though we pretty much stomp on this straight away.
1769
1770 // Coz things might change out from under us, find the current view.
1771 if (commandMode) view = commandLine;
1772 else view = currentBox->view;
1773 // TODO - Should check for tabs to, and insert them.
1774 // Though better off having a function for that?
1775 // TODO - see if I can get these out of here. Some sort of pushCharacter(buffer, blob) that is passed in.
1776 mooshStrings(view->line, buffer, view->iX, 0, !TT.overWriteMode);
1777 view->oW = formatLine(view, view->line->line, &(view->output));
1778 moveCursorRelative(view, strlen(buffer), 0, 0, 0);
1779}
1780
1781static void lineCommand(long extra, char *command, event *event)
1782{
1783 struct _view *view = (struct _view *) extra; // Though we pretty much stomp on this straight away.
1784
1785 // Coz things might change out from under us, find the current view.
1786 if (commandMode) view = commandLine;
1787 else view = currentBox->view;
1788 doCommand(view->content->context->commands, command, view, event);
1789}
1790
1744// Basically this is the main loop. 1791// Basically this is the main loop.
1745 1792
1746// X and Y are screen coords. 1793void editLine(long extra, struct keyCommand *(*lineLoop)(long extra), void (*lineChar)(long extra, char *buffer), void (*lineCommand)(long extra, char *command, event *event))
1747// W and H are the size of the editLine. EditLine will start one line high, and grow to a maximum of H if needed, then start to scroll.
1748// H more than one means the editLine can grow upwards, unless it's at the top of the box / screen, then it has to grow downwards.
1749// X, Y, W, and H can be -1, which means to grab suitable numbers from the views box.
1750void editLine(view *view, int16_t X, int16_t Y, int16_t W, int16_t H)
1751{ 1794{
1752 struct termios termio, oldtermio;
1753 struct pollfd pollfds[1]; 1795 struct pollfd pollfds[1];
1754 char buffer[20]; 1796 char buffer[20];
1755 char command[20]; 1797 char command[20];
@@ -1762,56 +1804,15 @@ void editLine(view *view, int16_t X, int16_t Y, int16_t W, int16_t H)
1762 buffer[0] = 0; 1804 buffer[0] = 0;
1763 command[0] = 0; 1805 command[0] = 0;
1764 1806
1765 if (view->box)
1766 sizeViewToBox(view->box, X, Y, W, H);
1767 // Assumes the view was already setup if it's not part of a box.
1768
1769 // All the mouse tracking methods suck one way or another. sigh
1770 // Enable mouse (VT200 normal tracking mode, UTF8 encoding). The limit is 2015. Seems to only be in later xterms.
1771// printf("\x1B[?1005h");
1772 // Enable mouse (DEC locator reporting mode). In theory has no limit. Wont actually work though.
1773 // On the other hand, only allows for four buttons, so only half a mouse wheel.
1774// printf("\x1B[1;2'z\x1B[1;3'{");
1775 // Enable mouse (VT200 normal tracking mode). Has a limit of 256 - 32 rows and columns. An xterm exclusive I think, but works in roxterm at least.
1776 printf("\x1B[?1000h");
1777 fflush(stdout);
1778 // TODO - Should remember to turn off mouse reporting when we leave.
1779
1780 // Grab the old terminal settings and save it.
1781 tcgetattr(0, &oldtermio);
1782 tcflush(0, TCIFLUSH);
1783 termio = oldtermio;
1784
1785 // Mould the terminal to our will.
1786 termio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
1787 termio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP|ICANON);
1788 termio.c_cc[VTIME]=0; // deciseconds.
1789 termio.c_cc[VMIN]=1;
1790 tcsetattr(0, TCSANOW, &termio);
1791
1792 calcBoxes(currentBox);
1793 drawBoxes(currentBox);
1794
1795 // TODO - OS buffered keys might be a problem, but we can't do the usual timestamp filter for now. 1807 // TODO - OS buffered keys might be a problem, but we can't do the usual timestamp filter for now.
1808 TT.stillRunning = 1;
1796 while (TT.stillRunning) 1809 while (TT.stillRunning)
1797 { 1810 {
1798 // TODO - We can reuse one or two of these to have less of them. 1811 int j, p;
1799 int j = 0, p, ret, y, len; 1812 char *found = NULL;
1800 1813 // We do this coz the command set might change out from under us in response to commands.
1801 // This is using the currentBox instead of view, coz the command line keys are part of the box context now, not separate. 1814 // So lineLoop should return the current command set if nothing else.
1802 // More importantly, the currentBox may change due to a command. 1815 struct keyCommand *ourKeys = lineLoop(extra);
1803 struct keyCommand *ourKeys = currentBox->view->content->context->modes[currentBox->view->mode].keys;
1804
1805 // Coz things might change out from under us, find the current view.
1806 // TODO - see if I can get this lot out of here.
1807 if (commandMode) view = commandLine;
1808 else view = currentBox->view;
1809 y = view->Y + (view->cY - view->offsetY);
1810 len = strlen(view->prompt);
1811 drawLine(y, view->X, view->X + view->W, "\0", " ", view->prompt, '\0', 0);
1812 drawContentLine(view, y, view->X + len, view->X + view->W, "\0", " ", view->line->line, '\0', 1);
1813 printf("\x1B[%d;%dH", y + 1, view->X + len + (view->cX - view->offsetX) + 1);
1814 fflush(stdout);
1815 1816
1816 // Apparently it's more portable to reset this each time. 1817 // Apparently it's more portable to reset this each time.
1817 memset(pollfds, 0, pollcount * sizeof(struct pollfd)); 1818 memset(pollfds, 0, pollcount * sizeof(struct pollfd));
@@ -1832,7 +1833,7 @@ void editLine(view *view, int16_t X, int16_t Y, int16_t W, int16_t H)
1832 index = 0; 1833 index = 0;
1833 buffer[0] = 0; 1834 buffer[0] = 0;
1834 } 1835 }
1835 // TODO - Send a timer event somewhere. This wont be a precise timed event, but don't think we need one. 1836 // TODO - Send a timer event to lineCommand(). This wont be a precise timed event, but don't think we need one.
1836 } 1837 }
1837 1838
1838 while (0 < p) 1839 while (0 < p)
@@ -1842,21 +1843,21 @@ void editLine(view *view, int16_t X, int16_t Y, int16_t W, int16_t H)
1842 { 1843 {
1843 // I am assuming that we get the input atomically, each multibyte key fits neatly into one read. 1844 // I am assuming that we get the input atomically, each multibyte key fits neatly into one read.
1844 // 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. 1845 // 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.
1845 ret = read(pollfds[p].fd, &buffer[index], sizeof(buffer) - (index + 1)); 1846 j = read(pollfds[p].fd, &buffer[index], sizeof(buffer) - (index + 1));
1846 if (ret < 0) // An error happened. 1847 if (j < 0) // An error happened.
1847 { 1848 {
1848 // For now, just ignore errors. 1849 // For now, just ignore errors.
1849 fprintf(stderr, "input error on %d\n", p); 1850 fprintf(stderr, "input error on %d\n", p);
1850 fflush(stderr); 1851 fflush(stderr);
1851 } 1852 }
1852 else if (ret == 0) // End of file. 1853 else if (j == 0) // End of file.
1853 { 1854 {
1854 fprintf(stderr, "EOF\n"); 1855 fprintf(stderr, "EOF\n");
1855 fflush(stderr); 1856 fflush(stderr);
1856 } 1857 }
1857 else 1858 else
1858 { 1859 {
1859 index += ret; 1860 index += j;
1860 if (sizeof(buffer) < (index + 1)) // Ran out of buffer. 1861 if (sizeof(buffer) < (index + 1)) // Ran out of buffer.
1861 { 1862 {
1862 fprintf(stderr, "Full buffer - %s -> %s\n", buffer, command); 1863 fprintf(stderr, "Full buffer - %s -> %s\n", buffer, command);
@@ -1873,7 +1874,7 @@ void editLine(view *view, int16_t X, int16_t Y, int16_t W, int16_t H)
1873 1874
1874 // For a real timeout checked Esc, buffer is now empty, so this for loop wont find it anyway. 1875 // For a real timeout checked Esc, buffer is now empty, so this for loop wont find it anyway.
1875 // 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. 1876 // 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.
1876 for (j = 0; keys[j].code; j++) // Search for multibyte keys and some control keys. 1877 for (j = 0; keys[j].code; j++) // Search for multibyte keys and control keys.
1877 { 1878 {
1878 if (strcmp(keys[j].code, buffer) == 0) 1879 if (strcmp(keys[j].code, buffer) == 0)
1879 { 1880 {
@@ -1887,42 +1888,26 @@ void editLine(view *view, int16_t X, int16_t Y, int16_t W, int16_t H)
1887 // See if it's an ordinary key, 1888 // See if it's an ordinary key,
1888 if ((1 == index) && isprint(buffer[0])) 1889 if ((1 == index) && isprint(buffer[0]))
1889 { 1890 {
1890 int visucks = 0; 1891 // Here we want to run it through the command finder first, and only "insert" it if it's not a command.
1891
1892 // Here we want to pass it to the command finder first, and only "insert" it if it's not a command.
1893 // Less and more have the "ZZ" command, but nothing else seems to have multi ordinary character commands.
1894 // Less and more also have some ordinary character commands, mostly vi like.
1895 for (j = 0; ourKeys[j].key; j++) 1892 for (j = 0; ourKeys[j].key; j++)
1896 { 1893 {
1897 // Yes, that's right, we are scanning ourKeys twice, coz vi.
1898 // TODO - We can wriggle out of this later by bumping visucks up a scope and storing a pointer to ourKeys[j].command.
1899 // In fact, visucks could be that pointer.
1900 if (strcmp(ourKeys[j].key, buffer) == 0) 1894 if (strcmp(ourKeys[j].key, buffer) == 0)
1901 { 1895 {
1902 strcpy(command, buffer); 1896 strcpy(command, buffer);
1903 visucks = 1; 1897 found = command;
1904 break; 1898 break;
1905 } 1899 }
1906 } 1900 }
1907 // If there's an outstanding command, add this to the end of it. 1901 if (NULL == found)
1908 if (!visucks)
1909 { 1902 {
1903 // If there's an outstanding command, add this to the end of it.
1910 if (command[0]) strcat(command, buffer); 1904 if (command[0]) strcat(command, buffer);
1911 else 1905 else lineChar(extra, buffer);
1912 {
1913 // TODO - Should check for tabs to, and insert them.
1914 // Though better off having a function for that?
1915 // TODO - see if I can get these out of here. Some sort of pushCharacter(buffer, blob) that is passed in.
1916 mooshStrings(view->line, buffer, view->iX, 0, !TT.overWriteMode);
1917 view->oW = formatLine(view, view->line->line, &(view->output));
1918 moveCursorRelative(view, strlen(buffer), 0, 0, 0);
1919 }
1920 } 1906 }
1921 index = 0; 1907 index = 0;
1922 buffer[0] = 0; 1908 buffer[0] = 0;
1923 } 1909 }
1924 1910
1925 // TODO - If the view->context has an event handler, use it, otherwise look up the specific event handler in the context modes ourselves.
1926 if (command[0]) // Search for a bound key. 1911 if (command[0]) // Search for a bound key.
1927 { 1912 {
1928 if (sizeof(command) < (strlen(command) + 1)) 1913 if (sizeof(command) < (strlen(command) + 1))
@@ -1932,21 +1917,25 @@ void editLine(view *view, int16_t X, int16_t Y, int16_t W, int16_t H)
1932 command[0] = 0; 1917 command[0] = 0;
1933 } 1918 }
1934 1919
1935 for (j = 0; ourKeys[j].key; j++) 1920 if (NULL == found)
1936 { 1921 {
1937 if (strcmp(ourKeys[j].key, command) == 0) 1922 for (j = 0; ourKeys[j].key; j++)
1938 { 1923 {
1939 doCommand(view->content->context->commands, ourKeys[j].command, view, NULL); 1924 if (strcmp(ourKeys[j].key, command) == 0)
1940 command[0] = 0; 1925 {
1941 break; 1926 found = ourKeys[j].command;
1927 break;
1928 }
1942 } 1929 }
1943 } 1930 }
1931 if (found)
1932 {
1933 // That last argument, an event pointer, should be the original raw keystrokes, though by this time that's been cleared.
1934 lineCommand(extra, found, NULL);
1935 command[0] = 0;
1936 }
1944 } 1937 }
1945
1946 } 1938 }
1947
1948 // Restore the old terminal settings.
1949 tcsetattr(0, TCSANOW, &oldtermio);
1950} 1939}
1951 1940
1952 1941
@@ -2591,22 +2580,10 @@ struct context simpleVi =
2591void boxes_main(void) 2580void boxes_main(void)
2592{ 2581{
2593 struct context *context = &simpleMcedit; // The default is mcedit, coz that's what I use. 2582 struct context *context = &simpleMcedit; // The default is mcedit, coz that's what I use.
2583 struct termios termio, oldtermio;
2594 char *prompt = "Enter a command : "; 2584 char *prompt = "Enter a command : ";
2595 unsigned W = 80, H = 24; 2585 unsigned W = 80, H = 24;
2596 2586
2597 // TODO - Should do an isatty() here, though not sure about the usefullness of driving this from a script or redirected input, since it's supposed to be a UI for terminals.
2598 // It would STILL need the terminal size for output though. Perhaps just bitch and abort if it's not a tty?
2599 // On the other hand, sed don't need no stinkin' UI. And things like more or less should be usable on the end of a pipe.
2600
2601 // TODO - set up a handler for SIGWINCH to find out when the terminal has been resized.
2602 terminal_size(&W, &H);
2603 if (toys.optflags & FLAG_w)
2604 W = TT.w;
2605 if (toys.optflags & FLAG_h)
2606 H = TT.h;
2607
2608 TT.stillRunning = 1;
2609
2610 // For testing purposes, figure out which context we use. When this gets real, the toybox multiplexer will sort this out for us instead. 2587 // For testing purposes, figure out which context we use. When this gets real, the toybox multiplexer will sort this out for us instead.
2611 if (toys.optflags & FLAG_m) 2588 if (toys.optflags & FLAG_m)
2612 { 2589 {
@@ -2626,8 +2603,48 @@ void boxes_main(void)
2626 context = &simpleVi; 2603 context = &simpleVi;
2627 } 2604 }
2628 2605
2606 // TODO - Should do an isatty() here, though not sure about the usefullness of driving this from a script or redirected input, since it's supposed to be a UI for terminals.
2607 // It would STILL need the terminal size for output though. Perhaps just bitch and abort if it's not a tty?
2608 // On the other hand, sed don't need no stinkin' UI. And things like more or less should be usable on the end of a pipe.
2609
2610 // Grab the old terminal settings and save it.
2611 tcgetattr(0, &oldtermio);
2612 tcflush(0, TCIFLUSH);
2613 termio = oldtermio;
2614
2615 // Mould the terminal to our will.
2616 /*
2617 IUCLC (not in POSIX) Map uppercase characters to lowercase on input.
2618 IXON Enable XON/XOFF flow control on output.
2619 IXOFF Enable XON/XOFF flow control on input.
2620 IXANY (not in POSIX.1; XSI) Enable any character to restart output.
2621
2622 ECHO Echo input characters.
2623 ECHOE If ICANON is also set, the ERASE character erases the preceding input character, and WERASE erases the preceding word.
2624 ECHOK If ICANON is also set, the KILL character erases the current line.
2625 ECHONL If ICANON is also set, echo the NL character even if ECHO is not set.
2626 TOSTOP Send the SIGTTOU signal to the process group of a background process which tries to write to its controlling terminal.
2627 ICANON Enable canonical mode. This enables the special characters EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, and WERASE, and buffers by lines.
2628
2629 VTIME Timeout in deciseconds for non-canonical read.
2630 VMIN Minimum number of characters for non-canonical read.
2631 */
2632 termio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
2633 termio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP|ICANON);
2634 termio.c_cc[VTIME]=0; // deciseconds.
2635 termio.c_cc[VMIN]=1;
2636 tcsetattr(0, TCSANOW, &termio);
2637
2638 // TODO - set up a handler for SIGWINCH to find out when the terminal has been resized.
2639 terminal_size(&W, &H);
2640 if (toys.optflags & FLAG_w)
2641 W = TT.w;
2642 if (toys.optflags & FLAG_h)
2643 H = TT.h;
2644
2629 // Create the main box. Right now the system needs one for wrapping around while switching. The H - 1 bit is to leave room for our example command line. 2645 // Create the main box. Right now the system needs one for wrapping around while switching. The H - 1 bit is to leave room for our example command line.
2630 rootBox = addBox("root", context, toys.optargs[0], 0, 0, W, H - 1); 2646 rootBox = addBox("root", context, toys.optargs[0], 0, 0, W, H - 1);
2647 currentBox = rootBox;
2631 2648
2632 // Create the command line view, sharing the same context as the root. It will differentiate based on the view mode of the current box. 2649 // Create the command line view, sharing the same context as the root. It will differentiate based on the view mode of the current box.
2633 // Also load the command line history as it's file. 2650 // Also load the command line history as it's file.
@@ -2639,9 +2656,29 @@ void boxes_main(void)
2639 // Move to the end of the history. 2656 // Move to the end of the history.
2640 moveCursorAbsolute(commandLine, 0, commandLine->content->lines.length, 0, 0); 2657 moveCursorAbsolute(commandLine, 0, commandLine->content->lines.length, 0, 0);
2641 2658
2659 // All the mouse tracking methods suck one way or another. sigh
2660 // http://leonerds-code.blogspot.co.uk/2012/04/wide-mouse-support-in-libvterm.html is helpful.
2661 // Enable mouse (VT200 normal tracking mode, UTF8 encoding). The limit is 2015. Seems to only be in later xterms.
2662// printf("\x1B[?1005h");
2663 // Enable mouse (VT340 locator reporting mode). In theory has no limit. Wont actually work though.
2664 // On the other hand, only allows for four buttons, so only half a mouse wheel.
2665 // Responds with "\1B[e;p;r;c;p&w" where e is event type, p is a bitmap of buttons pressed, r and c are the mouse coords in decimal, and p is the "page number".
2666// printf("\x1B[1;2'z\x1B[1;3'{");
2667 // Enable mouse (VT200 normal tracking mode). Has a limit of 256 - 32 rows and columns. An xterm exclusive I think, but works in roxterm at least. No wheel reports.
2668 // Responds with "\x1B[Mbxy" where x and y are the mouse coords, and b is bit encoded buttons and modifiers - 0=MB1 pressed, 1=MB2 pressed, 2=MB3 pressed, 3=release, 4=Shift, 8=Meta, 16=Control
2669 printf("\x1B[?1000h");
2670 fflush(stdout);
2671
2672 calcBoxes(currentBox);
2673 drawBoxes(currentBox);
2674
2642 // Run the main loop. 2675 // Run the main loop.
2643 currentBox = rootBox; 2676 editLine((long) currentBox->view, lineLoop, lineChar, lineCommand);
2644 editLine(currentBox->view, -1, -1, -1, -1); 2677
2678 // TODO - Should remember to turn off mouse reporting when we leave.
2679
2680 // Restore the old terminal settings.
2681 tcsetattr(0, TCSANOW, &oldtermio);
2645 2682
2646 puts("\n"); 2683 puts("\n");
2647 fflush(stdout); 2684 fflush(stdout);