source: trunk/tools/neercs/old/server.c @ 2183

Last change on this file since 2183 was 2183, checked in by sam, 7 years ago

build: fix the WTFPL site URL in all code comments.

  • Property svn:keywords set to Id
File size: 19.8 KB
Line 
1/*
2 *  neercs        console-based window manager
3 *  Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net>
4 *                2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org>
5 *                2008-2010 Pascal Terjan <pterjan@linuxfr.org>
6 *                All Rights Reserved
7 *
8 *  This program is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://www.wtfpl.net/ for more details.
13 */
14
15#if defined HAVE_CONFIG_H
16#   include "config.h"
17#endif
18
19#if !defined _WIN32
20
21#include <stdio.h>
22#include <string.h>
23#include <stdlib.h>
24#include <unistd.h>
25#include <fcntl.h>
26#include <signal.h>
27#include <sys/ioctl.h>
28#include <sys/socket.h>
29#include <sys/types.h>
30#include <sys/wait.h>
31#include <sys/time.h>
32#include <time.h>
33#include <pwd.h>
34
35#include <errno.h>
36#include <caca.h>
37
38#include "neercs.h"
39
40static void server_init(struct screen_list *screen_list);
41static void refresh_screen(struct screen_list *screen_list, int refresh);
42static int handle_key(struct screen_list *screen_list, unsigned int c,
43                      int refresh);
44static int handle_attach(struct screen_list *screen_list, char *buf);
45
46static int send_to_client(const char *msg, int size,
47                          struct screen_list *screen_list)
48{
49    int ret;
50    if (!screen_list->comm.socket[SOCK_CLIENT])
51        connect_socket(screen_list, SOCK_CLIENT);
52    if (!screen_list->comm.socket[SOCK_CLIENT])
53        ret = -1;
54    else
55        ret = write(screen_list->comm.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->comm.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->comm.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    int ndirty = caca_get_dirty_rect_count(screen_list->cv);
109    if (!ndirty)
110        return 0;
111    if (!screen_list->comm.socket[SOCK_CLIENT])
112        connect_socket(screen_list, SOCK_CLIENT);
113    if (screen_list->comm.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->comm.socket[SOCK_CLIENT], SOL_SOCKET,
124                   SO_SNDBUF, &bufsize, &optlen);
125        bufsize /= 2;
126        debug("bufsize=%d", bufsize);
127
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            debug("Requesting refresh for %d", bytes);
139            towrite = bytes;
140            written = 0;
141            sprintf(buf2, "UPDATE %10d %10d", x, y);
142            ret = send_to_client(buf2, strlen(buf2) + 1, screen_list);
143            if (ret < 29)
144            {
145                free(buf);
146                return -1;
147            }
148            /* Block to write the end of the message */
149            fcntl(screen_list->comm.socket[SOCK_CLIENT], F_SETFL, 0);
150            while (towrite > 0)
151            {
152                ssize_t n;
153                debug("Wrote %d, %d remaining", written, towrite);
154                n = send_to_client((char *)buf + written,
155                                   towrite > bufsize ? bufsize : towrite,
156                                   screen_list);
157                if (n < 0)
158                {
159                    debug("Can't refresh (%s), with %d bytes (out of %d)",
160                          strerror(errno),
161                          towrite > bufsize ? bufsize : towrite, towrite);
162                    return -1;
163                }
164                written += n;
165                towrite -= n;
166            }
167            fcntl(screen_list->comm.socket[SOCK_CLIENT], F_SETFL, O_NONBLOCK);
168            free(buf);
169        }
170        sprintf(buf2, "REFRESH %10d %10d", caca_get_cursor_x(screen_list->cv),
171                caca_get_cursor_y(screen_list->cv));
172        /* FIXME check value of r */
173        int r = send_to_client(buf2, strlen(buf2) + 1, screen_list);
174        (void)r;
175        caca_clear_dirty_rect_list(screen_list->cv);
176    }
177    return 0;
178}
179
180int detach(struct screen_list *screen_list)
181{
182    screen_list->comm.attached = 0;
183    if (screen_list->lock.lock_on_detach)
184        screen_list->lock.locked = 1;
185    if (screen_list->comm.socket[SOCK_CLIENT])
186    {
187        send_to_client("DETACH", 6, screen_list);
188        close(screen_list->comm.socket[SOCK_CLIENT]);
189        screen_list->comm.socket[SOCK_CLIENT] = 0;
190    }
191    return 0;
192}
193
194static int server_iteration(struct screen_list *screen_list)
195{
196    int i;
197    int eof = 0, refresh;
198
199    int quit = 0;
200    ssize_t n;
201    char buf[128];
202
203    /* Read program output */
204    refresh = update_screens_contents(screen_list);
205
206    /* Check if we got something from the client */
207    while (screen_list->comm.socket[SOCK_SERVER]
208           && (n =
209               read(screen_list->comm.socket[SOCK_SERVER], buf,
210                    sizeof(buf) - 1)) > 0)
211    {
212        buf[n] = 0;
213        debug("Received command %s", buf);
214        if (!strncmp("ATTACH ", buf, 7))
215        {
216            refresh |= handle_attach(screen_list, buf);
217        }
218        else if (!strncmp("QUIT", buf, 4))
219        {
220            quit = 1;
221        }
222        else if (!strncmp("DELAY ", buf, 6))
223        {
224            /* FIXME check the length before calling atoi */
225            screen_list->delay = atoi(buf + 6);
226        }
227        else if (!strncmp("RESIZE ", buf, 7))
228        {
229            caca_free_canvas(screen_list->cv);
230            /* FIXME check the length before calling atoi */
231            screen_list->cv =
232                caca_create_canvas(atoi(buf + 7), atoi(buf + 18));
233            screen_list->changed = 1;
234            refresh = 1;
235        }
236        else if (!strncmp("KEY ", buf, 4))
237        {
238            unsigned int c = atoi(buf + 4);
239            refresh |= handle_key(screen_list, c, refresh);
240        }
241        else if (!strncmp("MOUSEP ", buf, 6))
242        {
243            debug("Got mouse press '%s'\n", buf);
244            int x, y, b;
245            x = 1 + atoi(buf + 7) - screen_list->screen[screen_list->pty]->x;
246            y = 1 + atoi(buf + 18) - screen_list->screen[screen_list->pty]->y;
247            b = atoi(buf + 28);
248
249            switch (screen_list->screen[screen_list->pty]->report_mouse)
250            {
251            case MOUSE_VT200:
252            case MOUSE_VT200_HIGHLIGHT:
253            case MOUSE_BTN_EVENT:
254            case MOUSE_ANY_EVENT:
255                sprintf(buf, "\x1b[M%c%c%c", 32 + (b - 1), x + 32, y + 32);
256                debug("mousea send ESC[M %d %d %d", (b - 1), x + 32, y + 32);
257                send_ansi_sequence(screen_list, buf);
258                break;
259            case MOUSE_X10:
260                sprintf(buf, "\x1b[M%c%c%c", 32 + (b - 1), 32 + x, 32 + y);
261                debug("mousex send ESC[M %d %d %d", 32 + (b - 1), 32 + x,
262                      32 + y);
263                send_ansi_sequence(screen_list, buf);
264                break;
265            case MOUSE_NONE:
266                break;
267
268            }
269        }
270        else if (!strncmp("MOUSER ", buf, 6))
271        {
272            debug("Got mouse release '%s'\n", buf);
273            int x, y, b;
274            x = 1 + atoi(buf + 7) - screen_list->screen[screen_list->pty]->x;
275            y = 1 + atoi(buf + 18) - screen_list->screen[screen_list->pty]->y;
276            b = atoi(buf + 28);
277
278            switch (screen_list->screen[screen_list->pty]->report_mouse)
279            {
280            case MOUSE_VT200:
281            case MOUSE_VT200_HIGHLIGHT:
282            case MOUSE_BTN_EVENT:
283            case MOUSE_ANY_EVENT:
284                sprintf(buf, "\x1b[M%c%c%c", 32 + 3, x + 32, y + 32);
285                send_ansi_sequence(screen_list, buf);
286                break;
287            case MOUSE_X10:
288                sprintf(buf, "\x1b[M%c%c%c", 32 + 3, 32 + x, 32 + y);
289                send_ansi_sequence(screen_list, buf);
290                break;
291            case MOUSE_NONE:
292                break;
293            }
294        }
295
296        else if (!strncmp("MOUSEM ", buf, 6))
297        {
298            debug("Got mouse motion '%s'\n", buf);
299            int x, y, b;
300            x = 1 + atoi(buf + 7) - screen_list->screen[screen_list->pty]->x;
301            y = 1 + atoi(buf + 18) - screen_list->screen[screen_list->pty]->y;
302            b = atoi(buf + 28);
303
304            switch (screen_list->screen[screen_list->pty]->report_mouse)
305            {
306            case MOUSE_X10:    // X10 reports mouse clicks only
307                if (b)
308                {
309                    sprintf(buf, "\x1b[M%c%c%c", 32 + (b - 1), x + 32, y + 32);
310                    send_ansi_sequence(screen_list, buf);
311                }
312                break;
313            case MOUSE_VT200:
314            case MOUSE_VT200_HIGHLIGHT:
315            case MOUSE_BTN_EVENT:
316                if (b)
317                {
318                    sprintf(buf, "\x1b[M%c%c%c", 32 + (b - 1), x + 32, y + 32);
319                    send_ansi_sequence(screen_list, buf);
320                }
321            case MOUSE_ANY_EVENT:
322                sprintf(buf, "\x1b[M%c%c%c", 32 + (b - 1), x + 32, y + 32);
323                send_ansi_sequence(screen_list, buf);
324                break;
325            case MOUSE_NONE:
326                break;
327            }
328        }
329        else
330        {
331            fprintf(stderr, "Unknown command received: %s\n", buf);
332        }
333    }
334
335    /* No more screens, exit */
336    if (!screen_list->count)
337        return -1;
338
339    /* User requested to exit */
340    if (quit)
341        return -2;
342
343    /* Update each screen canvas */
344    refresh |= update_terms(screen_list);
345
346    /* Launch recurrents if any */
347    refresh |= handle_recurrents(screen_list);
348
349    /* Refresh screen */
350    refresh_screen(screen_list, refresh);
351
352    eof = 1;
353    for (i = 0; i < screen_list->count; i++)
354        if (screen_list->screen[i]->fd >= 0)
355            eof = 0;
356    if (eof)
357        return -3;
358
359    return 0;
360}
361
362static void server_main(struct screen_list *screen_list)
363{
364    screen_list->last_key_time = 0;
365    screen_list->comm.attached = 0;
366    screen_list->command = 0;
367    screen_list->was_in_bell = 0;
368    screen_list->last_refresh_time = 0;
369
370    server_init(screen_list);
371#ifdef USE_PYTHON
372    python_init(screen_list);
373#endif
374
375    for (;;)
376    {
377        if (server_iteration(screen_list))
378            break;
379    }
380
381    detach(screen_list);
382
383    free_screen_list(screen_list);
384
385#ifdef USE_PYTHON
386    python_close(screen_list);
387#endif
388
389    exit(0);
390}
391
392static void refresh_screen(struct screen_list *screen_list, int refresh)
393{
394    if (!screen_list->comm.attached)
395    {
396        /* No need to refresh Don't use the CPU too much Would be better to
397           select on terms + socket */
398        sleep(1);
399    }
400    else
401    {
402        long long unsigned int current_time = get_us();
403        long long int tdiff =
404            (current_time - screen_list->last_refresh_time) / 1000;
405
406        refresh |= screen_list->need_refresh;
407
408        if (screen_list->force_refresh)
409        {
410            wm_refresh(screen_list);
411            refresh = 1;
412        }
413
414        /* Draw lock window */
415        if (screen_list->lock.locked)
416        {
417            /* FIXME don't redraw it each iteration */
418            draw_lock(screen_list);
419            refresh = 1;
420        }
421#ifdef USE_LOCK
422        else if ((current_time - screen_list->last_key_time >=
423                  screen_list->lock.autolock_timeout))
424        {
425            screen_list->lock.locked = 1;
426            refresh = 1;
427        }
428#endif
429        else if ((current_time - screen_list->last_key_time >=
430                  screen_list->screensaver.timeout))
431        {
432            if (!screen_list->screensaver.in_screensaver)
433            {
434                screensaver_init(screen_list);
435                screen_list->screensaver.in_screensaver = 1;
436                set_cursor(0, screen_list);
437            }
438            draw_screensaver(screen_list);
439            refresh = 1;
440        }
441        else if (refresh || screen_list->was_in_bell)
442        {
443            if (tdiff >= screen_list->delay)
444            {
445                refresh_screens(screen_list);
446                set_title(screen_list);
447                refresh = 1;
448            }
449        }
450        if (refresh)
451        {
452            if (tdiff >= screen_list->delay)
453            {
454                request_refresh(screen_list);
455                screen_list->last_refresh_time = current_time;
456                screen_list->need_refresh = 0;
457            }
458            else
459            {
460                debug("Skipping refresh (%lld < %d)", tdiff,
461                      screen_list->delay);
462                screen_list->need_refresh = 1;
463            }
464        }
465    }
466}
467
468static void server_init(struct screen_list *screen_list)
469{
470    int i;
471    debug("Screen list at %p\n", screen_list);
472
473    /* Create socket and bind it */
474    create_socket(screen_list, SOCK_SERVER);
475
476    /* Connect to the client */
477    connect_socket(screen_list, SOCK_CLIENT);
478
479    screen_list->width = screen_list->height = 80;
480
481    /* Create main canvas */
482    screen_list->cv = caca_create_canvas(screen_list->width,
483                                         screen_list->height
484                                         + screen_list->modals.mini * 6
485                                         + screen_list->modals.status);
486
487    if (!screen_list->sys.to_grab && !screen_list->sys.to_start)
488    {
489        add_screen(screen_list,
490                   create_screen(screen_list->width,
491                                 screen_list->height,
492                                 screen_list->sys.default_shell));
493    }
494
495    /* Attach processes */
496    if (screen_list->sys.to_grab)
497    {
498        for (i = 0; screen_list->sys.to_grab[i]; i++)
499        {
500            add_screen(screen_list,
501                       create_screen_grab(screen_list->width,
502                                          screen_list->height,
503                                          screen_list->sys.to_grab[i]));
504        }
505        free(screen_list->sys.to_grab);
506        screen_list->sys.to_grab = NULL;
507    }
508
509    /* Launch command line processes */
510    if (screen_list->sys.to_start)
511    {
512        for (i = 0; screen_list->sys.to_start[i]; i++)
513        {
514            add_screen(screen_list,
515                       create_screen(screen_list->width,
516                                     screen_list->height,
517                                     screen_list->sys.to_start[i]));
518            free(screen_list->sys.to_start[i]);
519        }
520        free(screen_list->sys.to_start);
521        screen_list->sys.to_start = NULL;
522    }
523
524    screen_list->last_key_time = get_us();
525}
526
527static int handle_attach(struct screen_list *screen_list, char *buf)
528{
529    /* If we were attached to someone else, detach first */
530    if (screen_list->comm.attached)
531        detach(screen_list);
532    screen_list->comm.attached = 1;
533    caca_free_canvas(screen_list->cv);
534    screen_list->cv = caca_create_canvas(atoi(buf + 7), atoi(buf + 18));
535    screen_list->delay = atoi(buf + 29);
536    screen_list->changed = 1;
537    return 1;
538}
539
540static int handle_key(struct screen_list *screen_list, unsigned int c,
541                      int refresh)
542{
543    char *str = NULL;
544    int size = 0;
545
546    if (screen_list->modals.help)
547    {
548        return help_handle_key(screen_list, c);
549    }
550#ifdef USE_PYTHON
551    if (screen_list->modals.python_command)
552    {
553        return python_command_handle_key(screen_list, c);
554    }
555#endif
556
557    /* CTRL-A has been pressed before, handle this as a command, except that
558       CTRL-A a sends literal CTRL-A */
559    if (screen_list->command && (c != 'a'))
560    {
561        screen_list->command = 0;
562        refresh |= handle_command_input(screen_list, c);
563    }
564    else
565    {
566        /* Not in command mode */
567        screen_list->last_key_time = get_us();
568        set_cursor(1, screen_list);
569
570        /* Kill screensaver */
571        if (screen_list->screensaver.in_screensaver)
572        {
573            screensaver_kill(screen_list);
574            screen_list->screensaver.in_screensaver = 0;
575            screen_list->changed = 1;
576            refresh = 1;
577            return refresh;
578        }
579        /* Handle lock window */
580        if (screen_list->lock.locked)
581        {
582            refresh |= update_lock(c, screen_list);
583            screen_list->changed = 1;
584        }
585        else if (screen_list->modals.window_list)
586        {
587            refresh |= update_window_list(c, screen_list);
588            screen_list->changed = 1;
589        }
590        else
591        {
592            switch (c)
593            {
594            case 0x01:         // CACA_KEY_CTRL_A:
595                screen_list->command = 1;
596                break;
597            default:
598                /* CTRL-A a sends literal CTRL-A */
599                if (screen_list->command && (c == 'a'))
600                {
601                    c = 0x01;
602                }
603                /* Normal key, convert it if needed */
604                str = convert_input_ansi(&c, &size);
605                /* FIXME check value of r */
606                int r = write(screen_list->screen[screen_list->pty]->fd, str,
607                              size);
608                (void)r;
609                break;
610            }
611        }
612    }
613    return refresh;
614}
615
616int send_ansi_sequence(struct screen_list *screen_list, char *str)
617{
618    debug("Sending ansi '%s'\n", str);
619    return write(screen_list->screen[screen_list->pty]->fd, str, strlen(str));
620}
621
622
623int install_fds(struct screen_list *screen_list)
624{
625    int fd;
626    close(0);
627    close(1);
628    close(2);
629    fd = open("/dev/null", O_RDWR, 0);
630    if (fd < 0)
631    {
632        perror("Failed to open /dev/null");
633        return -2;
634    }
635    dup2(fd, 0);
636#ifndef DEBUG
637    dup2(fd, 1);
638    dup2(fd, 2);
639    if (fd > 2)
640        close(fd);
641#else
642    if (fd != 0)
643        close(fd);
644    screen_list->outfd =
645        open("/tmp/neercs-debug.txt", O_TRUNC | O_RDWR | O_CREAT,
646             S_IRUSR | S_IWUSR);
647    dup2(screen_list->outfd, 1);
648    dup2(screen_list->outfd, 2);
649    if (screen_list->outfd > 2)
650        close(screen_list->outfd);
651#endif
652    return 0;
653}
654
655int start_server(struct screen_list *screen_list)
656{
657    pid_t pid;
658
659    pid = fork();
660    if (pid < 0)
661    {
662        perror("Failed to create child process");
663        return -1;
664    }
665    if (pid == 0)
666    {
667        int r = install_fds(screen_list);
668        if (r)
669            return r;
670        setsid();
671
672        server_main(screen_list);
673        /* Never returns */
674    }
675
676    return 0;
677}
678
679long long get_us(void)
680{
681    struct timeval tv;
682    gettimeofday(&tv, NULL);
683    return (tv.tv_sec * (1000000) + tv.tv_usec);
684}
685
686#else
687/* FIXME: unimplemented */
688long long get_us(void) { return 0; }
689int send_ansi_sequence(struct screen_list *screen_list, char *str) { return 0; }
690#endif
691
Note: See TracBrowser for help on using the repository browser.