source: libcaca/trunk/test/term.c @ 1432

Last change on this file since 1432 was 1432, checked in by Sam Hocevar, 13 years ago
  • A few improvements to the screen/splitvt clone project.
  • Property svn:keywords set to Id
File size: 7.8 KB
Line 
1/*
2 *  term          swallow a terminal-based application
3 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id: term.c 1432 2006-11-27 01:58:00Z sam $
7 *
8 *  This program is free software; you can redistribute it and/or
9 *  modify it under the terms of the Do What The Fuck You Want To
10 *  Public License, Version 2, as published by Sam Hocevar. See
11 *  http://sam.zoy.org/wtfpl/COPYING for more details.
12 */
13
14#include "config.h"
15#include "common.h"
16
17#if !defined __KERNEL__
18#   include <stdio.h>
19#   include <string.h>
20#   include <stdlib.h>
21#   include <unistd.h>
22#   include <fcntl.h>
23#   include <sys/ioctl.h>
24#   if defined HAVE_PTY_H
25#       include <pty.h>  /* for openpty and forkpty */
26#   else
27#       include <util.h> /* for OS X */
28#   endif
29#endif
30
31#include "cucul.h"
32#include "caca.h"
33
34#define XTAB 3
35#define YTAB 2
36
37struct screen
38{
39    cucul_canvas_t *cv;
40    int fd;
41    unsigned char *buf;
42    long int total;
43    int w, h;
44};
45
46static int create_pty(char *cmd, unsigned int w, unsigned int h);
47static int set_tty_size(int fd, unsigned int w, unsigned int h);
48
49int main(int argc, char **argv)
50{
51    static cucul_canvas_t *cv;
52    static caca_display_t *dp;
53    static struct screen *screen;
54    int pty = 0, prevpty = 0, i, n, w, h, eof = 0, refresh = 1, command = 0;
55
56    if(argc < 2)
57    {
58        fprintf(stderr, "usage: %s <cmd1> [<cmd2> [<cmd3> ...]]\n", argv[0]);
59        fprintf(stderr, "eg. \"%s zsh bash\"\n", argv[0]);
60        return 1;
61    }
62
63    cv = cucul_create_canvas(0, 0);
64    dp = caca_create_display(cv);
65    if(!dp)
66        return 1;
67
68    n = argc - 1;
69    screen = malloc(n * sizeof(struct screen));
70
71    w = cucul_get_canvas_width(cv);
72    h = cucul_get_canvas_height(cv);
73
74    w = w <= XTAB * n ? 1 : w - XTAB * n;
75    h = h <= YTAB * n ? 1 : h - YTAB * n;
76
77    for(i = 0; i < n; i++)
78        screen[i].cv = cucul_create_canvas(w, h);
79
80    cucul_set_color_ansi(cv, CUCUL_LIGHTRED, CUCUL_BLACK);
81    cucul_draw_cp437_box(cv, (n - 1 - pty) * XTAB, pty * YTAB,
82                         w + (n - 1 - pty) * XTAB + 1, h + pty * YTAB + 1);
83
84    caca_refresh_display(dp);
85
86    for(i = 0; i < n; i++)
87    {
88        screen[i].buf = NULL;
89        screen[i].total = 0;
90        screen[i].fd = create_pty(argv[i + 1], w, h);
91        if(screen[i].fd < 0)
92            return -1;
93    }
94
95    for(;;)
96    {
97        struct timeval tv;
98        fd_set fdset;
99        caca_event_t ev;
100        int i, maxfd = 0, ret;
101
102        /* Read data, if any */
103        FD_ZERO(&fdset);
104        for(i = 0; i < n; i++)
105        {
106            FD_SET(screen[i].fd, &fdset);
107            if(screen[i].fd > maxfd)
108                maxfd = screen[i].fd;
109        }
110        tv.tv_sec = 0;
111        tv.tv_usec = 50000;
112        ret = select(maxfd + 1, &fdset, NULL, NULL, &tv);
113
114        if(ret < 0)
115        {
116            if(errno == EINTR)
117                ; /* We probably got a SIGWINCH, ignore it */
118            else
119            {
120                for(i = 0; i < n; i++)
121                    if(screen[i].total)
122                        break;
123                if(i == n)
124                    break;
125            }
126        }
127        else if(ret) for(i = 0; i < n; i++)
128        {
129            if(FD_ISSET(screen[i].fd, &fdset))
130            {
131                ssize_t n;
132
133                screen[i].buf = realloc(screen[i].buf, screen[i].total + 4096);
134                n = read(screen[i].fd, screen[i].buf + screen[i].total, 4096);
135
136                if(n > 0)
137                    screen[i].total += n;
138                else if(n == 0 || errno != EWOULDBLOCK)
139                    eof = 1;
140            }
141        }
142
143        for(i = 0; i < n; i++) if(screen[i].total)
144        {
145            unsigned long int bytes;
146
147            bytes = cucul_import_memory(screen[i].cv, screen[i].buf, screen[i].total, "utf8");
148
149            if(bytes > 0)
150            {
151                screen[i].total -= bytes;
152                memmove(screen[i].buf, screen[i].buf + bytes, screen[i].total);
153                refresh = 1;
154            }
155        }
156
157        /* Get events, if any */
158        ret = caca_get_event(dp, CACA_EVENT_ANY, &ev, 0);
159        if(ret && (ev.type & CACA_EVENT_KEY_PRESS))
160        {
161            if(command)
162            {
163                command = 0;
164
165                switch(ev.data.key.ch)
166                {
167                case CACA_KEY_CTRL_A:
168                    pty ^= prevpty;
169                    prevpty ^= pty;
170                    pty ^= prevpty;
171                    refresh = 1;
172                    break;
173                case 'n':
174                case ' ':
175                case '\0':
176                case CACA_KEY_CTRL_N:
177                    prevpty = pty;
178                    pty = (pty + 1) % n;
179                    refresh = 1;
180                    break;
181                case 'p':
182                case CACA_KEY_CTRL_P:
183                    prevpty = pty;
184                    pty = (pty + n - 1) % n;
185                    refresh = 1;
186                    break;
187                }
188            }
189            else
190            {
191                switch(ev.data.key.ch)
192                {
193                case CACA_KEY_CTRL_A:
194                    command = 1; break;
195                case CACA_KEY_UP:
196                    write(screen[pty].fd, "\x1b[A", 3); break;
197                case CACA_KEY_DOWN:
198                    write(screen[pty].fd, "\x1b[B", 3); break;
199                case CACA_KEY_RIGHT:
200                    write(screen[pty].fd, "\x1b[C", 3); break;
201                case CACA_KEY_LEFT:
202                    write(screen[pty].fd, "\x1b[D", 3); break;
203                default:
204                    write(screen[pty].fd, &ev.data.key.ch, 1); break;
205                }
206            }
207        }
208        else if(ret && (ev.type & CACA_EVENT_RESIZE))
209        {
210            w = cucul_get_canvas_width(cv);
211            h = cucul_get_canvas_height(cv);
212            w = w <= XTAB * n ? 1 : w - XTAB * n;
213            h = h <= YTAB * n ? 1 : h - YTAB * n;
214            for(i = 0; i < n; i++)
215            {
216                cucul_set_canvas_size(screen[i].cv, w, h);
217                set_tty_size(screen[i].fd, w, h);
218            }
219            cucul_clear_canvas(cv);
220            refresh = 1;
221        }
222
223        /* Refresh screen */
224        if(refresh)
225        {
226            refresh = 0;
227
228            for(i = 0; i < n; i++)
229            {
230                int j = (pty + n - 1 - i) % n;
231                cucul_blit(cv, (n - 1 - j) * XTAB + 1,
232                               j * YTAB + 1, screen[j].cv, NULL);
233                cucul_draw_cp437_box(cv, (n - 1 - j) * XTAB, j * YTAB,
234                                     w + (n - 1 - j) * XTAB + 1, h + j * YTAB + 1);
235            }
236            caca_refresh_display(dp);
237        }
238
239        if(eof)
240        {
241            for(i = 0; i < n; i++)
242                if(screen[i].total)
243                    break;
244            if(i == n)
245                break;
246        }
247    }
248
249    caca_get_event(dp, CACA_EVENT_KEY_PRESS, NULL, -1);
250
251    /* Clean up */
252    caca_free_display(dp);
253    cucul_free_canvas(cv);
254    for(i = 0; i < n; i++)
255        cucul_free_canvas(screen[i].cv);
256
257    return 0;
258}
259
260static int create_pty(char *cmd, unsigned int w, unsigned int h)
261{
262    char **argv;
263    int fd;
264    pid_t pid;
265
266    pid = forkpty(&fd, NULL, NULL, NULL);
267    if(pid < 0)
268    {
269        fprintf(stderr, "forkpty() error\n");
270        return -1;
271    }
272    else if(pid == 0)
273    {
274        set_tty_size(0, w, h);
275        putenv("CACA_DRIVER=slang");
276        putenv("TERM=xterm");
277        argv = malloc(2 * sizeof(char *));
278        argv[0] = cmd;
279        argv[1] = NULL;
280        execvp(cmd, argv);
281        fprintf(stderr, "execvp() error\n");
282        return -1;
283    }
284
285    fcntl(fd, F_SETFL, O_NDELAY);
286    return fd;
287#if 0
288    fprintf(stderr, "forkpty() not available\n");
289    return -1;
290#endif
291}
292
293static int set_tty_size(int fd, unsigned int w, unsigned int h)
294{
295    struct winsize ws;
296
297    memset(&ws, 0, sizeof(ws));
298    ws.ws_row = h;
299    ws.ws_col = w;
300    ioctl(fd, TIOCSWINSZ, (char *)&ws);
301
302    return 0;
303}
304
Note: See TracBrowser for help on using the repository browser.