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

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