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

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