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

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