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

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

Add svn:keywords to various .c files

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