aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2015-12-27 14:41:30 -0600
committerRob Landley <rob@landley.net>2015-12-27 14:41:30 -0600
commit4b4ab6a50998219cd94139c5669ef9a624c8f58f (patch)
tree3a1e0812da0cfc3c8f8040a381e763531c1026ba
parent6769f8eb580aa2ecac4009fcde4a113e0476de74 (diff)
downloadtoybox-4b4ab6a50998219cd94139c5669ef9a624c8f58f.tar.gz
Add scan_key_getsize() doing the ANSI probe, switch scan_key() to TAGGED_ARRAY,
and add test_scankey.
-rw-r--r--lib/interestingtimes.c68
-rw-r--r--lib/lib.c9
-rw-r--r--lib/lib.h12
-rw-r--r--lib/linestack.c2
-rwxr-xr-xscripts/make.sh2
-rw-r--r--toys/example/test_scankey.c67
-rw-r--r--toys/other/hexedit.c25
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;
}
diff --git a/lib/lib.c b/lib/lib.c
index a16439e7..5ee7325c 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -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);
}
diff --git a/lib/lib.h b/lib/lib.h
index 19992b98..5f70c8cc 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -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);