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

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