#include #include #include #include #include #include #include #include #include #include #define ESC "\033" #define CSI ESC "[" #define prn(...) dprintf(ttyfd, __VA_ARGS__) int ttyfd; /* enable/disable "raw" mode */ void raw(bool enable) { int r; static struct termios orig; struct termios raw; if (enable) { r = tcgetattr(0, &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(0, TCSAFLUSH, &raw); if (r == -1) err(1, "tcsetattr"); } else tcsetattr(0, 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; } void up() {} void down() {} void right() {} void left() {} int main() { char c, *p; int i, r; struct winsize w; int height; /* height of output in rows */ int x, y; /* original cursor position in cols, rows */ char *ex = "file1 file2 file3\r\nfile4 file5 file6\r\nfile7"; 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 */ prn("%s", ex); /* 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); 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; } } quit: prn(CSI "J"); /* delete from cursor to end of display (ED) */ raw(false); }