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

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