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

Last change on this file since 2370 was 2370, checked in by Jean-Yves Lamoureux, 13 years ago
  • Oops, added left arrow handling
  • Property svn:keywords set to Id
File size: 14.1 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 2370 2008-06-13 13:18: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);
39
40int main(int argc, char **argv)
41{
42    static cucul_canvas_t *cv;
43    static caca_display_t *dp;
44    struct screen_list *screen_list = NULL;
45    char *default_shell = NULL;
46    int pty = 0, prevpty = 0, i, w, h, nt, args = 0;
47    int eof = 0, refresh = 1, command = 0;
48
49    /* Add-ons*/
50    int mini = 1;
51    int status = 1;
52    int help = 0;
53
54
55    nt = argc - 1;
56    args = nt;
57
58    if(nt == 0)
59    {
60        nt = 1;
61    }
62
63    default_shell = getenv("SHELL");
64
65    if(default_shell == NULL  && !args)
66    {
67        fprintf(stderr, "Environment variable SHELL not set and no arguments given. kthxbye.\n");
68        return -1;
69    }
70
71    /* Create main canvas and associated caca window */
72    cv = cucul_create_canvas(0, 0);
73    dp = caca_create_display(cv);
74    if(!dp)
75        return 1;
76    caca_set_cursor(dp, 1);
77
78
79    w = cucul_get_canvas_width(cv);
80    h = cucul_get_canvas_height(cv);
81
82    w = w <= XTAB * nt ? 1 : w - XTAB * (nt - 1) - 2;
83    h = h <= YTAB * nt ? 1 : h - YTAB * (nt - 1) - 2 - 6;
84
85
86    /* Create screen list */
87    screen_list = (struct screen_list*)     malloc(sizeof(struct screen_list));
88    screen_list->screen = (struct screen**) malloc(sizeof(sizeof(struct screen*)));
89    screen_list->count = 0;
90    screen_list->width  = cucul_get_canvas_width(cv);
91    screen_list->height = cucul_get_canvas_height(cv) - ((mini*6) + (status));
92    screen_list->wm_type = WM_VSPLIT;
93    screen_list->in_bell = 0;
94
95    for(i = 0; i < nt; i++)
96    {
97        struct screen *tmp;
98        if(args) tmp = create_screen(w, h, argv[i + 1]);
99        else     tmp = create_screen(w, h, default_shell);
100
101        if(tmp)
102        {
103            if(add_screen(screen_list, tmp) < 0)
104            {
105                fprintf(stderr, "Can't add %p to %p\n", tmp, screen_list);
106            }
107        }
108        else
109        {
110            fprintf(stderr, "Can't create screen\n");
111        }
112    }
113
114    /* Windows are in a temporary state, resize them to the right dimensions */
115    update_windows_props(cv, screen_list, pty);
116
117    caca_refresh_display(dp);
118
119    for(;;)
120    {
121        struct timeval tv;
122        fd_set fdset;
123        caca_event_t ev;
124        int maxfd = 0, ret;
125
126        /* Read data, if any */
127        FD_ZERO(&fdset);
128        for(i = 0; i < screen_list->count; i++)
129        {
130            if(screen_list->screen[i]->fd >= 0)
131                FD_SET(screen_list->screen[i]->fd, &fdset);
132            if(screen_list->screen[i]->fd > maxfd)
133                maxfd = screen_list->screen[i]->fd;
134        }
135        tv.tv_sec = 0;
136        tv.tv_usec = 50000;
137        ret = select(maxfd + 1, &fdset, NULL, NULL, &tv);
138
139        if(ret < 0)
140        {
141            if(errno == EINTR)
142                ; /* We probably got a SIGWINCH, ignore it */
143            else
144            {
145                for(i = 0; i < screen_list->count; i++)
146                    if(screen_list->screen[i]->total)
147                        break;
148                if(i == screen_list->count)
149                    break;
150            }
151        }
152        else if(ret)
153            for(i = 0; i < screen_list->count; i++)
154            {
155                /* FIXME: try a new strategy: read all filedescriptors until
156                 * each of them starved at least once. */
157
158                if(screen_list->screen[i]->fd < 0 || !FD_ISSET(screen_list->screen[i]->fd, &fdset))
159                    continue;
160
161                for(;;)
162                {
163                    ssize_t nr;
164
165                    screen_list->screen[i]->buf = realloc(screen_list->screen[i]->buf,
166                                                          screen_list->screen[i]->total + 1024);
167                    nr = read(screen_list->screen[i]->fd,
168                              screen_list->screen[i]->buf + screen_list->screen[i]->total, 1024);
169
170                    if(nr > 0)
171                    {
172                        screen_list->screen[i]->total += nr;
173                        continue;
174                    }
175
176                    if(nr == 0 || errno != EWOULDBLOCK) {
177                        close(screen_list->screen[i]->fd);
178                        screen_list->screen[i]->fd = -1;
179                        destroy_screen(screen_list->screen[i]);
180                        remove_screen(screen_list, i);
181                        refresh = 1;
182                    }
183
184                    break;
185                }
186            }
187
188        for(i = 0; i < screen_list->count; i++) if(screen_list->screen[i]->total)
189        {
190            unsigned long int bytes;
191
192            bytes = import_term(screen_list, screen_list->screen[i], screen_list->screen[i]->buf, screen_list->screen[i]->total);
193
194            if(bytes > 0)
195            {
196                screen_list->screen[i]->total -= bytes;
197                memmove(screen_list->screen[i]->buf, screen_list->screen[i]->buf + bytes, screen_list->screen[i]->total);
198                refresh = 1;
199            }
200        }
201
202        /* Get events, if any */
203        ret = caca_get_event(dp, CACA_EVENT_ANY, &ev, 0);
204        if(ret && (caca_get_event_type(&ev) & CACA_EVENT_KEY_PRESS))
205        {
206            unsigned int c = caca_get_event_key_ch(&ev);
207
208            if(command)
209            {
210                command = 0;
211
212                switch(c)
213                {
214                case 0x01: //CACA_KEY_CTRL_A:
215                    pty ^= prevpty;
216                    prevpty ^= pty;
217                    pty ^= prevpty;
218                    refresh = 1;
219                    break;
220                case 'm':
221                case 0x0d: //CACA_KEY_CTRL_M:
222                    mini = !mini;
223                    refresh = 1;
224                    break;
225                case 'n':
226                case ' ':
227                case '\0':
228                case 0x0e: //CACA_KEY_CTRL_N:
229                    prevpty = pty;
230                    pty = (pty + 1) % screen_list->count;
231                    refresh = 1;
232                    break;
233                case 'p':
234                case 0x10: //CACA_KEY_CTRL_P:
235                    prevpty = pty;
236                    pty = (pty + screen_list->count - 1) % screen_list->count;
237                    refresh = 1;
238                    break;
239                case 'c':
240                case 0x03: //CACA_KEY_CTRL_C:
241                    prevpty = pty;
242                    pty = add_screen(screen_list, create_screen(w, h, default_shell));
243                    refresh = 1;
244                    break;
245                case 'w':
246                case 0x17: //CACA_KEY_CTRL_W:
247                    screen_list->wm_type = (screen_list->wm_type==(WM_MAX-1)?
248                                            screen_list->wm_type=0:
249                                            screen_list->wm_type+1);
250                    refresh = 1;
251                    break;
252                case 'h':
253                case 0x08: //CACA_KEY_CTRL_H:
254                    help = !help;
255                    refresh = 1;
256                    break;
257                }
258            }
259            else
260            {
261                switch(c)
262                {
263                case 0x01: //CACA_KEY_CTRL_A:
264                    command = 1; break;
265                case CACA_KEY_UP:
266                    write(screen_list->screen[pty]->fd, "\x1b[A", 3); break;
267                case CACA_KEY_DOWN:
268                    write(screen_list->screen[pty]->fd, "\x1b[B", 3); break;
269                case CACA_KEY_RIGHT:
270                    write(screen_list->screen[pty]->fd, "\x1b[C", 3); break;
271                case CACA_KEY_LEFT:
272                    write(screen_list->screen[pty]->fd, "\x1b[D", 3); break;
273                default:
274                    write(screen_list->screen[pty]->fd, &c, 1); break;
275                }
276            }
277        }
278        else if(ret && (caca_get_event_type(&ev) & CACA_EVENT_RESIZE))
279        {
280            update_windows_props(cv, screen_list, pty);
281
282            cucul_clear_canvas(cv);
283            refresh = 1;
284        }
285        else if(ret && (caca_get_event_type(&ev) & CACA_EVENT_QUIT))
286        {
287            break;
288        }
289
290        /* Refresh screen */
291        if(refresh || screen_list->in_bell)
292        {
293            refresh = 0;
294
295            screen_list->width  = cucul_get_canvas_width(cv);
296            screen_list->height = cucul_get_canvas_height(cv) - (mini*6);
297
298            update_windows_props(cv, screen_list, pty);
299
300
301            cucul_set_color_ansi(cv, CUCUL_DEFAULT, CUCUL_DEFAULT);
302            cucul_clear_canvas(cv);
303            cucul_set_color_ansi(cv, CUCUL_LIGHTRED, CUCUL_BLACK);
304
305            for(i = screen_list->count - 1; i >=0; i--)
306            {
307                if(i!=pty)
308                {
309                    cucul_blit(cv,
310                               screen_list->screen[i]->x,
311                               screen_list->screen[i]->y,
312                               screen_list->screen[i]->cv, NULL);
313                    cucul_draw_cp437_box(cv,
314                                         screen_list->screen[i]->x - 1,
315                                         screen_list->screen[i]->y - 1,
316                                         screen_list->screen[i]->w + 2,
317                                         screen_list->screen[i]->h + 2);
318                }
319
320            }
321
322
323            cucul_blit(cv,
324                       screen_list->screen[pty]->x,
325                       screen_list->screen[pty]->y,
326                   screen_list->screen[pty]->cv, NULL);
327            if(screen_list->screen[pty]->bell)
328            {
329                cucul_set_color_ansi(cv, CUCUL_RED, CUCUL_BLACK);
330                screen_list->screen[pty]->bell = 0;
331                screen_list->in_bell--;
332            }
333            else
334            {
335                cucul_set_color_ansi(cv, CUCUL_LIGHTGREEN, CUCUL_BLACK);
336            }
337            cucul_draw_cp437_box(cv,
338                                 screen_list->screen[pty]->x - 1,
339                                 screen_list->screen[pty]->y - 1,
340                                 screen_list->screen[pty]->w + 2,
341                                 screen_list->screen[pty]->h + 2);
342
343            cucul_gotoxy(cv,
344                         screen_list->screen[pty]->x + cucul_get_cursor_x(screen_list->screen[pty]->cv),
345                         screen_list->screen[pty]->y + cucul_get_cursor_y(screen_list->screen[pty]->cv));
346
347
348            if(mini)
349            {
350                draw_thumbnails(cv, screen_list, pty);
351            }
352            if(status)
353            {
354                draw_status(cv, screen_list, pty);
355            }
356            if(help)
357            {
358                draw_help(cv, screen_list, pty);
359            }
360
361
362            caca_refresh_display(dp);
363        }
364
365        eof = 1;
366        for(i = 0; i < screen_list->count; i++)
367            if(screen_list->screen[i]->fd >= 0)
368                eof = 0;
369        if(eof)
370            break;
371    }
372
373    /* Clean up */
374    caca_free_display(dp);
375    cucul_free_canvas(cv);
376    for(i = 0; i < screen_list->count; i++)
377    {
378        destroy_screen(screen_list->screen[i]);
379    }
380    free(screen_list->screen);
381    free(screen_list);
382
383
384    return 0;
385}
386
387static int create_pty(char *cmd, unsigned int w, unsigned int h)
388{
389    char **argv;
390    int fd;
391    pid_t pid;
392
393    pid = forkpty(&fd, NULL, NULL, NULL);
394    if(pid < 0)
395    {
396        fprintf(stderr, "forkpty() error\n");
397        return -1;
398    }
399    else if(pid == 0)
400    {
401        set_tty_size(0, w, h);
402        putenv("CACA_DRIVER=slang");
403        putenv("TERM=xterm");
404        argv = malloc(2 * sizeof(char *));
405        argv[0] = cmd;
406        argv[1] = NULL;
407        execvp(cmd, argv);
408        fprintf(stderr, "execvp() error\n");
409        return -1;
410    }
411
412    fcntl(fd, F_SETFL, O_NDELAY);
413    return fd;
414#if 0
415    fprintf(stderr, "forkpty() not available\n");
416    return -1;
417#endif
418}
419
420int set_tty_size(int fd, unsigned int w, unsigned int h)
421{
422    struct winsize ws;
423
424    memset(&ws, 0, sizeof(ws));
425    ws.ws_row = h;
426    ws.ws_col = w;
427    ioctl(fd, TIOCSWINSZ, (char *)&ws);
428
429    return 0;
430}
431
432
433
434
435struct screen* create_screen(int w, int h, char *command)
436{
437    struct screen *s = (struct screen*) malloc(sizeof(struct screen));
438
439    s->cv = cucul_create_canvas(w, h);
440    cucul_set_color_ansi(s->cv, CUCUL_BLACK, CUCUL_BLACK);
441    cucul_clear_canvas(s->cv);
442    s->init = 0;
443
444    s->buf = NULL;
445    s->total = 0;
446    s->w = w+1;
447    s->h = h+1;
448    s->fd = create_pty(command, w, h);
449    s->bell = 0;
450
451    if(s->fd < 0)
452    {
453        cucul_free_canvas(s->cv);
454        free(s);
455        return NULL;
456    }
457    return s;
458}
459
460int destroy_screen(struct screen *s)
461{
462    free(s->buf);
463    cucul_free_canvas(s->cv);
464    free(s);
465    return 1;
466}
467
468int add_screen(struct screen_list *list, struct screen *s)
469{
470    if(list == NULL || s == NULL) return -1;
471
472    else
473    {
474        list->screen = (struct screen**) realloc(list->screen,
475                                                 sizeof(sizeof(struct screen*))
476                                                 * (list->count+1));
477        list->screen[list->count] = s;
478        list->count++;
479    }
480
481    return list->count-1;
482}
483
484int remove_screen(struct screen_list *list, int n)
485{
486    if(n>list->count) return -1;
487
488    memmove(&list->screen[n],
489            &list->screen[n+1],
490            sizeof(struct screen*)*(list->count-(n+1)));
491
492    list->screen = (struct screen**) realloc(list->screen,
493                                             sizeof(sizeof(struct screen*))
494                                             * (list->count));
495
496    list->count--;
497    return 1;
498}
499
Note: See TracBrowser for help on using the repository browser.