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

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