source: neercs/trunk/src/server.c @ 4063

Revision 4063, 17.0 KB checked in by jylam, 3 years ago (diff)
  • Moved modal windows flags to struct modal
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
RevLine 
[3944]1/*
[2588]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>
[3431]26#include <sys/socket.h>
[2588]27#include <sys/types.h>
28#include <sys/wait.h>
29#include <sys/time.h>
30#include <time.h>
31#include <pwd.h>
32
33#include <errno.h>
34#include <caca.h>
35
36#include "neercs.h"
37
[3944]38static void server_init(struct screen_list *screen_list);
[4055]39static void refresh_screen(struct screen_list *screen_list, int refresh);
[3947]40static int handle_key(struct screen_list *screen_list, unsigned int c,
41                      int refresh);
[3944]42static int handle_attach(struct screen_list *screen_list, char *buf);
43
[4052]44static int send_to_client(const char *msg, int size,
45                          struct screen_list *screen_list)
[2588]46{
[2589]47    int ret;
[4059]48    if (!screen_list->comm.socket[SOCK_CLIENT])
[2614]49        connect_socket(screen_list, SOCK_CLIENT);
[4059]50    if (!screen_list->comm.socket[SOCK_CLIENT])
[2589]51        ret = -1;
52    else
[4059]53        ret = write(screen_list->comm.socket[SOCK_CLIENT], msg, size);
[3944]54    if (ret < 0 && errno != EAGAIN)
[2593]55    {
[3944]56        fprintf(stderr, "Failed to send message to client: %s\n",
57                strerror(errno));
[4059]58        if (screen_list->comm.attached)
[3396]59            detach(screen_list);
[2593]60    }
[2589]61    return ret;
[2588]62}
63
[3944]64static int set_title(struct screen_list *screen_list)
[2588]65{
66    char buf[1024];
67    int bytes;
[3946]68    char *title = NULL;
[2588]69
[4059]70    if (screen_list->comm.attached)
[2784]71    {
[3944]72        if (screen_list->pty < screen_list->count &&
73            screen_list->screen[screen_list->pty]->title)
[2784]74            title = screen_list->screen[screen_list->pty]->title;
75        else
76            title = PACKAGE_STRING;
77    }
78
[3944]79    if (screen_list->title)
[2784]80    {
[3944]81        if (!strcmp(screen_list->title, title))
[2784]82            return 0;
83        free(screen_list->title);
84    }
85
86    screen_list->title = strdup(title);
87
[3944]88    bytes = snprintf(buf, sizeof(buf) - 1, "TITLE %s", title);
[2588]89    buf[bytes] = '\0';
[4051]90    return send_to_client(buf, strlen(buf), screen_list);
[2588]91}
92
[3944]93static int set_cursor(int state, struct screen_list *screen_list)
[2588]94{
95    char buf[16];
96    int bytes;
97
[3944]98    bytes = snprintf(buf, sizeof(buf) - 1, "CURSOR %d", state);
[2588]99    buf[bytes] = '\0';
100
[4051]101    return send_to_client(buf, strlen(buf), screen_list);
[2588]102}
103
[3944]104static int request_refresh(struct screen_list *screen_list)
[2588]105{
[3498]106#if defined HAVE_CACA_DIRTY_RECTANGLES
107    int ndirty = caca_get_dirty_rect_count(screen_list->cv);
[3944]108    if (!ndirty)
[3498]109        return 0;
110#endif
[4059]111    if (!screen_list->comm.socket[SOCK_CLIENT])
[2614]112        connect_socket(screen_list, SOCK_CLIENT);
[4059]113    if (screen_list->comm.socket[SOCK_CLIENT])
[3431]114    {
[3498]115        size_t bufsize, towrite;
[3894]116        ssize_t written, ret;
[3431]117        socklen_t optlen = sizeof(bufsize);
[3498]118        size_t bytes;
119        void *buf;
[3500]120        char buf2[32];
[3541]121        int x, y, i;
[3498]122
[4059]123        getsockopt(screen_list->comm.socket[SOCK_CLIENT], SOL_SOCKET, SO_SNDBUF,
[3431]124                   &bufsize, &optlen);
[3438]125        bufsize /= 2;
126        debug("bufsize=%d", bufsize);
[3498]127
128#if defined HAVE_CACA_DIRTY_RECTANGLES
[3944]129        for (i = 0; i < ndirty; i++)
[2638]130        {
[3541]131            int w, h;
[3498]132            caca_get_dirty_rect(screen_list->cv, i, &x, &y, &w, &h);
[3944]133            debug("dirty @%d,%d %dx%d [%dx%d]", x, y, w, h,
134                  caca_get_canvas_width(screen_list->cv),
135                  caca_get_canvas_height(screen_list->cv));
136            buf =
137                caca_export_area_to_memory(screen_list->cv, x, y, w, h, "caca",
138                                           &bytes);
[3498]139#else
[3431]140        {
[3498]141            i = 0;
142            x = 0;
143            y = 0;
[3944]144            buf = caca_export_memory(screen_list->cv, "caca", &bytes);
[3498]145#endif
146            debug("Requesting refresh for %d", bytes);
147            towrite = bytes;
[3894]148            written = 0;
[3500]149            sprintf(buf2, "UPDATE %10d %10d", x, y);
[4052]150            ret = send_to_client(buf2, strlen(buf2) + 1, screen_list);
[3944]151            if (ret < 29 && errno != EAGAIN)
[3431]152            {
[3498]153                free(buf);
[3431]154                return -1;
[3498]155            }
[3944]156            while (towrite > 0)
[3498]157            {
158                ssize_t n;
159                debug("Wrote %d, %d remaining", written, towrite);
160                /* Block to write the end of the message */
[4059]161                fcntl(screen_list->comm.socket[SOCK_CLIENT], F_SETFL, 0);
[4052]162                n = send_to_client((char *)buf + written,
163                                   towrite > bufsize ? bufsize : towrite,
164                                   screen_list);
[3944]165                if (n < 0)
[3498]166                {
[3944]167                    debug("Can't refresh (%s), with %d bytes (out of %d)",
168                          strerror(errno),
169                          towrite > bufsize ? bufsize : towrite, towrite);
[3498]170                    return -1;
[3942]171                }
[3498]172                written += n;
173                towrite -= n;
174            }
[4059]175            fcntl(screen_list->comm.socket[SOCK_CLIENT], F_SETFL, O_NONBLOCK);
[3498]176            free(buf);
[3431]177        }
[3944]178        sprintf(buf2, "REFRESH %10d %10d", caca_get_cursor_x(screen_list->cv),
179                caca_get_cursor_y(screen_list->cv));
[3954]180        /* FIXME check value of r */
[4052]181        int r = send_to_client(buf2, strlen(buf2) + 1, screen_list);
[3954]182        (void)r;
[3488]183#if defined HAVE_CACA_DIRTY_RECTANGLES
[3496]184        caca_clear_dirty_rect_list(screen_list->cv);
[3488]185#endif
[3431]186    }
[2589]187    return 0;
[2588]188}
189
[3944]190int detach(struct screen_list *screen_list)
[2588]191{
[4059]192    screen_list->comm.attached = 0;
[4060]193    if (screen_list->lock.lock_on_detach)
194        screen_list->lock.locked = 1;
[4059]195    if (screen_list->comm.socket[SOCK_CLIENT])
[2593]196    {
[4051]197        send_to_client("DETACH", 6, screen_list);
[4059]198        close(screen_list->comm.socket[SOCK_CLIENT]);
199        screen_list->comm.socket[SOCK_CLIENT] = 0;
[2593]200    }
201    return 0;
[2588]202}
203
[4052]204static int server_iteration(struct screen_list *screen_list)
[2588]205{
206    int i;
[4055]207    int eof = 0, refresh;
[2588]208
[4052]209    int quit = 0;
210    ssize_t n;
211    char buf[128];
[4057]212
[4052]213    /* Read program output */
[4055]214    refresh = update_screens_contents(screen_list);
[4057]215
[4052]216    /* Check if we got something from the client */
[4059]217    while (screen_list->comm.socket[SOCK_SERVER]
[4052]218           && (n =
[4059]219               read(screen_list->comm.socket[SOCK_SERVER], buf,
[4052]220                    sizeof(buf) - 1)) > 0)
221    {
222        buf[n] = 0;
223        debug("Received command %s", buf);
224        if (!strncmp("ATTACH ", buf, 7))
225        {
226            refresh |= handle_attach(screen_list, buf);
227        }
228        else if (!strncmp("QUIT", buf, 4))
229        {
230            quit = 1;
231        }
232        else if (!strncmp("DELAY ", buf, 6))
233        {
234            /* FIXME check the length before calling atoi */
235            screen_list->delay = atoi(buf + 6);
236        }
237        else if (!strncmp("RESIZE ", buf, 7))
238        {
239            caca_free_canvas(screen_list->cv);
240            /* FIXME check the length before calling atoi */
241            screen_list->cv =
[4057]242                caca_create_canvas(atoi(buf + 7), atoi(buf + 18));
[4052]243            screen_list->changed = 1;
244            refresh = 1;
245        }
246        else if (!strncmp("KEY ", buf, 4))
247        {
248            unsigned int c = atoi(buf + 4);
249            refresh |= handle_key(screen_list, c, refresh);
250        }
[4057]251        else if (!strncmp("MOUSEP ", buf, 6))
252        {
253            if (screen_list->screen[screen_list->pty]->report_mouse)
254            {
255                int x = atoi(buf + 7);
256                int y = atoi(buf + 18);
257                int b = atoi(buf + 28);
258                sprintf(buf, "\x1b[M%c%c%c", '@'+(b-1), x+32, y+32);
259                send_ansi_sequence(screen_list, buf);
260            }
261        }
[4052]262        else
263        {
264            fprintf(stderr, "Unknown command received: %s\n", buf);
265        }
266    }
[4057]267
[4052]268    /* No more screens, exit */
269    if (!screen_list->count)
270        return -1;
[4057]271
[4052]272    /* User requested to exit */
273    if (quit)
274        return -2;
[4057]275
[4052]276    /* Update each screen canvas */
277    refresh |= update_terms(screen_list);
[4057]278
[4052]279    /* Launch recurrents if any */
280    refresh |= handle_recurrents(screen_list);
[4057]281
[4052]282    /* Refresh screen */
[4055]283    refresh_screen(screen_list, refresh);
[4057]284
[4052]285    eof = 1;
286    for (i = 0; i < screen_list->count; i++)
287        if (screen_list->screen[i]->fd >= 0)
288            eof = 0;
289    if (eof)
290        return -3;
[3972]291
[4053]292    return 0;
[4052]293}
[4033]294
[4052]295static void server_main(struct screen_list *screen_list)
296{
[3944]297    screen_list->last_key_time = 0;
[4059]298    screen_list->comm.attached = 0;
[3944]299    screen_list->command = 0;
[3946]300    screen_list->was_in_bell = 0;
301    screen_list->last_refresh_time = 0;
[2588]302
[3944]303    server_init(screen_list);
[4033]304#ifdef USE_PYTHON
305    python_init(screen_list);
306#endif
[2588]307
[3944]308    for (;;)
[2588]309    {
[4057]310        if (server_iteration(screen_list))
311            break;
[2588]312    }
313
314    detach(screen_list);
315
[3876]316    free_screen_list(screen_list);
[2588]317
[4033]318#ifdef USE_PYTHON
319    python_close(screen_list);
320#endif
[4052]321
[3555]322    exit(0);
[2588]323}
324
[4055]325static void refresh_screen(struct screen_list *screen_list, int refresh)
[3947]326{
[4059]327    if (!screen_list->comm.attached)
[3946]328    {
329        /* No need to refresh Don't use the CPU too much Would be better to
330           select on terms + socket */
331        sleep(1);
332    }
333    else
334    {
335        long long unsigned int current_time = get_us();
[3947]336        long long int tdiff =
337            (current_time - screen_list->last_refresh_time) / 1000;
[4052]338
[4055]339        refresh |= screen_list->need_refresh;
340
[3946]341        if (screen_list->force_refresh)
342        {
343            wm_refresh(screen_list);
[4049]344            refresh = 1;
[3946]345        }
[3947]346
[3946]347        /* Draw lock window */
[4060]348        if (screen_list->lock.locked)
[3946]349        {
[4055]350            /* FIXME don't redraw it each iteration */
[3946]351            draw_lock(screen_list);
352            refresh = 1;
353        }
[4056]354#ifdef USE_LOCK
[3946]355        else if ((current_time - screen_list->last_key_time >=
[4060]356                  screen_list->lock.autolock_timeout))
[3946]357        {
[4060]358            screen_list->lock.locked = 1;
[3946]359            refresh = 1;
360        }
[4056]361#endif
[3946]362        else if ((current_time - screen_list->last_key_time >=
[4058]363                  screen_list->screensaver.timeout))
[3946]364        {
[4058]365            if (!screen_list->screensaver.in_screensaver)
[3946]366            {
367                screensaver_init(screen_list);
[4058]368                screen_list->screensaver.in_screensaver = 1;
[3946]369                set_cursor(0, screen_list);
370            }
371            draw_screensaver(screen_list);
372            refresh = 1;
373        }
374        else if (refresh || screen_list->was_in_bell)
375        {
376            if (tdiff >= screen_list->delay)
377            {
378                refresh_screens(screen_list);
379                set_title(screen_list);
380                refresh = 1;
381            }
382        }
383        if (refresh)
384        {
385            if (tdiff >= screen_list->delay)
386            {
387                request_refresh(screen_list);
388                screen_list->last_refresh_time = current_time;
[4055]389                screen_list->need_refresh = 0;
[3946]390            }
391            else
392            {
393                debug("Skipping refresh (%lld < %d)", tdiff,
394                      screen_list->delay);
[4055]395                screen_list->need_refresh = 1;
[3946]396            }
397        }
398    }
399}
[3944]400
401static void server_init(struct screen_list *screen_list)
402{
403    int i;
[4038]404    debug("Screen list at %p\n", screen_list);
[4052]405
[3944]406    /* Create socket and bind it */
407    create_socket(screen_list, SOCK_SERVER);
408
409    /* Connect to the client */
410    connect_socket(screen_list, SOCK_CLIENT);
411
412    screen_list->width = screen_list->height = 80;
413
414    /* Create main canvas */
415    screen_list->cv = caca_create_canvas(screen_list->width,
416                                         screen_list->height
[4063]417                                         + screen_list->modals.mini * 6
418                                         + screen_list->modals.status);
[3944]419
420    if (!screen_list->to_grab && !screen_list->to_start)
421    {
422        add_screen(screen_list,
423                   create_screen(screen_list->width,
424                                 screen_list->height,
425                                 screen_list->default_shell));
426    }
427
428    /* Attach processes */
429    if (screen_list->to_grab)
430    {
431        for (i = 0; screen_list->to_grab[i]; i++)
432        {
433            add_screen(screen_list,
434                       create_screen_grab(screen_list->width,
435                                          screen_list->height,
436                                          screen_list->to_grab[i]));
437        }
438        free(screen_list->to_grab);
439        screen_list->to_grab = NULL;
440    }
441
442    /* Launch command line processes */
443    if (screen_list->to_start)
444    {
445        for (i = 0; screen_list->to_start[i]; i++)
446        {
447            add_screen(screen_list,
448                       create_screen(screen_list->width,
449                                     screen_list->height,
450                                     screen_list->to_start[i]));
451            free(screen_list->to_start[i]);
452        }
453        free(screen_list->to_start);
454        screen_list->to_start = NULL;
455    }
456
457    screen_list->last_key_time = get_us();
458}
459
460static int handle_attach(struct screen_list *screen_list, char *buf)
461{
462    /* If we were attached to someone else, detach first */
[4059]463    if (screen_list->comm.attached)
[3944]464        detach(screen_list);
[4059]465    screen_list->comm.attached = 1;
[3944]466    caca_free_canvas(screen_list->cv);
467    screen_list->cv = caca_create_canvas(atoi(buf + 7), atoi(buf + 18));
468    screen_list->delay = atoi(buf + 29);
469    screen_list->changed = 1;
470    return 1;
471}
472
[3947]473static int handle_key(struct screen_list *screen_list, unsigned int c,
474                      int refresh)
[3944]475{
476    char *str = NULL;
477    int size = 0;
[3962]478
[4063]479    if (screen_list->modals.help)
[3962]480    {
[4033]481        return help_handle_key(screen_list, c);
[3962]482    }
[4026]483#ifdef USE_PYTHON
[4063]484    if (screen_list->modals.python_command)
[4026]485    {
[4033]486        return python_command_handle_key(screen_list, c);
[4026]487    }
488#endif
[4033]489
[4016]490    /* CTRL-A has been pressed before, handle this as a command, except that
[4033]491       CTRL-A a sends literal CTRL-A */
[3944]492    if (screen_list->command && (c != 'a'))
493    {
494        screen_list->command = 0;
495        refresh |= handle_command_input(screen_list, c);
496    }
497    else
498    {
499        /* Not in command mode */
500        screen_list->last_key_time = get_us();
501        set_cursor(1, screen_list);
502
503        /* Kill screensaver */
[4058]504        if (screen_list->screensaver.in_screensaver)
[3944]505        {
506            screensaver_kill(screen_list);
[4058]507            screen_list->screensaver.in_screensaver = 0;
[3944]508            screen_list->changed = 1;
509            refresh = 1;
510            return refresh;
511        }
512        /* Handle lock window */
[4060]513        if (screen_list->lock.locked)
[3944]514        {
515            refresh |= update_lock(c, screen_list);
516            screen_list->changed = 1;
517        }
[4063]518        else if (screen_list->modals.window_list)
[3944]519        {
520            refresh |= update_window_list(c, screen_list);
521            screen_list->changed = 1;
522        }
523        else
524        {
525            switch (c)
526            {
527            case 0x01:         // CACA_KEY_CTRL_A:
528                screen_list->command = 1;
529                break;
530            default:
531                /* CTRL-A a sends literal CTRL-A */
532                if (screen_list->command && (c == 'a'))
533                {
534                    c = 0x01;
535                }
536                /* Normal key, convert it if needed */
537                str = convert_input_ansi(&c, &size);
[3954]538                /* FIXME check value of r */
539                int r = write(screen_list->screen[screen_list->pty]->fd, str,
540                              size);
541                (void)r;
[3944]542                break;
543            }
544        }
545    }
546    return refresh;
547}
548
[4016]549int send_ansi_sequence(struct screen_list *screen_list, char *str)
550{
551    debug("Sending ansi '%s'\n", str);
552    return write(screen_list->screen[screen_list->pty]->fd, str, strlen(str));
553}
[3944]554
[4033]555
556int install_fds(struct screen_list *screen_list)
557{
558    int fd;
559    close(0);
560    close(1);
561    close(2);
562    fd = open("/dev/null", O_RDWR, 0);
563    if (fd < 0)
564    {
565        perror("Failed to open /dev/null");
566        return -2;
567    }
568    dup2(fd, 0);
569#ifndef DEBUG
570    dup2(fd, 1);
571    dup2(fd, 2);
572    if (fd > 2)
573        close(fd);
574#else
575    if (fd != 0)
576        close(fd);
577    screen_list->outfd =
578        open("/tmp/neercs-debug.txt", O_TRUNC | O_RDWR | O_CREAT,
579             S_IRUSR | S_IWUSR);
580    dup2(screen_list->outfd, 1);
581    dup2(screen_list->outfd, 2);
582    if (screen_list->outfd > 2)
583        close(screen_list->outfd);
584#endif
[4052]585    return 0;
[4033]586}
587
[2673]588int start_server(struct screen_list *screen_list)
[2624]589{
590    pid_t pid;
591
592    pid = fork();
[3944]593    if (pid < 0)
[2624]594    {
[3321]595        perror("Failed to create child process");
596        return -1;
[2624]597    }
[3944]598    if (pid == 0)
[2624]599    {
[4033]600        int r = install_fds(screen_list);
[4052]601        if (r)
602            return r;
[2624]603        setsid();
[3972]604
[3555]605        server_main(screen_list);
[3944]606        /* Never returns */
[2624]607    }
[4033]608
[2624]609    return 0;
610}
611
[2588]612long long get_us(void)
613{
614    struct timeval tv;
615    gettimeofday(&tv, NULL);
[3944]616    return (tv.tv_sec * (1000000) + tv.tv_usec);
[2588]617}
Note: See TracBrowser for help on using the repository browser.