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

Last change on this file since 586 was 586, checked in by Jean-Yves Lamoureux, 15 years ago
  • Added CACA_NETWORK_PORT env and related documentation
  • Property svn:keywords set to Id
File size: 6.6 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 586 2006-03-10 15:11:40Z jylam $
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
31#if defined(HAVE_UNISTD_H)
32#   include <unistd.h>
33#endif
34#include <stdarg.h>
35
36#include "caca.h"
37#include "caca_internals.h"
38#include "cucul.h"
39#include "cucul_internals.h"
40
41static void manage_connections(caca_t *kk);
42static int send_data(caca_t *kk, int fd);
43
44
45struct driver_private
46{
47    unsigned int width, height;
48    unsigned int port;
49    int sockfd;
50    struct sockaddr_in my_addr;
51    struct sockaddr_in remote_addr;
52    socklen_t sin_size;
53    int clilen;
54
55    char *buffer;
56    int size;
57
58    int client_count;
59    int *fd_list;
60};
61
62
63
64#define BACKLOG 1337    /* Number of pending connections */
65
66
67
68/* Following vars are static */
69static char codes[] = {0xff, 0xfb, 0x01,  // WILL ECHO                                                             
70                       0xff, 0xfb, 0x03,  // WILL SUPPRESS GO AHEAD
71                       0xff, 253, 31,     // DO NAWS
72                       0xff, 254, 31,     // DON'T NAWS
73                       0xff, 31, 250, 0, 30, 0, 0xFF, // Set size, replaced in display
74                       0xff, 240};
75
76
77static int network_init_graphics(caca_t *kk)
78{
79    int yes=1;
80    int net_port = 7575;
81    char *network_port;
82
83
84    kk->drv.p = malloc(sizeof(struct driver_private));
85    if(kk->drv.p == NULL)
86        return -1;
87
88
89#if defined(HAVE_GETENV)
90    network_port = getenv("CACA_NETWORK_PORT");
91    if(network_port && *network_port) {
92        net_port = atoi(network_port);
93        if(!net_port)
94            net_port = 7575;
95    }
96#endif
97   
98
99    kk->drv.p->width = 80;
100    kk->drv.p->height = 23; // Avoid scrolling
101    kk->drv.p->client_count = 0;
102    kk->drv.p->fd_list = NULL;
103    kk->drv.p->port = net_port;
104
105
106    _cucul_set_size(kk->qq, kk->drv.p->width, kk->drv.p->height);
107
108
109    if ((kk->drv.p->sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
110        perror("socket");
111        return -1;
112    }
113
114    if (setsockopt(kk->drv.p->sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
115        perror("setsockopt");
116        return -1;
117    }
118
119    kk->drv.p->my_addr.sin_family = AF_INET;
120    kk->drv.p-> my_addr.sin_port = htons(kk->drv.p->port);
121    kk->drv.p->my_addr.sin_addr.s_addr = INADDR_ANY;
122    memset(&(kk->drv.p->my_addr.sin_zero), '\0', 8);
123
124    if (bind(kk->drv.p->sockfd, (struct sockaddr *)&kk->drv.p->my_addr, sizeof(struct sockaddr))
125                                                                   == -1) {
126        perror("bind");
127        return -1;
128    }
129
130    /* Non blocking socket */
131    fcntl(kk->drv.p->sockfd, F_SETFL, O_NONBLOCK);
132
133    if (listen(kk->drv.p->sockfd, BACKLOG) == -1) {
134        perror("listen");
135        return -1;
136    }
137
138    kk->drv.p->buffer = NULL;
139
140    return 0;
141}
142
143static int network_end_graphics(caca_t *kk)
144{
145    int i;
146
147    for(i = 0; i < kk->drv.p->client_count; i++) {
148        close(kk->drv.p->fd_list[i]);
149    }
150
151    return 0;
152}
153
154static int network_set_window_title(caca_t *kk, char const *title)
155{
156    /* Not handled (yet)*/
157    return 0;
158}
159
160static unsigned int network_get_window_width(caca_t *kk)
161{
162    return kk->drv.p->width * 6;
163}
164
165static unsigned int network_get_window_height(caca_t *kk)
166{
167    return kk->drv.p->height * 10;
168}
169
170static void network_display(caca_t *kk)
171{
172    int i;
173
174    /* Get ANSI representation of the image */
175    kk->drv.p->buffer = cucul_get_ansi(kk->qq, 0, &kk->drv.p->size);;
176
177    for(i = 0; i < kk->drv.p->client_count; i++)
178    {
179        if(send_data(kk, kk->drv.p->fd_list[i]))
180            kk->drv.p->fd_list[i] = -1;
181    }
182
183    manage_connections(kk);
184}
185 
186static void network_handle_resize(caca_t *kk)
187{
188    /* Not handled */
189}
190
191static unsigned int network_get_event(caca_t *kk)
192{
193    /* Manage new connections as this function will be called sometimes
194     *  more often than display
195     */
196    manage_connections(kk);
197
198    /* Event not handled */
199    return 0;
200}
201
202/*
203 * XXX: The following functions are local
204 */
205
206static void manage_connections(caca_t *kk)
207{
208    int fd;
209
210    kk->drv.p->clilen = sizeof(kk->drv.p->remote_addr);
211    fd = accept(kk->drv.p->sockfd, (struct sockaddr *) &kk->drv.p->remote_addr, &kk->drv.p->clilen);
212    if(fd != -1) /* That's non blocking socket, -1 if no connection received */
213    {
214        if(kk->drv.p->fd_list == NULL)
215        {
216            kk->drv.p->fd_list = malloc(sizeof(int));
217            if(kk->drv.p->fd_list == NULL)
218                return;
219        }
220        else
221        {
222            kk->drv.p->fd_list = realloc(kk->drv.p->fd_list, (kk->drv.p->client_count+1) * sizeof(int));
223        }
224
225        if(send_data(kk, fd) == 0)
226        {
227            kk->drv.p->fd_list[kk->drv.p->client_count] = fd;
228            kk->drv.p->client_count++;
229        }
230    }
231}
232
233static int send_data(caca_t *kk, int fd)
234{
235    /* No error, there's just nothing to send yet */
236    if(!kk->drv.p->buffer)
237        return 0;
238
239    if(fd < 0)
240        return -1;
241
242    /* FIXME, handle >255 sizes */
243    codes[15] = (unsigned char) (kk->drv.p->width & 0xff00)>>8;
244    codes[16] = (unsigned char) kk->drv.p->width & 0xff;
245    codes[17] = (unsigned char) (kk->drv.p->height & 0xff00)>>8;
246    codes[18] = (unsigned char) kk->drv.p->height & 0xff;
247           
248    /* Send basic telnet codes */
249    if (send(fd, codes,sizeof(codes) , 0) == -1)
250        return -1;
251   
252    /* ANSI code for move(0,0)*/
253    if (send(fd, "\033[1,1H", 6, 0) == -1)
254        return -1;
255   
256    /* Send actual data */
257    if (send(fd, kk->drv.p->buffer, kk->drv.p->size, 0) == -1)
258        return -1;
259
260    return 0;
261}
262
263/*
264 * Driver initialisation
265 */
266
267void network_init_driver(caca_t *kk)
268{
269    kk->drv.driver = CACA_DRIVER_NETWORK;
270
271    kk->drv.init_graphics = network_init_graphics;
272    kk->drv.end_graphics = network_end_graphics;
273    kk->drv.set_window_title = network_set_window_title;
274    kk->drv.get_window_width = network_get_window_width;
275    kk->drv.get_window_height = network_get_window_height;
276    kk->drv.display = network_display;
277    kk->drv.handle_resize = network_handle_resize;
278    kk->drv.get_event = network_get_event;
279}
280
281
282#endif // USE_NETWORK
283
Note: See TracBrowser for help on using the repository browser.