You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

334 lines
7.5 KiB

  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. /** \file driver_network.c
  12. * \version \$Id$
  13. * \author Jean-Yves Lamoureux <jylam@lnxscene.org>
  14. * \brief Network driver
  15. *
  16. * This file contains the libcaca network input and output driver
  17. */
  18. #include "config.h"
  19. #if defined(USE_NETWORK)
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <sys/types.h>
  23. #include <sys/socket.h>
  24. #include <arpa/inet.h>
  25. #include <fcntl.h>
  26. #include <string.h>
  27. #include <signal.h>
  28. #include <errno.h>
  29. #if defined(HAVE_UNISTD_H)
  30. # include <unistd.h>
  31. #endif
  32. #include <stdarg.h>
  33. #include "caca.h"
  34. #include "caca_internals.h"
  35. #include "cucul.h"
  36. #include "cucul_internals.h"
  37. static void manage_connections(caca_t *kk);
  38. static int send_data(caca_t *kk, int fd);
  39. ssize_t nonblock_write(int fd, char *buf, size_t len);
  40. struct driver_private
  41. {
  42. unsigned int width, height;
  43. unsigned int port;
  44. int sockfd;
  45. struct sockaddr_in my_addr;
  46. struct sockaddr_in remote_addr;
  47. socklen_t sin_size;
  48. int clilen;
  49. char *buffer;
  50. int size;
  51. int client_count;
  52. int *fd_list;
  53. };
  54. #define BACKLOG 1337 /* Number of pending connections */
  55. /* Following vars are static */
  56. static char codes[] = {0xff, 0xfb, 0x01, // WILL ECHO
  57. 0xff, 0xfb, 0x03, // WILL SUPPRESS GO AHEAD
  58. 0xff, 253, 31, // DO NAWS
  59. 0xff, 254, 31, // DON'T NAWS
  60. 0xff, 31, 250, 0, 30, 0, 0xFF, // Set size, replaced in display
  61. 0xff, 240};
  62. static int network_init_graphics(caca_t *kk)
  63. {
  64. int yes=1;
  65. int net_port = 7575;
  66. char *network_port;
  67. kk->drv.p = malloc(sizeof(struct driver_private));
  68. if(kk->drv.p == NULL)
  69. return -1;
  70. #if defined(HAVE_GETENV)
  71. network_port = getenv("CACA_NETWORK_PORT");
  72. if(network_port && *network_port) {
  73. net_port = atoi(network_port);
  74. if(!net_port)
  75. net_port = 7575;
  76. }
  77. #endif
  78. kk->drv.p->width = 80;
  79. kk->drv.p->height = 23; // Avoid scrolling
  80. kk->drv.p->client_count = 0;
  81. kk->drv.p->fd_list = NULL;
  82. kk->drv.p->port = net_port;
  83. _cucul_set_size(kk->qq, kk->drv.p->width, kk->drv.p->height);
  84. if ((kk->drv.p->sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
  85. perror("socket");
  86. return -1;
  87. }
  88. if (setsockopt(kk->drv.p->sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
  89. perror("setsockopt SO_REUSEADDR");
  90. return -1;
  91. }
  92. kk->drv.p->my_addr.sin_family = AF_INET;
  93. kk->drv.p-> my_addr.sin_port = htons(kk->drv.p->port);
  94. kk->drv.p->my_addr.sin_addr.s_addr = INADDR_ANY;
  95. memset(&(kk->drv.p->my_addr.sin_zero), '\0', 8);
  96. if (bind(kk->drv.p->sockfd, (struct sockaddr *)&kk->drv.p->my_addr, sizeof(struct sockaddr))
  97. == -1) {
  98. perror("bind");
  99. return -1;
  100. }
  101. /* Non blocking socket */
  102. fcntl(kk->drv.p->sockfd, F_SETFL, O_NONBLOCK);
  103. if (listen(kk->drv.p->sockfd, BACKLOG) == -1) {
  104. perror("listen");
  105. return -1;
  106. }
  107. kk->drv.p->buffer = NULL;
  108. /* Ignore SIGPIPE */
  109. signal(SIGPIPE, SIG_IGN);
  110. return 0;
  111. }
  112. static int network_end_graphics(caca_t *kk)
  113. {
  114. int i;
  115. for(i = 0; i < kk->drv.p->client_count; i++) {
  116. close(kk->drv.p->fd_list[i]);
  117. }
  118. return 0;
  119. }
  120. static int network_set_window_title(caca_t *kk, char const *title)
  121. {
  122. /* Not handled (yet)*/
  123. return 0;
  124. }
  125. static unsigned int network_get_window_width(caca_t *kk)
  126. {
  127. return kk->drv.p->width * 6;
  128. }
  129. static unsigned int network_get_window_height(caca_t *kk)
  130. {
  131. return kk->drv.p->height * 10;
  132. }
  133. static void network_display(caca_t *kk)
  134. {
  135. int i;
  136. /* Get ANSI representation of the image */
  137. kk->drv.p->buffer = cucul_get_ansi(kk->qq, 0, &kk->drv.p->size);;
  138. for(i = 0; i < kk->drv.p->client_count; i++)
  139. {
  140. if(kk->drv.p->fd_list[i] == -1)
  141. continue;
  142. if(send_data(kk, kk->drv.p->fd_list[i]))
  143. kk->drv.p->fd_list[i] = -1;
  144. }
  145. manage_connections(kk);
  146. }
  147. static void network_handle_resize(caca_t *kk)
  148. {
  149. /* Not handled */
  150. }
  151. static unsigned int network_get_event(caca_t *kk)
  152. {
  153. /* Manage new connections as this function will be called sometimes
  154. * more often than display
  155. */
  156. manage_connections(kk);
  157. /* Event not handled */
  158. return 0;
  159. }
  160. /*
  161. * XXX: The following functions are local
  162. */
  163. static void manage_connections(caca_t *kk)
  164. {
  165. int fd;
  166. kk->drv.p->clilen = sizeof(kk->drv.p->remote_addr);
  167. fd = accept(kk->drv.p->sockfd, (struct sockaddr *) &kk->drv.p->remote_addr, &kk->drv.p->clilen);
  168. if(fd == -1)
  169. return;
  170. /* Non blocking socket */
  171. fcntl(fd, F_SETFL, O_NONBLOCK);
  172. if(kk->drv.p->fd_list == NULL)
  173. {
  174. kk->drv.p->fd_list = malloc(sizeof(int));
  175. if(kk->drv.p->fd_list == NULL)
  176. return;
  177. }
  178. else
  179. {
  180. kk->drv.p->fd_list = realloc(kk->drv.p->fd_list,
  181. (kk->drv.p->client_count+1) * sizeof(int));
  182. }
  183. if(send_data(kk, fd) == 0)
  184. {
  185. kk->drv.p->fd_list[kk->drv.p->client_count] = fd;
  186. kk->drv.p->client_count++;
  187. }
  188. }
  189. static int send_data(caca_t *kk, int fd)
  190. {
  191. ssize_t ret;
  192. /* No error, there's just nothing to send yet */
  193. if(!kk->drv.p->buffer)
  194. return 0;
  195. if(fd < 0)
  196. return -1;
  197. /* FIXME, handle >255 sizes */
  198. codes[15] = (unsigned char) (kk->drv.p->width & 0xff00)>>8;
  199. codes[16] = (unsigned char) kk->drv.p->width & 0xff;
  200. codes[17] = (unsigned char) (kk->drv.p->height & 0xff00)>>8;
  201. codes[18] = (unsigned char) kk->drv.p->height & 0xff;
  202. /* Send basic telnet codes */
  203. ret = nonblock_write(fd, codes, sizeof(codes));
  204. if(ret == -1)
  205. return (errno == EAGAIN) ? 0 : -1;
  206. /* ANSI code for move(0,0)*/
  207. ret = nonblock_write(fd, "\033[1,1H", 6);
  208. if(ret == -1)
  209. return (errno == EAGAIN) ? 0 : -1;
  210. /* Send actual data */
  211. ret = nonblock_write(fd, kk->drv.p->buffer, kk->drv.p->size);
  212. if(ret == -1)
  213. return (errno == EAGAIN) ? 0 : -1;
  214. return 0;
  215. }
  216. ssize_t nonblock_write(int fd, char *buf, size_t len)
  217. {
  218. int retries = 10;
  219. size_t total = 0;
  220. ssize_t ret;
  221. while(total < len)
  222. {
  223. do
  224. {
  225. ret = write(fd, buf, len);
  226. if(ret < 0 && errno == EAGAIN)
  227. {
  228. retries--;
  229. if(retries < 0)
  230. break;
  231. }
  232. }
  233. while(ret < 0 && (errno == EINTR || errno == EAGAIN));
  234. if(ret < 0)
  235. return ret;
  236. else if(ret == 0)
  237. return total;
  238. total += len;
  239. buf += len;
  240. }
  241. return total;
  242. }
  243. /*
  244. * Driver initialisation
  245. */
  246. void network_init_driver(caca_t *kk)
  247. {
  248. kk->drv.driver = CACA_DRIVER_NETWORK;
  249. kk->drv.init_graphics = network_init_graphics;
  250. kk->drv.end_graphics = network_end_graphics;
  251. kk->drv.set_window_title = network_set_window_title;
  252. kk->drv.get_window_width = network_get_window_width;
  253. kk->drv.get_window_height = network_get_window_height;
  254. kk->drv.display = network_display;
  255. kk->drv.handle_resize = network_handle_resize;
  256. kk->drv.get_event = network_get_event;
  257. }
  258. #endif // USE_NETWORK