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

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

Log dirty rectangles used when refreshing

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