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

Last change on this file since 3496 was 3496, checked in by Sam Hocevar, 11 years ago

Minor updates for recent libcaca unreleased API changes.

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