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

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