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

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