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

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