From 4b4ab6a50998219cd94139c5669ef9a624c8f58f Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Sun, 27 Dec 2015 14:41:30 -0600 Subject: Add scan_key_getsize() doing the ANSI probe, switch scan_key() to TAGGED_ARRAY, and add test_scankey. --- lib/interestingtimes.c | 68 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 6 deletions(-) (limited to 'lib/interestingtimes.c') 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