µGFX library fork

main.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. /*
  2. * This file is subject to the terms of the GFX License. If a copy of
  3. * the license was not distributed with this file, you can obtain one at:
  4. *
  5. * http://ugfx.org/license.html
  6. */
  7. #include "gfx.h"
  8. #include "drivers/multiple/uGFXnet/uGFXnetProtocol.h"
  9. #ifndef GDISP_GFXNET_PORT
  10. #define GDISP_GFXNET_PORT GNETCODE_DEFAULT_PORT
  11. #endif
  12. // This definition is only required for for O/S's that don't support a command line eg ChibiOS
  13. // It is ignored by those that do support a command line.
  14. #ifndef GDISP_GFXNET_HOST
  15. #define GDISP_GFXNET_HOST "127.0.0.1" // Change this to your uGFXnet host.
  16. #endif
  17. // Do we wish to use old style socket calls. Some socket libraries only support the old version.
  18. // It is better to use the new version where possible however as it also supports IPv6.
  19. #ifndef OLD_STYLE_SOCKETS
  20. #define OLD_STYLE_SOCKETS FALSE
  21. #endif
  22. // Which operating systems support a command line
  23. #if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_OSX || GFX_USE_OS_LINUX
  24. #define EMBEDED_OS FALSE
  25. #else
  26. #define EMBEDED_OS TRUE
  27. #endif
  28. #if GNETCODE_VERSION != GNETCODE_VERSION_1_0
  29. #error "This uGFXnet display only supports protocol V1.0"
  30. #endif
  31. #if GDISP_PIXELFORMAT != GNETCODE_PIXELFORMAT
  32. #error "Oops - The uGFXnet protocol requires a different pixel format. Try defining GDISP_PIXELFORMAT in your gfxconf.h file."
  33. #endif
  34. #include <stdio.h>
  35. #include <string.h>
  36. #include <stdlib.h>
  37. #if defined(WIN32) || GFX_USE_OS_WIN32
  38. #if OLD_STYLE_SOCKETS
  39. #include <winsock.h>
  40. #else
  41. #include <ws2tcpip.h>
  42. #include <winsock2.h>
  43. #endif
  44. #define SOCKET_TYPE SOCKET
  45. static void StopSockets(void) {
  46. WSACleanup();
  47. }
  48. static void StartSockets(void) {
  49. WSADATA wsaData;
  50. if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
  51. gfxHalt("GDISP: uGFXnet - WSAStartup failed");
  52. atexit(StopSockets);
  53. }
  54. #elif GFX_USE_OS_LINUX || GFX_USE_OS_OSX
  55. #include <sys/types.h>
  56. #include <sys/socket.h>
  57. #include <netinet/in.h>
  58. #include <arpa/inet.h>
  59. #include <netdb.h>
  60. #define closesocket(fd) close(fd)
  61. #define ioctlsocket(fd,cmd,arg) ioctl(fd,cmd,arg)
  62. #define StartSockets()
  63. #define SOCKET_TYPE int
  64. #else
  65. #include <lwip/sockets.h>
  66. #include <lwip/netdb.h>
  67. #if GDISP_GFXNET_CUSTOM_LWIP_STARTUP
  68. extern void Start_LWIP(void); // Where the application does the lwip stack setup
  69. #define StartSockets() Start_LWIP();
  70. #else
  71. #include "lwipthread.h"
  72. #define StartSockets() gfxThreadClose(gfxThreadCreate(wa_lwip_thread, LWIP_THREAD_STACK_SIZE, NORMAL_PRIORITY, lwip_thread, 0))
  73. #endif
  74. #if !LWIP_SOCKET
  75. #error "GDISP: uGFXnet - LWIP_SOCKETS must be defined in your lwipopts.h file"
  76. #endif
  77. #if !LWIP_COMPAT_SOCKETS
  78. #error "GDISP: uGFXnet - LWIP_COMPAT_SOCKETS must be defined in your lwipopts.h file"
  79. #endif
  80. #if !LWIP_DNS
  81. #error "GDISP: uGFXnet - LWIP_DNS must be defined in your lwipopts.h file"
  82. #endif
  83. #define SOCKET_TYPE int
  84. // Mutex protection is required for LWIP
  85. #if !GDISP_GFXNET_UNSAFE_SOCKETS
  86. #if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT
  87. #warning "GDISP: uGFXnet - LWIP sockets are not thread-safe. GDISP_GFXNET_UNSAFE_SOCKETS has been turned on for you."
  88. #elif GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_MACRO
  89. COMPILER_WARNING("GDISP: uGFXnet - LWIP sockets are not thread-safe. GDISP_GFXNET_UNSAFE_SOCKETS has been turned on for you.")
  90. #endif
  91. #undef GDISP_GFXNET_UNSAFE_SOCKETS
  92. #define GDISP_GFXNET_UNSAFE_SOCKETS TRUE
  93. #endif
  94. #endif
  95. #if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
  96. static GListener gl;
  97. #endif
  98. static SOCKET_TYPE netfd = (SOCKET_TYPE)-1;
  99. static font_t font;
  100. #define STRINGOF_RAW(s) #s
  101. #define STRINGOF(s) STRINGOF_RAW(s)
  102. #if EMBEDED_OS
  103. #define cmd_args
  104. #define proto_args void
  105. #define xhost GDISP_GFXNET_HOST
  106. #define xport STRINGOF(GDISP_GFXNET_HOST)
  107. #define xportnum GDISP_GFXNET_HOST
  108. #else
  109. #define cmd_args argc, argv
  110. #define proto_args int argc, char **argv
  111. static char * xhost;
  112. static char * xport;
  113. #if OLD_STYLE_SOCKETS
  114. static int xportnum = GDISP_GFXNET_PORT;
  115. #endif
  116. #endif
  117. /**
  118. * Get a whole packet of data.
  119. * Len is specified in the number of uint16_t's we want as our protocol only talks uint16_t's.
  120. * If the connection closes before we get all the data - the call returns FALSE.
  121. */
  122. static bool_t getpkt(uint16_t *pkt, int len) {
  123. int got;
  124. int have;
  125. // Get the packet of data
  126. len *= sizeof(uint16_t);
  127. have = 0;
  128. while(len && (got = recv(netfd, ((char *)pkt)+have, len, 0)) > 0) {
  129. have += got;
  130. len -= got;
  131. }
  132. if (len)
  133. return FALSE;
  134. // Convert each uint16_t to host order
  135. for(got = 0, have /= 2; got < have; got++)
  136. pkt[got] = ntohs(pkt[got]);
  137. return TRUE;
  138. }
  139. /**
  140. * Send a whole packet of data.
  141. * Len is specified in the number of uint16_t's we want to send as our protocol only talks uint16_t's.
  142. * Note that contents of the packet are modified to ensure it will cross the wire in the correct format.
  143. * If the connection closes before we send all the data - the call returns FALSE.
  144. */
  145. static bool_t sendpkt(uint16_t *pkt, int len) {
  146. int i;
  147. // Convert each uint16_t to network order
  148. for(i = 0; i < len; i++)
  149. pkt[i] = htons(pkt[i]);
  150. // Send it
  151. len *= sizeof(uint16_t);
  152. return send(netfd, (const char *)pkt, len, 0) == len;
  153. }
  154. #if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
  155. /**
  156. * We use a separate thread to capture mouse events and send them down the pipe.
  157. * We do the send in a single transaction to prevent it getting interspersed with
  158. * any reply we need to send on the main thread.
  159. */
  160. static DECLARE_THREAD_STACK(waNetThread, 512);
  161. static DECLARE_THREAD_FUNCTION(NetThread, param) {
  162. GEventMouse *pem;
  163. uint16_t cmd[2];
  164. uint16_t lbuttons;
  165. coord_t lx, ly;
  166. (void) param;
  167. // Initialize the mouse and the listener.
  168. geventListenerInit(&gl);
  169. geventAttachSource(&gl, ginputGetMouse(0), GLISTEN_MOUSEDOWNMOVES|GLISTEN_MOUSEMETA);
  170. lbuttons = 0;
  171. lx = ly = -1;
  172. while(1) {
  173. // Get a (mouse) event
  174. pem = (GEventMouse *)geventEventWait(&gl, TIME_INFINITE);
  175. if (pem->type != GEVENT_MOUSE && pem->type != GEVENT_TOUCH)
  176. continue;
  177. // Nothing to do if the socket is not open
  178. if (netfd == (SOCKET)-1)
  179. continue;
  180. // Nothing to do if the mouse data has not changed
  181. if (lx == pem->x && ly == pem->y && lbuttons == pem->buttons)
  182. continue;
  183. // Transfer mouse data that has changed
  184. if (lx != pem->x) {
  185. lx = pem->x;
  186. cmd[0] = GNETCODE_MOUSE_X;
  187. cmd[1] = lx;
  188. sendpkt(cmd, 2);
  189. }
  190. if (ly != pem->y) {
  191. ly = pem->y;
  192. cmd[0] = GNETCODE_MOUSE_Y;
  193. cmd[1] = ly;
  194. sendpkt(cmd, 2);
  195. }
  196. // We always send the buttons as it also acts as a mouse sync signal
  197. lbuttons = pem->buttons;
  198. cmd[0] = GNETCODE_MOUSE_B;
  199. cmd[1] = lbuttons;
  200. sendpkt(cmd, 2);
  201. }
  202. return 0;
  203. }
  204. #endif
  205. /**
  206. * Do the connection to the remote host.
  207. * We have two prototypes here - one for embedded systems and one for systems with a command line.
  208. * We have two methods of using the sockets library - one very old style and the other the more modern approach.
  209. */
  210. static SOCKET_TYPE doConnect(proto_args) {
  211. SOCKET_TYPE fd;
  212. #if !EMBEDED_OS
  213. (void) argc;
  214. // Parse the command line arguments
  215. xhost = 0;
  216. xport = 0;
  217. while (*++argv) {
  218. if (!xhost)
  219. xhost = argv[0];
  220. else if (!xport) {
  221. xport = argv[0];
  222. #if OLD_STYLE_SOCKETS
  223. if (sscanf(xport, "%i", &xportnum) != 1 || xportnum >= 65536 || xportnum <= 0) {
  224. fprintf(stderr, "Error: Bad port specification '%s'\n\n", xport);
  225. goto usage;
  226. }
  227. #endif
  228. } else {
  229. fprintf(stderr, "Error: Unknown argument '%s'\n\n", argv[0]);
  230. goto usage;
  231. }
  232. }
  233. // Check the command line arguments were valid.
  234. if (!xport)
  235. xport = STRINGOF(GDISP_GFXNET_PORT);
  236. if (!xhost) {
  237. usage:
  238. fprintf(stderr, "Usage: uGFXnetDisplay host [port]\n");
  239. exit(1);
  240. }
  241. #endif
  242. #if OLD_STYLE_SOCKETS
  243. struct sockaddr_in serv_addr;
  244. struct hostent * h;
  245. h = gethostbyname(xhost);
  246. if (!h)
  247. // Error: Unable to find an ip-address for the specified server
  248. return (SOCKET_TYPE)-1;
  249. memset(&serv_addr, 0, sizeof(serv_addr));
  250. serv_addr.sin_port = htons(xportnum);
  251. serv_addr.sin_family = h->h_addrtype;
  252. memcpy(&serv_addr.sin_addr, h->h_addr_list[0], h->h_length);
  253. if ((fd = socket(serv_addr.sin_family, SOCK_STREAM, 0)) == (SOCKET_TYPE)-1)
  254. // Error: Socket failed
  255. return (SOCKET_TYPE)-1;
  256. if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
  257. // Error: Could not connect to the specified server
  258. closesocket(fd);
  259. fd = (SOCKET_TYPE)-1;
  260. }
  261. #else
  262. struct addrinfo hints, *servinfo, *p;
  263. memset(&hints, 0, sizeof hints);
  264. hints.ai_family = AF_UNSPEC;
  265. hints.ai_socktype = SOCK_STREAM;
  266. fd = (SOCKET_TYPE)-1;
  267. if (getaddrinfo(xhost, xport, &hints, &servinfo) != 0)
  268. // Error: Unable to find an ip-address for the specified server
  269. return (SOCKET_TYPE)-1;
  270. for(p = servinfo; p; p = p->ai_next) {
  271. if ((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == (SOCKET_TYPE)-1)
  272. continue;
  273. if (connect(fd, p->ai_addr, p->ai_addrlen) == -1) {
  274. closesocket(fd);
  275. fd = (SOCKET_TYPE)-1;
  276. continue;
  277. }
  278. break;
  279. }
  280. freeaddrinfo(servinfo);
  281. #endif
  282. return fd;
  283. }
  284. /**
  285. * Our main function.
  286. * There are two prototypes - one for systems with a command line and one for embedded systems without one.
  287. */
  288. int main(proto_args) {
  289. uint16_t cmd[5];
  290. unsigned cnt;
  291. // Initialize and clear the display
  292. gfxInit();
  293. font = gdispOpenFont("UI2");
  294. // Open the connection
  295. gdispDrawStringBox(0, 0, gdispGetWidth(), gdispGetHeight(), "Connecting to host...", font, White, justifyCenter);
  296. StartSockets();
  297. netfd = doConnect(cmd_args);
  298. if (netfd == (SOCKET_TYPE)-1)
  299. gfxHalt("Could not connect to the specified server");
  300. gdispClear(Black);
  301. // Get the initial packet from the host
  302. if (!getpkt(cmd, 2)) goto alldone;
  303. if (cmd[0] != GNETCODE_INIT || cmd[1] != GNETCODE_VERSION)
  304. gfxHalt("Oops - The protocol doesn't look like one we understand");
  305. // Get the rest of the initial arguments
  306. if (!getpkt(cmd, 4)) goto alldone; // cmd[] = width, height, pixelformat, hasmouse
  307. // We will ignore size mismatches but the pixel format must match
  308. if (cmd[2] != GDISP_PIXELFORMAT)
  309. gfxHalt("Oops - The remote display is using a different pixel format to us.\nTry defining GDISP_PIXELFORMAT in your gfxconf.h file.");
  310. #if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
  311. // Start the mouse thread if needed
  312. if (cmd[3])
  313. gfxThreadClose(gfxThreadCreate(waNetThread, sizeof(waNetThread), HIGH_PRIORITY, NetThread, 0));
  314. #endif
  315. // Process incoming instructions
  316. while(getpkt(cmd, 1)) {
  317. switch(cmd[0]) {
  318. case GNETCODE_FLUSH:
  319. gdispFlush();
  320. break;
  321. case GNETCODE_PIXEL:
  322. if (!getpkt(cmd, 3)) goto alldone; // cmd[] = x, y, color
  323. gdispDrawPixel(cmd[0], cmd[1], cmd[2]);
  324. break;
  325. case GNETCODE_FILL:
  326. if (!getpkt(cmd, 5)) goto alldone; // cmd[] = x, y, cx, cy, color
  327. gdispFillArea(cmd[0], cmd[1], cmd[2], cmd[3], cmd[4]);
  328. break;
  329. case GNETCODE_BLIT:
  330. if (!getpkt(cmd, 4)) goto alldone; // cmd[] = x, y, cx, cy - Followed by cx * cy pixels
  331. gdispStreamStart(cmd[0],cmd[1],cmd[2],cmd[3]);
  332. for(cnt = (unsigned)cmd[2] * cmd[3]; cnt; cnt--) {
  333. if (!getpkt(cmd, 1)) goto alldone;
  334. gdispStreamColor(cmd[0]);
  335. }
  336. gdispStreamStop();
  337. break;
  338. #if GDISP_NEED_PIXELREAD
  339. case GNETCODE_READ:
  340. if (!getpkt(cmd, 2)) goto alldone; // cmd[] = x, y - Response is GNETCODE_READ,color
  341. cmd[1] = gdispGetPixelColor(cmd[0], cmd[1]);
  342. cmd[0] = GNETCODE_READ;
  343. if (!sendpkt(cmd, 2)) goto alldone;
  344. break;
  345. #endif
  346. #if GDISP_NEED_SCROLL
  347. case GNETCODE_SCROLL:
  348. if (!getpkt(cmd, 5)) goto alldone; // cmd[] = x, y, cx, cy, lines
  349. gdispVerticalScroll(cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], Black);
  350. break;
  351. #endif
  352. case GNETCODE_CONTROL:
  353. if (!getpkt(cmd, 2)) goto alldone; // cmd[] = what,data - Response is GNETCODE_CONTROL, 0x0000 (fail) or GNETCODE_CONTROL, 0x0001 (success)
  354. gdispControl(cmd[0], (void *)(unsigned)cmd[1]);
  355. switch(cmd[0]) {
  356. case GDISP_CONTROL_ORIENTATION:
  357. cmd[1] = (uint16_t)gdispGetOrientation() == cmd[1] ? 1 : 0;
  358. break;
  359. case GDISP_CONTROL_POWER:
  360. cmd[1] = (uint16_t)gdispGetPowerMode() == cmd[1] ? 1 : 0;
  361. break;
  362. case GDISP_CONTROL_BACKLIGHT:
  363. cmd[1] = (uint16_t)gdispGetBacklight() == cmd[1] ? 1 : 0;
  364. break;
  365. default:
  366. cmd[1] = 0;
  367. break;
  368. }
  369. cmd[0] = GNETCODE_CONTROL;
  370. if (!sendpkt(cmd, 2)) goto alldone;
  371. break;
  372. default:
  373. gfxHalt("Oops - The host has sent invalid commands");
  374. break;
  375. }
  376. }
  377. alldone:
  378. closesocket(netfd);
  379. gfxHalt("Connection closed");
  380. return 0;
  381. }