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

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