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

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