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

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