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

Last change on this file since 3498 was 3498, checked in by Pascal Terjan, 11 years ago

Only send the dirty rectangles to the client

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