| @@ -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; | |||
| } | |||