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

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