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

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