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

Last change on this file since 2380 was 2380, checked in by Jean-Yves Lamoureux, 14 years ago
  • Get rid of the nice XTAB and YTAB macros, as they are unused, and noone ever knew why they were defined, anyway. This

was my longuest commit comment ever. (commit comment, lol) (Beer, nevermind).

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