#include #include #include #include #include #include #include #include #include #include #define ESC "\033" #define CSI ESC "[" #define prn(...) dprintf(ttyfd, __VA_ARGS__) char *lines[3] = { "line 1", "line 2", "line 3" }; int current = -1; /* currently selected item */ int height = 3; /* height of output in rows */ int ttyfd; int x, y; /* original cursor position in cols, rows */ /* enable/disable "raw" mode */ void raw(bool enable) { int r; static struct termios orig; struct termios raw; if (enable) { r = tcgetattr(ttyfd, &orig); if (r == -1) err(1, "tcgetattr"); raw = orig; raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); raw.c_oflag &= ~OPOST; raw.c_cflag |= CS8; raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); r = tcsetattr(ttyfd, TCSAFLUSH, &raw); if (r == -1) err(1, "tcsetattr"); } else tcsetattr(ttyfd, TCSAFLUSH, &orig); } /* cursor position report */ int cpr(int *x, int *y) { char c, xbuf[4], ybuf[4]; int i; prn(CSI "6n"); /* get CSI */ read(1, &c, 1); if (c != '\033') return -1; read(1, &c, 1); if (c != '[') return -1; /* get x */ i = 0; while (read(1, &c, 1) && c != ';') { if (i > 3 || !isdigit(c)) return -1; ybuf[i++] = c; } if (i == 0) return -1; ybuf[i] = '\0'; /* get y */ i = 0; while (read(1, &c, 1) && c != 'R') { if (i > 3 || !isdigit(c)) return -1; xbuf[i++] = c; } if (i == 0) return -1; xbuf[i] = '\0'; *x = atoi(xbuf); *y = atoi(ybuf); return 0; } /* select item */ int item(int n) { if (n < 1 || n > height) return -1; if (current != -1) prn("%s", lines[current - 1]); current = n; prn("%s%d;%dH", CSI, y + n - 1, 0); prn(CSI "7m"); prn("%s", lines[n - 1]); prn(CSI "0m"); prn("%s%d;%dH", CSI, y + n - 1, 0); return 0; } int up() { return item(current-1); } int down() { return item(current+1); } int right() {} int left() {} int main() { char c, *p, *phrase; int i, r; struct winsize w; phrase = NULL; ttyfd = open("/dev/tty", O_RDWR); if (ttyfd == -1) err(1, "open"); raw(true); /* save original cursor position */ r = cpr(&x, &y); if (r == -1) goto quit; /* print output */ for (i = 0; i < height; i++) prn("%s\r\n", lines[i]); /* get height of output */ /* p = ex - 1; height = 0; while (*(++p) != '\0') if (*p == '\n') height++; /* get height of terminal */ r = ioctl(ttyfd, TIOCGWINSZ, &w); if (r == -1) err(1, "ioctl"); /* correct cursor position if original cursor was near bottom */ r = y + height - w.ws_row; if (r > 0) y = y - r; /* restore original cursor position (CUP) */ prn("%s%d;%dH", CSI, y, x); /* select first item */ item(1); while (read(0, &c, 1) != 0) { switch (c) { case 'q': goto quit; break; case '\033': read(0, &c, 1); if (c != '[') break; read(0, &c, 1); if (c == 'A') up(); if (c == 'B') down(); if (c == 'C') right(); if (c == 'D') left(); break; case 13: /* enter */ goto quit; phrase = lines[current - 1]; break; } } quit: //prn("%s%d;%dH", CSI, y, x); //prn(CSI "J"); /* delete from cursor to end of display (ED) */ raw(false); if (phrase != NULL) printf("%s\n", phrase); }