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

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