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

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