diff --git a/test/term.c b/test/term.c index 7d0a81d..815f4c8 100644 --- a/test/term.c +++ b/test/term.c @@ -20,150 +20,230 @@ # include <stdlib.h> # include <unistd.h> # include <fcntl.h> +# include <sys/ioctl.h> # if defined HAVE_PTY_H # include <pty.h> /* for openpty and forkpty */ +# else +# include <util.h> /* for OS X */ # endif #endif #include "cucul.h" #include "caca.h" -static int run_in_pty(char *cmd); +#define XTAB 3 +#define YTAB 2 + +struct screen +{ + cucul_canvas_t *cv; + int fd; + unsigned char *buf; + long int total; + int w, h; +}; + +static int create_pty(char *cmd, unsigned int w, unsigned int h); +static int set_tty_size(int fd, unsigned int w, unsigned int h); int main(int argc, char **argv) { - static cucul_canvas_t *cv, *app[2]; + static cucul_canvas_t *cv; static caca_display_t *dp; - unsigned char *buf[2]; - long int total[2]; - int ptyfd[2], pty = 0; - int w, h, eof = 0; + static struct screen *screen; + int pty = 0, prevpty = 0, i, n, w, h, eof = 0, refresh = 1, command = 0; - if(argc < 3) + if(argc < 2) { - fprintf(stderr, "usage: %s <cmd1> <cmd2>\n", argv[0]); - fprintf(stderr, "eg. \"%s bash bash\"\n", argv[0]); + fprintf(stderr, "usage: %s <cmd1> [<cmd2> [<cmd3> ...]]\n", argv[0]); + fprintf(stderr, "eg. \"%s zsh bash\"\n", argv[0]); return 1; } - cv = cucul_create_canvas(80 + 7, 24 + 7); + cv = cucul_create_canvas(0, 0); dp = caca_create_display(cv); if(!dp) return 1; -// w = cucul_get_canvas_width(cv) - 7; -// h = cucul_get_canvas_height(cv) - 7; - w = 80; h = 24; + n = argc - 1; + screen = malloc(n * sizeof(struct screen)); - if(w < 0 || h < 0) - return 1; + w = cucul_get_canvas_width(cv); + h = cucul_get_canvas_height(cv); + + w = w <= XTAB * n ? 1 : w - XTAB * n; + h = h <= YTAB * n ? 1 : h - YTAB * n; - app[0] = cucul_create_canvas(w, h); - app[1] = cucul_create_canvas(w, h); + for(i = 0; i < n; i++) + screen[i].cv = cucul_create_canvas(w, h); cucul_set_color_ansi(cv, CUCUL_LIGHTRED, CUCUL_BLACK); - cucul_draw_thin_box(cv, pty * 5, pty * 5, - 80 + pty * 5 + 1, 24 + pty * 5 + 1); + cucul_draw_cp437_box(cv, (n - 1 - pty) * XTAB, pty * YTAB, + w + (n - 1 - pty) * XTAB + 1, h + pty * YTAB + 1); caca_refresh_display(dp); - buf[0] = buf[1] = NULL; - total[0] = total[1] = 0; - - ptyfd[0] = run_in_pty(argv[1]); - ptyfd[1] = run_in_pty(argv[2]); - - if(ptyfd[0] < 0 || ptyfd[1] < 0) - return -1; - - fcntl(ptyfd[0], F_SETFL, O_NDELAY); - fcntl(ptyfd[1], F_SETFL, O_NDELAY); + for(i = 0; i < n; i++) + { + screen[i].buf = NULL; + screen[i].total = 0; + screen[i].fd = create_pty(argv[i + 1], w, h); + if(screen[i].fd < 0) + return -1; + } for(;;) { struct timeval tv; fd_set fdset; caca_event_t ev; - int i, ret, refresh = 0; - - ret = caca_get_event(dp, CACA_EVENT_ANY, &ev, 0); - if(ret && ev.type & CACA_EVENT_KEY_PRESS) - { - switch(ev.data.key.ch) - { - case CACA_KEY_CTRL_A: - cucul_draw_box(cv, pty * 5, pty * 5, - 80 + pty * 5 + 1, 24 + pty * 5 + 1, ' '); - pty = 1 - pty; - refresh = 1; - break; - case CACA_KEY_UP: - write(ptyfd[pty], "\x1b[A", 3); break; - case CACA_KEY_DOWN: - write(ptyfd[pty], "\x1b[B", 3); break; - case CACA_KEY_RIGHT: - write(ptyfd[pty], "\x1b[C", 3); break; - case CACA_KEY_LEFT: - write(ptyfd[pty], "\x1b[D", 3); break; - default: - write(ptyfd[pty], &ev.data.key.ch, 1); break; - } - } + int i, maxfd = 0, ret; /* Read data, if any */ FD_ZERO(&fdset); - FD_SET(ptyfd[0], &fdset); - FD_SET(ptyfd[1], &fdset); + for(i = 0; i < n; i++) + { + FD_SET(screen[i].fd, &fdset); + if(screen[i].fd > maxfd) + maxfd = screen[i].fd; + } tv.tv_sec = 0; tv.tv_usec = 50000; - ret = select(ptyfd[0] + ptyfd[1] + 1, &fdset, NULL, NULL, &tv); + ret = select(maxfd + 1, &fdset, NULL, NULL, &tv); if(ret < 0) { - if(!total[0] && !total[1]) - break; + if(errno == EINTR) + ; /* We probably got a SIGWINCH, ignore it */ + else + { + for(i = 0; i < n; i++) + if(screen[i].total) + break; + if(i == n) + break; + } } - else if(ret) for(i = 0; i < 2; i++) + else if(ret) for(i = 0; i < n; i++) { - if(FD_ISSET(ptyfd[i], &fdset)) + if(FD_ISSET(screen[i].fd, &fdset)) { ssize_t n; - buf[i] = realloc(buf[i], total[i] + 4096); - n = read(ptyfd[i], buf[i] + total[i], 4096); + screen[i].buf = realloc(screen[i].buf, screen[i].total + 4096); + n = read(screen[i].fd, screen[i].buf + screen[i].total, 4096); if(n > 0) - total[i] += n; + screen[i].total += n; else if(n == 0 || errno != EWOULDBLOCK) eof = 1; } } - for(i = 0; i < 2; i++) if(total[i]) + for(i = 0; i < n; i++) if(screen[i].total) { unsigned long int bytes; - bytes = cucul_import_memory(app[i], buf[i], total[i], "utf8"); + bytes = cucul_import_memory(screen[i].cv, screen[i].buf, screen[i].total, "utf8"); if(bytes > 0) { - total[i] -= bytes; - memmove(buf[i], buf[i] + bytes, total[i]); + screen[i].total -= bytes; + memmove(screen[i].buf, screen[i].buf + bytes, screen[i].total); refresh = 1; } } + /* Get events, if any */ + ret = caca_get_event(dp, CACA_EVENT_ANY, &ev, 0); + if(ret && (ev.type & CACA_EVENT_KEY_PRESS)) + { + if(command) + { + command = 0; + + switch(ev.data.key.ch) + { + case CACA_KEY_CTRL_A: + pty ^= prevpty; + prevpty ^= pty; + pty ^= prevpty; + refresh = 1; + break; + case 'n': + case ' ': + case '\0': + case CACA_KEY_CTRL_N: + prevpty = pty; + pty = (pty + 1) % n; + refresh = 1; + break; + case 'p': + case CACA_KEY_CTRL_P: + prevpty = pty; + pty = (pty + n - 1) % n; + refresh = 1; + break; + } + } + else + { + switch(ev.data.key.ch) + { + case CACA_KEY_CTRL_A: + command = 1; break; + case CACA_KEY_UP: + write(screen[pty].fd, "\x1b[A", 3); break; + case CACA_KEY_DOWN: + write(screen[pty].fd, "\x1b[B", 3); break; + case CACA_KEY_RIGHT: + write(screen[pty].fd, "\x1b[C", 3); break; + case CACA_KEY_LEFT: + write(screen[pty].fd, "\x1b[D", 3); break; + default: + write(screen[pty].fd, &ev.data.key.ch, 1); break; + } + } + } + else if(ret && (ev.type & CACA_EVENT_RESIZE)) + { + w = cucul_get_canvas_width(cv); + h = cucul_get_canvas_height(cv); + w = w <= XTAB * n ? 1 : w - XTAB * n; + h = h <= YTAB * n ? 1 : h - YTAB * n; + for(i = 0; i < n; i++) + { + cucul_set_canvas_size(screen[i].cv, w, h); + set_tty_size(screen[i].fd, w, h); + } + cucul_clear_canvas(cv); + refresh = 1; + } + + /* Refresh screen */ if(refresh) { - cucul_blit(cv, 5 * !pty + 1, 5 * !pty + 1, app[!pty], NULL); - cucul_blit(cv, 5 * pty + 1, 5 * pty + 1, app[pty], NULL); - cucul_draw_thin_box(cv, pty * 5, pty * 5, - 80 + pty * 5 + 1, 24 + pty * 5 + 1); + refresh = 0; + + for(i = 0; i < n; i++) + { + int j = (pty + n - 1 - i) % n; + cucul_blit(cv, (n - 1 - j) * XTAB + 1, + j * YTAB + 1, screen[j].cv, NULL); + cucul_draw_cp437_box(cv, (n - 1 - j) * XTAB, j * YTAB, + w + (n - 1 - j) * XTAB + 1, h + j * YTAB + 1); + } caca_refresh_display(dp); } - if(eof && !total[0] && !total[1]) - break; + if(eof) + { + for(i = 0; i < n; i++) + if(screen[i].total) + break; + if(i == n) + break; + } } caca_get_event(dp, CACA_EVENT_KEY_PRESS, NULL, -1); @@ -171,15 +251,14 @@ int main(int argc, char **argv) /* Clean up */ caca_free_display(dp); cucul_free_canvas(cv); - cucul_free_canvas(app[0]); - cucul_free_canvas(app[1]); + for(i = 0; i < n; i++) + cucul_free_canvas(screen[i].cv); return 0; } -static int run_in_pty(char *cmd) +static int create_pty(char *cmd, unsigned int w, unsigned int h) { -#if defined HAVE_PTY_H char **argv; int fd; pid_t pid; @@ -192,7 +271,9 @@ static int run_in_pty(char *cmd) } else if(pid == 0) { + set_tty_size(0, w, h); putenv("CACA_DRIVER=slang"); + putenv("TERM=xterm"); argv = malloc(2 * sizeof(char *)); argv[0] = cmd; argv[1] = NULL; @@ -201,10 +282,23 @@ static int run_in_pty(char *cmd) return -1; } + fcntl(fd, F_SETFL, O_NDELAY); return fd; -#else +#if 0 fprintf(stderr, "forkpty() not available\n"); return -1; #endif } +static int set_tty_size(int fd, unsigned int w, unsigned int h) +{ + struct winsize ws; + + memset(&ws, 0, sizeof(ws)); + ws.ws_row = h; + ws.ws_col = w; + ioctl(fd, TIOCSWINSZ, (char *)&ws); + + return 0; +} +