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

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