diff options
Diffstat (limited to 'drivers/multiple/uGFXnetESP8266/gdisp_lld_uGFXnetESP8266.cpp')
-rw-r--r-- | drivers/multiple/uGFXnetESP8266/gdisp_lld_uGFXnetESP8266.cpp | 622 |
1 files changed, 622 insertions, 0 deletions
diff --git a/drivers/multiple/uGFXnetESP8266/gdisp_lld_uGFXnetESP8266.cpp b/drivers/multiple/uGFXnetESP8266/gdisp_lld_uGFXnetESP8266.cpp new file mode 100644 index 00000000..7963acab --- /dev/null +++ b/drivers/multiple/uGFXnetESP8266/gdisp_lld_uGFXnetESP8266.cpp @@ -0,0 +1,622 @@ +/* + * 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 + */ + +// 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 + +#include <ESP8266WiFi.h> + +#define GDISP_DRIVER_VMT GDISPVMT_uGFXnetESP8266 +#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 FALSE +#endif + +static WiFiServer server(GDISP_GFXNET_PORT); +static GTimer poller; +static bool_t uGFXInitDone; + +#ifndef GDISP_GFXNET_WIFI_INIT_FUNCTION + #define GDISP_GFXNET_WIFI_INIT_FUNCTION uGFXnetArduinoWifiInit + #ifndef GDISP_GFXNET_WIFI_SSID + #error "uGFXnetArduino: GDISP_GFXNET_WIFI_SSID is not set. You must define the Wifi SSID" + #endif + #ifndef GDISP_GFXNET_WIFI_PASSWORD + #error "uGFXnetArduino: GDISP_GFXNET_WIFI_PASSWORD is not set. You must define the Wifi password" + #endif + + static void uGFXnetArduinoWifiInit(WifiServer *ws) { + WiFi.begin(GDISP_GFXNET_WIFI_SSID, GDISP_GFXNET_WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + gfxYield(); + + ws->begin(); + } +#else + extern "C" void GDISP_GFXNET_WIFI_INIT_FUNCTION(WifiServer *ws); +#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 bool_t NMouseInit(GMouse *m, unsigned driverinstance); + static bool_t 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: uGFXnetESP8266 - This driver only support protocol V1.0" +#endif +#if GDISP_LLD_PIXELFORMAT != GNETCODE_PIXELFORMAT + #error "GDISP: uGFXnetESP8266 - The driver pixel format must match the protocol" +#endif + +#define GDISP_FLG_CONNECTED (GDISP_FLG_DRIVER<<0) +#define GDISP_FLG_HAVEDATA (GDISP_FLG_DRIVER<<1) + +#define CLIENTFD WifiClient * + +/*===========================================================================*/ +/* Driver local routines . */ +/*===========================================================================*/ + +typedef struct netPriv { + CLIENTFD netfd; // The current client + unsigned databytes; // How many bytes have been read + uint16_t data[2]; // Buffer for storing data read. + #if GINPUT_NEED_MOUSE + coord_t mousex, mousey; + uint16_t mousebuttons; + GMouse * mouse; + #endif +} netPriv; + +#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 + +static void endcon(GDisplay *g) { + netPriv * priv; + + g->flags &= ~GDISP_FLG_CONNECTED; + priv = g->priv; + priv->netfd->stop(); + delete priv->netfd; + priv->netfd = 0; +} + +/** + * 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(CLIENTFD fd, uint16_t *pkt, int len) { + // Convert each uint16_t to network order + #if GFX_CPU_ENDIAN == GFX_CPU_ENDIAN_LITTLE + { + int i; + + for(i = 0; i < len; i++) + pkt[i] = ((pkt[i]>>8)|(pkt[i]<<8)); + } + #endif + + // Send it + len *= sizeof(uint16_t); + return fd->write((uint8_t *)pkt, len) == len; +} + +static void rxdata(GDisplay *g) { + netPriv * priv; + CLIENTFD fd; + int len; + + 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. + return; + } + + priv = g->priv; + fd = priv->netfd; + + MUTEX_ENTER; + // Are we still connected? + if (!fd->connected()) { + MUTEX_EXIT; + endcon(g); + return; + } + + // Is there data available + if (!fd->available()) { + MUTEX_EXIT; + return; + } + + // Get the data + if ((len = fd->read(((uint8_t *)priv->data)+priv->databytes, sizeof(priv->data)-priv->databytes, 0)) <= 0) { + // Socket closed or in error state + MUTEX_EXIT; + endcon(g); + return; + } + MUTEX_EXIT; + + // Do we have a full reply yet + priv->databytes += len; + if (priv->databytes < sizeof(priv->data)) + return; + priv->databytes = 0; + + // Convert network byte or to host byte order + #if GFX_CPU_ENDIAN == GFX_CPU_ENDIAN_LITTLE + priv->data[0] = ((priv->data[0]>>8)|(priv->data[0]<<8)) + priv->data[1] = ((priv->data[1]>>8)|(priv->data[1]<<8)) + #endif + + // 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; + } +} + +void uGFXnetClientPoller(void *param) { + GDisplay *g; + (void) param; + + // Is there a new server connection? + if (server.hasClient()) { + + // 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)) { + netPriv * priv; + + // Reset the priv area + priv = g->priv; + priv->netfd = new WifiClient(server.available()); + 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, FALSE); + #endif + break; + } + } + } + + // Look for a display that is connected so we can check if it has data + 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)) + rxdata(g); + } +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +LLDSPEC bool_t gdisp_lld_init(GDisplay *g) { + netPriv * priv; + + // Initialise the receiver thread (if it hasn't been done already) + if (!uGFXInitDone) { + MUTEX_INIT; + // Init and Start the poller + GDISP_GFXNET_WIFI_INIT_FUNCTION(&server); + + // Initialise the poller + gtimerInit(&poller); + gtimerStart(&poller, uGFXnetClientPoller, 0, TRUE, 50); + uGFXInitDone = TRUE; + } + + // Create a private area for this window + if (!(priv = gfxAlloc(sizeof(netPriv)))) + gfxHalt("GDISP: uGFXnetESP8266 - 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 = GDISP_ROTATE_0; + g->g.Powermode = powerOn; + g->g.Backlight = 100; + g->g.Contrast = 50; + g->g.Width = GDISP_SCREEN_WIDTH; + g->g.Height = GDISP_SCREEN_HEIGHT; + + return TRUE; +} + +#if GDISP_HARDWARE_FLUSH + LLDSPEC void gdisp_lld_flush(GDisplay *g) { + netPriv * priv; + uint16_t 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; + uint16_t 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; + uint16_t 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; + pixel_t * buffer; + uint16_t buf[5]; + coord_t 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 color_t gdisp_lld_get_pixel_color(GDisplay *g) { + netPriv * priv; + uint16_t buf[3]; + color_t 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; + uint16_t 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; + uint16_t buf[3]; + bool_t 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 == (orientation_t)g->p.ptr) + return; + break; + case GDISP_CONTROL_POWER: + if (g->g.Powermode == (powermode_t)g->p.ptr) + return; + break; + case GDISP_CONTROL_BACKLIGHT: + if (g->g.Backlight == (uint16_t)(int)g->p.ptr) + return; + if ((uint16_t)(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] = (uint16_t)(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] ? TRUE : FALSE; + 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((orientation_t)g->p.ptr) { + case GDISP_ROTATE_0: + case GDISP_ROTATE_180: + g->g.Width = GDISP_SCREEN_WIDTH; + g->g.Height = GDISP_SCREEN_HEIGHT; + break; + case GDISP_ROTATE_90: + case GDISP_ROTATE_270: + g->g.Height = GDISP_SCREEN_WIDTH; + g->g.Width = GDISP_SCREEN_HEIGHT; + break; + default: + return; + } + g->g.Orientation = (orientation_t)g->p.ptr; + break; + case GDISP_CONTROL_POWER: + g->g.Powermode = (powermode_t)g->p.ptr; + break; + case GDISP_CONTROL_BACKLIGHT: + g->g.Backlight = (uint16_t)(int)g->p.ptr; + break; + } + } +#endif + +#if GINPUT_NEED_MOUSE + static bool_t NMouseInit(GMouse *m, unsigned driverinstance) { + (void) m; + (void) driverinstance; + return TRUE; + } + static bool_t 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 TRUE; + } +#endif /* GINPUT_NEED_MOUSE */ + +#endif /* GFX_USE_GDISP */ |