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

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