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

Last change on this file since 2625 was 2625, checked in by Pascal Terjan, 13 years ago
  • Filter out events that we don't use
  • Reduce caca_get_event timeout
  • Property svn:eol-style set to native
File size: 13.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/types.h>
27#include <sys/wait.h>
28#include <sys/time.h>
29#include <time.h>
30#include <pwd.h>
31
32#include <errno.h>
33#include <caca.h>
34
35#include "neercs.h"
36
37static int send_to_client(const char * msg, struct screen_list* screen_list)
38{
39    int ret;
40    if(!screen_list->socket[SOCK_CLIENT])
41        connect_socket(screen_list, SOCK_CLIENT);
42    debug("Sending message (%s) to client on socket %d", msg, screen_list->socket[SOCK_CLIENT]);
43    if(!screen_list->socket[SOCK_CLIENT])
44        ret = -1;
45    else
46        ret = write(screen_list->socket[SOCK_CLIENT], msg, strlen(msg));
47    if(ret < 0 && errno != EAGAIN)
48    {
49        fprintf(stderr, "Failed to send message to client: %s\n", strerror(errno));
50        detach(screen_list);
51    }
52    return ret;
53}
54
55static int set_title(const char * title, struct screen_list* screen_list)
56{
57    char buf[1024];
58    int bytes;
59
60    bytes = snprintf(buf, sizeof(buf)-1, "TITLE %s", title);
61    buf[bytes] = '\0';
62    return send_to_client(buf, screen_list);
63}
64
65static int set_cursor(int state, struct screen_list* screen_list)
66{
67    char buf[16];
68    int bytes;
69
70    bytes = snprintf(buf, sizeof(buf)-1, "CURSOR %d", state);
71    buf[bytes] = '\0';
72
73    return send_to_client(buf, screen_list);
74}
75
76static int request_refresh(struct screen_list* screen_list)
77{
78    size_t bytes;
79    void *buf;
80    char *buf2;
81    buf = cucul_export_memory (screen_list->cv, "caca", &bytes);
82    buf2 = malloc(bytes+8);
83    memcpy(buf2, "REFRESH ", 8);
84    memcpy(buf2+8, buf, bytes);
85    if(!screen_list->socket[SOCK_CLIENT])
86        connect_socket(screen_list, SOCK_CLIENT);
87    if(screen_list->socket[SOCK_CLIENT])
88        if(write(screen_list->socket[SOCK_CLIENT], buf2, bytes+8) <= 0 && errno != EAGAIN)
89            detach(screen_list);
90    free(buf);
91    free(buf2);
92    return 0;
93}
94
95int detach(struct screen_list* screen_list)
96{
97    screen_list->attached = 0;
98    if(screen_list->socket[SOCK_CLIENT])
99    {
100        send_to_client("DETACH", screen_list);
101        close(screen_list->socket[SOCK_CLIENT]);
102        screen_list->socket[SOCK_CLIENT] = 0;
103    }
104    return 0;
105}
106
107static int server_main(int *to_grab, char **to_start, struct screen_list *screen_list)
108{
109    int i;
110    int eof = 0, refresh = 1, command = 0;
111    long long unsigned int last_key_time = 0;
112    int mainret = 0;
113
114    screen_list->attached = 0;
115
116    /* Create socket and bind it */
117    create_socket(screen_list, SOCK_SERVER);
118
119    /* Connect to the client */
120    connect_socket(screen_list, SOCK_CLIENT);
121
122    screen_list->width = screen_list->height = 10;
123
124    /* Create main canvas */
125    screen_list->cv = cucul_create_canvas(screen_list->width,
126                                          screen_list->height
127                                          + screen_list->mini*6
128                                          + screen_list->status);
129
130    if(!to_grab && !to_start)
131    {
132        add_screen(screen_list,
133                   create_screen(screen_list->width,
134                                 screen_list->height,
135                                 screen_list->default_shell));
136    }
137
138    /* Attach processes */
139    if(to_grab)
140    {
141        for(i=0; to_grab[i]; i++)
142        {
143            add_screen(screen_list,
144                       create_screen_grab(screen_list->width,
145                                          screen_list->height,
146                                          to_grab[i]));
147        }
148        free(to_grab);
149    }
150
151    /* Launch command line processes */
152    if(to_start)
153    {
154        for(i=0; to_start[i]; i++)
155        {
156            add_screen(screen_list,
157                       create_screen(screen_list->width,
158                                     screen_list->height,
159                                     to_start[i]));
160        }
161        free(to_start);
162    }
163
164    last_key_time = get_us();
165
166    for(;;)
167    {
168        int quit = 0;
169        ssize_t n;
170        char buf[4097];
171
172        /* Read program output */
173        refresh |= update_screens_contents(screen_list);
174
175        /* Check if we got something from the client */
176        while (screen_list->socket[SOCK_SERVER] && (n = read(screen_list->socket[SOCK_SERVER], buf, sizeof(buf)-1)) > 0)
177        {
178            buf[n] = 0;
179            debug("Received command %s", buf);
180            if(!strncmp("ATTACH ", buf, 7))
181            {
182                screen_list->attached = 1;
183                cucul_free_canvas(screen_list->cv);
184                screen_list->cv = cucul_create_canvas(atoi(buf+7), atoi(buf+18));
185                screen_list->width  = cucul_get_canvas_width(screen_list->cv);
186                screen_list->height = cucul_get_canvas_height(screen_list->cv) - ((screen_list->mini*6) + (screen_list->status));
187                update_windows_props(screen_list);
188                cucul_clear_canvas(screen_list->cv);
189                refresh = 1;
190            }
191            else if(!strncmp("QUIT", buf, 4))
192            {
193                quit = 1;
194            }
195            else if(!strncmp("RESIZE ", buf, 7))
196            {
197                cucul_free_canvas(screen_list->cv);
198                screen_list->cv = cucul_create_canvas(atoi(buf+7), atoi(buf+18));
199                screen_list->width  = cucul_get_canvas_width(screen_list->cv);
200                screen_list->height = cucul_get_canvas_height(screen_list->cv) - ((screen_list->mini*6) + (screen_list->status));
201                update_windows_props(screen_list);
202                cucul_clear_canvas(screen_list->cv);
203                refresh = 1;
204            }
205            else if(!strncmp("KEY ", buf, 4))
206            {
207                unsigned int c = atoi(buf+4);
208                char *str = NULL;
209                int size = 0;
210                /* CTRL-A has been pressed before, handle this as a
211                 * command, except that CTRL-A a sends literal CTRL-A */
212                if(command && (c != 'a'))
213                {
214                    command = 0;
215                    refresh |= handle_command_input(screen_list, c);
216                }
217                else
218                {
219                    /* Not in command mode */
220                    last_key_time = get_us();
221                    set_cursor(1, screen_list);
222
223                    /* Kill screensaver */
224                    if(screen_list->in_screensaver)
225                    {
226                        screensaver_kill(screen_list);
227                        screen_list->in_screensaver = 0;
228                        refresh = 1;
229                        continue;
230                    }
231                    /* Handle lock window */
232                    if(screen_list->locked)
233                        refresh |= update_lock(c, screen_list);
234                    else
235                    {
236                        switch(c)
237                        {
238                        case 0x01: //CACA_KEY_CTRL_A:
239                            command = 1; break;
240                        case CACA_KEY_ESCAPE:
241                            if(screen_list->help)
242                            {
243                                screen_list->help = 0;
244                                refresh = 1;
245                                break;
246                            }
247                        default:
248                            /* CTRL-A a sends literal CTRL-A */
249                            if (command && (c == 'a'))
250                            {
251                                c = 0x01;
252                            }
253                            /* Normal key, convert it if needed */
254                            str = convert_input_ansi(&c, &size);
255                            write(screen_list->screen[screen_list->pty]->fd, str, size);
256                            break;
257                        }
258                    }
259                }
260            }
261            else
262            {
263                fprintf(stderr, "Unknown command received: %s\n", buf);
264            }
265        }
266
267        /* No more screens, exit */
268        if(!screen_list->count) break;
269
270        /* User requested to exit */
271        if(quit) break;
272
273        /* Update each screen canvas  */
274        refresh |= update_terms(screen_list);
275
276        /* Launch reccurents if any */
277        refresh |= handle_recurrents(screen_list);
278
279        /* Resfresh screen */
280        if(!screen_list->attached)
281        {
282            /* No need to refresh
283             * Don't use the CPU too much
284             * Would be better to select on terms + socket
285             */
286            sleep(1);
287        }
288        /* Draw lock window */
289        else if(screen_list->locked)
290        {
291            draw_lock(screen_list);
292            refresh = 1;
293        }
294        else
295        {
296            if((refresh || screen_list->in_bell) &&
297               (get_us() - last_key_time < screen_list->screensaver_timeout))
298            {
299                refresh_screens(screen_list);
300                if(screen_list->attached)
301                {
302                    if(screen_list->pty < screen_list->count &&
303                       screen_list->screen[screen_list->pty]->title)
304                        set_title(screen_list->screen[screen_list->pty]->title, screen_list);
305                    else
306                        set_title(PACKAGE_STRING, screen_list);
307                }
308                refresh = 1;
309
310            }
311            if((get_us() - last_key_time > screen_list->screensaver_timeout))
312            {
313                if(!screen_list->in_screensaver)
314                {
315                    screensaver_init(screen_list);
316                    screen_list->in_screensaver = 1;
317                    set_cursor(0, screen_list);
318                }
319                draw_screensaver(screen_list);
320                refresh = 1;
321            }
322
323            if((get_us() - last_key_time > screen_list->autolock_timeout))
324            {
325                screen_list->locked = 1;
326                refresh = 1;
327            }
328
329        }
330
331        if(refresh)
332        {
333            if(screen_list->attached)
334                request_refresh(screen_list);
335            refresh = 0;
336        }
337
338        eof = 1;
339        for(i=0; i < screen_list->count; i++)
340            if(screen_list->screen[i]->fd >= 0)
341                eof = 0;
342        if(eof)
343            break;
344    }
345
346    detach(screen_list);
347
348    /* Clean up */
349    cucul_free_canvas(screen_list->cv);
350
351    for(i = 0; i < screen_list->count; i++)
352    {
353        destroy_screen(screen_list->screen[i]);
354    }
355
356    if(screen_list->socket_path[SOCK_SERVER])
357    {
358        unlink(screen_list->socket_path[SOCK_SERVER]);
359        free(screen_list->socket_path[SOCK_SERVER]);
360    }
361
362    if(screen_list->socket_path[SOCK_CLIENT])
363        free(screen_list->socket_path[SOCK_CLIENT]);
364
365    if(screen_list->socket[SOCK_SERVER])
366        close(screen_list->socket[SOCK_SERVER]);
367
368    if(screen_list->socket[SOCK_CLIENT])
369        close(screen_list->socket[SOCK_CLIENT]);
370
371    if(screen_list->screen) free(screen_list->screen);
372
373    for(i=0; i<screen_list->recurrent_list->count; i++)
374    {
375        remove_recurrent(screen_list->recurrent_list, i);
376        i = 0;
377    }
378
379    if(screen_list->recurrent_list->recurrent) free(screen_list->recurrent_list->recurrent);
380    if(screen_list->recurrent_list)            free(screen_list->recurrent_list);
381
382    if(screen_list->session_name)
383        free(screen_list->session_name);
384
385    if(screen_list)
386        free(screen_list);
387
388    return mainret;
389}
390
391int start_server(int *to_grab, char **to_start, struct screen_list *screen_list)
392{
393    pid_t pid;
394    char * sess;
395
396    pid = fork();
397    if(pid < 0)
398    {
399        fprintf(stderr, "Failed to create child process\n");
400        return 1;
401    }
402    if(pid == 0)
403    {
404        int fd;
405        close(0);
406        close(1);
407        close(2);
408        fd = open("/dev/null", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
409        dup2(fd, 0);
410#ifndef DEBUG
411        dup2(fd, 1);
412        dup2(fd, 2);
413        if (fd > 2)
414            close(fd);
415#else
416        if(fd != 0)
417            close(fd);
418        fd = open("/tmp/log.txt", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
419        dup2(fd, 1);
420        dup2(fd, 2);
421        if (fd > 2)
422            close(fd);
423#endif
424        setsid();
425        return server_main(to_grab, to_start, screen_list);
426    }
427    create_socket(screen_list, SOCK_CLIENT);
428    while((sess = connect_socket(screen_list, SOCK_SERVER)) == NULL)
429        usleep(100);
430    free(sess);
431
432    /* Create main canvas and associated caca window */
433    screen_list->cv = cucul_create_canvas(0, 0);
434    screen_list->dp = caca_create_display(screen_list->cv);
435    if(!screen_list->dp)
436        return 1;
437    caca_set_cursor(screen_list->dp, 1);
438
439    request_attach(screen_list);
440
441    return 0;
442}
443
444long long get_us(void)
445{
446    struct timeval tv;
447    gettimeofday(&tv, NULL);
448    return (tv.tv_sec*(1000000) + tv.tv_usec);
449}
Note: See TracBrowser for help on using the repository browser.