/* * 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.io/license.html */ // We need to include stdio.h below. Turn off GFILE_NEED_STDIO just for this file to prevent conflicts #define GFILE_NEED_STDIO_MUST_BE_OFF #include "gfx.h" #if GFX_USE_GDISP #define GDISP_DRIVER_VMT GDISPVMT_uGFXnet #include "gdisp_lld_config.h" #include "../../../src/gdisp/gdisp_driver.h" #include "uGFXnetProtocol.h" #ifndef GDISP_SCREEN_WIDTH #define GDISP_SCREEN_WIDTH 640 #endif #ifndef GDISP_SCREEN_HEIGHT #define GDISP_SCREEN_HEIGHT 480 #endif #ifndef GDISP_GFXNET_PORT #define GDISP_GFXNET_PORT GNETCODE_DEFAULT_PORT #endif #ifndef GDISP_DONT_WAIT_FOR_NET_DISPLAY #define GDISP_DONT_WAIT_FOR_NET_DISPLAY GFXOFF #endif #ifndef GDISP_GFXNET_UNSAFE_SOCKETS #define GDISP_GFXNET_UNSAFE_SOCKETS GFXOFF #endif #ifndef GDISP_GFXNET_BROKEN_LWIP_ACCEPT #define GDISP_GFXNET_BROKEN_LWIP_ACCEPT GFXOFF #endif #if GINPUT_NEED_MOUSE // Include mouse support code #define GMOUSE_DRIVER_VMT GMOUSEVMT_uGFXnet #include "../../../src/ginput/ginput_driver_mouse.h" // Forward definitions static gBool NMouseInit(GMouse *m, unsigned driverinstance); static gBool NMouseRead(GMouse *m, GMouseReading *prd); const GMouseVMT const GMOUSE_DRIVER_VMT[1] = {{ { GDRIVER_TYPE_MOUSE, GMOUSE_VFLG_NOPOLL|GMOUSE_VFLG_DYNAMICONLY, // Extra flags for testing only //GMOUSE_VFLG_TOUCH|GMOUSE_VFLG_SELFROTATION|GMOUSE_VFLG_DEFAULTFINGER //GMOUSE_VFLG_CALIBRATE|GMOUSE_VFLG_CAL_EXTREMES|GMOUSE_VFLG_CAL_TEST|GMOUSE_VFLG_CAL_LOADFREE //GMOUSE_VFLG_ONLY_DOWN|GMOUSE_VFLG_POORUPDOWN sizeof(GMouse), _gmouseInitDriver, _gmousePostInitDriver, _gmouseDeInitDriver }, 1, // z_max 0, // z_min 1, // z_touchon 0, // z_touchoff { // pen_jitter 0, // calibrate 0, // click 0 // move }, { // finger_jitter 0, // calibrate 2, // click 2 // move }, NMouseInit, // init 0, // deinit NMouseRead, // get 0, // calsave 0 // calload }}; #endif #if GNETCODE_VERSION != GNETCODE_VERSION_1_0 #error "GDISP: uGFXnet - This driver only support protocol V1.0" #endif #if GDISP_LLD_PIXELFORMAT != GNETCODE_PIXELFORMAT #error "GDISP: uGFXnet - The driver pixel format must match the protocol" #endif #include #include #include #if defined(WIN32) || GFX_USE_OS_WIN32 #include #define SOCKET_TYPE SOCKET #define socklen_t int 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 #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, gThreadpriorityNormal, 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 #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 GFXON #endif #endif #define GDISP_FLG_CONNECTED (GDISP_FLG_DRIVER<<0) #define GDISP_FLG_HAVEDATA (GDISP_FLG_DRIVER<<1) /*===========================================================================*/ /* Driver local routines . */ /*===========================================================================*/ typedef struct netPriv { SOCKET_TYPE netfd; // The current socket unsigned databytes; // How many bytes have been read gU16 data[2]; // Buffer for storing data read. #if GINPUT_NEED_MOUSE gCoord mousex, mousey; gU16 mousebuttons; GMouse * mouse; #endif } netPriv; static gThread hThread; #if GDISP_GFXNET_UNSAFE_SOCKETS static gfxMutex uGFXnetMutex; #define MUTEX_INIT gfxMutexInit(&uGFXnetMutex) #define MUTEX_ENTER gfxMutexEnter(&uGFXnetMutex) #define MUTEX_EXIT gfxMutexExit(&uGFXnetMutex) #else #define MUTEX_INIT #define MUTEX_ENTER #define MUTEX_EXIT #endif /** * Send a whole packet of data. * Len is specified in the number of gU16's we want to send as our protocol only talks gU16'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 gFalse. */ static gBool sendpkt(SOCKET_TYPE netfd, gU16 *pkt, int len) { int i; // Convert each gU16 to network order for(i = 0; i < len; i++) pkt[i] = htons(pkt[i]); // Send it len *= sizeof(gU16); return send(netfd, (const char *)pkt, len, 0) == len; } static gBool newconnection(SOCKET_TYPE clientfd) { GDisplay * g; netPriv * priv; // Look for a display that isn't connected for(g = 0; (g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, (GDriver *)g));) { // Ignore displays for other controllers #ifdef GDISP_DRIVER_LIST if (gvmt(g) != &GDISPVMT_uGFXnet) continue; #endif if (!(g->flags & GDISP_FLG_CONNECTED)) break; } // Was anything found? if (!g) return gFalse; // Reset the priv area priv = g->priv; priv->netfd = clientfd; priv->databytes = 0; priv->mousebuttons = 0; // Send the initialisation data (2 words at a time) priv->data[0] = GNETCODE_INIT; priv->data[1] = GNETCODE_VERSION; sendpkt(priv->netfd, priv->data, 2); priv->data[0] = GDISP_SCREEN_WIDTH; priv->data[1] = GDISP_SCREEN_HEIGHT; sendpkt(priv->netfd, priv->data, 2); priv->data[0] = GDISP_LLD_PIXELFORMAT; priv->data[1] = 1; // We have a mouse MUTEX_ENTER; sendpkt(priv->netfd, priv->data, 2); MUTEX_EXIT; // The display is now working g->flags |= GDISP_FLG_CONNECTED; // Send a redraw all #if GFX_USE_GWIN && GWIN_NEED_WINDOWMANAGER gdispGClear(g, gwinGetDefaultBgColor()); gwinRedrawDisplay(g, gFalse); #endif return gTrue; } static gBool rxdata(SOCKET_TYPE fd) { GDisplay * g; netPriv * priv; int len; // Look for a display that is connected and the socket descriptor matches for(g = 0; (g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, (GDriver *)g));) { // Ignore displays for other controllers #ifdef GDISP_DRIVER_LIST if (gvmt(g) != &GDISPVMT_uGFXnet) continue; #endif priv = g->priv; if ((g->flags & GDISP_FLG_CONNECTED) && priv->netfd == fd) break; } if (!g) gfxHalt("GDISP: uGFXnet - Got data from unrecognized connection"); if ((g->flags & GDISP_FLG_HAVEDATA)) { // The higher level is still processing the previous data. // Give it a chance to run by coming back to this data. gfxSleepMilliseconds(1); return gTrue; } /* handle data from a client */ MUTEX_ENTER; if ((len = recv(fd, ((char *)priv->data)+priv->databytes, sizeof(priv->data)-priv->databytes, 0)) <= 0) { // Socket closed or in error state MUTEX_EXIT; g->flags &= ~GDISP_FLG_CONNECTED; return gFalse; } MUTEX_EXIT; // Do we have a full reply yet priv->databytes += len; if (priv->databytes < sizeof(priv->data)) return gTrue; priv->databytes = 0; // Convert network byte or to host byte order priv->data[0] = ntohs(priv->data[0]); priv->data[1] = ntohs(priv->data[1]); // Process the data received switch(priv->data[0]) { #if GINPUT_NEED_MOUSE case GNETCODE_MOUSE_X: priv->mousex = priv->data[1]; break; case GNETCODE_MOUSE_Y: priv->mousey = priv->data[1]; break; case GNETCODE_MOUSE_B: priv->mousebuttons = priv->data[1]; // Treat the button event as the sync signal _gmouseWakeup(priv->mouse); break; #endif case GNETCODE_CONTROL: case GNETCODE_READ: g->flags |= GDISP_FLG_HAVEDATA; break; case GNETCODE_KILL: gfxHalt("GDISP: uGFXnet - Display sent KILL command"); break; default: // Just ignore unrecognised data break; } return gTrue; } static DECLARE_THREAD_STACK(waNetThread, 512); static DECLARE_THREAD_FUNCTION(NetThread, param) { SOCKET_TYPE listenfd, fdmax, i, clientfd; socklen_t len; fd_set master, read_fds; struct sockaddr_in addr; (void)param; // Start the sockets layer StartSockets(); gfxSleepMilliseconds(100); // Make sure the thread has time to start. /* clear the master and temp sets */ FD_ZERO(&master); FD_ZERO(&read_fds); if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == (SOCKET_TYPE)-1) gfxHalt("GDISP: uGFXnet - Socket failed"); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(GDISP_GFXNET_PORT); if (bind(listenfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) gfxHalt("GDISP: uGFXnet - Bind failed"); if (listen(listenfd, 10) == -1) gfxHalt("GDISP: uGFXnet - Listen failed"); /* add the listener to the master set */ FD_SET(listenfd, &master); /* keep track of the biggest file descriptor */ fdmax = listenfd; /* so far, it's this one*/ #if GDISP_GFXNET_BROKEN_LWIP_ACCEPT #if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT #warning "Using GDISP_GFXNET_BROKEN_LWIP_ACCEPT limits the number of displays and the use of GFXNET. Avoid if possible!" #elif GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_MACRO COMPILER_WARNING("Using GDISP_GFXNET_BROKEN_LWIP_ACCEPT limits the number of displays and the use of GFXNET. Avoid if possible!") #endif len = sizeof(addr); if((clientfd = accept(listenfd, (struct sockaddr *)&addr, &len)) == (SOCKET_TYPE)-1) gfxHalt("GDISP: uGFXnet - Accept failed"); //printf("New connection from %s on socket %d\n", inet_ntoa(addr.sin_addr), clientfd); if (!newconnection(clientfd)) { // No Just close the connection closesocket(clientfd); gfxHalt("GDISP: uGFXnet - Can't find display for connection"); return 0; } // Save the descriptor FD_SET(clientfd, &master); if (clientfd > fdmax) fdmax = clientfd; #endif /* loop */ for(;;) { /* copy it */ read_fds = master; if (select(fdmax+1, &read_fds, 0, 0, 0) == -1) gfxHalt("GDISP: uGFXnet - Select failed"); // Run through the existing connections looking for data to be read for(i = 0; i <= fdmax; i++) { if(!FD_ISSET(i, &read_fds)) continue; // Handle new connections if(i == listenfd) { // Accept the connection len = sizeof(addr); if((clientfd = accept(listenfd, (struct sockaddr *)&addr, &len)) == (SOCKET_TYPE)-1) gfxHalt("GDISP: uGFXnet - Accept failed"); //printf("New connection from %s on socket %d\n", inet_ntoa(addr.sin_addr), clientfd); // Can we handle it? if (!newconnection(clientfd)) { // No - Just close the connection closesocket(clientfd); //printf("Rejected connection as all displays are already connected\n"); continue; } // Save the descriptor FD_SET(clientfd, &master); if (clientfd > fdmax) fdmax = clientfd; continue; } // Handle data from a client if (!rxdata(i)) { closesocket(i); FD_CLR(clientfd, &master); } } } return 0; } /*===========================================================================*/ /* Driver exported functions. */ /*===========================================================================*/ LLDSPEC gBool gdisp_lld_init(GDisplay *g) { netPriv * priv; // Initialise the receiver thread (if it hasn't been done already) if (!hThread) { MUTEX_INIT; hThread = gfxThreadCreate(waNetThread, sizeof(waNetThread), gThreadpriorityHigh, NetThread, 0); gfxThreadClose(hThread); } // Create a private area for this window if (!(priv = gfxAlloc(sizeof(netPriv)))) gfxHalt("GDISP: uGFXnet - Memory allocation failed"); memset(priv, 0, sizeof(netPriv)); g->priv = priv; g->board = 0; // no board interface for this controller // Create the associated mouse #if GINPUT_NEED_MOUSE priv->mouse = (GMouse *)gdriverRegister((const GDriverVMT const *)GMOUSE_DRIVER_VMT, g); #endif // Initialise the GDISP structure g->g.Orientation = gOrientation0; g->g.Powermode = gPowerOn; g->g.Backlight = 100; g->g.Contrast = 50; g->g.Width = GDISP_SCREEN_WIDTH; g->g.Height = GDISP_SCREEN_HEIGHT; return gTrue; } #if GDISP_HARDWARE_FLUSH LLDSPEC void gdisp_lld_flush(GDisplay *g) { netPriv * priv; gU16 buf[1]; #if GDISP_DONT_WAIT_FOR_NET_DISPLAY if (!(g->flags & GDISP_FLG_CONNECTED)) return; #else while(!(g->flags & GDISP_FLG_CONNECTED)) gfxSleepMilliseconds(200); #endif priv = g->priv; buf[0] = GNETCODE_FLUSH; MUTEX_ENTER; sendpkt(priv->netfd, buf, 1); MUTEX_EXIT; } #endif #if GDISP_HARDWARE_DRAWPIXEL LLDSPEC void gdisp_lld_draw_pixel(GDisplay *g) { netPriv * priv; gU16 buf[4]; #if GDISP_DONT_WAIT_FOR_NET_DISPLAY if (!(g->flags & GDISP_FLG_CONNECTED)) return; #else while(!(g->flags & GDISP_FLG_CONNECTED)) gfxSleepMilliseconds(200); #endif priv = g->priv; buf[0] = GNETCODE_PIXEL; buf[1] = g->p.x; buf[2] = g->p.y; buf[3] = gdispColor2Native(g->p.color); MUTEX_ENTER; sendpkt(priv->netfd, buf, 4); MUTEX_EXIT; } #endif /* ---- Optional Routines ---- */ #if GDISP_HARDWARE_FILLS LLDSPEC void gdisp_lld_fill_area(GDisplay *g) { netPriv * priv; gU16 buf[6]; #if GDISP_DONT_WAIT_FOR_NET_DISPLAY if (!(g->flags & GDISP_FLG_CONNECTED)) return; #else while(!(g->flags & GDISP_FLG_CONNECTED)) gfxSleepMilliseconds(200); #endif priv = g->priv; buf[0] = GNETCODE_FILL; buf[1] = g->p.x; buf[2] = g->p.y; buf[3] = g->p.cx; buf[4] = g->p.cy; buf[5] = gdispColor2Native(g->p.color); MUTEX_ENTER; sendpkt(priv->netfd, buf, 6); MUTEX_EXIT; } #endif #if GDISP_HARDWARE_BITFILLS LLDSPEC void gdisp_lld_blit_area(GDisplay *g) { netPriv * priv; gPixel * buffer; gU16 buf[5]; gCoord x, y; #if GDISP_DONT_WAIT_FOR_NET_DISPLAY if (!(g->flags & GDISP_FLG_CONNECTED)) return; #else while(!(g->flags & GDISP_FLG_CONNECTED)) gfxSleepMilliseconds(200); #endif // Make everything relative to the start of the line buffer = g->p.ptr; buffer += g->p.x2*g->p.y1; priv = g->priv; buf[0] = GNETCODE_BLIT; buf[1] = g->p.x; buf[2] = g->p.y; buf[3] = g->p.cx; buf[4] = g->p.cy; MUTEX_ENTER; sendpkt(priv->netfd, buf, 5); for(y = 0; y < g->p.cy; y++, buffer += g->p.x2 - g->p.cx) { for(x = 0; x < g->p.cx; x++, buffer++) { buf[0] = gdispColor2Native(buffer[0]); sendpkt(priv->netfd, buf, 1); } } MUTEX_EXIT; } #endif #if GDISP_HARDWARE_PIXELREAD LLDSPEC gColor gdisp_lld_get_pixel_color(GDisplay *g) { netPriv * priv; gU16 buf[3]; gColor data; #if GDISP_DONT_WAIT_FOR_NET_DISPLAY if (!(g->flags & GDISP_FLG_CONNECTED)) return 0; #else while(!(g->flags & GDISP_FLG_CONNECTED)) gfxSleepMilliseconds(200); #endif priv = g->priv; buf[0] = GNETCODE_READ; buf[1] = g->p.x; buf[2] = g->p.y; MUTEX_ENTER; sendpkt(priv->netfd, buf, 3); MUTEX_EXIT; // Now wait for a reply while(!(g->flags & GDISP_FLG_HAVEDATA) || priv->data[0] != GNETCODE_READ) gfxSleepMilliseconds(1); data = gdispNative2Color(priv->data[1]); g->flags &= ~GDISP_FLG_HAVEDATA; return data; } #endif #if GDISP_NEED_SCROLL && GDISP_HARDWARE_SCROLL LLDSPEC void gdisp_lld_vertical_scroll(GDisplay *g) { netPriv * priv; gU16 buf[6]; #if GDISP_DONT_WAIT_FOR_NET_DISPLAY if (!(g->flags & GDISP_FLG_CONNECTED)) return; #else while(!(g->flags & GDISP_FLG_CONNECTED)) gfxSleepMilliseconds(200); #endif priv = g->priv; buf[0] = GNETCODE_SCROLL; buf[1] = g->p.x; buf[2] = g->p.y; buf[3] = g->p.cx; buf[4] = g->p.cy; buf[5] = g->p.y1; MUTEX_ENTER; sendpkt(priv->netfd, buf, 6); MUTEX_EXIT; } #endif #if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL LLDSPEC void gdisp_lld_control(GDisplay *g) { netPriv * priv; gU16 buf[3]; gBool allgood; #if GDISP_DONT_WAIT_FOR_NET_DISPLAY if (!(g->flags & GDISP_FLG_CONNECTED)) return; #else while(!(g->flags & GDISP_FLG_CONNECTED)) gfxSleepMilliseconds(200); #endif // Check if we might support the code switch(g->p.x) { case GDISP_CONTROL_ORIENTATION: if (g->g.Orientation == (gOrientation)g->p.ptr) return; break; case GDISP_CONTROL_POWER: if (g->g.Powermode == (gPowermode)g->p.ptr) return; break; case GDISP_CONTROL_BACKLIGHT: if (g->g.Backlight == (gU16)(int)g->p.ptr) return; if ((gU16)(int)g->p.ptr > 100) g->p.ptr = (void *)100; break; default: return; } // Send the command priv = g->priv; buf[0] = GNETCODE_CONTROL; buf[1] = g->p.x; buf[2] = (gU16)(int)g->p.ptr; MUTEX_ENTER; sendpkt(priv->netfd, buf, 3); MUTEX_EXIT; // Now wait for a reply while(!(g->flags & GDISP_FLG_HAVEDATA) || priv->data[0] != GNETCODE_CONTROL) gfxSleepMilliseconds(1); // Extract the return status allgood = priv->data[1] ? gTrue : gFalse; g->flags &= ~GDISP_FLG_HAVEDATA; // Do nothing more if the operation failed if (!allgood) return; // Update the local stuff switch(g->p.x) { case GDISP_CONTROL_ORIENTATION: switch((gOrientation)g->p.ptr) { case gOrientation0: case gOrientation180: g->g.Width = GDISP_SCREEN_WIDTH; g->g.Height = GDISP_SCREEN_HEIGHT; break; case gOrientation90: case gOrientation270: g->g.Height = GDISP_SCREEN_WIDTH; g->g.Width = GDISP_SCREEN_HEIGHT; break; default: return; } g->g.Orientation = (gOrientation)g->p.ptr; break; case GDISP_CONTROL_POWER: g->g.Powermode = (gPowermode)g->p.ptr; break; case GDISP_CONTROL_BACKLIGHT: g->g.Backlight = (gU16)(int)g->p.ptr; break; } } #endif #if GINPUT_NEED_MOUSE static gBool NMouseInit(GMouse *m, unsigned driverinstance) { (void) m; (void) driverinstance; return gTrue; } static gBool NMouseRead(GMouse *m, GMouseReading *pt) { GDisplay * g; netPriv * priv; g = m->display; priv = g->priv; pt->x = priv->mousex; pt->y = priv->mousey; pt->z = (priv->mousebuttons & GINPUT_MOUSE_BTN_LEFT) ? 1 : 0; pt->buttons = priv->mousebuttons; return gTrue; } #endif /* GINPUT_NEED_MOUSE */ #endif /* GFX_USE_GDISP */