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

Last change on this file since 3501 was 3501, checked in by Pascal Terjan, 13 years ago

Add svn:keywords to various .c files

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