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

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