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

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