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

Last change on this file since 1838 was 1838, checked in by Sam Hocevar, 13 years ago
  • Cursor support in neercs.
  • 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 1838 2007-10-19 22:23:06Z 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 && (ev.type & CACA_EVENT_KEY_PRESS))
168        {
169            if(command)
170            {
171                command = 0;
172
173                switch(ev.data.key.ch)
174                {
175                case 0x01: //CACA_KEY_CTRL_A:
176                    pty ^= prevpty;
177                    prevpty ^= pty;
178                    pty ^= prevpty;
179                    refresh = 1;
180                    break;
181                case 'm':
182                case 0x0d: //CACA_KEY_CTRL_M:
183                    mini = !mini;
184                    refresh = 1;
185                    break;
186                case 'n':
187                case ' ':
188                case '\0':
189                case 0x0e: //CACA_KEY_CTRL_N:
190                    prevpty = pty;
191                    pty = (pty + 1) % n;
192                    refresh = 1;
193                    break;
194                case 'p':
195                case 0x10: //CACA_KEY_CTRL_P:
196                    prevpty = pty;
197                    pty = (pty + n - 1) % n;
198                    refresh = 1;
199                    break;
200                }
201            }
202            else
203            {
204                switch(ev.data.key.ch)
205                {
206                case 0x01: //CACA_KEY_CTRL_A:
207                    command = 1; break;
208                case CACA_KEY_UP:
209                    write(screen[pty].fd, "\x1b[A", 3); break;
210                case CACA_KEY_DOWN:
211                    write(screen[pty].fd, "\x1b[B", 3); break;
212                case CACA_KEY_RIGHT:
213                    write(screen[pty].fd, "\x1b[C", 3); break;
214                case CACA_KEY_LEFT:
215                    write(screen[pty].fd, "\x1b[D", 3); break;
216                default:
217                    write(screen[pty].fd, &ev.data.key.ch, 1); break;
218                }
219            }
220        }
221        else if(ret && (ev.type & CACA_EVENT_RESIZE))
222        {
223            w = cucul_get_canvas_width(cv);
224            h = cucul_get_canvas_height(cv);
225            w = w <= XTAB * n ? 1 : w - XTAB * (n - 1) - 2;
226            h = h <= YTAB * n ? 1 : h - YTAB * (n - 1) - 2;
227            for(i = 0; i < n; i++)
228            {
229                cucul_set_canvas_size(screen[i].cv, w, h);
230                set_tty_size(screen[i].fd, w, h);
231            }
232            cucul_clear_canvas(cv);
233            refresh = 1;
234        }
235
236        /* Refresh screen */
237        if(refresh)
238        {
239            refresh = 0;
240
241            cucul_set_color_ansi(cv, CUCUL_LIGHTRED, CUCUL_BLACK);
242
243            for(i = 0; i < n; i++)
244            {
245                int j = (pty + n - 1 - i) % n;
246                cucul_blit(cv, (n - 1 - j) * XTAB + 1,
247                               j * YTAB + 1, screen[j].cv, NULL);
248                cucul_draw_cp437_box(cv, (n - 1 - j) * XTAB, j * YTAB,
249                                     w + (n - 1 - j) * XTAB + 1, h + j * YTAB + 1);
250                /* Set cursor to the correct position */
251                if(i == n - 1)
252                    cucul_gotoxy(cv, (n - 1 - j) * XTAB + 1
253                                        + cucul_get_cursor_x(screen[j].cv),
254                                     j * YTAB + 1
255                                        + cucul_get_cursor_y(screen[j].cv));
256            }
257
258            if(mini)
259            {
260                char const * const *fonts;
261                cucul_dither_t *d;
262                cucul_font_t *f;
263                uint8_t *buf;
264                int miniw, minih;
265
266                fonts = cucul_get_font_list();
267                f = cucul_load_font(fonts[0], 0);
268
269                miniw = cucul_get_canvas_width(screen[0].cv)
270                         * cucul_get_font_width(f);
271                minih = cucul_get_canvas_height(screen[0].cv)
272                         * cucul_get_font_height(f);
273                buf = malloc(4 * miniw * minih);
274
275#if defined(HAVE_ENDIAN_H)
276                if(__BYTE_ORDER == __BIG_ENDIAN)
277#else
278                /* This is compile-time optimised with at least -O1 or -Os */
279                uint32_t const tmp = 0x12345678;
280                if(*(uint8_t const *)&tmp == 0x12)
281#endif
282                    d = cucul_create_dither(32, miniw, minih, 4 * miniw,
283                                            0xff0000, 0xff00, 0xff, 0x0);
284                else
285                    d = cucul_create_dither(32, miniw, minih, 4 * miniw,
286                                            0xff00, 0xff0000, 0xff000000, 0x0);
287
288                for(i = 0; i < n; i++)
289                {
290                    cucul_render_canvas(screen[i].cv, f, buf,
291                                        miniw, minih, miniw * 4);
292                    cucul_dither_bitmap(cv, 20 * i,
293                              cucul_get_canvas_height(cv) - 6, 19, 6, d, buf);
294                    cucul_set_color_ansi(cv, CUCUL_WHITE, CUCUL_BLUE);
295                    cucul_printf(cv, 20 * i,
296                                 cucul_get_canvas_height(cv) - 6, "(%i)", i);
297                }
298
299                cucul_free_dither(d);
300                cucul_free_font(f);
301
302                free(buf);
303            }
304
305            caca_refresh_display(dp);
306        }
307
308        if(eof)
309        {
310            for(i = 0; i < n; i++)
311                if(screen[i].total)
312                    break;
313            if(i == n)
314                break;
315        }
316    }
317
318    caca_get_event(dp, CACA_EVENT_KEY_PRESS, NULL, -1);
319
320    /* Clean up */
321    caca_free_display(dp);
322    cucul_free_canvas(cv);
323    for(i = 0; i < n; i++)
324        cucul_free_canvas(screen[i].cv);
325
326    return 0;
327}
328
329static int create_pty(char *cmd, unsigned int w, unsigned int h)
330{
331    char **argv;
332    int fd;
333    pid_t pid;
334
335    pid = forkpty(&fd, NULL, NULL, NULL);
336    if(pid < 0)
337    {
338        fprintf(stderr, "forkpty() error\n");
339        return -1;
340    }
341    else if(pid == 0)
342    {
343        set_tty_size(0, w, h);
344        putenv("CACA_DRIVER=slang");
345        putenv("TERM=xterm");
346        argv = malloc(2 * sizeof(char *));
347        argv[0] = cmd;
348        argv[1] = NULL;
349        execvp(cmd, argv);
350        fprintf(stderr, "execvp() error\n");
351        return -1;
352    }
353
354    fcntl(fd, F_SETFL, O_NDELAY);
355    return fd;
356#if 0
357    fprintf(stderr, "forkpty() not available\n");
358    return -1;
359#endif
360}
361
362static int set_tty_size(int fd, unsigned int w, unsigned int h)
363{
364    struct winsize ws;
365
366    memset(&ws, 0, sizeof(ws));
367    ws.ws_row = h;
368    ws.ws_col = w;
369    ioctl(fd, TIOCSWINSZ, (char *)&ws);
370
371    return 0;
372}
373
Note: See TracBrowser for help on using the repository browser.