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

Last change on this file since 2473 was 2473, checked in by Jean-Yves Lamoureux, 14 years ago
  • get_ms() returns ... microseconds. Renamed to get_us(). (fsck you)
  • Also made caca refresh function-independant
  • Property svn:keywords set to Id
File size: 17.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 2473 2008-06-20 15:43:58Z 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_us();
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_us();
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_us());
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        if(!screen_list->attached)
465        {
466            /* No need to refresh */
467        }
468        else if(screen_list->locked)
469        {
470            draw_lock(cv, screen_list);
471            caca_refresh_display(dp);
472        }
473        else
474        {
475            if((refresh || screen_list->in_bell) &&
476               (get_us() - last_key_time < screen_list->screensaver_timeout))
477            {
478                refresh = 0;
479                refresh_screens(cv, dp, screen_list);
480                caca_refresh_display(dp);
481            }
482
483            if((get_us() - last_key_time > screen_list->screensaver_timeout))
484            {
485                if(!screen_list->in_screensaver)
486                    screensaver_init(cv, dp, screen_list);
487                screen_list->in_screensaver = 1;
488
489                caca_set_cursor(dp, 0);
490                draw_screensaver(cv, dp, screen_list);
491                caca_refresh_display(dp);
492            }
493        }
494
495        eof = 1;
496        for(i=0; i < screen_list->count; i++)
497            if(screen_list->screen[i]->fd >= 0)
498                eof = 0;
499        if(eof)
500            break;
501    }
502
503end:
504    /* Clean up */
505    caca_free_display(dp);
506    cucul_free_canvas(cv);
507    for(i = 0; i < screen_list->count; i++)
508    {
509        destroy_screen(screen_list->screen[i]);
510    }
511
512    free(screen_list->screen);
513
514
515    struct option *option = screen_list->config;
516
517
518    while(option)
519    {
520        struct option *kromeugnon = option;
521        option = option->next;
522        free(kromeugnon->key);
523        free(kromeugnon->value);
524        free(kromeugnon);
525    }
526
527    free(screen_list);
528
529    for(i=0; i<recurrent_list->count; i++)
530    {
531        remove_recurrent(recurrent_list, i);
532        i = 0;
533    }
534
535    free(recurrent_list->recurrent);
536    free(recurrent_list);
537
538    return 0;
539}
540
541
542
543long long get_us(void)
544{
545    struct timeval tv;
546    gettimeofday(&tv, NULL);
547    return (tv.tv_sec*(1000000) + tv.tv_usec);
548}
Note: See TracBrowser for help on using the repository browser.