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

Last change on this file since 4033 was 4033, checked in by Jean-Yves Lamoureux, 11 years ago
  • Added basic and non-working python interpreter
  • 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: server.c 4033 2009-11-25 11:47:40Z 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, 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 (%.8s,%d) to client on socket %d", msg, strlen(msg),
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, strlen(msg));
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, 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, 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 =
152                write(screen_list->socket[SOCK_CLIENT], buf2,
153                      strlen(buf2) + 1);
154            if (ret < 29 && errno != EAGAIN)
155            {
156                free(buf);
157                return -1;
158            }
159            while (towrite > 0)
160            {
161                ssize_t n;
162                debug("Wrote %d, %d remaining", written, towrite);
163                /* Block to write the end of the message */
164                fcntl(screen_list->socket[SOCK_CLIENT], F_SETFL, 0);
165                n = write(screen_list->socket[SOCK_CLIENT],
166                          (char *)buf + written,
167                          towrite > bufsize ? bufsize : towrite);
168                if (n < 0)
169                {
170                    debug("Can't refresh (%s), with %d bytes (out of %d)",
171                          strerror(errno),
172                          towrite > bufsize ? bufsize : towrite, towrite);
173                    return -1;
174                }
175                written += n;
176                towrite -= n;
177            }
178            fcntl(screen_list->socket[SOCK_CLIENT], F_SETFL, O_NONBLOCK);
179            free(buf);
180        }
181        sprintf(buf2, "REFRESH %10d %10d", caca_get_cursor_x(screen_list->cv),
182                caca_get_cursor_y(screen_list->cv));
183        /* FIXME check value of r */
184        int r =
185            write(screen_list->socket[SOCK_CLIENT], buf2, strlen(buf2) + 1);
186        (void)r;
187#if defined HAVE_CACA_DIRTY_RECTANGLES
188        caca_clear_dirty_rect_list(screen_list->cv);
189#endif
190    }
191    return 0;
192}
193
194int detach(struct screen_list *screen_list)
195{
196    screen_list->attached = 0;
197    if (screen_list->lock_on_detach)
198        screen_list->locked = 1;
199    if (screen_list->socket[SOCK_CLIENT])
200    {
201        send_to_client("DETACH", screen_list);
202        close(screen_list->socket[SOCK_CLIENT]);
203        screen_list->socket[SOCK_CLIENT] = 0;
204    }
205    return 0;
206}
207
208static void server_main(struct screen_list *screen_list)
209{
210    int i;
211    int eof = 0, refresh = 1;
212
213
214
215    screen_list->last_key_time = 0;
216    screen_list->attached = 0;
217    screen_list->command = 0;
218    screen_list->was_in_bell = 0;
219    screen_list->last_refresh_time = 0;
220
221    server_init(screen_list);
222#ifdef USE_PYTHON
223    python_init(screen_list);
224#endif
225
226    for (;;)
227    {
228        int quit = 0;
229        ssize_t n;
230        char buf[128];
231
232        /* Read program output */
233        refresh |= update_screens_contents(screen_list);
234
235        /* Check if we got something from the client */
236        while (screen_list->socket[SOCK_SERVER]
237               && (n =
238                   read(screen_list->socket[SOCK_SERVER], buf,
239                        sizeof(buf) - 1)) > 0)
240        {
241            buf[n] = 0;
242            debug("Received command %s", buf);
243            if (!strncmp("ATTACH ", buf, 7))
244            {
245                refresh |= handle_attach(screen_list, buf);
246            }
247            else if (!strncmp("QUIT", buf, 4))
248            {
249                quit = 1;
250            }
251            else if (!strncmp("DELAY ", buf, 6))
252            {
253                /* FIXME check the length before calling atoi */
254                screen_list->delay = atoi(buf + 6);
255            }
256            else if (!strncmp("RESIZE ", buf, 7))
257            {
258                caca_free_canvas(screen_list->cv);
259                /* FIXME check the length before calling atoi */
260                screen_list->cv =
261                    caca_create_canvas(atoi(buf + 7), atoi(buf + 18));
262                screen_list->changed = 1;
263                refresh = 1;
264            }
265            else if (!strncmp("KEY ", buf, 4))
266            {
267                unsigned int c = atoi(buf + 4);
268                refresh |= handle_key(screen_list, c, refresh);
269            }
270            else
271            {
272                fprintf(stderr, "Unknown command received: %s\n", buf);
273            }
274        }
275
276        /* No more screens, exit */
277        if (!screen_list->count)
278            break;
279
280        /* User requested to exit */
281        if (quit)
282            break;
283
284        /* Update each screen canvas */
285        refresh |= update_terms(screen_list);
286
287        /* Launch recurrents if any */
288        refresh |= handle_recurrents(screen_list);
289
290        /* Refresh screen */
291        refresh |= refresh_screen(screen_list, refresh);
292
293        eof = 1;
294        for (i = 0; i < screen_list->count; i++)
295            if (screen_list->screen[i]->fd >= 0)
296                eof = 0;
297        if (eof)
298            break;
299    }
300
301    detach(screen_list);
302
303    free_screen_list(screen_list);
304
305#ifdef USE_PYTHON
306    python_close(screen_list);
307#endif
308   
309    exit(0);
310}
311
312static int refresh_screen(struct screen_list *screen_list, int refresh)
313{
314    if (!screen_list->attached)
315    {
316        /* No need to refresh Don't use the CPU too much Would be better to
317           select on terms + socket */
318        sleep(1);
319    }
320    else
321    {
322        long long unsigned int current_time = get_us();
323        long long int tdiff =
324            (current_time - screen_list->last_refresh_time) / 1000;
325
326
327        if (screen_list->force_refresh)
328        {
329            wm_refresh(screen_list);
330        }
331
332        /* Draw lock window */
333        if (screen_list->locked)
334        {
335            draw_lock(screen_list);
336            refresh = 1;
337        }
338        else if ((current_time - screen_list->last_key_time >=
339                  screen_list->autolock_timeout))
340        {
341            screen_list->locked = 1;
342            refresh = 1;
343        }
344        else if ((current_time - screen_list->last_key_time >=
345                  screen_list->screensaver_timeout))
346        {
347            if (!screen_list->in_screensaver)
348            {
349                screensaver_init(screen_list);
350                screen_list->in_screensaver = 1;
351                set_cursor(0, screen_list);
352            }
353            draw_screensaver(screen_list);
354            refresh = 1;
355        }
356        else if (refresh || screen_list->was_in_bell)
357        {
358            if (tdiff >= screen_list->delay)
359            {
360                screen_list->was_in_bell = screen_list->in_bell;
361                refresh_screens(screen_list);
362                set_title(screen_list);
363                refresh = 1;
364            }
365        }
366        if (refresh)
367        {
368            if (tdiff >= screen_list->delay)
369            {
370                request_refresh(screen_list);
371                screen_list->last_refresh_time = current_time;
372            }
373            else
374            {
375                debug("Skipping refresh (%lld < %d)", tdiff,
376                      screen_list->delay);
377            }
378        }
379    }
380
381    return 1;
382}
383
384static void server_init(struct screen_list *screen_list)
385{
386    int i;
387    /* Create socket and bind it */
388    create_socket(screen_list, SOCK_SERVER);
389
390    /* Connect to the client */
391    connect_socket(screen_list, SOCK_CLIENT);
392
393    screen_list->width = screen_list->height = 80;
394
395    /* Create main canvas */
396    screen_list->cv = caca_create_canvas(screen_list->width,
397                                         screen_list->height
398                                         + screen_list->mini * 6
399                                         + screen_list->status);
400
401    if (!screen_list->to_grab && !screen_list->to_start)
402    {
403        add_screen(screen_list,
404                   create_screen(screen_list->width,
405                                 screen_list->height,
406                                 screen_list->default_shell));
407    }
408
409    /* Attach processes */
410    if (screen_list->to_grab)
411    {
412        for (i = 0; screen_list->to_grab[i]; i++)
413        {
414            add_screen(screen_list,
415                       create_screen_grab(screen_list->width,
416                                          screen_list->height,
417                                          screen_list->to_grab[i]));
418        }
419        free(screen_list->to_grab);
420        screen_list->to_grab = NULL;
421    }
422
423    /* Launch command line processes */
424    if (screen_list->to_start)
425    {
426        for (i = 0; screen_list->to_start[i]; i++)
427        {
428            add_screen(screen_list,
429                       create_screen(screen_list->width,
430                                     screen_list->height,
431                                     screen_list->to_start[i]));
432            free(screen_list->to_start[i]);
433        }
434        free(screen_list->to_start);
435        screen_list->to_start = NULL;
436    }
437
438    screen_list->last_key_time = get_us();
439}
440
441static int handle_attach(struct screen_list *screen_list, char *buf)
442{
443    /* If we were attached to someone else, detach first */
444    if (screen_list->attached)
445        detach(screen_list);
446    screen_list->attached = 1;
447    caca_free_canvas(screen_list->cv);
448    screen_list->cv = caca_create_canvas(atoi(buf + 7), atoi(buf + 18));
449    screen_list->delay = atoi(buf + 29);
450    screen_list->changed = 1;
451    return 1;
452}
453
454static int handle_key(struct screen_list *screen_list, unsigned int c,
455                      int refresh)
456{
457    char *str = NULL;
458    int size = 0;
459
460    if (screen_list->help)
461    {
462        return help_handle_key(screen_list, c);
463    }
464#ifdef USE_PYTHON
465    if (screen_list->python_command)
466    {
467        return python_command_handle_key(screen_list, c);
468    }
469#endif
470
471    /* CTRL-A has been pressed before, handle this as a command, except that
472       CTRL-A a sends literal CTRL-A */
473    if (screen_list->command && (c != 'a'))
474    {
475        screen_list->command = 0;
476        refresh |= handle_command_input(screen_list, c);
477    }
478    else
479    {
480        /* Not in command mode */
481        screen_list->last_key_time = get_us();
482        set_cursor(1, screen_list);
483
484        /* Kill screensaver */
485        if (screen_list->in_screensaver)
486        {
487            screensaver_kill(screen_list);
488            screen_list->in_screensaver = 0;
489            screen_list->changed = 1;
490            refresh = 1;
491            return refresh;
492        }
493        /* Handle lock window */
494        if (screen_list->locked)
495        {
496            refresh |= update_lock(c, screen_list);
497            screen_list->changed = 1;
498        }
499        else if (screen_list->window_list)
500        {
501            refresh |= update_window_list(c, screen_list);
502            screen_list->changed = 1;
503        }
504        else
505        {
506            switch (c)
507            {
508            case 0x01:         // CACA_KEY_CTRL_A:
509                screen_list->command = 1;
510                break;
511            default:
512                /* CTRL-A a sends literal CTRL-A */
513                if (screen_list->command && (c == 'a'))
514                {
515                    c = 0x01;
516                }
517                /* Normal key, convert it if needed */
518                str = convert_input_ansi(&c, &size);
519                /* FIXME check value of r */
520                int r = write(screen_list->screen[screen_list->pty]->fd, str,
521                              size);
522                (void)r;
523                break;
524            }
525        }
526    }
527    return refresh;
528}
529
530int send_ansi_sequence(struct screen_list *screen_list, char *str)
531{
532    debug("Sending ansi '%s'\n", str);
533    return write(screen_list->screen[screen_list->pty]->fd, str, strlen(str));
534}
535
536
537int install_fds(struct screen_list *screen_list)
538{
539    int fd;
540    close(0);
541    close(1);
542    close(2);
543    fd = open("/dev/null", O_RDWR, 0);
544    if (fd < 0)
545    {
546        perror("Failed to open /dev/null");
547        return -2;
548    }
549    dup2(fd, 0);
550#ifndef DEBUG
551    dup2(fd, 1);
552    dup2(fd, 2);
553    if (fd > 2)
554        close(fd);
555#else
556    if (fd != 0)
557        close(fd);
558    screen_list->outfd =
559        open("/tmp/neercs-debug.txt", O_TRUNC | O_RDWR | O_CREAT,
560             S_IRUSR | S_IWUSR);
561    dup2(screen_list->outfd, 1);
562    dup2(screen_list->outfd, 2);
563    if (screen_list->outfd > 2)
564        close(screen_list->outfd);
565#endif
566        return 0;
567}
568
569int start_server(struct screen_list *screen_list)
570{
571    pid_t pid;
572
573    pid = fork();
574    if (pid < 0)
575    {
576        perror("Failed to create child process");
577        return -1;
578    }
579    if (pid == 0)
580    {
581        int r = install_fds(screen_list);
582        if(r) return r;
583        setsid();
584
585        server_main(screen_list);
586        /* Never returns */
587    }
588
589    return 0;
590}
591
592long long get_us(void)
593{
594    struct timeval tv;
595    gettimeofday(&tv, NULL);
596    return (tv.tv_sec * (1000000) + tv.tv_usec);
597}
Note: See TracBrowser for help on using the repository browser.