components/vnc_server/vnc-server.c

changeset 0
b74b0e4902c3
child 1
ad2c8b13eb88
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/vnc_server/vnc-server.c	Sat Oct 20 13:23:15 2018 +0200
@@ -0,0 +1,1520 @@
+/**
+ * @file vnc-server.c
+ * @brief VNC server original written for eCos.
+ *
+ * The VNC server runs on the target platform and waits for a client 
+ * (vncviewer program running on a PC) to connect via an ethernet connection
+ * (port 5900). Once a client has connected, the target platform has a 
+ * virtual screen, keyboard and mouse.
+ *
+ * This port of the VNC server has been designed with some limitations in 
+ * order to keep memory and processor power requirements to a minimum.
+ *
+ * The VNC client must accept the settings that the VNC server suggests (bits-per-pixel, 
+ * number-of-colours and so on as specified at build time with the eCos configuration 
+ * tool) or the VNC server will just disconnect.
+ *
+ * The VNC server only supports CoRRE encoding and RAW encoding for sending 
+ * display data to the client.
+ *
+ * The VNC server does not support password authentication, the client is able 
+ * to connect without the user typing in a password.
+ *
+ * Only one VNC client may connect to the VNC server at a time.
+ *
+ * In reality these limitations are not a problem.
+ *
+ * @author Chris Garry <cgarry@sweeneydesign.co.uk>
+ * @author Michiel Broek.
+ */
+
+#include "vnc-server.h"
+#include "websocket_server.h"
+
+static const char *TAG = "vnc-server";
+
+#define BACKLOG             5       /* Number of pending connections queue will hold */
+#define MESSAGE_BUFFER_SIZE 60	/* Was 50, but I have seen 52 bytes length */
+#define TILE_SIZE           16
+#define TRUE_COLOUR_FLAG    1       /* True colour is set */
+#define BIG_ENDIAN_FLAG     1       /* Always send colour data big endian */
+
+/* Default display parameters */
+#define BITS_PER_PIXEL      8      /* Bits per pixel */
+#define PIXEL_DEPTH         8      /* Usefull bits per pixel */
+#define RED_MAX             7
+#define GREEN_MAX           7
+#define BLUE_MAX            3
+#define RED_SHIFT           5
+#define GREEN_SHIFT         2
+#define BLUE_SHIFT          0
+
+
+/* Initial default RGB332 */
+uint8_t			Bits_Per_Pixel = 8;
+
+uint16_t		Red_Max     = RED_MAX;
+uint16_t		Green_Max   = GREEN_MAX;
+uint16_t		Blue_Max    = BLUE_MAX;
+uint8_t			Red_Shift   = RED_SHIFT;
+uint8_t			Green_Shift = GREEN_SHIFT;
+uint8_t			Blue_Shift  = BLUE_SHIFT;
+bool			AltPixels   = false;
+
+/* Client to Server message types */
+#define SET_PIXEL_FORMAT         0
+#define FIX_COLOUR_MAP_ENTRIES   1
+#define SET_ENCODINGS            2
+#define FRAME_BUFFER_UPDATE_REQ  3
+#define KEY_EVENT                4
+#define POINTER_EVENT            5
+#define CLIENT_CUT_TEXT          6
+
+/* Macros to split colour to bytes */
+#define COLOUR2BYTE1(col) ((col>>8)&0xFF)
+#define COLOUR2BYTE0(col) (col&0xFF)
+
+
+/* Thread function prototypes */
+static TaskHandle_t	xTaskClientHandler = NULL;	///< Task for VNC clients
+static TaskHandle_t	xTaskFrameUpdate   = NULL;	///< Task for framebuffer updates
+static TaskHandle_t	xTaskWSclient      = NULL;	///< Task for websocket clients
+
+SemaphoreHandle_t	SoundBell_lock     = NULL;	///< Lock for the bell sound.
+
+static QueueHandle_t	message_queue;			///< Websockets message queue
+const static int	message_queue_size = 5;		///< Message queue size
+
+struct strMessage {
+    int			length;				///< Length of the message
+    uint8_t		message[MESSAGE_BUFFER_SIZE];	///< The message
+};
+
+
+uint8_t			VNC_pointer_button = 0;
+uint16_t		VNC_pointer_x = 0;
+uint16_t		VNC_pointer_y = 0;
+
+
+/* Define size of each thread's stack */
+#define MIN_STACK_SIZE	3072
+
+
+/* Messages */
+char			server_ProtocolVersion[] = "RFB 003.003\n";
+char			bad_protocol[] = "Unsupported ProtocolVersion";
+char			server_busy[] = "Server is Busy";
+char			sound_bell[] = "\2";
+char			desktop_name[] = "MBSE BrewBoard";	// Hardcoded, don't change or the VNC webclient breaks.
+
+/* Frame Buffer */
+vnc_color_t		frame_buffer[CONFIG_VNC_SERVER_FRAME_HEIGHT+1][CONFIG_VNC_SERVER_FRAME_WIDTH+1];
+
+/* Calculate the number of tiles in the X and Y directions */
+#if (CONFIG_VNC_SERVER_FRAME_HEIGHT % TILE_SIZE) != 0
+#define NUM_TILES_Y_AXIS (CONFIG_VNC_SERVER_FRAME_HEIGHT/TILE_SIZE + 1)
+#define LAST_TILE_HEIGHT (CONFIG_VNC_SERVER_FRAME_HEIGHT % TILE_SIZE)
+#else
+#define NUM_TILES_Y_AXIS (CONFIG_VNC_SERVER_FRAME_HEIGHT/TILE_SIZE)
+#define LAST_TILE_HEIGHT TILE_SIZE
+#endif
+
+#if (CONFIG_VNC_SERVER_FRAME_WIDTH % TILE_SIZE) != 0
+#define NUM_TILES_X_AXIS (CONFIG_VNC_SERVER_FRAME_WIDTH/TILE_SIZE + 1)
+#define LAST_TILE_WIDTH (CONFIG_VNC_SERVER_FRAME_WIDTH % TILE_SIZE)
+#else
+#define NUM_TILES_X_AXIS (CONFIG_VNC_SERVER_FRAME_WIDTH/TILE_SIZE)
+#define LAST_TILE_WIDTH TILE_SIZE
+#endif
+
+/* Array for marking tiles that have been updated */
+int			tile_updated[NUM_TILES_Y_AXIS+1][NUM_TILES_X_AXIS+1];
+
+/* Conditional variable to signal that a client is connected and initialised */
+EventGroupHandle_t	xEventGroupVNC;
+const int 		VNC_CLIENT_UPDATE_REQ = BIT0;
+int			vnc_client_sock = -1;
+bool			vnc_client_connected = false;
+
+/* Variable for sounding the client's bell */
+int			SoundBellCount;
+
+/* Variables for the Websocket client */
+bool			VNC_WS_run = false;
+int			VNC_WS_num = -1;
+
+
+
+/* Variable to hold the frame format details */
+vnc_frame_format_t	frame_format = {CONFIG_VNC_SERVER_FRAME_WIDTH, CONFIG_VNC_SERVER_FRAME_HEIGHT, frame_buffer,
+                                   1,			// RGB332 server native.
+                                   0,			// RGB555
+                                   0,			// RGB565
+                                   0,			// BGR233
+                                   0,			// TRUECOLOR0888
+};
+
+
+
+#if 0
+void dump_msg(char *buf, uint16_t buflen, char *mode)
+{   
+    int i, l = 0;
+
+    printf("%s %d", mode, buflen);
+    for (i = 0; i < buflen; i++) {
+	if ((i % 16) == 0) {
+	    printf("\n%02d: ", l);
+	    l++;
+	}
+	printf("%02x ", buf[i]);
+    }
+    printf("\n");
+}
+#endif
+
+
+/* Structure to hold the encoding type details */
+volatile struct encoding_type_struct
+{
+    uint8_t		raw;
+    uint8_t		copy_rectangle;
+    uint8_t		rre;
+    uint8_t		corre;
+    uint8_t		hextile;
+} encoding_type;
+
+
+static int GetMessageData(int, char *, int);
+static int GenTileUpdateData(uint8_t *);
+void task_VNCserver(void *);
+void task_frame(void *);
+void task_WS(void *);
+
+
+
+/**
+ * @brief Serve a new VNC client.
+ */
+void vnc_netconn_serve(void);
+
+
+/* VNC startup. */
+void VncStartup(void)
+{
+    /* Initialise mutex & cond vars */
+    xEventGroupVNC = xEventGroupCreate();
+    SoundBell_lock = xSemaphoreCreateMutex();
+
+    xTaskCreate(&task_VNCserver, "VNCserver",    MIN_STACK_SIZE, NULL, 5, &xTaskClientHandler);
+    xTaskCreate(&task_frame,     "frame_update", MIN_STACK_SIZE, NULL, 6, &xTaskFrameUpdate);
+}
+
+
+/** 
+ * @brief Client Handler Task.
+ *
+ * This task handles the client initialisation sequence. Once the client
+ * is initialised this task handles all received messages from the client,
+ * but does not send any data to the client.
+ */
+void task_VNCserver(void *pvParameter)
+{
+    int			server_sock; 
+    struct sockaddr_in	server_addr;
+    struct sockaddr_in	client_addr;
+    socklen_t		client_addr_size;
+
+    ESP_LOGI(TAG, "Starting VNC server");
+
+    /* Clear the encoding type structure */
+    encoding_type.raw = 0;
+    encoding_type.copy_rectangle = 0;
+    encoding_type.rre = 0;
+    encoding_type.corre = 0;
+    encoding_type.hextile = 0;
+
+    /* Clear the sound bell counter */
+    SoundBellCount = 0;
+    VNC_pointer_button = 0;
+    VNC_pointer_x = VNC_pointer_y = 0;
+
+    /* Create socket for incomming connections */
+    if ((server_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+        ESP_LOGE(TAG, "socket() VNC function failed");
+        exit(1);
+    }
+
+    /* Construct the server address structure */
+    memset(&server_addr, 0, sizeof(server_addr));  /* Fill entire structure with 0's */
+    server_addr.sin_family      = AF_INET;         /* Internet address family */
+    server_addr.sin_addr.s_addr = INADDR_ANY;      /* Autofill with my IP address */
+    server_addr.sin_port        = htons(CONFIG_VNC_SERVER_PORT);
+
+    /* Bind socket to local address */
+    if (bind(server_sock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
+        ESP_LOGE(TAG, "bind() VNC function failed");
+        exit(1);
+    }
+
+    /* Set the socket to listen for incoming connections */
+    if (listen(server_sock, BACKLOG) < 0) {
+        ESP_LOGE(TAG, "listen() VNC function failed");
+        exit(1);
+    }
+
+    do {
+	vnc_client_sock = (accept(server_sock, (struct sockaddr *) &client_addr, &client_addr_size));
+	if (vnc_client_sock >= 0) {
+	    vnc_client_connected = true;
+	    vnc_netconn_serve();
+	    vnc_client_connected = false;
+	}
+	vTaskDelay((TickType_t)10); /* allows the freeRTOS scheduler to take over if needed */
+    } while (vnc_client_sock >= 0);
+}
+
+
+
+/**
+ * @brief Serve a new VNC client and handle the whole session.
+ */
+void vnc_netconn_serve(void)
+{
+    long int			temp_long;
+    char 			protocol_ver[8], message_buffer[MESSAGE_BUFFER_SIZE];
+    int				i, j, message_len, ProtocolOkay;
+    uint32_t			*ptr_to_uint32;
+    uint16_t			*ptr_to_uint16;
+    struct sockaddr_storage	addr;
+    socklen_t			len = sizeof addr;
+    char			ipstr[INET6_ADDRSTRLEN];
+
+    getpeername(vnc_client_sock, (struct sockaddr*)&addr, &len);
+    if (addr.ss_family == AF_INET) {
+	struct sockaddr_in *s = (struct sockaddr_in *)&addr;
+	inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
+    }
+    ESP_LOGI(TAG, "VNC new client %s socket %d", ipstr, vnc_client_sock);
+
+    /* ProtocolVersion Handshake - begin */
+    /* Send ProtocolVersion we want to use to client */
+    message_len = sprintf(message_buffer, "RFB 003.003\n");
+    if (send(vnc_client_sock, message_buffer, message_len, 0) != message_len) {
+	goto close_connection;
+    }
+
+    /* Receive ProtocolVersion the client wants to use */
+    if (GetMessageData(vnc_client_sock, &(message_buffer[0]), 12) == 0) {
+	goto close_connection;
+    }
+
+    /* Check this is acceptable (RFB 003.xxx is okay) */
+    ProtocolOkay = 1;
+    for (i = 0; i < 8; i++) {
+	if (message_buffer[i] != server_ProtocolVersion[i]) {
+	    ProtocolOkay = 0;
+	}
+
+        /* Store the protocol version - ignoring thr 'RFB ' part */
+        protocol_ver[i] = message_buffer[i + 4];
+    }
+    protocol_ver[7] = 0;
+    /* ProtocolVersion Handshake - end */
+
+    /* Authentication - begin */
+    /* Send Authentication scheme to be used to client */
+    if (ProtocolOkay == 0) {
+	/* ProtocolVerion is not okay */
+
+	/* Generate the Bad ProtocolVerion message */
+	ptr_to_uint32 = (uint32_t *) &(message_buffer[0]);
+	*ptr_to_uint32 = htonl(0);
+	ptr_to_uint32 = (uint32_t *) &(message_buffer[4]);
+	*ptr_to_uint32 = htonl(strlen(bad_protocol));
+	strcpy(&(message_buffer[8]), bad_protocol);
+
+	if (send(vnc_client_sock, message_buffer, 8 + strlen(bad_protocol), 0) != (8 + strlen(bad_protocol))) {
+	    printf("Call to send() 1 failed\n");
+	}
+	goto close_connection;
+
+    } else if (VNC_WS_num != -1) {
+	/* Busy with a websocket client */
+	ptr_to_uint32 = (uint32_t *) &(message_buffer[0]);
+	*ptr_to_uint32 = htonl(0);
+	ptr_to_uint32 = (uint32_t *) &(message_buffer[4]);
+	*ptr_to_uint32 = htonl(strlen(server_busy));
+	strcpy(&(message_buffer[8]), server_busy);
+
+	if (send(vnc_client_sock, message_buffer, 8 + strlen(server_busy), 0) != (8 + strlen(server_busy))) {
+	    printf("Call to send() 1 failed\n");
+	}
+	goto close_connection;
+
+    } else {
+	/* ProtocolVerion is okay - connect with no authentication*/
+
+	/* Generate the No Authentication message */
+	ptr_to_uint32 = (uint32_t *) &(message_buffer[0]);
+	*ptr_to_uint32 = htonl((uint32_t)1);
+
+	if (send(vnc_client_sock, message_buffer, 4, 0) != 4) {
+	    goto close_connection;
+	}
+    }
+    /* Authentication - end */
+
+    /* ClientInitialisation - begin */
+    /* Receive initialisation message from client (1 byte) */
+    if (GetMessageData(vnc_client_sock, &(message_buffer[0]), 1) == 0) {
+	goto close_connection;
+    }
+    /* Do nothing with this as we only support 1 Client at a time */
+    /* ClientInitialisation - end */
+
+    /* ServerInitialisation - begin */
+
+    /* Initial default RGB332 */
+    Red_Max     = RED_MAX;
+    Green_Max   = GREEN_MAX;
+    Blue_Max    = BLUE_MAX;
+    Red_Shift   = RED_SHIFT;
+    Green_Shift = GREEN_SHIFT;
+    Blue_Shift  = BLUE_SHIFT;
+    AltPixels   = false;
+
+    /* Create Initialisation message for client */
+    ptr_to_uint16 = (uint16_t *) &(message_buffer[0]);
+    *ptr_to_uint16 = htons((uint16_t)CONFIG_VNC_SERVER_FRAME_WIDTH);
+
+    ptr_to_uint16 = (uint16_t *) &(message_buffer[2]);
+    *ptr_to_uint16 = htons((uint16_t)CONFIG_VNC_SERVER_FRAME_HEIGHT);
+
+    message_buffer[4] = (uint8_t)BITS_PER_PIXEL;
+    message_buffer[5] = (uint8_t)PIXEL_DEPTH;
+    message_buffer[6] = (uint8_t)BIG_ENDIAN_FLAG;
+    message_buffer[7] = (uint8_t)TRUE_COLOUR_FLAG;
+
+    ptr_to_uint16 = (uint16_t *) &(message_buffer[8]);
+    *ptr_to_uint16 = htons(Red_Max);
+
+    ptr_to_uint16 = (uint16_t *) &(message_buffer[10]);
+    *ptr_to_uint16 = htons(Green_Max);
+
+    ptr_to_uint16 = (uint16_t *) &(message_buffer[12]);
+    *ptr_to_uint16 = htons(Blue_Max);
+
+    message_buffer[14] = Red_Shift;
+    message_buffer[15] = Green_Shift;
+    message_buffer[16] = Blue_Shift;
+
+    ptr_to_uint32 = (uint32_t *) &(message_buffer[20]);
+    *ptr_to_uint32 = htonl(strlen(desktop_name));
+    strcpy(&(message_buffer[24]), desktop_name);
+
+    if (send(vnc_client_sock, message_buffer, 24 + strlen(desktop_name), 0) != (24 + strlen(desktop_name))) {
+	printf("Call to send() 3 failed\n");
+    }
+    /* ServerInitialisation - end */
+
+    /* Cancel any outstanding Sound Bell requests */
+    if (xSemaphoreTake(SoundBell_lock, 10) == pdTRUE) {
+	SoundBellCount = 0;
+	xSemaphoreGive(SoundBell_lock);
+    }
+
+    ESP_LOGI(TAG, "VNC client connected (RFB Protocol Ver: %s)", protocol_ver);
+
+    /* Main message handling loop */
+    while(1) {
+	int num_of_encodings;
+
+	/* Receive 1st byte of message from client */
+	if (GetMessageData(vnc_client_sock, &(message_buffer[0]), 1) == 0) {
+	    goto close_connection;	/* Connection lost */
+	}
+
+	switch(message_buffer[0]) {
+            case SET_PIXEL_FORMAT:
+
+                /* Get the remainder (19 bytes) of message */
+                if (GetMessageData(vnc_client_sock, &(message_buffer[1]), 19) == 0) {
+                    goto close_connection;
+                }
+
+                /* Check pixel format is as expected */
+                i = 0;
+                if (message_buffer[4] != BITS_PER_PIXEL) {
+		    ESP_LOGI(TAG, "SetPixelFormat client wants %d bits-per-pixel", message_buffer[4]);
+                    i++;
+                }
+
+                if (message_buffer[5] != PIXEL_DEPTH) {
+		    ESP_LOGI(TAG, "SetPixelFormat client wants %d pixel-depth", message_buffer[5]);
+                    i++;
+                }
+
+                if ((message_buffer[7] & 0x01) != TRUE_COLOUR_FLAG) {
+		    ESP_LOGI(TAG, "SetPixelFormat client wants %d true-colour-flag", message_buffer[7]);
+                    i++;
+                }
+
+                ptr_to_uint16 = (uint16_t *)&(message_buffer[8]);
+                if (htons(*ptr_to_uint16) != Red_Max) {
+		    Red_Max = htons(*ptr_to_uint16);
+		    AltPixels = true;
+		    ESP_LOGI(TAG, "SetPixelFormat client granted %d red-max", Red_Max);
+                }
+
+                ptr_to_uint16 = (uint16_t *)&(message_buffer[10]);
+                if (htons(*ptr_to_uint16) != Green_Max) {
+		    Green_Max = htons(*ptr_to_uint16);
+		    AltPixels = true;
+		    ESP_LOGI(TAG, "SetPixelFormat client granted %d green-max", Green_Max);
+                }
+
+                ptr_to_uint16 = (uint16_t *)&(message_buffer[12]);
+                if (htons(*ptr_to_uint16) != Blue_Max) {
+		    Blue_Max = htons(*ptr_to_uint16);
+		    AltPixels = true;
+		    ESP_LOGI(TAG, "SetPixelFormat client granted %d blue-max", Blue_Max);
+                }
+
+                if (message_buffer[14] != Red_Shift) {
+		    Red_Shift = message_buffer[14];
+		    AltPixels = true;
+		    ESP_LOGI(TAG, "SetPixelFormat client granted %d red-shift", Red_Shift);
+                }
+
+                if (message_buffer[15] != Green_Shift) {
+		    Green_Shift = message_buffer[15];
+		    AltPixels = true;
+		    ESP_LOGI(TAG, "SetPixelFormat client granted %d green-shift", Green_Shift);
+                }
+
+                if (message_buffer[16] != Blue_Shift) {
+		    Blue_Shift = message_buffer[16];
+		    AltPixels = true;
+		    ESP_LOGI(TAG, "SetPixelFormat client granted %d blue-shift", Blue_Shift);
+                }
+
+                if (i) {
+		    ESP_LOGI(TAG, "SetPixelFormat %d errors, disconnect client", i);
+                     ESP_LOGI(TAG, "Ensure the 'Auto select' is not enabled in your vncviewer options,");
+                    goto close_connection_quietly;
+                }
+
+                break;
+
+            case FIX_COLOUR_MAP_ENTRIES:
+printf("fix colormap entries\n");
+                /* Not supported, just get the data from the buffer and ignore it */
+
+                /* Get the next 5 bytes of message */
+                if (GetMessageData(vnc_client_sock, &(message_buffer[1]), 5) == 0) {
+                    goto close_connection;
+                }
+
+                /* Calculate how many colour entries are in the buffer */
+                i = message_buffer[4]*255 + message_buffer[5];
+                i *= 6;
+
+                /* Get this amount of data from the buffer */
+                for (j = 0; j < i; j++) {
+                    if (GetMessageData(vnc_client_sock, &(message_buffer[6]), 6) == 0) {
+                        goto close_connection;
+                    }
+                }
+
+                break;
+
+            case SET_ENCODINGS:
+
+                /* Get the next 3 bytes of message */
+                if (GetMessageData(vnc_client_sock, &(message_buffer[1]), 3) == 0) {
+                    goto close_connection;
+                }
+
+                num_of_encodings = message_buffer[2]*255 + message_buffer[3];
+
+                /* Get the remainder of message */
+                if (GetMessageData(vnc_client_sock, &(message_buffer[0]), 4 * num_of_encodings) == 0) {
+                    goto close_connection;
+                }
+
+                /* Clear the encoding type structure */
+                encoding_type.raw = 0;
+                encoding_type.copy_rectangle = 0;
+                encoding_type.rre = 0;
+                encoding_type.corre = 0;
+                encoding_type.hextile = 0;
+
+                for (i = 0; i < num_of_encodings; i++) {
+                    switch(message_buffer[3 + (i*4)]) {
+                    case 0:  /* Raw encoding */
+                        encoding_type.raw = i + 1;
+                        break;
+                    case 1:  /* Copy rectangle encoding */
+                        encoding_type.copy_rectangle = i + 1;
+                        break;
+                    case 2:  /* RRE encoding */
+                        encoding_type.rre = i + 1;
+                        break;
+                    case 4:  /* CoRRE encoding */
+                        encoding_type.corre = i + 1;
+                        break;
+                    case 5:  /* Hextile encoding */
+                        encoding_type.hextile = i + 1;
+                        break;
+                    default:  /* Unknown coding type - do nothing */
+                        break;
+                    }
+                }
+
+                if (encoding_type.corre) {
+		    ESP_LOGI(TAG, "SetEncodings use CORRE");
+		} else {
+		    ESP_LOGI(TAG, "SetEncodings use RAW");
+		}
+                break;
+
+            case FRAME_BUFFER_UPDATE_REQ:
+                /* Get the remainder of message (9 bytes) */
+                if (GetMessageData(vnc_client_sock, &(message_buffer[1]), 9) == 0) {
+                    goto close_connection;
+                }
+
+                if (!message_buffer[1]) {
+                    /* Non-incremental mode - mark the squares that need to be updated */
+                    for (i = (message_buffer[2]*255 + message_buffer[3])/TILE_SIZE;
+                         i <= (message_buffer[2]*255 + message_buffer[3] + message_buffer[6]*255 + message_buffer[7])/TILE_SIZE;
+                         i++) {
+                        for (j = (message_buffer[4]*255 + message_buffer[5])/TILE_SIZE;
+                             j <= (message_buffer[4]*255 + message_buffer[5] + message_buffer[8]*255 + message_buffer[9])/TILE_SIZE;
+                             j++) {
+                            tile_updated[j][i] = 1;
+                        }
+                    }
+                }
+
+                /* Signal that there is now a pending update request */
+                xEventGroupSetBits(xEventGroupVNC, VNC_CLIENT_UPDATE_REQ);
+		break;
+
+            case KEY_EVENT:
+                /* Handle the key event, ignored for brewboard */
+                /* Get the remainder of message (7 bytes) */
+                if (GetMessageData(vnc_client_sock, &(message_buffer[1]), 7) == 0) {
+                    goto close_connection;
+                }
+                break;
+
+            case POINTER_EVENT:
+                /* Handle the pointer event, simulate the touch screen. */
+                /* Get the remainder of message (5 bytes) */
+                if (GetMessageData(vnc_client_sock, &(message_buffer[1]), 5) == 0) {
+                    goto close_connection;
+                }
+		/* Set global variables that will be read by another task. */
+		VNC_pointer_button = message_buffer[1];
+		VNC_pointer_x = message_buffer[2]*255 + message_buffer[3];
+		VNC_pointer_y = message_buffer[4]*255 + message_buffer[5];
+                break;
+
+            case CLIENT_CUT_TEXT:
+                /* Handle the client has cut text event */
+                /* Current we just get and discard the data */
+                /* Get the next 7 bytes of the message */
+                if (GetMessageData(vnc_client_sock, &(message_buffer[1]), 7) == 0) {
+                    goto close_connection;
+                }
+
+                ptr_to_uint32 = (uint32_t *)&(message_buffer[4]);
+                temp_long = htonl(*ptr_to_uint32);
+
+                while (temp_long > 0) {
+                    /* Get the text in chunks MESSAGE_BUFFER_SIZE-1 characters */
+                    if (temp_long > MESSAGE_BUFFER_SIZE-2) {
+                        if (GetMessageData(vnc_client_sock, &(message_buffer[0]), MESSAGE_BUFFER_SIZE-1) == 0) {
+                            goto close_connection;
+                        }
+
+                        message_buffer[MESSAGE_BUFFER_SIZE-1] = 0;
+                        temp_long -= (MESSAGE_BUFFER_SIZE-1);
+                    } else {
+                        if (GetMessageData(vnc_client_sock, &(message_buffer[0]), temp_long) == 0) {
+                            goto close_connection;
+                        }
+
+                        message_buffer[temp_long] = 0;
+                        temp_long = 0;
+                    }
+                }
+
+                break;
+
+            default:
+		ESP_LOGI(TAG, "Unknown message %d from client", message_buffer[0]);
+        }
+
+	vTaskDelay( (TickType_t)1);
+    }
+
+close_connection:
+    ESP_LOGI(TAG, "VNC client disconnected");
+
+close_connection_quietly:
+
+    /* Cancel any outstanding update requests */
+    xEventGroupClearBits(xEventGroupVNC, VNC_CLIENT_UPDATE_REQ);
+    close(vnc_client_sock);
+}
+
+
+
+/**
+ * @brief Frame update task. This thread handles the sending of all frame
+ *        update data to the client and sends the 'ring bell' message to
+ *        the client when required.
+ *        Works for VNC port connected clients and Websocket clients.
+ * @param pvParameter Ignored
+ */
+void task_frame(void *pvParameter)
+{
+    int			i, j, x_pos, y_pos, packet_length, num_updated_tiles;
+    uint16_t		*ptr_to_uint16;
+    /* These are declared static so they don't use thread stack memory */
+    static uint8_t	FramebufferUpdate_msg[4 + 12 + TILE_SIZE*TILE_SIZE*BITS_PER_PIXEL/8 + 1460];
+    static int		FrameBufferPtr;  /* Pointer to next space in buffer */
+    static int		tile_updated_local[NUM_TILES_Y_AXIS][NUM_TILES_X_AXIS];
+
+    ESP_LOGI(TAG, "Starting VNC frame updater");
+
+    while(1) {
+        /* Wait until client sends a frame update request */
+wait_for_client:
+	xEventGroupWaitBits(xEventGroupVNC, VNC_CLIENT_UPDATE_REQ, pdFALSE, pdFALSE, portMAX_DELAY);
+
+        /* Copy tile_updated array to local version and clear copied tiles */
+	vTaskSuspendAll();
+        num_updated_tiles = 0;
+        for (i = 0; i < NUM_TILES_Y_AXIS; i++) {
+            for (j = 0; j < NUM_TILES_X_AXIS; j++) {
+                if (tile_updated[i][j]) {
+                    tile_updated_local[i][j] = 1;
+                    tile_updated[i][j] = 0;
+                    num_updated_tiles++;  /* Keep count of the updated tiles */
+                }
+            }
+        }
+	xTaskResumeAll();
+
+        if (num_updated_tiles) {
+	    /* Cancel update request */
+	    xEventGroupClearBits(xEventGroupVNC, VNC_CLIENT_UPDATE_REQ);
+            /* Fill in constant parts of FramebufferUpdate Message */
+            FramebufferUpdate_msg[0] = 0;  /* Message-type */
+            FramebufferUpdate_msg[1] = 0;  /* Padding */
+            ptr_to_uint16 = (uint16_t *) &(FramebufferUpdate_msg[2]);
+            *ptr_to_uint16 = htons(num_updated_tiles);  /* Number-of-rectangles */
+            FrameBufferPtr = 4;
+
+            for (y_pos = 0; y_pos < NUM_TILES_Y_AXIS; y_pos++) {
+                for (x_pos = 0; x_pos < NUM_TILES_X_AXIS; x_pos++) {
+                    if (tile_updated_local[y_pos][x_pos]) {
+                        /* Send current square data to client */
+
+                        /* x-position */
+                        FramebufferUpdate_msg[FrameBufferPtr+0] = (x_pos * TILE_SIZE) / 256;
+                        FramebufferUpdate_msg[FrameBufferPtr+1] = (x_pos * TILE_SIZE) % 256;
+
+                        /* y-position */
+                        FramebufferUpdate_msg[FrameBufferPtr+2] = (y_pos * TILE_SIZE) / 256;
+                        FramebufferUpdate_msg[FrameBufferPtr+3] = (y_pos * TILE_SIZE) % 256;
+
+
+                        /* Set width of tile in packet */
+                        if (x_pos == (NUM_TILES_X_AXIS -1)) {
+                            /* Last tile in X-axis */
+                            FramebufferUpdate_msg[FrameBufferPtr+4] = LAST_TILE_WIDTH / 256;
+                            FramebufferUpdate_msg[FrameBufferPtr+5] = LAST_TILE_WIDTH % 256;
+                        } else {
+                            FramebufferUpdate_msg[FrameBufferPtr+4] = TILE_SIZE / 256;
+                            FramebufferUpdate_msg[FrameBufferPtr+5] = TILE_SIZE % 256;
+                        }
+
+                        if (y_pos == (NUM_TILES_Y_AXIS -1)) {
+                            /* Last tile in Y-axis */
+                            FramebufferUpdate_msg[FrameBufferPtr+6] = LAST_TILE_HEIGHT / 256;
+                            FramebufferUpdate_msg[FrameBufferPtr+7] = LAST_TILE_HEIGHT % 256;
+                        } else {
+                            FramebufferUpdate_msg[FrameBufferPtr+6] = TILE_SIZE / 256;
+                            FramebufferUpdate_msg[FrameBufferPtr+7] = TILE_SIZE % 256;
+                        }
+
+                        /* Generate the packet data for this tile */
+                        packet_length = GenTileUpdateData(&(FramebufferUpdate_msg[FrameBufferPtr]));
+
+                        /* Send the packet data for this tile to the client */
+                        FrameBufferPtr += packet_length;
+
+                        if (FrameBufferPtr > 1460) {
+                            /* Send the data to the client */
+			    if (VNC_WS_num != -1) {
+				ws_server_send_bin_client(VNC_WS_num, (char *)FramebufferUpdate_msg, FrameBufferPtr);
+			    }
+			    if (vnc_client_connected) {
+                            	if (send(vnc_client_sock, FramebufferUpdate_msg, FrameBufferPtr, 0) != FrameBufferPtr) {
+                            	    goto wait_for_client;
+                            	}
+			    }
+                            FrameBufferPtr = 0;
+                        }
+
+                        tile_updated_local[y_pos][x_pos] = 0;  /* Clear the update bit for this square */
+                    }
+                }
+            }
+
+            if (FrameBufferPtr > 0) {
+                /* Last data for this update, send it to the client */
+		if (VNC_WS_num != -1) {
+		    ws_server_send_bin_client(VNC_WS_num, (char *)FramebufferUpdate_msg, FrameBufferPtr);
+		}
+		if (vnc_client_connected) {
+                    if (send(vnc_client_sock, FramebufferUpdate_msg, FrameBufferPtr, 0) != FrameBufferPtr) {
+                    	goto wait_for_client;
+                    }
+		}
+
+                FrameBufferPtr = 0;
+            }
+
+        } else { /* if (num_updated_tiles) */
+            /* There was no new display data to send to the client */
+            /* Sleep for 1/20th second before checking again */
+	    vTaskDelay(20 / portTICK_PERIOD_MS);
+	}
+
+        /* Check for sound bell event */
+	if (xSemaphoreTake(SoundBell_lock, 10) == pdTRUE) {
+            if (SoundBellCount) {
+            	--SoundBellCount;
+		xSemaphoreGive(SoundBell_lock);
+
+		if (vnc_client_connected) {
+            	    if (send(vnc_client_sock, sound_bell, 1, 0) != 1) {
+                    	goto wait_for_client;
+            	    }
+		}
+		if (VNC_WS_num != -1) {
+		    ws_server_send_bin_client(VNC_WS_num, sound_bell, 1);
+		}
+            } else {
+	    	xSemaphoreGive(SoundBell_lock);
+	    }
+	}
+    }
+}
+
+
+
+/**
+ * Convert a framebuffer pixel to BGR233 if needed.
+ * @param pixel The 8 bit pixel value
+ * @return Then unchanged or changed pixel.
+ */
+vnc_color_t IRAM_ATTR PixelConvert(vnc_color_t pixel)
+{
+    if (!AltPixels)
+    	return pixel;
+
+    return (((pixel >> RED_SHIFT) & RED_MAX) << Red_Shift) | 
+	   (((pixel >> GREEN_SHIFT) & GREEN_MAX) << Green_Shift) | 
+	   (((pixel >> BLUE_SHIFT) & BLUE_MAX) << Blue_Shift);
+}
+
+
+
+/** 
+ * @brief Generate tile update data function
+ *
+ *        This function is called by the frame_update thread to generate the message
+ *        data for a tile to send to the client.  This function expects the
+ *        x-position, y-position, width and height fields of the buffer to be filled
+ *        in before it is called.
+ *
+ *        The format of the buffer is:
+ *        packet_buffer[0:1] - x-position of tile
+ *        packet_buffer[2:3] - y-position of tile
+ *        packet_buffer[4:5] - width of tile
+ *        packet_buffer[6:7] - height of tile
+ *        packet_buffer[8 onwards] - Pixel data for the tile
+ *
+ *        The pixel data will be encoded with CoRRE encoding (if the CDL option is
+ *        enabled and the client can handle it) or RAW encoding if that is smaller
+ *        than CoRRE encoding for that particular tile.
+ *
+ * @param  packet_buffer  Buffer to store tile data
+ * @return Length of generated data in bytes
+ */
+static int GenTileUpdateData(uint8_t *packet_buffer)
+{
+    uint16_t		x_pos, y_pos;
+    int			i, j;
+    int			tile_width, tile_height;
+    int			packet_length;
+    static vnc_color_t	tile_buffer[TILE_SIZE][TILE_SIZE];  /* Buffer to hold tile to be encoded */
+    vnc_color_t		pixel_colour;
+    vnc_color_t		bg_colour;
+    int			no_of_subrects, subrect_width, subrect_height;
+    int			k, l;
+
+    no_of_subrects = 0;  /* Set to no sub-rectangles to start with */
+    packet_length = 20-4+(Bits_Per_Pixel/8);  /* Set to minimum packet length to start with */
+
+    /* Get the X and Y positions of this tile from the packet buffer */
+    x_pos = packet_buffer[0] * 256 + packet_buffer[1];
+    y_pos = packet_buffer[2] * 256 + packet_buffer[3];
+
+    /* Get the tile width and height from the packet buffer */
+    tile_width = packet_buffer[4] * 256 + packet_buffer[5];
+    tile_height = packet_buffer[6] * 256 + packet_buffer[7];
+
+    /* Set the encoding type to RRE  */
+    if (!encoding_type.corre) {
+        /* CoRRE encoding is not supported - just use raw encoding */
+        goto use_raw_encoding;
+    }
+
+    /* Set encoding type to CoRRE encoding in packet buffer */
+    packet_buffer[8+0] = 0;
+    packet_buffer[8+1] = 0;
+    packet_buffer[8+2] = 0;
+    packet_buffer[8+3] = 4;
+
+    /* Copy tile from the main frame buffer to the local tile buffer */
+    for (i = 0; i < tile_height; i++) {
+        for (j = 0; j < tile_width; j++) {
+            tile_buffer[i][j] = frame_buffer[y_pos + i][x_pos + j];
+        }
+    }
+
+    /* Find the background colour */
+    /* We just assume the (0, 0) pixel in the tile is the bgcolour */
+    /* Its quick!!! */
+    bg_colour = frame_buffer[y_pos][x_pos];
+
+    /* Set the background colour in the packet buffer */
+    if (Bits_Per_Pixel == 8) {
+    	packet_buffer[16] = PixelConvert(bg_colour); /* (vnc_color_t) bg_colour; */
+    } else {
+	packet_buffer[16]   = COLOUR2BYTE0(bg_colour);
+	packet_buffer[16+1] = COLOUR2BYTE1(bg_colour);
+    }
+
+#ifdef CYGNUM_VNC_SERVER_CORRE_ENCODING_HACK
+    /* Add an initial sub-rectangle to paint the background the background colour */
+    /* This is required because of a known bug in the VNC viewer (x86 version) */
+//#if BITS_PER_PIXEL == 8
+    packet_buffer[packet_length] = (vnc_color_t) bg_colour;
+    packet_length++;
+//#endif
+//#if BITS_PER_PIXEL == 16
+//    packet_buffer[packet_length]   = packet_buffer[16];
+//    packet_buffer[packet_length+1] = packet_buffer[16+1];
+//    packet_length += 2;
+//#endif
+    packet_buffer[packet_length]   = (uint8_t) 0;  /* Sub-rect x-pos */
+    packet_buffer[packet_length+1] = (uint8_t) 0;  /* Sub-rect y-pos*/
+    packet_buffer[packet_length+2] = (uint8_t) tile_width;  /* Sub-rect width*/
+    packet_buffer[packet_length+3] = (uint8_t) tile_height;  /* Sub-rect height*/
+    packet_length += 4;
+    no_of_subrects++;  /* Increment sub-rectangle count */
+#endif
+
+    /* Scan trough tile and find sub-rectangles */
+    for (i = 0; i < tile_height; i++) {
+        for (j = 0; j < tile_width; j++) {
+            if (tile_buffer[i][j] != bg_colour) {
+                /* This is a non-background pixel */
+                subrect_width = 1;
+                pixel_colour = tile_buffer[i][j];
+
+                /* Extend the sub-rectangle to its maximum width */
+                for (subrect_width = 1; subrect_width <= tile_width-j-1; subrect_width++) {
+                    if (tile_buffer[i][j+subrect_width] != pixel_colour) {
+                        goto got_subrect_width;
+                    }
+                }
+
+got_subrect_width:
+
+                /* Extend the sub-rectangle to its maximum height */
+                for (subrect_height=1; subrect_height <= tile_height-i-1; subrect_height++) {
+                    for (k = j; k < j+subrect_width; k++) {
+                        if (tile_buffer[i+subrect_height][k] != pixel_colour) {
+                            goto got_subrect_height;
+                        }
+                    }
+                }
+
+got_subrect_height:
+
+                /* Delete the pixels for the sub-rectangle from the sub-rectangle */
+                for (k = i; k < i+subrect_height; k++) {
+                    for (l = j; l < j+subrect_width; l++) {
+                        tile_buffer[k][l] = bg_colour;
+                    }
+                }
+
+                /* Append new sub-rectangle data to the packet buffer */
+		if (Bits_Per_Pixel == 8) {
+                    packet_buffer[packet_length] = PixelConvert(pixel_colour); //  (vnc_color_t) pixel_colour;
+                    packet_length++;
+		} else {
+		    packet_buffer[packet_length]   = COLOUR2BYTE0(pixel_colour);
+		    packet_buffer[packet_length+1] = COLOUR2BYTE1(pixel_colour);
+		    packet_length += 2;
+		}
+
+                packet_buffer[packet_length] = (uint8_t) j;  /* Sub-rect x-pos */
+                packet_length++;
+
+                packet_buffer[packet_length] = (uint8_t) i;  /* Sub-rect y-pos*/
+                packet_length++;
+
+                packet_buffer[packet_length] = (uint8_t) subrect_width;  /* Sub-rect width*/
+                packet_length++;
+
+                packet_buffer[packet_length] = (uint8_t) subrect_height;  /* Sub-rect height*/
+                packet_length++;
+
+                no_of_subrects++;  /* Increment sub-rectangle count */
+
+                if (packet_length >= 12 + tile_height*tile_width*(BITS_PER_PIXEL/8) - 6) {
+                    /* The next sub-rectangle will make the packet size   */
+                    /* larger than a rew encoded packet - so just use raw */
+                    goto use_raw_encoding;
+                }
+            }
+        }
+    }
+
+    /* Fill in no_of_sub-rectangles field in packet buffer */
+    packet_buffer[12+0] = 0;
+    packet_buffer[12+1] = 0;
+    packet_buffer[12+2] = no_of_subrects / 256;
+    packet_buffer[12+3] = no_of_subrects % 256;
+
+    /* CoRRE data encoding for tile complete */
+    return packet_length;
+
+use_raw_encoding:
+
+    /* Create packet data using RAW encoding */
+    for (i = 0; i < tile_height; i++) {
+        for (j = 0; j < tile_width; j++) {
+	    if (Bits_Per_Pixel == 8) {
+             	packet_buffer[12 + tile_width * i + j] = PixelConvert(frame_buffer[y_pos + i][x_pos + j]);
+	    } else {
+		packet_buffer[12 + 2 * tile_width * i + 2*j]    = COLOUR2BYTE0(frame_buffer[y_pos + i][x_pos + j]);
+		packet_buffer[12 + 2 * tile_width * i + 2*j+ 1] = COLOUR2BYTE1(frame_buffer[y_pos + i][x_pos + j]);
+	    }
+        }
+    }
+
+    /* Set the encoding type to raw */
+    packet_buffer[8+0] = 0;
+    packet_buffer[8+1] = 0;
+    packet_buffer[8+2] = 0;
+    packet_buffer[8+3] = 0;
+
+    return (12 + tile_width*tile_height*(Bits_Per_Pixel/8));
+}
+
+
+
+/**
+ * @brief Get message data function
+ *        This function is called by the client_handler thread to get data
+ *        from the client's socket.
+ *
+ * @param socket_fd    File descriptor of the socket to get the data from.
+ * @param *buffer      Buffer to store received data in.
+ * @param num_bytes    Number of bytes to attempt to get.
+ *
+ * @return  1 on sucessfull completion - 0 on error.
+ */
+static int GetMessageData(int socket_fd, char *buffer, int num_bytes)
+{
+    int bytes_rxd;
+    int message_len = 0;
+
+    while (message_len < num_bytes) {
+        if ((bytes_rxd = recv(socket_fd, buffer, num_bytes, 0)) <= 0) {
+            return 0;
+        }
+        message_len += bytes_rxd;
+    }
+
+    return 1;
+}
+
+
+
+void VncCls(vnc_color_t color)
+{
+    /* Clear the frame buffer */
+    int i, j;
+
+    for (i = 0; i < CONFIG_VNC_SERVER_FRAME_HEIGHT; i++) {
+        for (j = 0; j < CONFIG_VNC_SERVER_FRAME_WIDTH; j++) {
+	    VncDrawPixel(j, i, color);
+        }
+    }
+}
+
+
+
+void IRAM_ATTR VncDrawPixel(uint16_t x, uint16_t y, vnc_color_t color)
+{
+    if (x >= CONFIG_VNC_SERVER_FRAME_WIDTH || y >= CONFIG_VNC_SERVER_FRAME_HEIGHT) {
+	printf("write_frame(%d, %d) tile %d/%d\n", x, y, x/TILE_SIZE, y/TILE_SIZE);
+	return;
+    }
+
+    /* Set that pixel to 'colour' */
+    frame_buffer[y][x] = color;
+
+    /* Mark the tile for update */
+    tile_updated[y/TILE_SIZE][x/TILE_SIZE] = 1;
+}
+
+
+
+void IRAM_ATTR VncDrawHorzLine(uint16_t x1, uint16_t x2, uint16_t y, vnc_color_t color)
+{
+    int i;
+
+    /* Draw the line */
+    for (i = x1; i <= x2; i++) {
+	VncDrawPixel(i, y, color);
+    }
+}
+
+
+
+void IRAM_ATTR VncDrawVertLine(uint16_t x, uint16_t y1, uint16_t y2, vnc_color_t color)
+{
+    int i;
+
+    /* Draw the line */
+    for (i = y1; i <= y2; i++) {
+	VncDrawPixel(x, i, color);
+    }
+}
+
+
+
+void IRAM_ATTR VncFillRect(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, vnc_color_t color)
+{
+    /* Draw a solid rectangle */
+    int i, j;
+
+    for (i = y1; i <= y2; i++) {
+        for (j = x1; j <= x2; j++) {
+	    VncDrawPixel(j, i, color);
+        }
+    }
+}
+
+
+
+void VncSoundBell(void)
+{
+    if (xSemaphoreTake(SoundBell_lock, 10) == pdTRUE) {
+    	SoundBellCount++;
+	xSemaphoreGive(SoundBell_lock);
+    }
+}
+
+
+
+/**
+ * @brief Websocket task. Created on a websocket connection.
+ */
+void task_WS(void *pvParameter)
+{
+    int			i, j, num_of_encodings, rest_time;
+    uint16_t		*ptr_to_uint16;
+    struct strMessage   _msg;
+
+    // Do pin/pong.
+
+    VNC_WS_run = true;
+    rest_time = 0;
+
+    while (VNC_WS_run) {
+
+	/*
+	 * Wait until something is received from the client. Exit the queue receiver
+	 * each 5 milliseconds to allow to evaluate the VNC_WS_run flag and to allow
+	 * a clean exit of this task.
+	 */
+	if (xQueueReceive(message_queue, &_msg, 5 / portTICK_PERIOD_MS) == pdTRUE) {
+//	    dump_msg((char *)_msg.message, _msg.length, "pull");
+
+	    switch (_msg.message[0]) {
+
+	    	case SET_PIXEL_FORMAT:
+//		    printf("SET_PIXEL_FORMAT\n");
+		    /* Check total message length */
+		    if (_msg.length >= 20) {
+			/* Check pixel format is as expected */
+			i = 0;
+			if (_msg.message[4] != BITS_PER_PIXEL) {
+			    ESP_LOGI(TAG, "SetPixelFormat client wants %d bits-per-pixel", _msg.message[4]);
+			    i++;
+			}
+
+			if (_msg.message[5] != PIXEL_DEPTH) {
+			    ESP_LOGI(TAG, "SetPixelFormat client wants %d pixel-depth", _msg.message[5]);
+			    i++;
+			}
+
+			if ((_msg.message[7] & 0x01) != TRUE_COLOUR_FLAG) {
+			    ESP_LOGI(TAG, "SetPixelFormat client wants %d true-colour-flag", _msg.message[7]);
+			    i++;
+			}
+
+			ptr_to_uint16 = (uint16_t *)&(_msg.message[8]);
+			if (htons(*ptr_to_uint16) != Red_Max) {
+			    Red_Max = htons(*ptr_to_uint16);
+			    AltPixels = true;
+			    ESP_LOGI(TAG, "SetPixelFormat client granted %d red-max", Red_Max);
+			}
+
+			ptr_to_uint16 = (uint16_t *)&(_msg.message[10]);
+			if (htons(*ptr_to_uint16) != Green_Max) {
+			    Green_Max = htons(*ptr_to_uint16);
+			    AltPixels = true;
+			    ESP_LOGI(TAG, "SetPixelFormat client granted %d green-max", Green_Max);
+			}
+
+			ptr_to_uint16 = (uint16_t *)&(_msg.message[12]);
+			if (htons(*ptr_to_uint16) != Blue_Max) {
+			    Blue_Max = htons(*ptr_to_uint16);
+			    AltPixels = true;
+			    ESP_LOGI(TAG, "SetPixelFormat client granted %d blue-max", Blue_Max);
+			}
+
+			if (_msg.message[14] != Red_Shift) {
+			    Red_Shift = _msg.message[14];
+			    AltPixels = true;
+			    ESP_LOGI(TAG, "SetPixelFormat client granted %d red-shift", Red_Shift);
+			}
+
+			if (_msg.message[15] != Green_Shift) {
+			    Green_Shift = _msg.message[15];
+			    AltPixels = true;
+			    ESP_LOGI(TAG, "SetPixelFormat client granted %d green-shift", Green_Shift);
+			}
+
+			if (_msg.message[16] != Blue_Shift) {
+			    Blue_Shift = _msg.message[16];
+			    AltPixels = true;
+			    ESP_LOGI(TAG, "SetPixelFormat client granted %d blue-shift", Blue_Shift);
+			}
+
+			if (i) {
+			    ESP_LOGI(TAG, "SetPixelFormat %d errors, disconnect client", i);
+			    ESP_LOGI(TAG, "Ensure the 'Auto select' is not enabled in your vncviewer options,");
+			    _msg.length = 0;
+			    ws_server_remove_client(VNC_WS_num);
+			}
+		    }
+		    break;
+
+	    	case FIX_COLOUR_MAP_ENTRIES:
+		    printf("FIX_COLOUR_MAP_ENTRIES\n");
+		    break;
+
+	    	case SET_ENCODINGS:
+		    if (_msg.length >= 3) {
+			// Get the total encodings
+			num_of_encodings = _msg.message[2]*255 + _msg.message[3];
+
+			/* Clear the encoding type structure */
+			encoding_type.raw = 0;
+			encoding_type.copy_rectangle = 0;
+			encoding_type.rre = 0;
+			encoding_type.corre = 0;
+			encoding_type.hextile = 0;
+
+			for (i = 0; i < num_of_encodings; i++) {
+			    switch(_msg.message[3 + (i*4)]) {
+				case 0:  /* Raw encoding */
+					encoding_type.raw = i + 1;
+					break;
+				case 1:  /* Copy rectangle encoding */
+					encoding_type.copy_rectangle = i + 1;
+					break;
+				case 2:  /* RRE encoding */
+					encoding_type.rre = i + 1;
+					break;
+				case 4:  /* CoRRE encoding */
+					encoding_type.corre = i + 1;
+					break;
+				case 5:  /* Hextile encoding */
+					encoding_type.hextile = i + 1;
+					break;
+				default:  /* Unknown coding type - do nothing */
+					break;
+			    }
+			}
+
+			if (encoding_type.corre) {
+			    ESP_LOGI(TAG, "SetEncodings use CORRE");
+			} else {
+			    ESP_LOGI(TAG, "SetEncodings use RAW");
+			}
+		    }
+		    break;
+
+	    	case FRAME_BUFFER_UPDATE_REQ:
+		    if (_msg.length == 10) {
+			if (!_msg.message[1]) {
+			    /* Non-incremental mode - mark the squares that need to be updated */
+			    for (i = (_msg.message[2]*255 + _msg.message[3])/TILE_SIZE;
+				i <= (_msg.message[2]*255 + _msg.message[3] + _msg.message[6]*255 + _msg.message[7])/TILE_SIZE;
+				i++) {
+				for (j = (_msg.message[4]*255 + _msg.message[5])/TILE_SIZE;
+				    j <= (_msg.message[4]*255 + _msg.message[5] + _msg.message[8]*255 + _msg.message[9])/TILE_SIZE;
+				    j++) {
+					tile_updated[j][i] = 1;
+				}
+			    }
+			}
+
+			/* Signal that there is now a pending update request */
+			xEventGroupSetBits(xEventGroupVNC, VNC_CLIENT_UPDATE_REQ);
+		    }
+		    break;
+
+	    	case KEY_EVENT:
+		    printf("KEY_EVENT\n");
+		    /* Handle the key event, ignored for brewboard */
+		    /* Get the remainder of message (7 bytes) */
+		    break;
+
+	    	case POINTER_EVENT:
+		    /* Handle the pointer event, simulate the touch screen. */
+		    if (_msg.length == 6) {
+			/* Set global variables that will be read by another task. */
+			VNC_pointer_button = _msg.message[1];
+			VNC_pointer_x = _msg.message[2]*255 + _msg.message[3];
+			VNC_pointer_y = _msg.message[4]*255 + _msg.message[5];
+//			printf("POINTER_EVENT button=%02x  x=%d y=%d\n", VNC_pointer_button, VNC_pointer_x, VNC_pointer_y);
+		    }
+		    _msg.length = 0;
+		    break;
+
+	    	case CLIENT_CUT_TEXT:
+		    printf("CLIENT_CUT_TEXT\n");
+		    break;
+
+	    	default:
+		    ESP_LOGI(TAG, "Unknown message %d from client", _msg.message[0]);
+	    }
+	    rest_time = 0;
+	} else {
+	    rest_time++;	// Each 5 milliseconds
+	    if (rest_time == 2000) {
+		// 10 seconds silence, send a PING
+		rest_time = 0;
+//		int rc = ws_server_ping(VNC_WS_num);
+//		ESP_LOGI(TAG, "Send PING to %d, rc=%d", VNC_WS_num, rc);
+	    }
+	}
+    }
+
+    VNC_WS_num = -1;
+    xEventGroupClearBits(xEventGroupVNC, VNC_CLIENT_UPDATE_REQ);
+    ESP_LOGI(TAG, "task_WS finished");
+    vTaskDelete(NULL);
+}
+
+
+
+int VncStartWS(int num)
+{
+    char		protocol_ver[8], message_buffer[MESSAGE_BUFFER_SIZE];
+    int			i, ProtocolOkay;
+    uint32_t		*ptr_to_uint32;
+    uint16_t		*ptr_to_uint16;
+    struct strMessage	_msg;
+
+    ESP_LOGI(TAG, "Start VNC WebSocket connection %d", num);
+    message_queue = xQueueCreate(message_queue_size, sizeof(struct strMessage));
+
+    /*
+     * Initial handshake
+     */
+    ws_server_send_bin_client(num, "RFB 003.003\n", 12);
+    ProtocolOkay = 1;
+    if (xQueueReceive(message_queue, &_msg, 5000 / portTICK_PERIOD_MS) == pdTRUE) {
+//	dump_msg((char *)_msg.message, _msg.length, "pull");
+
+	for (i = 0; i < 8; i++) {
+	    if (_msg.message[i] != server_ProtocolVersion[i]) {
+		ProtocolOkay = 0;
+	    }
+	    /* Store the protocol version - ignoring thr 'RFB ' part */
+	    protocol_ver[i] = _msg.message[i + 4];
+	}
+	protocol_ver[7] = 0;
+    } else {
+	ESP_LOGE(TAG, "Client timeout after initial message");
+	return -5;
+    }
+    
+    if (ProtocolOkay == 0) {
+	/* Generate the Bad ProtocolVerion message */
+	ESP_LOGI(TAG, "Start VNC WebSocket task failed, bad protocol.");
+	ptr_to_uint32 = (uint32_t *) &(message_buffer[0]);
+	*ptr_to_uint32 = htonl(0);
+	ptr_to_uint32 = (uint32_t *) &(message_buffer[4]);
+	*ptr_to_uint32 = htonl(strlen(bad_protocol));
+	strcpy(&(message_buffer[8]), bad_protocol);
+	ws_server_send_bin_client(num, message_buffer, 8 + strlen(bad_protocol));
+	vTaskDelay(500 / portTICK_PERIOD_MS);
+	ws_server_remove_client(num);
+	return -2;
+    }
+    /* ProtocolVerion is okay - connect with no authentication*/
+
+    /* Server is busy with another client */
+    if (VNC_WS_run || vnc_client_connected) {
+	ESP_LOGI(TAG, "Start VNC WebSocket task failed, server busy.");
+	ptr_to_uint32 = (uint32_t *) &(message_buffer[0]);
+	*ptr_to_uint32 = htonl(0);
+	ptr_to_uint32 = (uint32_t *) &(message_buffer[4]);
+	*ptr_to_uint32 = htonl(strlen(server_busy));
+	strcpy(&(message_buffer[8]), server_busy);
+	ws_server_send_bin_client(num, message_buffer, 8 + strlen(server_busy));
+	vTaskDelay(500 / portTICK_PERIOD_MS);
+	ws_server_remove_client(num);
+	return -1;
+    }
+
+    /* Generate the No Authentication message */
+    ptr_to_uint32 = (uint32_t *) &(message_buffer[0]);
+    *ptr_to_uint32 = htonl((uint32_t)1);
+    ws_server_send_bin_client(num, message_buffer, 4);
+    /* Authentication - end */
+
+    /* ClientInitialisation - begin */
+    /* Receive initialisation message from client (1 byte) */
+    if (xQueueReceive(message_queue, &_msg, 5000 / portTICK_PERIOD_MS) == pdFALSE) {
+	ESP_LOGE(TAG, "Client timeout after auth message");
+	return -5;
+    }
+    /* Do nothing with this as we only support 1 Client at a time */
+    /* ClientInitialisation - end */
+
+    /* Initial default RGB332 */
+    Red_Max     = RED_MAX;
+    Green_Max   = GREEN_MAX;
+    Blue_Max    = BLUE_MAX;
+    Red_Shift   = RED_SHIFT;
+    Green_Shift = GREEN_SHIFT;
+    Blue_Shift  = BLUE_SHIFT;
+    AltPixels   = false;
+
+    /* ServerInitialisation - begin */
+    /* Create Initialisation message for client */
+    ptr_to_uint16 = (uint16_t *) &(message_buffer[0]);
+    *ptr_to_uint16 = htons((uint16_t)CONFIG_VNC_SERVER_FRAME_WIDTH);
+
+    ptr_to_uint16 = (uint16_t *) &(message_buffer[2]);
+    *ptr_to_uint16 = htons((uint16_t)CONFIG_VNC_SERVER_FRAME_HEIGHT);
+
+    message_buffer[4] = (uint8_t)BITS_PER_PIXEL;
+    message_buffer[5] = (uint8_t)PIXEL_DEPTH;
+    message_buffer[6] = (uint8_t)BIG_ENDIAN_FLAG;
+    message_buffer[7] = (uint8_t)TRUE_COLOUR_FLAG;
+
+    ptr_to_uint16 = (uint16_t *) &(message_buffer[8]);
+    *ptr_to_uint16 = htons(Red_Max);
+
+    ptr_to_uint16 = (uint16_t *) &(message_buffer[10]);
+    *ptr_to_uint16 = htons(Green_Max);
+
+    ptr_to_uint16 = (uint16_t *) &(message_buffer[12]);
+    *ptr_to_uint16 = htons(Blue_Max);
+
+    message_buffer[14] = Red_Shift;
+    message_buffer[15] = Green_Shift;
+    message_buffer[16] = Blue_Shift;
+
+    ptr_to_uint32 = (uint32_t *) &(message_buffer[20]);
+    *ptr_to_uint32 = htonl(strlen(desktop_name));
+    strcpy(&(message_buffer[24]), desktop_name);
+
+//  dump_msg(message_buffer, 24 + strlen(desktop_name), (char *)"send");
+    ws_server_send_bin_client(num, message_buffer, 24 + strlen(desktop_name));
+
+    /* ServerInitialisation - end */
+    ESP_LOGI(TAG, "VNC WebSocket client connected (RFB Protocol Ver: %s)", protocol_ver);
+
+    VNC_WS_num = num;
+    xTaskCreate(&task_WS, "WSclient",    MIN_STACK_SIZE, NULL, 5, &xTaskWSclient);
+    return 0;
+}
+
+
+
+void VncStopWS(int num)
+{
+    if (! VNC_WS_run) {
+	ESP_LOGI(TAG, "Stop VNC WebSocket, not running.");
+	return;
+    }
+
+    if (num != VNC_WS_num) {
+	ESP_LOGI(TAG, "Stop VNC WebSocket, %d is running, requested %d", VNC_WS_num, num);
+	return;
+    }
+
+    ESP_LOGI(TAG, "Stop VNC WebSocket task connection %d", num);
+    VNC_WS_run = false;
+    xQueueReset(message_queue);
+}
+
+
+
+void VncGetWSmessage(char *msg, uint16_t len)
+{
+    int			max;
+    struct strMessage	_msg;
+ 
+    if (len == 0)
+	return;
+
+    max = len;
+    if (max > MESSAGE_BUFFER_SIZE) {
+	    ESP_LOGE(TAG, "VncGetWSmessage need %d bytes in a %d length buffer", len, MESSAGE_BUFFER_SIZE);
+	    max = MESSAGE_BUFFER_SIZE;
+    }
+
+    for (int i = 0; i < max; i++) {
+	    _msg.message[i] = msg[i];
+    }
+    _msg.length = max;
+    if (xQueueSendToBack(message_queue, &_msg, 200 / portTICK_PERIOD_MS) == pdFALSE) {
+	ESP_LOGE(TAG, "VncGetWSmessage() Queue full, message lost");
+    }
+}
+

mercurial