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

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