source: neercs/trunk/src/main.c @ 1459

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