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

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