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

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