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

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