diff options
-rw-r--r-- | lib/interestingtimes.c | 68 | ||||
-rw-r--r-- | lib/lib.c | 9 | ||||
-rw-r--r-- | lib/lib.h | 12 | ||||
-rw-r--r-- | lib/linestack.c | 2 | ||||
-rwxr-xr-x | scripts/make.sh | 2 | ||||
-rw-r--r-- | toys/example/test_scankey.c | 67 | ||||
-rw-r--r-- | toys/other/hexedit.c | 25 |
7 files changed, 151 insertions, 34 deletions
diff --git a/lib/interestingtimes.c b/lib/interestingtimes.c index 8f8b35c2..ed8a0751 100644 --- a/lib/interestingtimes.c +++ b/lib/interestingtimes.c @@ -46,6 +46,38 @@ int terminal_size(unsigned *xx, unsigned *yy) return x || y; } +// Wrapper that parses results from ANSI probe. If block|2 query size and +// send ANSI probe if we can't. (Probe queries xterm size through +// serial connection, when local TTY doesn't know and only remote end knows.) +// Otherwise acts like scan_key() +int scan_key_getsize(char *scratch, int block, unsigned *xx, unsigned *yy) +{ + int key; + + if (block&2) { + struct pollfd pfd; + + if (terminal_size(xx, yy)) { + if (!(block&1)) return -2; + } else { + // Send probe: bookmark cursor position, jump to bottom right, + // query position, return cursor to bookmarked position. + xprintf("\e[s\e[999C\e[999B\e[6n\e[u"); + + // Wait up to a quarter second for a result + memset(&pfd, 0, sizeof(struct pollfd)); + pfd.events = POLLIN; + xpoll(&pfd, 0, 250); + } + } + while (512&(key = scan_key(scratch, block&1))) { + if (xx) *xx = (key>>10)&1023; + if (yy) *yy = (key>>20)&1023; + } + + return key; +} + // Reset terminal to known state, saving copy of old state if old != NULL. int set_terminal(int fd, int raw, struct termios *old) { @@ -80,16 +112,23 @@ int set_terminal(int fd, int raw, struct termios *old) return tcsetattr(fd, TCSANOW, &termio); } +struct scan_key_list { + char *name, *seq; +} static const scan_key_list[] = TAGGED_ARRAY(KEY, + // up down right left pgup pgdn home end ins + {"UP", "\033[A"}, {"DOWN", "\033[B"}, {"RIGHT", "\033[C"}, {"LEFT", "\033[D"}, + {"PGUP", "\033[5~"}, {"PGDN", "\033[6~"}, {"HOME", "\033OH"}, + {"END", "\033OF"}, {"INSERT", "\033[2~"} +); + // Scan stdin for a keypress, parsing known escape sequences -// Returns: 0-255=literal, -1=EOF, -2=NONE, 256-...=index into seq +// Returns: 0-255=literal, -1=EOF, -2=NONE, 256-...=index into scan_key_list +// >512 is x<<9+y<<21 // scratch space is necessary because last char of !seq could start new seq // Zero out first byte of scratch before first call to scan_key // block=0 allows fetching multiple characters before updating display int scan_key(char *scratch, int block) { - // up down right left pgup pgdn home end ins - char *seqs[] = {"\033[A", "\033[B", "\033[C", "\033[D", "\033[5~", "\033[6~", - "\033OH", "\033OF", "\033[2~", 0}; struct pollfd pfd; int maybe, i, j; char *test; @@ -99,10 +138,24 @@ int scan_key(char *scratch, int block) pfd.events = POLLIN; pfd.revents = 0; - // check sequences maybe = 0; if (*scratch) { - for (i = maybe = 0; (test = seqs[i]); i++) { + int pos[6]; + unsigned x, y; + + // Check for return from terminal size probe + memset(pos, 0, 6*sizeof(int)); + sscanf(scratch+1, "\033%n[%n%3u%n,%n%3u%nR%n", pos, pos+1, &y, + pos+2, pos+3, &x, pos+4, pos+5); + if (pos[5]) { + // Recognized X/Y position, consume and return + *scratch = 0; + return 512+(x<<10)+(y<<20); + } else for (i=0; i<6; i++) if (pos[i]==*scratch) maybe = 1; + + // Check sequences + for (i = 0; i<ARRAY_LEN(scan_key_list); i++) { + test = scan_key_list[i].seq; for (j = 0; j<*scratch; j++) if (scratch[j+1] != test[j]) break; if (j == *scratch) { maybe = 1; @@ -113,6 +166,7 @@ int scan_key(char *scratch, int block) } } } + // If current data can't be a known sequence, return next raw char if (!maybe) break; } @@ -122,6 +176,8 @@ int scan_key(char *scratch, int block) // 30 miliseconds is about the gap between characters at 300 baud if (maybe || !block) if (!xpoll(&pfd, 1, 30*maybe)) break; + // Read 1 byte so we don't overshoot sequence match. (We can deviate + // and fail to match, but match consumes entire buffer.) if (1 != read(0, scratch+1+*scratch, 1)) return -1; ++*scratch; } @@ -516,13 +516,12 @@ void loopfiles_rw(char **argv, int flags, int permissions, int failok, // Inability to open a file prints a warning, but doesn't exit. if (!strcmp(*argv, "-")) fd=0; - else if (0>(fd = open(*argv, flags, permissions)) && !failok) { + else if (0>(fd = open(*argv, flags, permissions)) && !failok) perror_msg("%s", *argv); - toys.exitval = 1; - continue; + else { + function(fd, *argv); + if (flags & O_CLOEXEC) close(fd); } - function(fd, *argv); - if (flags & O_CLOEXEC) close(fd); } while (*++argv); } @@ -216,6 +216,7 @@ int draw_rstr(char *start, int width, int (*escout)(FILE *out, wchar_t wc)); // interestingtimes.c int xgettty(void); int terminal_size(unsigned *xx, unsigned *yy); +int scan_key_getsize(char *scratch, int block, unsigned *xx, unsigned *yy); int set_terminal(int fd, int raw, struct termios *old); int scan_key(char *scratch, int block); void tty_esc(char *s); @@ -223,17 +224,6 @@ void tty_jump(int x, int y); void tty_reset(void); void tty_sigreset(int i); -// Results from scan_key() -#define KEY_UP 256 -#define KEY_DOWN 257 -#define KEY_RIGHT 258 -#define KEY_LEFT 259 -#define KEY_PGUP 260 -#define KEY_PGDN 261 -#define KEY_HOME 262 -#define KEY_END 263 -#define KEY_INSERT 264 - // net.c int xsocket(int domain, int type, int protocol); void xsetsockopt(int fd, int level, int opt, void *val, socklen_t len); diff --git a/lib/linestack.c b/lib/linestack.c index b06c97e7..cc6b6724 100644 --- a/lib/linestack.c +++ b/lib/linestack.c @@ -88,7 +88,7 @@ int crunch_str(char **str, int width, FILE *out, for (end = start = *str; *end;) { wchar_t wc; - if (width>=0 && columns+lowlen>width) break; + if (columns+lowlen>width) break; bytes = mbrtowc(&wc, end, 99, 0); if (bytes<0 || wc<32 || (col = wcwidth(wc))<0) { diff --git a/scripts/make.sh b/scripts/make.sh index e1bc399a..efa70904 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -220,7 +220,7 @@ then echo -n "generated/tags.h " sed -n '/TAGGED_ARRAY(/,/^)/{s/.*TAGGED_ARRAY[(]\([^,]*\),/\1/;p}' \ - toys/*/*.c | generated/mktags > generated/tags.h + toys/*/*.c lib/*.c | generated/mktags > generated/tags.h fi echo "generated/help.h" diff --git a/toys/example/test_scankey.c b/toys/example/test_scankey.c new file mode 100644 index 00000000..65c07204 --- /dev/null +++ b/toys/example/test_scankey.c @@ -0,0 +1,67 @@ +/* test_scankey.c - collate incoming ansi escape sequences. + * + * Copyright 2015 Rob Landley <rob@landley.net> + +USE_TEST_SCANKEY(NEWTOY(test_scankey, 0, 0)) + +config TEST_SCANKEY + bool "test_scankey" + default n + help + usage: test_scankey + + Move a letter around the screen. Hit ESC to exit. +*/ + +#define FOR_test_scankey +#include "toys.h" + +void test_scankey_main(void) +{ + time_t t[2]; + unsigned width, height, tick; + char c = 'X', scratch[16]; + int key, x, y; + + t[0] = t[1] = x = tick = 0; + memset(scratch, 0, 16); + y = 1; + + sigatexit(tty_sigreset); // Make ctrl-c restore tty + tty_esc("?25l"); // hide cursor + tty_esc("0m"); // reset color to default + tty_esc("2J"); // Clear screen + set_terminal(1, 1, 0); // Raw mode + + for (;;) { + tty_jump(x, y); + xputc(c); + t[1&++tick] = time(0); + key = scan_key_getsize(scratch, !!t[0]+2*(t[0] != t[1]), &width, &height); + tty_jump(0, 0); + printf("ESC to exit: key=%d x=%d y=%d width=%d height=%d ", + key, x, y, width, height); + + if (key == -2) continue; + if (key <= ' ') break; + if (key>=256) { + tty_jump(x, y); + xputc(' '); + + key -= 256; + if (key==KEY_UP) y--; + else if (key==KEY_DOWN) y++; + else if (key==KEY_RIGHT) x++; + else if (key==KEY_LEFT) x--; + else if (key==KEY_PGUP) y = 0; + else if (key==KEY_PGDN) y = 999; + else if (key==KEY_HOME) x = 0; + else if (key==KEY_END) x = 999; + if (y<1) y = 1; + if (y>=height) y = height-1; + if (x<0) x = 0; + if (x>=width) x = width-1; + } else c = key; + } + tty_reset(); +} diff --git a/toys/other/hexedit.c b/toys/other/hexedit.c index 1c2ad88e..b3bde2e5 100644 --- a/toys/other/hexedit.c +++ b/toys/other/hexedit.c @@ -213,16 +213,21 @@ void hexedit_main(void) pos = ll[--TT.undo]; TT.data[pos] = toybuf[sizeof(long long)*UNDO_LEN+TT.undo]; } - } else if (key==KEY_UP) pos -= 16; - else if (key==KEY_DOWN) pos += 16; - else if (key==KEY_RIGHT) { - if (x<15) pos++; - } else if (key==KEY_LEFT) { - if (x) pos--; - } else if (key==KEY_PGUP) pos -= 16*TT.height; - else if (key==KEY_PGDN) pos += 16*TT.height; - else if (key==KEY_HOME) pos = 0; - else if (key==KEY_END) pos = TT.len-1; + } + if (key>256) { + key -= 256; + + if (key==KEY_UP) pos -= 16; + else if (key==KEY_DOWN) pos += 16; + else if (key==KEY_RIGHT) { + if (x<15) pos++; + } else if (key==KEY_LEFT) { + if (x) pos--; + } else if (key==KEY_PGUP) pos -= 16*TT.height; + else if (key==KEY_PGDN) pos += 16*TT.height; + else if (key==KEY_HOME) pos = 0; + else if (key==KEY_END) pos = TT.len-1; + } } munmap(TT.data, TT.len); close(fd); |