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

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