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

Last change on this file since 2143 was 2143, checked in by Sam Hocevar, 13 years ago
  • Require libcaca >= 0.99beta13.
  • Update event code to comply with the last API change.
  • Property svn:keywords set to Id
File size: 10.5 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 2143 2007-12-16 12:08:27Z 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;
46    int eof = 0, refresh = 1, command = 0, mini = 1;
47
48    if(argc < 2)
49    {
50        fprintf(stderr, "usage: %s <cmd1> [<cmd2> [<cmd3> ...]]\n", argv[0]);
51        fprintf(stderr, "eg. \"%s zsh bash\"\n", argv[0]);
52        return 1;
53    }
54
55    cv = cucul_create_canvas(0, 0);
56    dp = caca_create_display(cv);
57    if(!dp)
58        return 1;
59    caca_set_cursor(dp, 1);
60
61    n = argc - 1;
62    screen = malloc(n * sizeof(struct screen));
63
64    w = cucul_get_canvas_width(cv);
65    h = cucul_get_canvas_height(cv);
66
67    w = w <= XTAB * n ? 1 : w - XTAB * (n - 1) - 2;
68    h = h <= YTAB * n ? 1 : h - YTAB * (n - 1) - 2 - 6;
69
70    for(i = 0; i < n; i++)
71    {
72        screen[i].cv = cucul_create_canvas(w, h);
73        screen[i].init = 0;
74    }
75
76    cucul_set_color_ansi(cv, CUCUL_LIGHTRED, CUCUL_BLACK);
77    cucul_draw_cp437_box(cv, (n - 1 - pty) * XTAB, pty * YTAB,
78                         w + (n - 1 - pty) * XTAB + 1, h + pty * YTAB + 1);
79
80    caca_refresh_display(dp);
81
82    for(i = 0; i < n; i++)
83    {
84        screen[i].buf = NULL;
85        screen[i].total = 0;
86        screen[i].fd = create_pty(argv[i + 1], w, h);
87        if(screen[i].fd < 0)
88            return -1;
89    }
90
91    for(;;)
92    {
93        struct timeval tv;
94        fd_set fdset;
95        caca_event_t ev;
96        int maxfd = 0, ret;
97
98        /* Read data, if any */
99        FD_ZERO(&fdset);
100        for(i = 0; i < n; i++)
101        {
102            FD_SET(screen[i].fd, &fdset);
103            if(screen[i].fd > maxfd)
104                maxfd = screen[i].fd;
105        }
106        tv.tv_sec = 0;
107        tv.tv_usec = 50000;
108        ret = select(maxfd + 1, &fdset, NULL, NULL, &tv);
109
110        if(ret < 0)
111        {
112            if(errno == EINTR)
113                ; /* We probably got a SIGWINCH, ignore it */
114            else
115            {
116                for(i = 0; i < n; i++)
117                    if(screen[i].total)
118                        break;
119                if(i == n)
120                    break;
121            }
122        }
123        else if(ret) for(i = 0; i < n; i++)
124        {
125            /* FIXME: try a new strategy: read all filedescriptors until
126             * each of them starved at least once. */
127
128            if(!FD_ISSET(screen[i].fd, &fdset))
129                continue;
130
131            for(;;)
132            {
133                ssize_t nr;
134
135                screen[i].buf = realloc(screen[i].buf, screen[i].total + 1024);
136                nr = read(screen[i].fd, screen[i].buf + screen[i].total, 1024);
137
138                if(nr > 0)
139                {
140                    screen[i].total += nr;
141                    continue;
142                }
143
144                if(nr == 0 || errno != EWOULDBLOCK)
145                    eof = 1;
146
147                break;
148            }
149        }
150
151        for(i = 0; i < n; i++) if(screen[i].total)
152        {
153            unsigned long int bytes;
154
155            bytes = import_term(&screen[i], screen[i].buf, screen[i].total);
156
157            if(bytes > 0)
158            {
159                screen[i].total -= bytes;
160                memmove(screen[i].buf, screen[i].buf + bytes, screen[i].total);
161                refresh = 1;
162            }
163        }
164
165        /* Get events, if any */
166        ret = caca_get_event(dp, CACA_EVENT_ANY, &ev, 0);
167        if(ret && (caca_get_event_type(&ev) & CACA_EVENT_KEY_PRESS))
168        {
169            unsigned int c = caca_get_event_key_ch(&ev);
170            if(command)
171            {
172                command = 0;
173
174                switch(c)
175                {
176                case 0x01: //CACA_KEY_CTRL_A:
177                    pty ^= prevpty;
178                    prevpty ^= pty;
179                    pty ^= prevpty;
180                    refresh = 1;
181                    break;
182                case 'm':
183                case 0x0d: //CACA_KEY_CTRL_M:
184                    mini = !mini;
185                    refresh = 1;
186                    break;
187                case 'n':
188                case ' ':
189                case '\0':
190                case 0x0e: //CACA_KEY_CTRL_N:
191                    prevpty = pty;
192                    pty = (pty + 1) % n;
193                    refresh = 1;
194                    break;
195                case 'p':
196                case 0x10: //CACA_KEY_CTRL_P:
197                    prevpty = pty;
198                    pty = (pty + n - 1) % n;
199                    refresh = 1;
200                    break;
201                }
202            }
203            else
204            {
205                switch(c)
206                {
207                case 0x01: //CACA_KEY_CTRL_A:
208                    command = 1; break;
209                case CACA_KEY_UP:
210                    write(screen[pty].fd, "\x1b[A", 3); break;
211                case CACA_KEY_DOWN:
212                    write(screen[pty].fd, "\x1b[B", 3); break;
213                case CACA_KEY_RIGHT:
214                    write(screen[pty].fd, "\x1b[C", 3); break;
215                case CACA_KEY_LEFT:
216                    write(screen[pty].fd, "\x1b[D", 3); break;
217                default:
218                    write(screen[pty].fd, &c, 1); break;
219                }
220            }
221        }
222        else if(ret && (caca_get_event_type(&ev) & CACA_EVENT_RESIZE))
223        {
224            w = cucul_get_canvas_width(cv);
225            h = cucul_get_canvas_height(cv);
226            w = w <= XTAB * n ? 1 : w - XTAB * (n - 1) - 2;
227            h = h <= YTAB * n ? 1 : h - YTAB * (n - 1) - 2;
228            for(i = 0; i < n; i++)
229            {
230                cucul_set_canvas_size(screen[i].cv, w, h);
231                set_tty_size(screen[i].fd, w, h);
232            }
233            cucul_clear_canvas(cv);
234            refresh = 1;
235        }
236
237        /* Refresh screen */
238        if(refresh)
239        {
240            refresh = 0;
241
242            cucul_set_color_ansi(cv, CUCUL_LIGHTRED, CUCUL_BLACK);
243
244            for(i = 0; i < n; i++)
245            {
246                int j = (pty + n - 1 - i) % n;
247                cucul_blit(cv, (n - 1 - j) * XTAB + 1,
248                               j * YTAB + 1, screen[j].cv, NULL);
249                cucul_draw_cp437_box(cv, (n - 1 - j) * XTAB, j * YTAB,
250                                     w + (n - 1 - j) * XTAB + 1, h + j * YTAB + 1);
251                /* Set cursor to the correct position */
252                if(i == n - 1)
253                    cucul_gotoxy(cv, (n - 1 - j) * XTAB + 1
254                                        + cucul_get_cursor_x(screen[j].cv),
255                                     j * YTAB + 1
256                                        + cucul_get_cursor_y(screen[j].cv));
257            }
258
259            if(mini)
260            {
261                char const * const *fonts;
262                cucul_dither_t *d;
263                cucul_font_t *f;
264                uint8_t *buf;
265                int miniw, minih;
266
267                fonts = cucul_get_font_list();
268                f = cucul_load_font(fonts[0], 0);
269
270                miniw = cucul_get_canvas_width(screen[0].cv)
271                         * cucul_get_font_width(f);
272                minih = cucul_get_canvas_height(screen[0].cv)
273                         * cucul_get_font_height(f);
274                buf = malloc(4 * miniw * minih);
275
276#if defined(HAVE_ENDIAN_H)
277                if(__BYTE_ORDER == __BIG_ENDIAN)
278#else
279                /* This is compile-time optimised with at least -O1 or -Os */
280                uint32_t const tmp = 0x12345678;
281                if(*(uint8_t const *)&tmp == 0x12)
282#endif
283                    d = cucul_create_dither(32, miniw, minih, 4 * miniw,
284                                            0xff0000, 0xff00, 0xff, 0x0);
285                else
286                    d = cucul_create_dither(32, miniw, minih, 4 * miniw,
287                                            0xff00, 0xff0000, 0xff000000, 0x0);
288
289                for(i = 0; i < n; i++)
290                {
291                    cucul_render_canvas(screen[i].cv, f, buf,
292                                        miniw, minih, miniw * 4);
293                    cucul_dither_bitmap(cv, 20 * i,
294                              cucul_get_canvas_height(cv) - 6, 19, 6, d, buf);
295                    cucul_set_color_ansi(cv, CUCUL_WHITE, CUCUL_BLUE);
296                    cucul_printf(cv, 20 * i,
297                                 cucul_get_canvas_height(cv) - 6, "(%i)", i);
298                }
299
300                cucul_free_dither(d);
301                cucul_free_font(f);
302
303                free(buf);
304            }
305
306            caca_refresh_display(dp);
307        }
308
309        if(eof)
310        {
311            for(i = 0; i < n; i++)
312                if(screen[i].total)
313                    break;
314            if(i == n)
315                break;
316        }
317    }
318
319    caca_get_event(dp, CACA_EVENT_KEY_PRESS, NULL, -1);
320
321    /* Clean up */
322    caca_free_display(dp);
323    cucul_free_canvas(cv);
324    for(i = 0; i < n; i++)
325        cucul_free_canvas(screen[i].cv);
326
327    return 0;
328}
329
330static int create_pty(char *cmd, unsigned int w, unsigned int h)
331{
332    char **argv;
333    int fd;
334    pid_t pid;
335
336    pid = forkpty(&fd, NULL, NULL, NULL);
337    if(pid < 0)
338    {
339        fprintf(stderr, "forkpty() error\n");
340        return -1;
341    }
342    else if(pid == 0)
343    {
344        set_tty_size(0, w, h);
345        putenv("CACA_DRIVER=slang");
346        putenv("TERM=xterm");
347        argv = malloc(2 * sizeof(char *));
348        argv[0] = cmd;
349        argv[1] = NULL;
350        execvp(cmd, argv);
351        fprintf(stderr, "execvp() error\n");
352        return -1;
353    }
354
355    fcntl(fd, F_SETFL, O_NDELAY);
356    return fd;
357#if 0
358    fprintf(stderr, "forkpty() not available\n");
359    return -1;
360#endif
361}
362
363static int set_tty_size(int fd, unsigned int w, unsigned int h)
364{
365    struct winsize ws;
366
367    memset(&ws, 0, sizeof(ws));
368    ws.ws_row = h;
369    ws.ws_col = w;
370    ioctl(fd, TIOCSWINSZ, (char *)&ws);
371
372    return 0;
373}
374
Note: See TracBrowser for help on using the repository browser.