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

Last change on this file since 2358 was 2358, checked in by Jean-Yves Lamoureux, 14 years ago
  • Added a default shell roolback if no arguments are givent
  • ctrl-a-c adds a new screen
  • Property svn:keywords set to Id
File size: 14.3 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
4 *                2008 Jean-Yves Lamoureux <jylam@lnxscene.org>
5 *                All Rights Reserved
6 *
7 *  $Id: main.c 2358 2008-06-11 15:45:02Z jylam $
8 *
9 *  This program is free software. It comes without any warranty, to
10 *  the extent permitted by applicable law. You can redistribute it
11 *  and/or modify it under the terms of the Do What The Fuck You Want
12 *  To Public License, Version 2, as published by Sam Hocevar. See
13 *  http://sam.zoy.org/wtfpl/COPYING for more details.
14 */
15
16#include "config.h"
17
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#include <errno.h>
30#include <cucul.h>
31#include <caca.h>
32
33#include "neercs.h"
34
35#define XTAB 5
36#define YTAB 3
37
38static int create_pty(char *cmd, unsigned int w, unsigned int h);
39static int set_tty_size(int fd, unsigned int w, unsigned int h);
40
41int main(int argc, char **argv)
42{
43    static cucul_canvas_t *cv;
44    static caca_display_t *dp;
45    struct screen_list *screen_list = NULL;
46    char *default_shell = NULL;
47    int pty = 0, prevpty = 0, i, w, h, nt, args = 0;
48    int eof = 0, refresh = 1, command = 0, mini = 1;
49
50    nt = argc - 1;
51    args = nt;
52
53    if(nt == 0)
54    {
55        nt = 1;
56    }
57    default_shell = getenv("SHELL");
58
59
60    /* Create main canvas and associated caca window */
61    cv = cucul_create_canvas(0, 0);
62    dp = caca_create_display(cv);
63    if(!dp)
64        return 1;
65    caca_set_cursor(dp, 1);
66
67
68    w = cucul_get_canvas_width(cv);
69    h = cucul_get_canvas_height(cv);
70
71    w = w <= XTAB * nt ? 1 : w - XTAB * (nt - 1) - 2;
72    h = h <= YTAB * nt ? 1 : h - YTAB * (nt - 1) - 2 - 6;
73
74
75    /* Create screen list */
76    screen_list = (struct screen_list*)   malloc(sizeof(struct screen_list));
77    screen_list->screen = (struct screen**) malloc(sizeof(sizeof(struct screen*)));
78    screen_list->count = 0;
79
80    for(i = 0; i < nt; i++)
81    {
82        struct screen *tmp;
83        if(args) tmp = create_screen(w, h, argv[i + 1]);
84        else     tmp = create_screen(w, h, default_shell);
85
86        if(tmp)
87        {
88            if(add_screen(screen_list, tmp) < 0)
89            {
90                fprintf(stderr, "Can't add %p to %p\n", tmp, screen_list);
91            }
92        }
93        else
94        {
95            fprintf(stderr, "Can't create screen\n");
96        }
97    }
98
99    cucul_set_color_ansi(cv, CUCUL_LIGHTRED, CUCUL_BLACK);
100    cucul_draw_cp437_box(cv, (screen_list->count - 1 - pty) * XTAB, pty * YTAB,
101                         w + (screen_list->count - 1 - pty) * XTAB + 1, h + pty * YTAB + 1);
102
103    caca_refresh_display(dp);
104
105    for(;;)
106    {
107        struct timeval tv;
108        fd_set fdset;
109        caca_event_t ev;
110        int maxfd = 0, ret;
111
112        /* Read data, if any */
113        FD_ZERO(&fdset);
114        for(i = 0; i < screen_list->count; i++)
115        {
116            if(screen_list->screen[i]->fd >= 0)
117                FD_SET(screen_list->screen[i]->fd, &fdset);
118            if(screen_list->screen[i]->fd > maxfd)
119                maxfd = screen_list->screen[i]->fd;
120        }
121        tv.tv_sec = 0;
122        tv.tv_usec = 50000;
123        ret = select(maxfd + 1, &fdset, NULL, NULL, &tv);
124
125        if(ret < 0)
126        {
127            if(errno == EINTR)
128                ; /* We probably got a SIGWINCH, ignore it */
129            else
130            {
131                for(i = 0; i < screen_list->count; i++)
132                    if(screen_list->screen[i]->total)
133                        break;
134                if(i == screen_list->count)
135                    break;
136            }
137        }
138        else if(ret)
139            for(i = 0; i < screen_list->count; i++)
140            {
141                /* FIXME: try a new strategy: read all filedescriptors until
142                 * each of them starved at least once. */
143
144                if(screen_list->screen[i]->fd < 0 || !FD_ISSET(screen_list->screen[i]->fd, &fdset))
145                    continue;
146
147                for(;;)
148                {
149                    ssize_t nr;
150
151                    screen_list->screen[i]->buf = realloc(screen_list->screen[i]->buf,
152                                                          screen_list->screen[i]->total + 1024);
153                    nr = read(screen_list->screen[i]->fd,
154                              screen_list->screen[i]->buf + screen_list->screen[i]->total, 1024);
155
156                    if(nr > 0)
157                    {
158                        screen_list->screen[i]->total += nr;
159                        continue;
160                    }
161
162                    if(nr == 0 || errno != EWOULDBLOCK) {
163                        close(screen_list->screen[i]->fd);
164                        screen_list->screen[i]->fd = -1;
165                        destroy_screen(screen_list->screen[i]);
166                        remove_screen(screen_list, i);
167                        refresh = 1;
168                    }
169
170                    break;
171                }
172            }
173
174        for(i = 0; i < screen_list->count; i++) if(screen_list->screen[i]->total)
175        {
176            unsigned long int bytes;
177
178            bytes = import_term(screen_list->screen[i], screen_list->screen[i]->buf, screen_list->screen[i]->total);
179
180            if(bytes > 0)
181            {
182                screen_list->screen[i]->total -= bytes;
183                memmove(screen_list->screen[i]->buf, screen_list->screen[i]->buf + bytes, screen_list->screen[i]->total);
184                refresh = 1;
185            }
186        }
187
188        /* Get events, if any */
189        ret = caca_get_event(dp, CACA_EVENT_ANY, &ev, 0);
190        if(ret && (caca_get_event_type(&ev) & CACA_EVENT_KEY_PRESS))
191        {
192            unsigned int c = caca_get_event_key_ch(&ev);
193            if(command)
194            {
195                command = 0;
196
197                switch(c)
198                {
199                case 0x01: //CACA_KEY_CTRL_A:
200                    pty ^= prevpty;
201                    prevpty ^= pty;
202                    pty ^= prevpty;
203                    refresh = 1;
204                    break;
205                case 'm':
206                case 0x0d: //CACA_KEY_CTRL_M:
207                    mini = !mini;
208                    refresh = 1;
209                    break;
210                case 'n':
211                case ' ':
212                case '\0':
213                case 0x0e: //CACA_KEY_CTRL_N:
214                    prevpty = pty;
215                    pty = (pty + 1) % screen_list->count;
216                    refresh = 1;
217                    break;
218                case 'p':
219                case 0x10: //CACA_KEY_CTRL_P:
220                    prevpty = pty;
221                    pty = (pty + screen_list->count - 1) % screen_list->count;
222                    refresh = 1;
223                    break;
224                case 0x03: //CACA_KEY_CTRL_C:
225                    prevpty = pty;
226                    pty = add_screen(screen_list, create_screen(w, h, default_shell));
227                    refresh = 1;
228                    break;
229                }
230            }
231            else
232            {
233                switch(c)
234                {
235                case 0x01: //CACA_KEY_CTRL_A:
236                    command = 1; break;
237                case CACA_KEY_UP:
238                    write(screen_list->screen[pty]->fd, "\x1b[A", 3); break;
239                case CACA_KEY_DOWN:
240                    write(screen_list->screen[pty]->fd, "\x1b[B", 3); break;
241                case CACA_KEY_RIGHT:
242                    write(screen_list->screen[pty]->fd, "\x1b[C", 3); break;
243                case CACA_KEY_LEFT:
244                    write(screen_list->screen[pty]->fd, "\x1b[D", 3); break;
245                default:
246                    write(screen_list->screen[pty]->fd, &c, 1); break;
247                }
248            }
249        }
250        else if(ret && (caca_get_event_type(&ev) & CACA_EVENT_RESIZE))
251        {
252            w = cucul_get_canvas_width(cv);
253            h = cucul_get_canvas_height(cv);
254            w = w <= XTAB * screen_list->count ? 1 : w - XTAB * (screen_list->count - 1) - 2;
255            h = h <= YTAB * screen_list->count ? 1 : h - YTAB * (screen_list->count - 1) - 2;
256            for(i = 0; i < screen_list->count; i++)
257            {
258                cucul_set_canvas_size(screen_list->screen[i]->cv, w, h);
259                if(screen_list->screen[i]->fd >= 0)
260                    set_tty_size(screen_list->screen[i]->fd, w, h);
261            }
262            cucul_clear_canvas(cv);
263            refresh = 1;
264        }
265        else if(ret && (caca_get_event_type(&ev) & CACA_EVENT_QUIT))
266        {
267            break;
268        }
269
270        /* Refresh screen */
271        if(refresh)
272        {
273            refresh = 0;
274
275            cucul_set_color_ansi(cv, CUCUL_DEFAULT, CUCUL_DEFAULT);
276            cucul_clear_canvas(cv);
277            cucul_set_color_ansi(cv, CUCUL_LIGHTRED, CUCUL_BLACK);
278
279            for(i = 0; i < screen_list->count; i++)
280            {
281                int n = screen_list->count;
282                if(screen_list->screen[i]->fd < 0)
283                    continue;
284                int j = (pty + n - 1 - i) % n;
285                cucul_blit(cv, (n - 1 - j) * XTAB + 1,
286                           j * YTAB + 1, screen_list->screen[j]->cv, NULL);
287                cucul_draw_cp437_box(cv, (n - 1 - j) * XTAB, j * YTAB,
288                                     w + (n - 1 - j) * XTAB + 1, h + j * YTAB + 2);
289                /* Set cursor to the correct position */
290                if(i == n - 1)
291                    cucul_gotoxy(cv, (n - 1 - j) * XTAB + 1
292                                 + cucul_get_cursor_x(screen_list->screen[j]->cv),
293                                 j * YTAB + 1
294                                 + cucul_get_cursor_y(screen_list->screen[j]->cv));
295            }
296
297            if(mini)
298            {
299                char const * const *fonts;
300                cucul_dither_t *d;
301                cucul_font_t *f;
302                uint8_t *buf;
303                int miniw, minih;
304
305                if(screen_list->count)
306                {
307                    fonts = cucul_get_font_list();
308                    f = cucul_load_font(fonts[0], 0);
309
310                    miniw = cucul_get_canvas_width(screen_list->screen[0]->cv)
311                        * cucul_get_font_width(f);
312                    minih = cucul_get_canvas_height(screen_list->screen[0]->cv)
313                        * cucul_get_font_height(f);
314                    buf = malloc(4 * miniw * minih);
315
316#if defined(HAVE_ENDIAN_H)
317                    if(__BYTE_ORDER == __BIG_ENDIAN)
318#else
319                        /* This is compile-time optimised with at least -O1 or -Os */
320                        uint32_t const tmp = 0x12345678;
321                    if(*(uint8_t const *)&tmp == 0x12)
322#endif
323                        d = cucul_create_dither(32, miniw, minih, 4 * miniw,
324                                                0xff0000, 0xff00, 0xff, 0x0);
325                    else
326                        d = cucul_create_dither(32, miniw, minih, 4 * miniw,
327                                                0xff00, 0xff0000, 0xff000000, 0x0);
328
329                    for(i = 0; i < screen_list->count; i++)
330                    {
331                        cucul_render_canvas(screen_list->screen[i]->cv, f, buf,
332                                            miniw, minih, miniw * 4);
333                        cucul_dither_bitmap(cv, 20 * i,
334                                            cucul_get_canvas_height(cv) - 6, 19, 6, d, buf);
335                        cucul_set_color_ansi(cv, CUCUL_WHITE, CUCUL_BLUE);
336                        cucul_printf(cv, 20 * i,
337                                     cucul_get_canvas_height(cv) - 6, "(%i)", i);
338                    }
339
340                    cucul_free_dither(d);
341                    cucul_free_font(f);
342
343                    free(buf);
344                }
345            }
346
347            caca_refresh_display(dp);
348        }
349
350        eof = 1;
351        for(i = 0; i < screen_list->count; i++)
352            if(screen_list->screen[i]->fd >= 0)
353                eof = 0;
354        if(eof)
355            break;
356    }
357
358    /* Clean up */
359    caca_free_display(dp);
360    cucul_free_canvas(cv);
361    for(i = 0; i < screen_list->count; i++)
362    {
363        destroy_screen(screen_list->screen[i]);
364    }
365    free(screen_list->screen);
366    free(screen_list);
367
368
369    return 0;
370}
371
372static int create_pty(char *cmd, unsigned int w, unsigned int h)
373{
374    char **argv;
375    int fd;
376    pid_t pid;
377
378    pid = forkpty(&fd, NULL, NULL, NULL);
379    if(pid < 0)
380    {
381        fprintf(stderr, "forkpty() error\n");
382        return -1;
383    }
384    else if(pid == 0)
385    {
386        set_tty_size(0, w, h);
387        putenv("CACA_DRIVER=slang");
388        putenv("TERM=xterm");
389        argv = malloc(2 * sizeof(char *));
390        argv[0] = cmd;
391        argv[1] = NULL;
392        execvp(cmd, argv);
393        fprintf(stderr, "execvp() error\n");
394        return -1;
395    }
396
397    fcntl(fd, F_SETFL, O_NDELAY);
398    return fd;
399#if 0
400    fprintf(stderr, "forkpty() not available\n");
401    return -1;
402#endif
403}
404
405static int set_tty_size(int fd, unsigned int w, unsigned int h)
406{
407    struct winsize ws;
408
409    memset(&ws, 0, sizeof(ws));
410    ws.ws_row = h;
411    ws.ws_col = w;
412    ioctl(fd, TIOCSWINSZ, (char *)&ws);
413
414    return 0;
415}
416
417
418
419
420struct screen* create_screen(int w, int h, char *command)
421{
422    struct screen *s = (struct screen*) malloc(sizeof(struct screen));
423
424    s->cv = cucul_create_canvas(w, h);
425    s->init = 0;
426
427    s->buf = NULL;
428    s->total = 0;
429    s->fd = create_pty(command, w, h);
430    if(s->fd < 0)
431    {
432        cucul_free_canvas(s->cv);
433        free(s);
434        return NULL;
435    }
436    return s;
437}
438
439
440int add_screen(struct screen_list *list, struct screen *s)
441{
442    if(list == NULL || s == NULL)
443    {
444        return -1;
445    }
446    else
447    {
448        list->screen = (struct screen**) realloc(list->screen,
449                                                 sizeof(sizeof(struct screen*))
450                                                 * (list->count+1));
451        list->screen[list->count] = s;
452        list->count++;
453    }
454
455    return list->count-1;
456}
457
458
459int remove_screen(struct screen_list *list, int n)
460{
461    if(n>list->count) return -1;
462
463    memmove(&list->screen[n],
464            &list->screen[n+1],
465            sizeof(struct screen*)*(list->count-(n+1)));
466
467    list->screen = (struct screen**) realloc(list->screen,
468                                             sizeof(sizeof(struct screen*))
469                                             * (list->count));
470
471    list->count--;
472    return 1;
473}
474
475int destroy_screen(struct screen *s)
476{
477    free(s->buf);
478    cucul_free_canvas(s->cv);
479    free(s);
480    return 1;
481}
Note: See TracBrowser for help on using the repository browser.