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

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