source: libcaca/trunk/caca/driver_network.c @ 596

Last change on this file since 596 was 596, checked in by Sam Hocevar, 15 years ago
  • Improvements to the network output: + Changed port to 51914 (rationale: it's 0xCACA; so okay, it's outside

the registered ports area, but we'd never be accepted anyway).

+ Retry network sends in caca_get_event(), too.
+ Have a per-client buffer in case of network congestion. If that buffer

gets full, just drop it and start again at the next frame.

+ Set the window title to "caca for the network".
+ Do not send the ANSI buffer's terminating \0.
+ Restore the SIGPIPE handler on close.
+ Set window size to 80x24 instead of 80x25.

  • Property svn:keywords set to Id
File size: 11.5 KB
Line 
1/*
2 *  libcaca       ASCII-Art library
3 *  Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  This library is free software; you can redistribute it and/or
7 *  modify it under the terms of the Do What The Fuck You Want To
8 *  Public License, Version 2, as published by Sam Hocevar. See
9 *  http://sam.zoy.org/wtfpl/COPYING for more details.
10 */
11
12/** \file driver_network.c
13 *  \version \$Id: driver_network.c 596 2006-03-13 19:04:29Z sam $
14 *  \author Jean-Yves Lamoureux <jylam@lnxscene.org>
15 *  \brief Network driver
16 *
17 *  This file contains the libcaca network input and output driver
18 */
19
20#include "config.h"
21
22#if defined(USE_NETWORK)
23#include <stdio.h>
24#include <stdlib.h>
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <arpa/inet.h>
28#include <fcntl.h>
29#include <string.h>
30#include <signal.h>
31#include <errno.h>
32
33#if defined(HAVE_UNISTD_H)
34#   include <unistd.h>
35#endif
36#include <stdarg.h>
37
38#include "caca.h"
39#include "caca_internals.h"
40#include "cucul.h"
41#include "cucul_internals.h"
42
43#define BACKLOG 1337    /* Number of pending connections */
44#define BACKBUFFER 300000 /* Size of per-client buffer */
45
46/* Following vars are static */
47#define INIT_PREFIX \
48    "\xff\xfb\x01" /* WILL ECHO */ \
49    "\xff\xfb\x03" /* WILL SUPPRESS GO AHEAD */ \
50    "\xff\xfd\x31" /* DO NAWS */ \
51    "\xff\xfe\x1f" /* DON'T NAWS */ \
52    "\xff\x1f\xfa____" /* Set size, replaced in display */ \
53    "\xff\xf0" \
54    "\x1b]2;caca for the network\x07" /* Change window title */ \
55    "\x1b[?1049h" /* Clear screen */ \
56    /*"\x1b[?25l"*/ /* Hide cursor */ \
57
58#define ANSI_PREFIX \
59    "\x1b[1;1H" /* move(0,0) */ \
60    "\x1b[1;1H" /* move(0,0) again */
61
62#define ANSI_RESET \
63    "    " /* Garbage */ \
64    "\x1b[?1049h" /* Clear screen */ \
65    "\x1b[?1049h" /* Clear screen again */
66
67struct client
68{
69    int fd;
70    int ready;
71    char buffer[BACKBUFFER];
72    int start, stop;
73};
74
75struct driver_private
76{
77    unsigned int width, height;
78    unsigned int port;
79    int sockfd;
80    struct sockaddr_in my_addr;
81    socklen_t sin_size;
82
83    char prefix[sizeof(INIT_PREFIX)];
84
85    char *buffer;
86    int size;
87
88    int client_count;
89    struct client *clients;
90
91    void (*sigpipe_handler)(int);
92};
93
94static void manage_connections(caca_t *kk);
95static int send_data(caca_t *kk, struct client *c);
96ssize_t nonblock_write(int fd, char *buf, size_t len);
97
98static int network_init_graphics(caca_t *kk)
99{
100    int yes = 1, flags;
101    int port = 51914;
102    char *network_port, *tmp;
103
104    kk->drv.p = malloc(sizeof(struct driver_private));
105    if(kk->drv.p == NULL)
106        return -1;
107
108#if defined(HAVE_GETENV)
109    network_port = getenv("CACA_NETWORK_PORT");
110    if(network_port && *network_port)
111    {
112        int new_port = atoi(network_port);
113        if(new_port)
114            port = new_port;
115    }
116#endif
117
118    kk->drv.p->width = 80;
119    kk->drv.p->height = 24;
120    kk->drv.p->client_count = 0;
121    kk->drv.p->clients = NULL;
122    kk->drv.p->port = port;
123
124    _cucul_set_size(kk->qq, kk->drv.p->width, kk->drv.p->height);
125
126    /* FIXME, handle >255 sizes */
127    memcpy(kk->drv.p->prefix, INIT_PREFIX, sizeof(INIT_PREFIX));
128    tmp = strstr(kk->drv.p->prefix, "____");
129    tmp[0] = (unsigned char) (kk->drv.p->width & 0xff00) >> 8;
130    tmp[1] = (unsigned char) kk->drv.p->width & 0xff;
131    tmp[2] = (unsigned char) (kk->drv.p->height & 0xff00) >> 8;
132    tmp[3] = (unsigned char) kk->drv.p->height & 0xff;
133
134    if ((kk->drv.p->sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
135    {
136        perror("socket");
137        return -1;
138    }
139
140    if (setsockopt(kk->drv.p->sockfd, SOL_SOCKET,
141                   SO_REUSEADDR, &yes, sizeof(int)) == -1)
142    {
143        perror("setsockopt SO_REUSEADDR");
144        return -1;
145    }
146
147    kk->drv.p->my_addr.sin_family = AF_INET;
148    kk->drv.p-> my_addr.sin_port = htons(kk->drv.p->port);
149    kk->drv.p->my_addr.sin_addr.s_addr = INADDR_ANY;
150    memset(&(kk->drv.p->my_addr.sin_zero), '\0', 8);
151
152    if (bind(kk->drv.p->sockfd, (struct sockaddr *)&kk->drv.p->my_addr,
153             sizeof(struct sockaddr)) == -1)
154    {
155        perror("bind");
156        return -1;
157    }
158
159    /* Non blocking socket */
160    flags = fcntl(kk->drv.p->sockfd, F_GETFL, 0);
161    fcntl(kk->drv.p->sockfd, F_SETFL, flags | O_NONBLOCK);
162
163    if (listen(kk->drv.p->sockfd, BACKLOG) == -1)
164    {
165        perror("listen");
166        return -1;
167    }
168
169    kk->drv.p->buffer = NULL;
170    kk->drv.p->size = 0;
171
172    /* Ignore SIGPIPE */
173    kk->drv.p->sigpipe_handler = signal(SIGPIPE, SIG_IGN);
174
175    return 0;
176}
177
178static int network_end_graphics(caca_t *kk)
179{
180    int i;
181
182    for(i = 0; i < kk->drv.p->client_count; i++)
183    {
184        close(kk->drv.p->clients[i].fd);
185        kk->drv.p->clients[i].fd = -1;
186    }
187
188    /* Restore SIGPIPE handler */
189    signal(SIGPIPE, kk->drv.p->sigpipe_handler);
190
191    free(kk->drv.p);
192
193    return 0;
194}
195
196static int network_set_window_title(caca_t *kk, char const *title)
197{
198    /* Not handled (yet) */
199    return 0;
200}
201
202static unsigned int network_get_window_width(caca_t *kk)
203{
204    return kk->drv.p->width * 6;
205}
206
207static unsigned int network_get_window_height(caca_t *kk)
208{
209    return kk->drv.p->height * 10;
210}
211
212static void network_display(caca_t *kk)
213{
214    int i;
215
216    /* Get ANSI representation of the image and skip the end-of buffer
217     * linefeed ("\r\n\0", 3 bytes) */
218    kk->drv.p->buffer = cucul_get_ansi(kk->qq, 0, &kk->drv.p->size);
219    kk->drv.p->size -= 3;
220
221    for(i = 0; i < kk->drv.p->client_count; i++)
222    {
223        if(kk->drv.p->clients[i].fd == -1)
224            continue;
225
226        if(send_data(kk, &kk->drv.p->clients[i]))
227            kk->drv.p->clients[i].fd = -1;
228    }
229
230    manage_connections(kk);
231}
232
233static void network_handle_resize(caca_t *kk)
234{
235    /* Not handled */
236}
237
238static unsigned int network_get_event(caca_t *kk)
239{
240    /* Manage new connections as this function will be called sometimes
241     * more often than display */
242    manage_connections(kk);
243
244    /* Event not handled */
245    return 0;
246}
247
248/*
249 * XXX: The following functions are local
250 */
251
252static void manage_connections(caca_t *kk)
253{
254    int fd, flags;
255    struct sockaddr_in remote_addr;
256    socklen_t len = sizeof(struct sockaddr_in);
257
258    fd = accept(kk->drv.p->sockfd, (struct sockaddr *)&remote_addr, &len);
259    if(fd == -1)
260        return;
261
262    /* Non blocking socket */
263    flags = fcntl(fd, F_SETFL, 0);
264    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
265
266    if(kk->drv.p->clients == NULL)
267    {
268        kk->drv.p->clients = malloc(sizeof(struct client));
269        if(kk->drv.p->clients == NULL)
270            return;
271    }
272    else
273    {
274        kk->drv.p->clients = realloc(kk->drv.p->clients,
275                                     (kk->drv.p->client_count+1) * sizeof(struct client));
276    }
277
278    kk->drv.p->clients[kk->drv.p->client_count].fd = fd;
279    kk->drv.p->clients[kk->drv.p->client_count].ready = 0;
280    kk->drv.p->clients[kk->drv.p->client_count].start = 0;
281    kk->drv.p->clients[kk->drv.p->client_count].stop = 0;
282
283    /* If we already have data to send, send it to the new client */
284    send_data(kk, &kk->drv.p->clients[kk->drv.p->client_count]);
285
286    kk->drv.p->client_count++;
287}
288
289static int send_data(caca_t *kk, struct client *c)
290{
291    ssize_t ret;
292
293    /* Not for us */
294    if(c->fd < 0)
295        return -1;
296
297    /* Debug: show incoming data */
298    for(;;)
299    {
300        unsigned char in;
301        ret = read(c->fd, &in, 1);
302        if(ret <= 0)
303            break;
304
305        fprintf(stderr, "client %i said \\x%.02x\n", c->fd, in);
306    }
307
308    /* Send the telnet initialisation commands */
309    if(!c->ready)
310    {
311        ret = nonblock_write(c->fd, kk->drv.p->prefix, sizeof(INIT_PREFIX));
312        if(ret == -1)
313            return (errno == EAGAIN) ? 0 : -1;
314
315        if(ret < (ssize_t)sizeof(INIT_PREFIX))
316            return 0;
317
318        c->ready = 1;
319    }
320
321    /* No error, there's just nothing to send yet */
322    if(!kk->drv.p->buffer)
323        return 0;
324
325    /* If we have backlog, send the backlog */
326    if(c->stop)
327    {
328        ret = nonblock_write(c->fd, c->buffer + c->start, c->stop - c->start);
329
330        if(ret == -1)
331        {
332            if(errno == EAGAIN)
333                ret = 0;
334            else
335                return -1;
336        }
337
338        if(ret == c->stop - c->start)
339        {
340            /* We got rid of the backlog! */
341            c->start = c->stop = 0;
342        }
343        else
344        {
345            c->start += ret;
346
347            if(c->stop - c->start + strlen(ANSI_PREFIX) + kk->drv.p->size
348                > BACKBUFFER)
349            {
350                /* Overflow! Empty buffer and start again */
351                memcpy(c->buffer, ANSI_RESET, strlen(ANSI_RESET));
352                c->start = 0;
353                c->stop = strlen(ANSI_RESET);
354                return 0;
355            }
356
357            /* Need to move? */
358            if(c->stop + strlen(ANSI_PREFIX) + kk->drv.p->size > BACKBUFFER)
359            {
360                memmove(c->buffer, c->buffer + c->start, c->stop - c->start);
361                c->stop -= c->start;
362                c->start = 0;
363            }
364
365            memcpy(c->buffer + c->stop, ANSI_PREFIX, strlen(ANSI_PREFIX));
366            c->stop += strlen(ANSI_PREFIX);
367            memcpy(c->buffer + c->stop, kk->drv.p->buffer, kk->drv.p->size);
368            c->stop += kk->drv.p->size;
369
370            return 0;
371        }
372    }
373
374    /* We no longer have backlog, send our new data */
375
376    /* Send ANSI prefix */
377    ret = nonblock_write(c->fd, ANSI_PREFIX, strlen(ANSI_PREFIX));
378    if(ret == -1)
379    {
380        if(errno == EAGAIN)
381            ret = 0;
382        else
383            return -1;
384    }
385
386    if(ret < (ssize_t)strlen(ANSI_PREFIX))
387    {
388        if(strlen(ANSI_PREFIX) + kk->drv.p->size > BACKBUFFER)
389        {
390            /* Overflow! Empty buffer and start again */
391            memcpy(c->buffer, ANSI_RESET, strlen(ANSI_RESET));
392            c->start = 0;
393            c->stop = strlen(ANSI_RESET);
394            return 0;
395        }
396
397        memcpy(c->buffer, ANSI_PREFIX, strlen(ANSI_PREFIX) - ret);
398        c->stop = strlen(ANSI_PREFIX) - ret;
399        memcpy(c->buffer + c->stop, kk->drv.p->buffer, kk->drv.p->size);
400        c->stop += kk->drv.p->size;
401
402        return 0;
403    }
404
405    /* Send actual data */
406    ret = nonblock_write(c->fd, kk->drv.p->buffer, kk->drv.p->size);
407    if(ret == -1)
408    {
409        if(errno == EAGAIN)
410            ret = 0;
411        else
412            return -1;
413    }
414
415    if(ret < kk->drv.p->size)
416    {
417        if(kk->drv.p->size > BACKBUFFER)
418        {
419            /* Overflow! Empty buffer and start again */
420            memcpy(c->buffer, ANSI_RESET, strlen(ANSI_RESET));
421            c->start = 0;
422            c->stop = strlen(ANSI_RESET);
423            return 0;
424        }
425
426        memcpy(c->buffer, kk->drv.p->buffer, kk->drv.p->size - ret);
427        c->stop = kk->drv.p->size - ret;
428
429        return 0;
430    }
431
432    return 0;
433}
434
435ssize_t nonblock_write(int fd, char *buf, size_t len)
436{
437    size_t total = 0;
438    ssize_t ret;
439
440    while(total < len)
441    {
442        do
443        {
444            ret = write(fd, buf, len);
445        }
446        while(ret < 0 && errno == EINTR);
447
448        if(ret < 0)
449            return ret;
450        else if(ret == 0)
451            return total;
452
453        total += len;
454        buf += len;
455    }
456
457    return total;
458}
459
460/*
461 * Driver initialisation
462 */
463
464void network_init_driver(caca_t *kk)
465{
466    kk->drv.driver = CACA_DRIVER_NETWORK;
467
468    kk->drv.init_graphics = network_init_graphics;
469    kk->drv.end_graphics = network_end_graphics;
470    kk->drv.set_window_title = network_set_window_title;
471    kk->drv.get_window_width = network_get_window_width;
472    kk->drv.get_window_height = network_get_window_height;
473    kk->drv.display = network_display;
474    kk->drv.handle_resize = network_handle_resize;
475    kk->drv.get_event = network_get_event;
476}
477
478
479#endif /* USE_NETWORK */
480
Note: See TracBrowser for help on using the repository browser.