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

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