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

Last change on this file since 3914 was 3914, checked in by Jean-Yves Lamoureux, 11 years ago
  • Added 'cube' window manager (needs latest svn libcaca for caca_fill_triangle_textured), at last.
  • Property svn:keywords set to Id
File size: 17.2 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 3914 2009-11-17 15:36:37Z 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 <caca.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 process\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    int i, args;
83    long long unsigned int last_key_time = 0;
84    int mainret = -1;
85#define NEERCS_RECV_BUFSIZE 128*1024
86    char * buf = NULL;
87   
88    screen_list = create_screen_list();
89    screen_list->default_shell = getenv("SHELL");
90
91    args = argc -1;
92    if(screen_list->default_shell == NULL  && args <= 0)
93    {
94        fprintf(stderr, "Environment variable SHELL not set and no arguments given. kthxbye.\n");
95        goto end;
96    }
97
98    if(handle_command_line(argc, argv, screen_list) < 0)
99        goto end;
100
101    /* Read global configuration first */
102    read_configuration_file("/etc/neercsrc", screen_list);
103
104    /* Then local one  */
105    if(screen_list->user_path)
106    {
107        read_configuration_file(screen_list->user_path, screen_list);
108        free(screen_list->user_path);
109    }
110
111    if(screen_list->attach)
112    {
113        if(screen_list->nb_to_grab || screen_list->to_start)
114        {
115            fprintf(stderr, "-R can not be associated with commands or pids!\n");
116            goto end;
117        }
118
119        attach(screen_list);
120
121        if(screen_list->forceattach && !screen_list->attach)
122            goto end;
123    }
124
125    /* Build default session name */
126    if(!screen_list->session_name)
127    {
128        char mypid[32]; /* FIXME Compute the length of PID_MAX ? */
129        snprintf(mypid, 31, "%d", getpid());
130        mypid[31]= '\0';
131        screen_list->session_name = strdup(mypid);
132        if(!screen_list->session_name)
133        {
134            fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
135            goto end;
136        }
137    }
138    if(!screen_list->socket_path[SOCK_CLIENT])
139        screen_list->socket_path[SOCK_CLIENT] =
140            build_socket_path(screen_list->socket_dir, screen_list->session_name, SOCK_CLIENT);
141
142    if(!screen_list->socket_path[SOCK_SERVER])
143        screen_list->socket_path[SOCK_SERVER] =
144            build_socket_path(screen_list->socket_dir, screen_list->session_name, SOCK_SERVER);
145
146    /* Fork the server if needed */
147    if(!screen_list->attach)
148    {
149        debug("Spawning a new server");
150        if(start_server(screen_list))
151            goto end;
152    }
153
154    last_key_time = get_us();
155
156    for(;;)
157    {
158        caca_event_t ev;
159        int ret = 0;
160        ssize_t n;
161        if(!screen_list) goto end;
162        if(!buf)
163            buf = malloc(NEERCS_RECV_BUFSIZE);
164        if(!buf)
165        {
166            debug("Failed to allocate memory");
167            goto end;
168        }
169        if(screen_list->socket[SOCK_CLIENT] && (n = read(screen_list->socket[SOCK_CLIENT], buf, NEERCS_RECV_BUFSIZE-1)) > 0)
170        {
171            buf[n] = 0;
172            debug("Received from server: %s", buf);
173            if(!strncmp("DETACH", buf, 6))
174            {
175                ret = 1;
176                break;
177            }
178            else if(!strncmp("UPDATE ", buf, 7))
179            {
180                int x, y;
181                ssize_t l2 = 0, lb = 0;
182                char * buf2;
183                size_t l = 0;
184                /* FIXME check the length before calling atoi*/
185                x = atoi(buf+8);
186                y = atoi(buf+19);
187                /* 0 means we have valid data but incomplete, so read the rest */
188                while(l == 0)
189                {
190                    buf2 = realloc(buf, l2 + NEERCS_RECV_BUFSIZE);
191                    if(!buf2)
192                    {
193                        debug("Failed to allocate memory");
194                        goto end;
195                    }
196                    buf = buf2;
197                    fcntl(screen_list->socket[SOCK_CLIENT], F_SETFL, 0);
198                    lb = read(screen_list->socket[SOCK_CLIENT], buf+l2, NEERCS_RECV_BUFSIZE-1);
199                    if(lb < 0)
200                    {
201                        debug("Failed to read the end of the refresh message (%s)", strerror(errno));
202                        l = -1;
203                    }
204                    else
205                    {
206                        l2 += lb;
207#if defined HAVE_CACA_DIRTY_RECTANGLES
208                        l = caca_import_area_from_memory(screen_list->cv, x, y, buf, l2, "caca");
209#else
210                        l = caca_import_memory(screen_list->cv, buf, l2, "caca");
211#endif
212                    }
213                }
214                fcntl(screen_list->socket[SOCK_CLIENT], F_SETFL, O_NONBLOCK);
215            }
216            else if(!strncmp("REFRESH ", buf, 8))
217            {
218                int dt, x, y;
219                /* FIXME check the length before calling atoi*/
220                x = atoi(buf+8);
221                y = atoi(buf+19);
222                caca_gotoxy(screen_list->cv, x, y);
223                caca_refresh_display(screen_list->dp);
224                dt = caca_get_display_time(screen_list->dp);
225                debug("refreshtime=%dms (limit %d, requested %d)", dt/1000, screen_list->delay, screen_list->requested_delay);
226                /* Adjust refresh delay so that the server do not compute useless things */
227                if(dt > 2*1000*screen_list->delay && screen_list->delay <= 100)
228                {
229                    screen_list->delay*=2;
230                    send_delay(screen_list);
231                }
232                else if(dt < screen_list->delay*1000*1.2 &&
233                        screen_list->delay >= 3*screen_list->requested_delay/2)
234                {
235                    screen_list->delay=2*screen_list->delay/3;
236                    send_delay(screen_list);
237                }
238            }
239            else if(!strncmp("CURSOR ", buf, 7))
240            {
241                caca_set_cursor(screen_list->dp, atoi(buf+7));
242            }
243            else if(!strncmp("TITLE ", buf, 6))
244            {
245                caca_set_display_title(screen_list->dp, buf+6);
246                caca_refresh_display(screen_list->dp);
247            }
248            else
249            {
250                debug("Unknown message received from server: %s", buf);
251            }
252        }
253       
254        if(ret)
255            break;
256
257        ret = caca_get_event(screen_list->dp,
258                             CACA_EVENT_KEY_PRESS
259                             |CACA_EVENT_RESIZE
260                             |CACA_EVENT_QUIT,
261                             &ev, 10000);
262        if(ret)
263            ret = send_event(ev, screen_list);
264
265        if(ret)
266            break;
267    }
268
269    /* Clean up */
270    mainret = 0;
271end:
272    if(buf)
273        free(buf);
274    if(screen_list)
275    {
276        free_screen_list(screen_list);
277    }
278
279    return mainret;
280}
281
282void free_screen_list(struct screen_list *screen_list)
283{
284    int i;
285    struct option *option;
286
287    if(screen_list->dp)
288        caca_free_display(screen_list->dp);
289
290    if(screen_list->cv)
291        caca_free_canvas(screen_list->cv);
292   
293    for(i = 0; i < screen_list->count; i++)
294    {
295        destroy_screen(screen_list->screen[i]);
296    }
297
298    if(screen_list->socket_path[SOCK_SERVER])
299        free(screen_list->socket_path[SOCK_SERVER]);
300   
301    if(screen_list->socket_path[SOCK_CLIENT])
302    {
303        unlink(screen_list->socket_path[SOCK_CLIENT]);
304        free(screen_list->socket_path[SOCK_CLIENT]);
305    }
306   
307    if(screen_list->socket[SOCK_CLIENT])
308        close(screen_list->socket[SOCK_CLIENT]);
309   
310    if(screen_list->socket[SOCK_SERVER])
311        close(screen_list->socket[SOCK_SERVER]);
312
313    if(screen_list->screen)
314        free(screen_list->screen);
315
316    option = screen_list->config;
317   
318    while(option)
319    {
320        struct option *kromeugnon = option;
321        option = option->next;
322        if(kromeugnon->key)   free(kromeugnon->key);
323        if(kromeugnon->value) free(kromeugnon->value);
324        free(kromeugnon);
325    }
326   
327    for(i=0; i<screen_list->recurrent_list->count; i++)
328    {
329        remove_recurrent(screen_list->recurrent_list, i);
330        i = 0;
331    }
332
333    if(screen_list->recurrent_list->recurrent)
334        free(screen_list->recurrent_list->recurrent);
335    if(screen_list->recurrent_list)
336        free(screen_list->recurrent_list);
337   
338    if(screen_list->session_name)
339        free(screen_list->session_name);
340
341    if(screen_list->title)
342        free(screen_list->title);
343   
344    free(screen_list);
345}
346
347struct screen_list *create_screen_list(void)
348{
349
350    struct screen_list *screen_list = NULL;
351    struct passwd *user_info;
352    char *user_dir = NULL;
353
354    /* Create screen list */
355    screen_list = (struct screen_list*)     malloc(sizeof(struct screen_list));
356    if(!screen_list)
357    {
358        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
359        return NULL;
360    }
361    screen_list->screen = (struct screen**) malloc(sizeof(sizeof(struct screen*)));
362    if(!screen_list->screen)
363    {
364        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
365        free(screen_list);
366        return NULL;
367    }
368
369    screen_list->count = 0;
370    screen_list->mini = 1;
371    screen_list->help = 0;
372    screen_list->status = 1;
373    screen_list->title = NULL;
374    screen_list->window_list = 0;
375    screen_list->wm_type = WM_VSPLIT;
376    screen_list->in_bell = 0;
377    screen_list->changed = 0;
378    screen_list->requested_delay = 0;
379    screen_list->delay = 1000/60; /* Don't refresh more than 60 times per second */
380    screen_list->pty = screen_list->prevpty = 0;
381    screen_list->dont_update_coords = 0;
382    screen_list->screensaver_timeout = (60) * 1000000;
383    screen_list->screensaver_data = NULL;
384    screen_list->in_screensaver = 0;
385    screen_list->locked = 0;
386    screen_list->lock_offset = 0;
387    screen_list->lock_on_detach = 0;   
388    screen_list->attached = 1;
389    screen_list->socket[SOCK_SERVER] = 0;
390    screen_list->socket[SOCK_CLIENT] = 0;
391    screen_list->socket_dir    = NULL;
392    screen_list->socket_path[SOCK_SERVER] = NULL;
393    screen_list->socket_path[SOCK_CLIENT] = NULL;
394    screen_list->session_name  = NULL;
395    screen_list->default_shell = NULL;
396    screen_list->user_path     = NULL;
397    screen_list->autolock_timeout = -1;
398    screen_list->to_grab = NULL;
399    screen_list->to_start = NULL;
400    screen_list->nb_to_grab = 0;
401    screen_list->attach = 0;
402    screen_list->forceattach = 0;
403   
404    screen_list->force_refresh = 0;
405    screen_list->cube.in_switch = 0;
406    screen_list->cube.first=0;
407    screen_list->cube.second=0;
408
409
410    screen_list->recurrent_list = NULL;
411    screen_list->cv = NULL;
412    screen_list->dp = NULL;
413
414    memset(screen_list->lockmsg, 0, 1024);
415    memset(screen_list->lockpass, 0, 1024);
416
417
418
419    /* Build local config file path */
420    user_dir = getenv("HOME");
421    if(!user_dir)
422    {
423        user_info = getpwuid(getuid());
424        if(user_info)
425        {
426            user_dir = user_info->pw_dir;
427        }
428    }
429    if(user_dir)
430    {
431        screen_list->user_path = malloc(strlen(user_dir) + strlen("/.neercsrc") + 1);
432        sprintf(screen_list->user_path, "%s/%s", user_dir, ".neercsrc");
433    }
434
435
436    screen_list->recurrent_list = (struct recurrent_list*) malloc(sizeof(struct recurrent_list));
437    screen_list->recurrent_list->recurrent = (struct recurrent**) malloc(sizeof(struct recurrent*));
438    if(!screen_list->recurrent_list->recurrent)
439    {
440        fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
441        free(screen_list);
442        free(screen_list->screen);
443        return NULL;
444    }
445    screen_list->recurrent_list->count = 0;
446
447
448
449    return screen_list;
450}
451
452
453
454int handle_command_line(int argc, char *argv[], struct screen_list *screen_list)
455{
456    int s = 0, i;
457    for(;;)
458    {
459        int option_index = 0;
460        int pidopt;
461        static struct myoption long_options[] =
462            {
463                { "config",      1, NULL, 'c' },
464#if defined USE_GRAB
465                { "pid",         0, NULL, 'P' },
466#endif
467                { "help",        0, NULL, 'h' },
468                { "version",     0, NULL, 'v' },
469                { NULL, 0, NULL, 0 },
470            };
471#if defined USE_GRAB
472        int c = mygetopt(argc, argv, "c:S:R::r::P::hv", long_options, &option_index);
473#else
474        int c = mygetopt(argc, argv, "c:S:R::r::hv", long_options, &option_index);
475#endif
476        if(c == -1)
477            break;
478
479        switch(c)
480        {
481        case 'c': /* --config */
482            if(screen_list->user_path)
483                free(screen_list->user_path);
484            screen_list->user_path = strdup(myoptarg);
485            s+=2;
486            break;
487        case 'S':
488            if(!screen_list->session_name)
489                screen_list->session_name = strdup(myoptarg);
490            s+=2;
491            break;
492        case 'P': /* --pid */
493            if(myoptarg)
494            {
495                pidopt = atoi(myoptarg);
496                if(pidopt <= 0)
497                {
498                    fprintf(stderr, "Invalid pid %d\n", pidopt);
499                    if(screen_list->to_grab)
500                        free(screen_list->to_grab);
501                    return -1;
502                }
503            }
504            else
505                pidopt = select_process(screen_list);
506            if(pidopt <= 0)
507            {
508                s+=1;
509                break;
510            }
511            if(!screen_list->to_grab)
512            {
513                /* At most argc-1-s times -P <pid> + final 0 */
514                screen_list->to_grab = (int *)malloc(((argc-1-s)/2+1)*sizeof(int));
515                if(!screen_list->to_grab)
516                {
517                    fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
518                    return -1;
519                }
520            }
521            screen_list->to_grab[screen_list->nb_to_grab++] = pidopt;
522            screen_list->to_grab[screen_list->nb_to_grab] = 0;
523            s+=2;
524            break;
525        case 'r':
526            screen_list->forceattach = 1;
527        case 'R':
528            if(screen_list->attach)
529            {
530                fprintf(stderr, "Attaching can only be requested once\n");
531                return -1;
532            }
533            if(myoptarg)
534            {
535                if(screen_list->session_name)
536                    free(screen_list->session_name);
537                screen_list->session_name = strdup(myoptarg);
538                s+=1;
539            }
540            screen_list->attach = 1;
541            s+=1;
542            break;
543        case 'h': /* --help */
544            usage(argc, argv);
545            return -1;
546            break;
547        case 'v': /* --version */
548            version();
549            return -1;
550            break;
551        case -2:
552            return -1;
553        default:
554            fprintf(stderr, "Unknown argument #%d\n", myoptind);
555            return -1;
556            break;
557        }
558    }
559    if(s >= 0 && s < argc - 1)
560    {
561        screen_list->to_start = (char**)malloc((argc-s)*sizeof(char*));
562        if(!screen_list->to_start)
563        {
564            fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, __LINE__);
565            return -1;
566        }
567        for(i=0; i<(argc-1) - s; i++)
568        {
569            screen_list->to_start[i] = strdup(argv[i+s+1]);
570        }
571        screen_list->to_start[argc-1-s] = NULL;
572    }
573    return s;
574}
Note: See TracBrowser for help on using the repository browser.