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

Last change on this file since 2581 was 2581, checked in by bsittler, 13 years ago

Ctrl-A a & Ctrl-A ? behaviour

  • Property svn:keywords set to Id
File size: 16.6 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: main.c 2581 2008-07-23 21:54:24Z bsittler $
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    struct screen_list *screen_list = NULL;
82    struct passwd *user_info;
83    char *user_path = NULL, *user_dir = NULL, *session_name = NULL;
84    int i, args, s=0;
85    int eof = 0, refresh = 1, command = 0;
86    long long unsigned int last_key_time = 0;
87    int mainret = 0;
88    int attach = 0, forceattach = 0;
89    int *to_grab = NULL;
90    int nb_to_grab = 0;
91
92    screen_list = create_screen_list();
93    screen_list->default_shell = getenv("SHELL");
94
95    args = argc -1;
96    if(screen_list->default_shell == NULL  && args <= 0)
97    {
98        fprintf(stderr, "Environment variable SHELL not set and no arguments given. kthxbye.\n");
99        return -1;
100    }
101
102    if(args==0)
103        args = 1;
104
105    /* Build local config file path */
106    user_dir = getenv("HOME");
107    if(!user_dir)
108    {
109        user_info = getpwuid(getuid());
110        if(user_info)
111        {
112            user_dir = user_info->pw_dir;
113        }
114    }
115    if(user_dir)
116    {
117        user_path = malloc(strlen(user_dir) + strlen("/.neercsrc") + 1);
118        sprintf(user_path, "%s/%s", user_dir, ".neercsrc");
119    }
120
121
122    screen_list->recurrent_list = (struct recurrent_list*) malloc(sizeof(struct recurrent_list));
123    screen_list->recurrent_list->recurrent = (struct recurrent**) malloc(sizeof(struct recurrent*));
124    if(!screen_list->recurrent_list->recurrent)
125    {
126        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
127        return -1;
128    }
129    screen_list->recurrent_list->count = 0;
130
131    for(;;)
132    {
133        int option_index = 0;
134        int pidopt;
135        static struct myoption long_options[] =
136            {
137                { "config",      1, NULL, 'c' },
138#if defined USE_GRAB
139                { "pid",         1, NULL, 'P' },
140#endif
141                { "help",        0, NULL, 'h' },
142                { "version",     0, NULL, 'v' },
143            };
144#if defined USE_GRAB
145        int c = mygetopt(argc, argv, "c:S:R::r::P:hv", long_options, &option_index);
146#else
147        int c = mygetopt(argc, argv, "c:S:R::r::hv", long_options, &option_index);
148#endif
149        if(c == -1)
150            break;
151
152        switch(c)
153        {
154        case 'c': /* --config */
155            if(user_path)
156                free(user_path);
157            user_path = strdup(myoptarg);
158            s+=2;
159            break;
160        case 'S':
161            if(screen_list->session_name)
162                free(screen_list->session_name);
163            screen_list->session_name = strdup(myoptarg);
164            s+=2;
165            break;
166        case 'P': /* --pid */
167            pidopt = atoi(myoptarg);
168            if(pidopt <= 0)
169            {
170                fprintf(stderr, "Invalid pid %d\n", pidopt);
171                if(to_grab)
172                    free(to_grab);
173                return -1;
174            }
175            if(!to_grab)
176            {
177                /* At most argc-1-s times -P <pid> */
178                to_grab = (int *)malloc(((argc-1-s)/2)*sizeof(int));
179                if(!to_grab)
180                {
181                    fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
182                    return -1;
183                }
184            }
185            to_grab[nb_to_grab++] = pidopt;
186            s+=2;
187            break;
188        case 'r':
189            forceattach = 1;
190        case 'R':
191            if(attach)
192            {
193                fprintf(stderr, "Attaching can only be requested once\n");
194                return -1;
195            }
196            if(myoptarg)
197            {
198                session_name = strdup(myoptarg);
199                s+=1;
200            }
201            attach = 1;
202            s+=1;
203            break;
204        case 'h': /* --help */
205            usage(argc, argv);
206            return 0;
207            break;
208        case 'v': /* --version */
209            version();
210            return 0;
211            break;
212        default:
213            fprintf(stderr, "Unknown argument #%d\n", myoptind);
214            return -1;
215            break;
216        }
217    }
218
219    /* Read global configuration first */
220    read_configuration_file("/etc/neercsrc", screen_list);
221
222    /* Then local one  */
223    if(user_path)
224    {
225        read_configuration_file(user_path, screen_list);
226        free(user_path);
227    }
228
229    if(attach)
230    {
231        char **sockets;
232        if(nb_to_grab || (argc-1 > s))
233        {
234            fprintf(stderr, "-R can not be associated with commands or pids!\n");
235            return -1;
236        }
237        sockets = list_sockets(screen_list->socket_dir, session_name);
238        if(!screen_list->session_name)
239            screen_list->session_name = session_name;
240        else
241            free(session_name);
242        if(sockets && sockets[0])
243        {
244            request_attach(sockets[0]);
245            fprintf(stderr, "Failed to attach!\n");
246            for(i=0; sockets[i]; i++)
247                free(sockets[i]);
248            free(sockets);
249        }
250        else
251        {
252            fprintf(stderr, "No socket found!\n");
253        }
254        if(forceattach)
255            return -1;
256    }
257
258    /* Build default session name */
259    if(!screen_list->session_name)
260    {
261        char mypid[32]; /* FIXME Compute the length of PID_MAX ? */
262        snprintf(mypid, 31, "%d", getpid());
263        mypid[31]= '\0';
264        screen_list->session_name = strdup(mypid);
265        if(!screen_list->session_name)
266        {
267            fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
268            return -1;
269        }
270    }
271
272    /* Create main canvas and associated caca window */
273    screen_list->cv = cucul_create_canvas(0, 0);
274    screen_list->dp = caca_create_display(screen_list->cv);
275    if(!screen_list->dp)
276        return 1;
277    caca_set_cursor(screen_list->dp, 1);
278
279    screen_list->width  = cucul_get_canvas_width(screen_list->cv);
280    screen_list->height = cucul_get_canvas_height(screen_list->cv) - ((screen_list->mini*6) + (screen_list->status));
281
282    if(nb_to_grab == 0 && s == argc -1)
283    {
284        add_screen(screen_list,
285                   create_screen(screen_list->width,
286                                 screen_list->height,
287                                 screen_list->default_shell));
288    }
289
290    /* Attach processes */
291    for(i=0; i<nb_to_grab; i++)
292    {
293        add_screen(screen_list,create_screen_grab(screen_list->width, screen_list->height, to_grab[i]));
294    }
295    free(to_grab);
296
297    /* Launch command line processes */
298    for(i=0; i<(argc-1) - s; i++)
299    {
300        add_screen(screen_list, create_screen(screen_list->width, screen_list->height, argv[i+s+1]));
301    }
302
303    /* Windows are in a temporary state, resize them to the right dimensions */
304    update_windows_props(screen_list);
305
306    last_key_time = get_us();
307
308    for(;;)
309    {
310        caca_event_t ev;
311        int ret = 0;
312
313        /* Read program output */
314        refresh |= update_screens_contents(screen_list);
315
316        /* If screen was attached, read its output */
317        refresh |= read_socket(screen_list, screen_list->cv, &screen_list->dp);
318
319        /* No more screens, exit */
320        if(!screen_list->count) break;
321
322        /* Update each screen canvas  */
323        refresh |= update_terms(screen_list);
324
325        /* Get events, if any */
326        if(screen_list->attached)
327            ret = caca_get_event(screen_list->dp, CACA_EVENT_ANY, &ev, 0);
328        else
329            sleep(1);
330
331        if(ret && (caca_get_event_type(&ev) & CACA_EVENT_KEY_PRESS))
332        {
333            unsigned int c = caca_get_event_key_ch(&ev);
334            char *str = NULL;
335            int size = 0;
336            /* CTRL-A has been pressed before, handle this as a
337             * command, except that CTRL-A a sends literal CTRL-A */
338            if(command && (c != 'a'))
339            {
340                command = 0;
341                refresh |= handle_command_input(screen_list, c);
342            }
343            else
344            {
345                /* Not in command mode */
346                last_key_time = get_us();
347                caca_set_cursor(screen_list->dp, 1);
348
349                /* Kill screensaver */
350                if(screen_list->in_screensaver)
351                {
352                    screensaver_kill(screen_list);
353                    screen_list->in_screensaver = 0;
354                    refresh = 1;
355                    continue;
356                }
357                /* Handle lock window */
358                if(screen_list->locked)
359                    refresh |= update_lock(c, screen_list);
360                else
361                {
362                    switch(c)
363                    {
364                    case 0x01: //CACA_KEY_CTRL_A:
365                        command = 1; break;
366                    case CACA_KEY_ESCAPE:
367                        if(screen_list->help)
368                        {
369                            screen_list->help = 0;
370                            refresh = 1;
371                            break;
372                        }
373                    default:
374                        /* CTRL-A a sends literal CTRL-A */
375                        if (command && (c == 'a'))
376                        {
377                            c = 0x01;
378                        }
379                        /* Normal key, convert it if needed */
380                        str = convert_input_ansi(&c, &size);
381                        write(screen_list->screen[screen_list->pty]->fd, str, size);
382                        break;
383                    }
384                }
385            }
386        }
387        /* Window resized */
388        else if(ret && (caca_get_event_type(&ev) & CACA_EVENT_RESIZE))
389        {
390            update_windows_props(screen_list);
391            cucul_clear_canvas(screen_list->cv);
392            refresh = 1;
393        }
394        /* Window closed */
395        else if(ret && (caca_get_event_type(&ev) & CACA_EVENT_QUIT))
396        {
397            refresh = 1;
398            break;
399        }
400
401        /* Launch reccurents if any */
402        refresh |= handle_recurrents(screen_list);
403
404        /* Resfresh screen */
405        if(!screen_list->attached)
406        {
407            /* No need to refresh */
408        }
409        /* Draw lock window */
410        else if(screen_list->locked)
411        {
412            draw_lock(screen_list);
413            refresh = 1;
414        }
415        else
416        {
417            if((refresh || screen_list->in_bell) &&
418               (get_us() - last_key_time < screen_list->screensaver_timeout))
419            {
420                refresh_screens(screen_list);
421                if(screen_list->screen[screen_list->pty]->title)
422                    caca_set_display_title(screen_list->dp, screen_list->screen[screen_list->pty]->title);
423                else
424                    caca_set_display_title(screen_list->dp, PACKAGE_STRING);
425                refresh = 1;
426
427            }
428            if((get_us() - last_key_time > screen_list->screensaver_timeout))
429            {
430                if(!screen_list->in_screensaver)
431                    screensaver_init(screen_list);
432                screen_list->in_screensaver = 1;
433
434                caca_set_cursor(screen_list->dp, 0);
435                draw_screensaver(screen_list);
436                refresh = 1;
437            }
438
439            if((get_us() - last_key_time > screen_list->autolock_timeout))
440            {
441                screen_list->locked = 1;
442                refresh = 1;
443            }
444
445        }
446
447        /* Refresh screen if needed */
448        if(refresh)
449        {
450            caca_refresh_display(screen_list->dp);
451            refresh = 0;
452        }
453
454        eof = 1;
455        for(i=0; i < screen_list->count; i++)
456            if(screen_list->screen[i]->fd >= 0)
457                eof = 0;
458        if(eof)
459            break;
460    }
461
462    /* Clean up */
463    if(screen_list->dp)
464        caca_free_display(screen_list->dp);
465
466    cucul_free_canvas(screen_list->cv);
467
468    for(i = 0; i < screen_list->count; i++)
469    {
470        destroy_screen(screen_list->screen[i]);
471    }
472
473    if(screen_list->socket_path) {
474        unlink(screen_list->socket_path);
475        free(screen_list->socket_path);
476    }
477
478    if(screen_list->socket)
479        close(screen_list->socket);
480
481    if(screen_list->screen) free(screen_list->screen);
482
483
484    struct option *option = screen_list->config;
485
486    while(option)
487    {
488        struct option *kromeugnon = option;
489        option = option->next;
490        if(kromeugnon->key)   free(kromeugnon->key);
491        if(kromeugnon->value) free(kromeugnon->value);
492        free(kromeugnon);
493    }
494
495    for(i=0; i<screen_list->recurrent_list->count; i++)
496    {
497        remove_recurrent(screen_list->recurrent_list, i);
498        i = 0;
499    }
500
501    if(screen_list->recurrent_list->recurrent) free(screen_list->recurrent_list->recurrent);
502    if(screen_list->recurrent_list)            free(screen_list->recurrent_list);
503
504    if(screen_list->session_name)
505        free(screen_list->session_name);
506
507    if(screen_list)
508        free(screen_list);
509
510    return mainret;
511}
512
513
514
515
516struct screen_list *create_screen_list(void)
517{
518
519    struct screen_list *screen_list = NULL;
520
521    /* Create screen list */
522    screen_list = (struct screen_list*)     malloc(sizeof(struct screen_list));
523    if(!screen_list)
524    {
525        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
526        return NULL;
527    }
528    screen_list->screen = (struct screen**) malloc(sizeof(sizeof(struct screen*)));
529    if(!screen_list->screen)
530    {
531        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
532        free(screen_list);
533        return NULL;
534    }
535
536    screen_list->count = 0;
537    screen_list->mini = 1;
538    screen_list->help = 0;
539    screen_list->status = 1;
540    screen_list->wm_type = WM_VSPLIT;
541    screen_list->in_bell = 0;
542    screen_list->pty = screen_list->prevpty = 0;
543    screen_list->dont_update_coords = 0;
544    screen_list->screensaver_timeout = (60) * 1000000;
545    screen_list->screensaver_data = NULL;
546    screen_list->in_screensaver = 0;
547    screen_list->locked = 0;
548    screen_list->lock_offset = 0;
549    screen_list->attached = 1;
550    screen_list->socket = 0;
551    screen_list->socket_dir    = NULL;
552    screen_list->socket_path   = NULL;
553    screen_list->session_name  = NULL;
554    screen_list->default_shell = NULL;
555
556    screen_list->autolock_timeout = -1;
557
558
559    screen_list->recurrent_list = NULL;
560    screen_list->cv = NULL;
561    screen_list->dp = NULL;
562
563    memset(screen_list->lockmsg, 0, 1024);
564    memset(screen_list->lockpass, 0, 1024);
565
566    return screen_list;
567}
568
569
570
571long long get_us(void)
572{
573    struct timeval tv;
574    gettimeofday(&tv, NULL);
575    return (tv.tv_sec*(1000000) + tv.tv_usec);
576}
Note: See TracBrowser for help on using the repository browser.