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

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