| @@ -20,150 +20,230 @@ | |||||
| # include <stdlib.h> | # include <stdlib.h> | ||||
| # include <unistd.h> | # include <unistd.h> | ||||
| # include <fcntl.h> | # include <fcntl.h> | ||||
| # include <sys/ioctl.h> | |||||
| # if defined HAVE_PTY_H | # if defined HAVE_PTY_H | ||||
| # include <pty.h> /* for openpty and forkpty */ | # include <pty.h> /* for openpty and forkpty */ | ||||
| # else | |||||
| # include <util.h> /* for OS X */ | |||||
| # endif | # endif | ||||
| #endif | #endif | ||||
| #include "cucul.h" | #include "cucul.h" | ||||
| #include "caca.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) | int main(int argc, char **argv) | ||||
| { | { | ||||
| static cucul_canvas_t *cv, *app[2]; | |||||
| static cucul_canvas_t *cv; | |||||
| static caca_display_t *dp; | 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; | return 1; | ||||
| } | } | ||||
| cv = cucul_create_canvas(80 + 7, 24 + 7); | |||||
| cv = cucul_create_canvas(0, 0); | |||||
| dp = caca_create_display(cv); | dp = caca_create_display(cv); | ||||
| if(!dp) | if(!dp) | ||||
| return 1; | 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_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); | 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(;;) | for(;;) | ||||
| { | { | ||||
| struct timeval tv; | struct timeval tv; | ||||
| fd_set fdset; | fd_set fdset; | ||||
| caca_event_t ev; | 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 */ | /* Read data, if any */ | ||||
| FD_ZERO(&fdset); | 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_sec = 0; | ||||
| tv.tv_usec = 50000; | 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(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; | 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) | if(n > 0) | ||||
| total[i] += n; | |||||
| screen[i].total += n; | |||||
| else if(n == 0 || errno != EWOULDBLOCK) | else if(n == 0 || errno != EWOULDBLOCK) | ||||
| eof = 1; | 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; | 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) | 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; | 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) | 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); | 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); | caca_get_event(dp, CACA_EVENT_KEY_PRESS, NULL, -1); | ||||
| @@ -171,15 +251,14 @@ int main(int argc, char **argv) | |||||
| /* Clean up */ | /* Clean up */ | ||||
| caca_free_display(dp); | caca_free_display(dp); | ||||
| cucul_free_canvas(cv); | 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; | 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; | char **argv; | ||||
| int fd; | int fd; | ||||
| pid_t pid; | pid_t pid; | ||||
| @@ -192,7 +271,9 @@ static int run_in_pty(char *cmd) | |||||
| } | } | ||||
| else if(pid == 0) | else if(pid == 0) | ||||
| { | { | ||||
| set_tty_size(0, w, h); | |||||
| putenv("CACA_DRIVER=slang"); | putenv("CACA_DRIVER=slang"); | ||||
| putenv("TERM=xterm"); | |||||
| argv = malloc(2 * sizeof(char *)); | argv = malloc(2 * sizeof(char *)); | ||||
| argv[0] = cmd; | argv[0] = cmd; | ||||
| argv[1] = NULL; | argv[1] = NULL; | ||||
| @@ -201,10 +282,23 @@ static int run_in_pty(char *cmd) | |||||
| return -1; | return -1; | ||||
| } | } | ||||
| fcntl(fd, F_SETFL, O_NDELAY); | |||||
| return fd; | return fd; | ||||
| #else | |||||
| #if 0 | |||||
| fprintf(stderr, "forkpty() not available\n"); | fprintf(stderr, "forkpty() not available\n"); | ||||
| return -1; | return -1; | ||||
| #endif | #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; | |||||
| } | |||||