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

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