/* * This file is subject to the terms of the GFX License. If a copy of * the license was not distributed with this file, you can obtain one at: * * http://ugfx.org/license.html */ #include "gfx.h" #include "drivers/multiple/uGFXnet/uGFXnetProtocol.h" #ifndef GDISP_GFXNET_PORT #define GDISP_GFXNET_PORT GNETCODE_DEFAULT_PORT #endif // This definition is only required for for O/S's that don't support a command line eg ChibiOS // It is ignored by those that do support a command line. #ifndef GDISP_GFXNET_HOST #define GDISP_GFXNET_HOST "127.0.0.1" // Change this to your uGFXnet host. #endif // Do we wish to use old style socket calls. Some socket libraries only support the old version. // It is better to use the new version where possible however as it also supports IPv6. #ifndef OLD_STYLE_SOCKETS #define OLD_STYLE_SOCKETS FALSE #endif // Which operating systems support a command line #if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_OSX || GFX_USE_OS_LINUX #define EMBEDED_OS FALSE #else #define EMBEDED_OS TRUE #endif #if GNETCODE_VERSION != GNETCODE_VERSION_1_0 #error "This uGFXnet display only supports protocol V1.0" #endif #if GDISP_PIXELFORMAT != GNETCODE_PIXELFORMAT #error "Oops - The uGFXnet protocol requires a different pixel format. Try defining GDISP_PIXELFORMAT in your gfxconf.h file." #endif #include #include #include #if defined(WIN32) || GFX_USE_OS_WIN32 #if OLD_STYLE_SOCKETS #include #else #include #include #endif #define SOCKET_TYPE SOCKET static void StopSockets(void) { WSACleanup(); } static void StartSockets(void) { WSADATA wsaData; if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) gfxHalt("GDISP: uGFXnet - WSAStartup failed"); atexit(StopSockets); } #elif GFX_USE_OS_LINUX || GFX_USE_OS_OSX #include #include #include #include #include #define closesocket(fd) close(fd) #define ioctlsocket(fd,cmd,arg) ioctl(fd,cmd,arg) #define StartSockets() #define SOCKET_TYPE int #else #include #include #if GDISP_GFXNET_CUSTOM_LWIP_STARTUP extern void Start_LWIP(void); // Where the application does the lwip stack setup #define StartSockets() Start_LWIP(); #else #include "lwipthread.h" #define StartSockets() gfxThreadClose(gfxThreadCreate(wa_lwip_thread, LWIP_THREAD_STACK_SIZE, NORMAL_PRIORITY, lwip_thread, 0)) #endif #if !LWIP_SOCKET #error "GDISP: uGFXnet - LWIP_SOCKETS must be defined in your lwipopts.h file" #endif #if !LWIP_COMPAT_SOCKETS #error "GDISP: uGFXnet - LWIP_COMPAT_SOCKETS must be defined in your lwipopts.h file" #endif #if !LWIP_DNS #error "GDISP: uGFXnet - LWIP_DNS must be defined in your lwipopts.h file" #endif #define SOCKET_TYPE int // Mutex protection is required for LWIP #if !GDISP_GFXNET_UNSAFE_SOCKETS #if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT #warning "GDISP: uGFXnet - LWIP sockets are not thread-safe. GDISP_GFXNET_UNSAFE_SOCKETS has been turned on for you." #elif GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_MACRO COMPILER_WARNING("GDISP: uGFXnet - LWIP sockets are not thread-safe. GDISP_GFXNET_UNSAFE_SOCKETS has been turned on for you.") #endif #undef GDISP_GFXNET_UNSAFE_SOCKETS #define GDISP_GFXNET_UNSAFE_SOCKETS TRUE #endif #endif #if GFX_USE_GINPUT && GINPUT_NEED_MOUSE static GListener gl; #endif static SOCKET_TYPE netfd = (SOCKET_TYPE)-1; static font_t font; #define STRINGOF_RAW(s) #s #define STRINGOF(s) STRINGOF_RAW(s) #if EMBEDED_OS #define cmd_args #define proto_args void #define xhost GDISP_GFXNET_HOST #define xport STRINGOF(GDISP_GFXNET_HOST) #define xportnum GDISP_GFXNET_HOST #else #define cmd_args argc, argv #define proto_args int argc, char **argv static char * xhost; static char * xport; #if OLD_STYLE_SOCKETS static int xportnum = GDISP_GFXNET_PORT; #endif #endif /** * Get a whole packet of data. * Len is specified in the number of uint16_t's we want as our protocol only talks uint16_t's. * If the connection closes before we get all the data - the call returns FALSE. */ static bool_t getpkt(uint16_t *pkt, int len) { int got; int have; // Get the packet of data len *= sizeof(uint16_t); have = 0; while(len && (got = recv(netfd, ((char *)pkt)+have, len, 0)) > 0) { have += got; len -= got; } if (len) return FALSE; // Convert each uint16_t to host order for(got = 0, have /= 2; got < have; got++) pkt[got] = ntohs(pkt[got]); return TRUE; } /** * Send a whole packet of data. * Len is specified in the number of uint16_t's we want to send as our protocol only talks uint16_t's. * Note that contents of the packet are modified to ensure it will cross the wire in the correct format. * If the connection closes before we send all the data - the call returns FALSE. */ static bool_t sendpkt(uint16_t *pkt, int len) { int i; // Convert each uint16_t to network order for(i = 0; i < len; i++) pkt[i] = htons(pkt[i]); // Send it len *= sizeof(uint16_t); return send(netfd, (const char *)pkt, len, 0) == len; } #if GFX_USE_GINPUT && GINPUT_NEED_MOUSE /** * We use a separate thread to capture mouse events and send them down the pipe. * We do the send in a single transaction to prevent it getting interspersed with * any reply we need to send on the main thread. */ static DECLARE_THREAD_STACK(waNetThread, 512); static DECLARE_THREAD_FUNCTION(NetThread, param) { GEventMouse *pem; uint16_t cmd[2]; uint16_t lbuttons; coord_t lx, ly; (void) param; // Initialize the mouse and the listener. geventListenerInit(&gl); geventAttachSource(&gl, ginputGetMouse(0), GLISTEN_MOUSEDOWNMOVES|GLISTEN_MOUSEMETA); lbuttons = 0; lx = ly = -1; while(1) { // Get a (mouse) event pem = (GEventMouse *)geventEventWait(&gl, TIME_INFINITE); if (pem->type != GEVENT_MOUSE && pem->type != GEVENT_TOUCH) continue; // Nothing to do if the socket is not open if (netfd == (SOCKET)-1) continue; // Nothing to do if the mouse data has not changed if (lx == pem->x && ly == pem->y && lbuttons == pem->buttons) continue; // Transfer mouse data that has changed if (lx != pem->x) { lx = pem->x; cmd[0] = GNETCODE_MOUSE_X; cmd[1] = lx; sendpkt(cmd, 2); } if (ly != pem->y) { ly = pem->y; cmd[0] = GNETCODE_MOUSE_Y; cmd[1] = ly; sendpkt(cmd, 2); } // We always send the buttons as it also acts as a mouse sync signal lbuttons = pem->buttons; cmd[0] = GNETCODE_MOUSE_B; cmd[1] = lbuttons; sendpkt(cmd, 2); } return 0; } #endif /** * Do the connection to the remote host. * We have two prototypes here - one for embedded systems and one for systems with a command line. * We have two methods of using the sockets library - one very old style and the other the more modern approach. */ static SOCKET_TYPE doConnect(proto_args) { SOCKET_TYPE fd; #if !EMBEDED_OS (void) argc; // Parse the command line arguments xhost = 0; xport = 0; while (*++argv) { if (!xhost) xhost = argv[0]; else if (!xport) { xport = argv[0]; #if OLD_STYLE_SOCKETS if (sscanf(xport, "%i", &xportnum) != 1 || xportnum >= 65536 || xportnum <= 0) { fprintf(stderr, "Error: Bad port specification '%s'\n\n", xport); goto usage; } #endif } else { fprintf(stderr, "Error: Unknown argument '%s'\n\n", argv[0]); goto usage; } } // Check the command line arguments were valid. if (!xport) xport = STRINGOF(GDISP_GFXNET_PORT); if (!xhost) { usage: fprintf(stderr, "Usage: uGFXnetDisplay host [port]\n"); exit(1); } #endif #if OLD_STYLE_SOCKETS struct sockaddr_in serv_addr; struct hostent * h; h = gethostbyname(xhost); if (!h) // Error: Unable to find an ip-address for the specified server return (SOCKET_TYPE)-1; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_port = htons(xportnum); serv_addr.sin_family = h->h_addrtype; memcpy(&serv_addr.sin_addr, h->h_addr_list[0], h->h_length); if ((fd = socket(serv_addr.sin_family, SOCK_STREAM, 0)) == (SOCKET_TYPE)-1) // Error: Socket failed return (SOCKET_TYPE)-1; if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) { // Error: Could not connect to the specified server closesocket(fd); fd = (SOCKET_TYPE)-1; } #else struct addrinfo hints, *servinfo, *p; memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; fd = (SOCKET_TYPE)-1; if (getaddrinfo(xhost, xport, &hints, &servinfo) != 0) // Error: Unable to find an ip-address for the specified server return (SOCKET_TYPE)-1; for(p = servinfo; p; p = p->ai_next) { if ((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == (SOCKET_TYPE)-1) continue; if (connect(fd, p->ai_addr, p->ai_addrlen) == -1) { closesocket(fd); fd = (SOCKET_TYPE)-1; continue; } break; } freeaddrinfo(servinfo); #endif return fd; } /** * Our main function. * There are two prototypes - one for systems with a command line and one for embedded systems without one. */ int main(proto_args) { uint16_t cmd[5]; unsigned cnt; // Initialize and clear the display gfxInit(); font = gdispOpenFont("UI2"); // Open the connection gdispDrawStringBox(0, 0, gdispGetWidth(), gdispGetHeight(), "Connecting to host...", font, White, justifyCenter); StartSockets(); netfd = doConnect(cmd_args); if (netfd == (SOCKET_TYPE)-1) gfxHalt("Could not connect to the specified server"); gdispClear(Black); // Get the initial packet from the host if (!getpkt(cmd, 2)) goto alldone; if (cmd[0] != GNETCODE_INIT || cmd[1] != GNETCODE_VERSION) gfxHalt("Oops - The protocol doesn't look like one we understand"); // Get the rest of the initial arguments if (!getpkt(cmd, 4)) goto alldone; // cmd[] = width, height, pixelformat, hasmouse // We will ignore size mismatches but the pixel format must match if (cmd[2] != GDISP_PIXELFORMAT) gfxHalt("Oops - The remote display is using a different pixel format to us.\nTry defining GDISP_PIXELFORMAT in your gfxconf.h file."); #if GFX_USE_GINPUT && GINPUT_NEED_MOUSE // Start the mouse thread if needed if (cmd[3]) gfxThreadClose(gfxThreadCreate(waNetThread, sizeof(waNetThread), HIGH_PRIORITY, NetThread, 0)); #endif // Process incoming instructions while(getpkt(cmd, 1)) { switch(cmd[0]) { case GNETCODE_FLUSH: gdispFlush(); break; case GNETCODE_PIXEL: if (!getpkt(cmd, 3)) goto alldone; // cmd[] = x, y, color gdispDrawPixel(cmd[0], cmd[1], cmd[2]); break; case GNETCODE_FILL: if (!getpkt(cmd, 5)) goto alldone; // cmd[] = x, y, cx, cy, color gdispFillArea(cmd[0], cmd[1], cmd[2], cmd[3], cmd[4]); break; case GNETCODE_BLIT: if (!getpkt(cmd, 4)) goto alldone; // cmd[] = x, y, cx, cy - Followed by cx * cy pixels gdispStreamStart(cmd[0],cmd[1],cmd[2],cmd[3]); for(cnt = (unsigned)cmd[2] * cmd[3]; cnt; cnt--) { if (!getpkt(cmd, 1)) goto alldone; gdispStreamColor(cmd[0]); } gdispStreamStop(); break; #if GDISP_NEED_PIXELREAD case GNETCODE_READ: if (!getpkt(cmd, 2)) goto alldone; // cmd[] = x, y - Response is GNETCODE_READ,color cmd[1] = gdispGetPixelColor(cmd[0], cmd[1]); cmd[0] = GNETCODE_READ; if (!sendpkt(cmd, 2)) goto alldone; break; #endif #if GDISP_NEED_SCROLL case GNETCODE_SCROLL: if (!getpkt(cmd, 5)) goto alldone; // cmd[] = x, y, cx, cy, lines gdispVerticalScroll(cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], Black); break; #endif case GNETCODE_CONTROL: if (!getpkt(cmd, 2)) goto alldone; // cmd[] = what,data - Response is GNETCODE_CONTROL, 0x0000 (fail) or GNETCODE_CONTROL, 0x0001 (success) gdispControl(cmd[0], (void *)(unsigned)cmd[1]); switch(cmd[0]) { case GDISP_CONTROL_ORIENTATION: cmd[1] = (uint16_t)gdispGetOrientation() == cmd[1] ? 1 : 0; break; case GDISP_CONTROL_POWER: cmd[1] = (uint16_t)gdispGetPowerMode() == cmd[1] ? 1 : 0; break; case GDISP_CONTROL_BACKLIGHT: cmd[1] = (uint16_t)gdispGetBacklight() == cmd[1] ? 1 : 0; break; default: cmd[1] = 0; break; } cmd[0] = GNETCODE_CONTROL; if (!sendpkt(cmd, 2)) goto alldone; break; default: gfxHalt("Oops - The host has sent invalid commands"); break; } } alldone: closesocket(netfd); gfxHalt("Connection closed"); return 0; }