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

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