|
|
@@ -1,304 +0,0 @@ |
|
|
|
/* |
|
|
|
* term swallow a terminal-based application |
|
|
|
* Copyright (c) 2006 Sam Hocevar <sam@zoy.org> |
|
|
|
* All Rights Reserved |
|
|
|
* |
|
|
|
* $Id$ |
|
|
|
* |
|
|
|
* This program is free software; you can redistribute it and/or |
|
|
|
* modify it under the terms of the Do What The Fuck You Want To |
|
|
|
* Public License, Version 2, as published by Sam Hocevar. See |
|
|
|
* http://sam.zoy.org/wtfpl/COPYING for more details. |
|
|
|
*/ |
|
|
|
|
|
|
|
#include "config.h" |
|
|
|
#include "common.h" |
|
|
|
|
|
|
|
#if !defined __KERNEL__ |
|
|
|
# include <stdio.h> |
|
|
|
# include <string.h> |
|
|
|
# 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" |
|
|
|
|
|
|
|
#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; |
|
|
|
static caca_display_t *dp; |
|
|
|
static struct screen *screen; |
|
|
|
int pty = 0, prevpty = 0, i, n, w, h, eof = 0, refresh = 1, command = 0; |
|
|
|
|
|
|
|
if(argc < 2) |
|
|
|
{ |
|
|
|
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(0, 0); |
|
|
|
dp = caca_create_display(cv); |
|
|
|
if(!dp) |
|
|
|
return 1; |
|
|
|
|
|
|
|
n = argc - 1; |
|
|
|
screen = malloc(n * sizeof(struct screen)); |
|
|
|
|
|
|
|
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++) |
|
|
|
screen[i].cv = cucul_create_canvas(w, h); |
|
|
|
|
|
|
|
cucul_set_color_ansi(cv, CUCUL_LIGHTRED, CUCUL_BLACK); |
|
|
|
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); |
|
|
|
|
|
|
|
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, maxfd = 0, ret; |
|
|
|
|
|
|
|
/* Read data, if any */ |
|
|
|
FD_ZERO(&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(maxfd + 1, &fdset, NULL, NULL, &tv); |
|
|
|
|
|
|
|
if(ret < 0) |
|
|
|
{ |
|
|
|
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 < n; i++) |
|
|
|
{ |
|
|
|
if(FD_ISSET(screen[i].fd, &fdset)) |
|
|
|
{ |
|
|
|
ssize_t n; |
|
|
|
|
|
|
|
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) |
|
|
|
screen[i].total += n; |
|
|
|
else if(n == 0 || errno != EWOULDBLOCK) |
|
|
|
eof = 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
for(i = 0; i < n; i++) if(screen[i].total) |
|
|
|
{ |
|
|
|
unsigned long int bytes; |
|
|
|
|
|
|
|
bytes = cucul_import_memory(screen[i].cv, screen[i].buf, screen[i].total, "utf8"); |
|
|
|
|
|
|
|
if(bytes > 0) |
|
|
|
{ |
|
|
|
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) |
|
|
|
{ |
|
|
|
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) |
|
|
|
{ |
|
|
|
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); |
|
|
|
|
|
|
|
/* Clean up */ |
|
|
|
caca_free_display(dp); |
|
|
|
cucul_free_canvas(cv); |
|
|
|
for(i = 0; i < n; i++) |
|
|
|
cucul_free_canvas(screen[i].cv); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int create_pty(char *cmd, unsigned int w, unsigned int h) |
|
|
|
{ |
|
|
|
char **argv; |
|
|
|
int fd; |
|
|
|
pid_t pid; |
|
|
|
|
|
|
|
pid = forkpty(&fd, NULL, NULL, NULL); |
|
|
|
if(pid < 0) |
|
|
|
{ |
|
|
|
fprintf(stderr, "forkpty() error\n"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
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; |
|
|
|
execvp(cmd, argv); |
|
|
|
fprintf(stderr, "execvp() error\n"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
fcntl(fd, F_SETFL, O_NDELAY); |
|
|
|
return fd; |
|
|
|
#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; |
|
|
|
} |
|
|
|
|