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

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