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

Last change on this file since 2359 was 2359, checked in by Jean-Yves Lamoureux, 14 years ago
  • moved thumbnail code to its own function, and added a box around current one
  • Cosmetic changes
  • Property svn:keywords set to Id
File size: 14.7 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 2359 2008-06-11 16:00:26Z 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                draw_thumbnails(cv, screen_list, pty);
300            }
301
302            caca_refresh_display(dp);
303        }
304
305        eof = 1;
306        for(i = 0; i < screen_list->count; i++)
307            if(screen_list->screen[i]->fd >= 0)
308                eof = 0;
309        if(eof)
310            break;
311    }
312
313    /* Clean up */
314    caca_free_display(dp);
315    cucul_free_canvas(cv);
316    for(i = 0; i < screen_list->count; i++)
317    {
318        destroy_screen(screen_list->screen[i]);
319    }
320    free(screen_list->screen);
321    free(screen_list);
322
323
324    return 0;
325}
326
327static int create_pty(char *cmd, unsigned int w, unsigned int h)
328{
329    char **argv;
330    int fd;
331    pid_t pid;
332
333    pid = forkpty(&fd, NULL, NULL, NULL);
334    if(pid < 0)
335    {
336        fprintf(stderr, "forkpty() error\n");
337        return -1;
338    }
339    else if(pid == 0)
340    {
341        set_tty_size(0, w, h);
342        putenv("CACA_DRIVER=slang");
343        putenv("TERM=xterm");
344        argv = malloc(2 * sizeof(char *));
345        argv[0] = cmd;
346        argv[1] = NULL;
347        execvp(cmd, argv);
348        fprintf(stderr, "execvp() error\n");
349        return -1;
350    }
351
352    fcntl(fd, F_SETFL, O_NDELAY);
353    return fd;
354#if 0
355    fprintf(stderr, "forkpty() not available\n");
356    return -1;
357#endif
358}
359
360static int set_tty_size(int fd, unsigned int w, unsigned int h)
361{
362    struct winsize ws;
363
364    memset(&ws, 0, sizeof(ws));
365    ws.ws_row = h;
366    ws.ws_col = w;
367    ioctl(fd, TIOCSWINSZ, (char *)&ws);
368
369    return 0;
370}
371
372
373
374
375struct screen* create_screen(int w, int h, char *command)
376{
377    struct screen *s = (struct screen*) malloc(sizeof(struct screen));
378
379    s->cv = cucul_create_canvas(w, h);
380    s->init = 0;
381
382
383    s->buf = NULL;
384    s->total = 0;
385    s->fd = create_pty(command, w, h);
386    if(s->fd < 0)
387    {
388        cucul_free_canvas(s->cv);
389        free(s);
390        return NULL;
391    }
392    return s;
393}
394
395
396int add_screen(struct screen_list *list, struct screen *s)
397{
398    if(list == NULL || s == NULL) return -1;
399
400    else
401    {
402        list->screen = (struct screen**) realloc(list->screen,
403                                                 sizeof(sizeof(struct screen*))
404                                                 * (list->count+1));
405        list->screen[list->count] = s;
406        list->count++;
407    }
408
409    return list->count-1;
410}
411
412
413int remove_screen(struct screen_list *list, int n)
414{
415    if(n>list->count) return -1;
416
417    memmove(&list->screen[n],
418            &list->screen[n+1],
419            sizeof(struct screen*)*(list->count-(n+1)));
420
421    list->screen = (struct screen**) realloc(list->screen,
422                                             sizeof(sizeof(struct screen*))
423                                             * (list->count));
424
425    list->count--;
426    return 1;
427}
428
429int destroy_screen(struct screen *s)
430{
431    free(s->buf);
432    cucul_free_canvas(s->cv);
433    free(s);
434    return 1;
435}
436
437
438
439void draw_thumbnails(cucul_canvas_t *cv, struct screen_list *screen_list, int pty)
440{
441                char const * const *fonts;
442                cucul_dither_t *d;
443                cucul_font_t *f;
444                uint8_t *buf;
445                int i;
446                int miniw, minih;
447
448                if(screen_list->count)
449                {
450                    fonts = cucul_get_font_list();
451                    f = cucul_load_font(fonts[0], 0);
452
453                    miniw = cucul_get_canvas_width(screen_list->screen[0]->cv)
454                        * cucul_get_font_width(f);
455                    minih = cucul_get_canvas_height(screen_list->screen[0]->cv)
456                        * cucul_get_font_height(f);
457                    buf = malloc(4 * miniw * minih);
458
459#if defined(HAVE_ENDIAN_H)
460                    if(__BYTE_ORDER == __BIG_ENDIAN)
461#else
462                        /* This is compile-time optimised with at least -O1 or -Os */
463                        uint32_t const tmp = 0x12345678;
464                    if(*(uint8_t const *)&tmp == 0x12)
465#endif
466                        d = cucul_create_dither(32, miniw, minih, 4 * miniw,
467                                                0xff0000, 0xff00, 0xff, 0x0);
468                    else
469                        d = cucul_create_dither(32, miniw, minih, 4 * miniw,
470                                                0xff00, 0xff0000, 0xff000000, 0x0);
471
472                    for(i = 0; i < screen_list->count; i++)
473                    {
474                        cucul_render_canvas(screen_list->screen[i]->cv, f, buf,
475                                            miniw, minih, miniw * 4);
476                        cucul_dither_bitmap(cv, 20 * i,
477                                            cucul_get_canvas_height(cv) - 6, 19, 6, d, buf);
478                        cucul_set_color_ansi(cv, CUCUL_WHITE, CUCUL_BLUE);
479
480                        if(pty == i)
481                            cucul_draw_cp437_box(cv,20 * i,
482                                                 cucul_get_canvas_height(cv) - 6, 19, 6);
483                        cucul_printf(cv, 20 * i,
484                                     cucul_get_canvas_height(cv) - 6, "(%i)", i);
485                    }
486
487                    cucul_free_dither(d);
488                    cucul_free_font(f);
489
490                    free(buf);
491                }
492
493}
Note: See TracBrowser for help on using the repository browser.