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

Last change on this file since 2478 was 2478, checked in by Pascal Terjan, 12 years ago
  • Don't use the caca_display in refresh_screens, it would make clean detach very hard
  • Property svn:keywords set to Id
File size: 19.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: main.c 2478 2008-06-23 14:37:19Z 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 <signal.h>
24#include <sys/ioctl.h>
25#include <sys/types.h>
26#include <sys/wait.h>
27#include <sys/time.h>
28#include <time.h>
29
30#if defined HAVE_PTY_H
31#   include <pty.h>  /* for openpty and forkpty */
32#else
33#   include <util.h> /* for OS X */
34#endif
35#if !defined HAVE_GETOPT_LONG
36#   include "mygetopt.h"
37#elif defined HAVE_GETOPT_H
38#   include <getopt.h>
39#endif
40#if defined HAVE_GETOPT_LONG
41#   define mygetopt getopt_long
42#   define myoptind optind
43#   define myoptarg optarg
44#   define myoption option
45#endif
46#include <errno.h>
47#include <cucul.h>
48#include <caca.h>
49
50#include "neercs.h"
51
52
53void version(void)
54{
55    printf("%s\n", PACKAGE_STRING);
56    printf("Copyright (C) 2006, 2008 Sam Hocevar <sam@zoy.org>\n");
57    printf("                         Jean-Yves Lamoureux <jylam@lnxscene.org>\n\n");
58    printf("This is free software.  You may redistribute copies of it under the\n");
59    printf("terms of the Do What The Fuck You Want To Public License, Version 2\n");
60    printf("<http://sam.zoy.org/wtfpl/>.\n");
61    printf("There is NO WARRANTY, to the extent permitted by law.\n");
62    printf("\n");
63    printf("For more informations, visit http://libcaca.zoy.org/wiki/neercs\n");
64}
65
66void usage(int argc, char **argv)
67{
68    printf("%s\n", PACKAGE_STRING);
69    printf("Usage : %s [command1] [command2] ... [commandN]\n", argv[0]);
70    printf("Example : %s zsh top \n\n", argv[0]);
71    printf("Options :\n");
72    printf("\t--pid\t\t-P <pid>\t\tattach <pid>\n");
73    printf("\t\t\t-R\t\t\tre-attach another neercs\n");
74    printf("\t--version\t-v \t\t\tdisplay version and exit\n");
75    printf("\t--help\t\t-h \t\t\tthis help\n");
76}
77
78int main(int argc, char **argv)
79{
80    static cucul_canvas_t *cv;
81    static caca_display_t *dp;
82    struct screen_list *screen_list = NULL;
83    struct recurrent_list *recurrent_list = NULL;
84    char *default_shell = NULL;
85    int i, w, h, args, s=0;
86    int eof = 0, refresh = 1, command = 0;
87    long long unsigned int last_key_time = 0;
88    int lock_offset = 0;
89    int mainret = 0;
90
91    default_shell = getenv("SHELL");
92
93    args = argc -1;
94    if(default_shell == NULL  && args <= 0)
95    {
96        fprintf(stderr, "Environment variable SHELL not set and no arguments given. kthxbye.\n");
97        return -1;
98    }
99
100    if(args==0)
101        args = 1;
102
103    /* Create main canvas and associated caca window */
104    /* FIXME: do not create display for -R/-h/-v */
105    cv = cucul_create_canvas(0, 0);
106    dp = caca_create_display(cv);
107    if(!dp)
108        return 1;
109    caca_set_cursor(dp, 1);
110
111    w = cucul_get_canvas_width(cv);
112    h = cucul_get_canvas_height(cv);
113
114
115    /* Create screen list */
116    screen_list = (struct screen_list*)     malloc(sizeof(struct screen_list));
117    if(!screen_list)
118    {
119        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
120        return -1;
121    }
122    screen_list->screen = (struct screen**) malloc(sizeof(sizeof(struct screen*)));
123    if(!screen_list->screen)
124    {
125        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
126        return -1;
127    }
128
129
130    screen_list->count = 0;
131    screen_list->width  = cucul_get_canvas_width(cv);
132    screen_list->mini = 1;
133    screen_list->help = 0;
134    screen_list->status = 1;
135    screen_list->height = cucul_get_canvas_height(cv) - ((screen_list->mini*6) + (screen_list->status));
136    screen_list->wm_type = WM_VSPLIT;
137    screen_list->in_bell = 0;
138    screen_list->pty = screen_list->prevpty = 0;
139    screen_list->dont_update_coords = 0;
140    screen_list->screensaver_timeout = (60) * 1000000;
141    screen_list->screensaver_data = NULL;
142    screen_list->in_screensaver = 0;
143    screen_list->locked = 0;
144    screen_list->attached = 1;
145    screen_list->socket = 0;
146    screen_list->socket_dir = NULL;
147    screen_list->socket_path = NULL;
148    memset(screen_list->lockmsg, 0, 1024);
149    memset(screen_list->lockpass, 0, 1024);
150
151    if(!read_configuration_file("neercs.ini", screen_list))
152    {
153
154        if(!read_configuration_file("~/neercs.ini", screen_list))
155        {
156            if(!read_configuration_file("/etc/neercs.ini", screen_list))
157            {
158                printf("Failed to read configuration file\n");
159            }
160        }
161    }
162
163    recurrent_list = (struct recurrent_list*) malloc(sizeof(struct recurrent_list));
164    recurrent_list->recurrent = (struct recurrent**) malloc(sizeof(struct recurrent*));
165    if(!recurrent_list->recurrent)
166    {
167        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
168        return -1;
169    }
170    recurrent_list->count = 0;
171
172    for(;;)
173    {
174        int option_index = 0;
175        char **sockets;
176        int grab = 0;
177        static struct myoption long_options[] =
178            {
179#if defined USE_GRAB
180                { "pid",         1, NULL, 'P' },
181#endif
182                { "help",        0, NULL, 'h' },
183                { "version",     0, NULL, 'v' },
184            };
185#if defined USE_GRAB
186        int c = mygetopt(argc, argv, "RP:hv", long_options, &option_index);
187#else
188        int c = mygetopt(argc, argv, "Rhv", long_options, &option_index);
189#endif
190        if(c == -1)
191            break;
192
193        switch(c)
194        {
195        case 'P': /* --pid */
196            add_screen(screen_list,create_screen_grab(w, h, atoi(myoptarg)));
197            s+=2;
198            grab = 1;
199            break;
200        case 'R':
201            if(grab)
202            {
203                fprintf(stderr, "-P and -R can not be used together!\n");
204                mainret = -1;
205                goto end;
206            }
207            caca_free_display(dp);
208            dp = NULL;
209            sockets = list_sockets(screen_list->socket_dir);
210            if(sockets && sockets[0])
211            {
212                request_attach(sockets[0]);
213                for(i=0; sockets[i]; i++)
214                    free(sockets[i]);
215                free(sockets);
216            }
217            else
218            {
219                fprintf(stderr, "No socket found!\n");
220                mainret = -1;
221            }
222            goto end;
223            break;
224        case 'h': /* --help */
225            usage(argc, argv);
226            goto end;
227            break;
228        case 'v': /* --version */
229            version();
230            goto end;
231            break;
232        default:
233            fprintf(stderr, "Unknown argument #%d\n", myoptind);
234            mainret = -1;
235            goto end;
236            break;
237        }
238    }
239
240
241    if(s == 0 && argc<2)
242    {
243        add_screen(screen_list, create_screen(w, h, default_shell));
244    }
245
246    /* Launch command line processes */
247    for(i=0; i<(argc-1) - s; i++)
248    {
249        add_screen(screen_list, create_screen(w, h, argv[i+s+1]));
250    }
251
252
253    /* Windows are in a temporary state, resize them to the right dimensions */
254    update_windows_props(cv, screen_list);
255
256    last_key_time = get_us();
257
258    /* Refresh */
259    caca_refresh_display(dp);
260
261    for(;;)
262    {
263        caca_event_t ev;
264        int ret = 0;
265
266        refresh |= update_screens_contents(screen_list);
267
268        refresh |= read_socket(screen_list, cv, &dp);
269
270        /* No more screens, exit */
271        if(!screen_list->count) break;
272
273        /* Update each screen canvas  */
274        refresh |= update_terms(screen_list);
275
276        /* Get events, if any */
277        if(screen_list->attached)
278            ret = caca_get_event(dp, CACA_EVENT_ANY, &ev, 0);
279        else
280            sleep(1);
281
282        if(ret && (caca_get_event_type(&ev) & CACA_EVENT_KEY_PRESS))
283        {
284            unsigned int c = caca_get_event_key_ch(&ev);
285            if(command)
286            {
287                command = 0;
288
289                switch(c)
290                {
291                case 0x01: //CACA_KEY_CTRL_A:
292                    screen_list->pty ^= screen_list->prevpty;
293                    screen_list->prevpty ^= screen_list->pty;
294                    screen_list->pty ^= screen_list->prevpty;
295                    refresh = 1;
296                    break;
297                case 'm':
298                case 0x0d: //CACA_KEY_CTRL_M:
299                    screen_list->mini = !screen_list->mini;
300                    refresh = 1;
301                    break;
302                case 'n':
303                case ' ':
304                case '\0':
305                case 0x0e: //CACA_KEY_CTRL_N:
306                    screen_list->prevpty = screen_list->pty;
307                    screen_list->pty = (screen_list->pty + 1) % screen_list->count;
308                    refresh = 1;
309                    break;
310                case 'p':
311                case 0x10: //CACA_KEY_CTRL_P:
312                    screen_list->prevpty = screen_list->pty;
313                    screen_list->pty = (screen_list->pty + screen_list->count - 1) % screen_list->count;
314                    refresh = 1;
315                    break;
316                case 'c':
317                case 0x03: //CACA_KEY_CTRL_C:
318                    screen_list->prevpty = screen_list->pty;
319                    screen_list->pty =
320                        add_screen(screen_list, create_screen(w, h, default_shell));
321                    refresh = 1;
322                    break;
323                case 'w':
324                case 0x17: //CACA_KEY_CTRL_W:
325                    screen_list->wm_type = (screen_list->wm_type==(WM_MAX-1)?
326                                            screen_list->wm_type=0:
327                                            screen_list->wm_type+1);
328                    refresh = 1;
329                    break;
330                case 0x0b: //CACA_KEY_CTRL_K:
331                    add_recurrent(recurrent_list, close_screen_recurrent, cv);
332                    refresh = 1;
333                    break;
334                case 'x':
335                case 0x18: //CACA_KEY_CTRL_X:
336                    memset(screen_list->lockpass, 0, 1024);
337                    screen_list->locked = 1;
338                    lock_offset = 0;
339                    refresh = 1;
340                    break;
341                case 'h':
342                case 0x08: //CACA_KEY_CTRL_H:
343                    screen_list->help = !screen_list->help;
344                    refresh = 1;
345                    break;
346                case 'd':
347                case 0x04: //CACA_KEY_CTRL_D:
348                    detach(screen_list, dp);
349                    break;
350                }
351            }
352            else
353            {
354
355                last_key_time = get_us();
356                caca_set_cursor(dp, 1);
357
358                if(screen_list->in_screensaver)
359                {
360                    screensaver_kill(cv, dp, screen_list);
361                    screen_list->in_screensaver = 0;
362                    refresh = 1;
363                    continue;
364                }
365                else if(screen_list->locked)
366                {
367                    if(c==0x08)
368                    {
369                        if(lock_offset)
370                        {
371                            screen_list->lockpass[lock_offset-1] = 0;
372                            lock_offset--;
373                        }
374                    }
375                    else if(c==0x0d) // RETURN
376                    {
377                        memset(screen_list->lockmsg, 0, 1024);
378                        if(validate_lock(screen_list, getenv("USER"), screen_list->lockpass))
379                        {
380                            memset(screen_list->lockpass, 0, 1024);
381                            screen_list->locked = 0;
382                            lock_offset = 0;
383                            refresh = 1;
384                        }
385                        else
386                        {
387                            memset(screen_list->lockpass, 0, 1024);
388                            lock_offset = 0;
389                            refresh = 1;
390                        }
391                    }
392                    else
393                    {
394                        if(lock_offset < 1023)
395                        {
396                            screen_list->lockpass[lock_offset++] = c;
397                            screen_list->lockpass[lock_offset]   = 0;
398                        }
399                    }
400                }
401                else
402                {
403                    switch(c)
404                    {
405                    case 0x01: //CACA_KEY_CTRL_A:
406                        command = 1; break;
407                    case CACA_KEY_UP:
408                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[A", 3); break;
409                    case CACA_KEY_DOWN:
410                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[B", 3); break;
411                    case CACA_KEY_RIGHT:
412                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[C", 3); break;
413                    case CACA_KEY_LEFT:
414                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[D", 3); break;
415                    case CACA_KEY_PAGEUP:
416                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[5~", 4); break;
417                    case CACA_KEY_PAGEDOWN:
418                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[6~", 4); break;
419                    case CACA_KEY_HOME:
420                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[1~", 4); break;
421                    case CACA_KEY_INSERT:
422                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[2~", 4); break;
423                    case CACA_KEY_DELETE:
424                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[3~", 4); break;
425                    case CACA_KEY_END:
426                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[4~", 4); break;
427                    case CACA_KEY_F1:
428                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[11~", 5); break;
429                    case CACA_KEY_F2:
430                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[12~", 5); break;
431                    case CACA_KEY_F3:
432                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[13~", 5); break;
433                    case CACA_KEY_F4:
434                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[14~", 5); break;
435                    case CACA_KEY_F5:
436                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[15~", 5); break;
437                    case CACA_KEY_F6:
438                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[16~", 5); break;
439                    case CACA_KEY_F7:
440                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[17~", 5); break;
441                    case CACA_KEY_F8:
442                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[18~", 5); break;
443                    case CACA_KEY_F9:
444                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[19~", 5); break;
445                    case CACA_KEY_F10:
446                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[20~", 5); break;
447                    case CACA_KEY_F11:
448                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[21~", 5); break;
449                    case CACA_KEY_F12:
450                        write(screen_list->screen[screen_list->pty]->fd, "\x1b[22~", 5); break;
451
452                    case CACA_KEY_ESCAPE:
453                        if(screen_list->help)
454                        {
455                            screen_list->help = 0;
456                            refresh = 1;
457                            break;
458                        }
459                    default:
460                        write(screen_list->screen[screen_list->pty]->fd, &c, 1); break;
461                    }
462                }
463            }
464        }
465        else if(ret && (caca_get_event_type(&ev) & CACA_EVENT_RESIZE))
466        {
467            update_windows_props(cv, screen_list);
468            cucul_clear_canvas(cv);
469            refresh = 1;
470        }
471        else if(ret && (caca_get_event_type(&ev) & CACA_EVENT_QUIT))
472        {
473            break;
474        }
475
476        /* Recurrent functions */
477        for(i=0; i<recurrent_list->count; i++)
478        {
479            if(recurrent_list->recurrent[i]->function)
480            {
481                refresh |= recurrent_list->recurrent[i]->function(screen_list,
482                                                                  recurrent_list->recurrent[i],
483                                                                  recurrent_list->recurrent[i]->user,
484                                                                  get_us());
485            }
486        }
487        /* Delete recurrent functions */
488        for(i=0; i<recurrent_list->count; i++)
489        {
490            if(recurrent_list->recurrent[i]->kill_me)
491            {
492                remove_recurrent(recurrent_list, i);
493                i = 0;
494            }
495        }
496
497        /* Resfresh screen */
498        if(!screen_list->attached)
499        {
500            /* No need to refresh */
501        }
502        else if(screen_list->locked)
503        {
504            draw_lock(cv, screen_list);
505            caca_refresh_display(dp);
506        }
507        else
508        {
509            if((refresh || screen_list->in_bell) &&
510               (get_us() - last_key_time < screen_list->screensaver_timeout))
511            {
512                refresh = 0;
513                refresh_screens(cv, screen_list);
514                if(screen_list->screen[screen_list->pty]->title)
515                    caca_set_display_title(dp, screen_list->screen[screen_list->pty]->title);
516                else
517                    caca_set_display_title(dp, PACKAGE_STRING);
518                caca_refresh_display(dp);
519            }
520
521            if((get_us() - last_key_time > screen_list->screensaver_timeout))
522            {
523                if(!screen_list->in_screensaver)
524                    screensaver_init(cv, dp, screen_list);
525                screen_list->in_screensaver = 1;
526
527                caca_set_cursor(dp, 0);
528                draw_screensaver(cv, dp, screen_list);
529                caca_refresh_display(dp);
530            }
531        }
532
533        eof = 1;
534        for(i=0; i < screen_list->count; i++)
535            if(screen_list->screen[i]->fd >= 0)
536                eof = 0;
537        if(eof)
538            break;
539    }
540
541end:
542    /* Clean up */
543    if(dp)
544        caca_free_display(dp);
545    cucul_free_canvas(cv);
546    for(i = 0; i < screen_list->count; i++)
547    {
548        destroy_screen(screen_list->screen[i]);
549    }
550
551    if(screen_list->socket_path) {
552        unlink(screen_list->socket_path);
553        free(screen_list->socket_path);
554    }
555    if(screen_list->socket)
556        close(screen_list->socket);
557
558    free(screen_list->screen);
559
560
561    struct option *option = screen_list->config;
562
563
564    while(option)
565    {
566        struct option *kromeugnon = option;
567        option = option->next;
568        free(kromeugnon->key);
569        free(kromeugnon->value);
570        free(kromeugnon);
571    }
572
573    free(screen_list);
574
575    for(i=0; i<recurrent_list->count; i++)
576    {
577        remove_recurrent(recurrent_list, i);
578        i = 0;
579    }
580
581    free(recurrent_list->recurrent);
582    free(recurrent_list);
583
584    return mainret;
585}
586
587
588
589long long get_us(void)
590{
591    struct timeval tv;
592    gettimeofday(&tv, NULL);
593    return (tv.tv_sec*(1000000) + tv.tv_usec);
594}
Note: See TracBrowser for help on using the repository browser.