components/vnc_server/vnc-server.c

Mon, 22 Oct 2018 21:43:45 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Mon, 22 Oct 2018 21:43:45 +0200
changeset 6
e84200edc852
parent 1
ad2c8b13eb88
child 21
9e4cce24f6ff
permissions
-rw-r--r--

Updated esp-ide. Removed VNC server corre encoding because no clients would use it. Enabled WiFi error logmessages. Write runtime record is now debug logging. Removed recipe.Record number, not usefull and was wrong too. Removed console print of json log data.

/**
 * @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

/**
 * @brief VNC messages.
 */
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;
                    }
                }
                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;

    /* 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];

    /* 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