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

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