Sat, 20 Oct 2018 13:23:15 +0200
Initial checkin brewboard
0 | 1 | /** |
2 | * @file vnc-server.c | |
3 | * @brief VNC server original written for eCos. | |
4 | * | |
5 | * The VNC server runs on the target platform and waits for a client | |
6 | * (vncviewer program running on a PC) to connect via an ethernet connection | |
7 | * (port 5900). Once a client has connected, the target platform has a | |
8 | * virtual screen, keyboard and mouse. | |
9 | * | |
10 | * This port of the VNC server has been designed with some limitations in | |
11 | * order to keep memory and processor power requirements to a minimum. | |
12 | * | |
13 | * The VNC client must accept the settings that the VNC server suggests (bits-per-pixel, | |
14 | * number-of-colours and so on as specified at build time with the eCos configuration | |
15 | * tool) or the VNC server will just disconnect. | |
16 | * | |
17 | * The VNC server only supports CoRRE encoding and RAW encoding for sending | |
18 | * display data to the client. | |
19 | * | |
20 | * The VNC server does not support password authentication, the client is able | |
21 | * to connect without the user typing in a password. | |
22 | * | |
23 | * Only one VNC client may connect to the VNC server at a time. | |
24 | * | |
25 | * In reality these limitations are not a problem. | |
26 | * | |
27 | * @author Chris Garry <cgarry@sweeneydesign.co.uk> | |
28 | * @author Michiel Broek. | |
29 | */ | |
30 | ||
31 | #include "vnc-server.h" | |
32 | #include "websocket_server.h" | |
33 | ||
34 | static const char *TAG = "vnc-server"; | |
35 | ||
36 | #define BACKLOG 5 /* Number of pending connections queue will hold */ | |
37 | #define MESSAGE_BUFFER_SIZE 60 /* Was 50, but I have seen 52 bytes length */ | |
38 | #define TILE_SIZE 16 | |
39 | #define TRUE_COLOUR_FLAG 1 /* True colour is set */ | |
40 | #define BIG_ENDIAN_FLAG 1 /* Always send colour data big endian */ | |
41 | ||
42 | /* Default display parameters */ | |
43 | #define BITS_PER_PIXEL 8 /* Bits per pixel */ | |
44 | #define PIXEL_DEPTH 8 /* Usefull bits per pixel */ | |
45 | #define RED_MAX 7 | |
46 | #define GREEN_MAX 7 | |
47 | #define BLUE_MAX 3 | |
48 | #define RED_SHIFT 5 | |
49 | #define GREEN_SHIFT 2 | |
50 | #define BLUE_SHIFT 0 | |
51 | ||
52 | ||
53 | /* Initial default RGB332 */ | |
54 | uint8_t Bits_Per_Pixel = 8; | |
55 | ||
56 | uint16_t Red_Max = RED_MAX; | |
57 | uint16_t Green_Max = GREEN_MAX; | |
58 | uint16_t Blue_Max = BLUE_MAX; | |
59 | uint8_t Red_Shift = RED_SHIFT; | |
60 | uint8_t Green_Shift = GREEN_SHIFT; | |
61 | uint8_t Blue_Shift = BLUE_SHIFT; | |
62 | bool AltPixels = false; | |
63 | ||
64 | /* Client to Server message types */ | |
65 | #define SET_PIXEL_FORMAT 0 | |
66 | #define FIX_COLOUR_MAP_ENTRIES 1 | |
67 | #define SET_ENCODINGS 2 | |
68 | #define FRAME_BUFFER_UPDATE_REQ 3 | |
69 | #define KEY_EVENT 4 | |
70 | #define POINTER_EVENT 5 | |
71 | #define CLIENT_CUT_TEXT 6 | |
72 | ||
73 | /* Macros to split colour to bytes */ | |
74 | #define COLOUR2BYTE1(col) ((col>>8)&0xFF) | |
75 | #define COLOUR2BYTE0(col) (col&0xFF) | |
76 | ||
77 | ||
78 | /* Thread function prototypes */ | |
79 | static TaskHandle_t xTaskClientHandler = NULL; ///< Task for VNC clients | |
80 | static TaskHandle_t xTaskFrameUpdate = NULL; ///< Task for framebuffer updates | |
81 | static TaskHandle_t xTaskWSclient = NULL; ///< Task for websocket clients | |
82 | ||
83 | SemaphoreHandle_t SoundBell_lock = NULL; ///< Lock for the bell sound. | |
84 | ||
85 | static QueueHandle_t message_queue; ///< Websockets message queue | |
86 | const static int message_queue_size = 5; ///< Message queue size | |
87 | ||
88 | struct strMessage { | |
89 | int length; ///< Length of the message | |
90 | uint8_t message[MESSAGE_BUFFER_SIZE]; ///< The message | |
91 | }; | |
92 | ||
93 | ||
94 | uint8_t VNC_pointer_button = 0; | |
95 | uint16_t VNC_pointer_x = 0; | |
96 | uint16_t VNC_pointer_y = 0; | |
97 | ||
98 | ||
99 | /* Define size of each thread's stack */ | |
100 | #define MIN_STACK_SIZE 3072 | |
101 | ||
102 | ||
103 | /* Messages */ | |
104 | char server_ProtocolVersion[] = "RFB 003.003\n"; | |
105 | char bad_protocol[] = "Unsupported ProtocolVersion"; | |
106 | char server_busy[] = "Server is Busy"; | |
107 | char sound_bell[] = "\2"; | |
108 | char desktop_name[] = "MBSE BrewBoard"; // Hardcoded, don't change or the VNC webclient breaks. | |
109 | ||
110 | /* Frame Buffer */ | |
111 | vnc_color_t frame_buffer[CONFIG_VNC_SERVER_FRAME_HEIGHT+1][CONFIG_VNC_SERVER_FRAME_WIDTH+1]; | |
112 | ||
113 | /* Calculate the number of tiles in the X and Y directions */ | |
114 | #if (CONFIG_VNC_SERVER_FRAME_HEIGHT % TILE_SIZE) != 0 | |
115 | #define NUM_TILES_Y_AXIS (CONFIG_VNC_SERVER_FRAME_HEIGHT/TILE_SIZE + 1) | |
116 | #define LAST_TILE_HEIGHT (CONFIG_VNC_SERVER_FRAME_HEIGHT % TILE_SIZE) | |
117 | #else | |
118 | #define NUM_TILES_Y_AXIS (CONFIG_VNC_SERVER_FRAME_HEIGHT/TILE_SIZE) | |
119 | #define LAST_TILE_HEIGHT TILE_SIZE | |
120 | #endif | |
121 | ||
122 | #if (CONFIG_VNC_SERVER_FRAME_WIDTH % TILE_SIZE) != 0 | |
123 | #define NUM_TILES_X_AXIS (CONFIG_VNC_SERVER_FRAME_WIDTH/TILE_SIZE + 1) | |
124 | #define LAST_TILE_WIDTH (CONFIG_VNC_SERVER_FRAME_WIDTH % TILE_SIZE) | |
125 | #else | |
126 | #define NUM_TILES_X_AXIS (CONFIG_VNC_SERVER_FRAME_WIDTH/TILE_SIZE) | |
127 | #define LAST_TILE_WIDTH TILE_SIZE | |
128 | #endif | |
129 | ||
130 | /* Array for marking tiles that have been updated */ | |
131 | int tile_updated[NUM_TILES_Y_AXIS+1][NUM_TILES_X_AXIS+1]; | |
132 | ||
133 | /* Conditional variable to signal that a client is connected and initialised */ | |
134 | EventGroupHandle_t xEventGroupVNC; | |
135 | const int VNC_CLIENT_UPDATE_REQ = BIT0; | |
136 | int vnc_client_sock = -1; | |
137 | bool vnc_client_connected = false; | |
138 | ||
139 | /* Variable for sounding the client's bell */ | |
140 | int SoundBellCount; | |
141 | ||
142 | /* Variables for the Websocket client */ | |
143 | bool VNC_WS_run = false; | |
144 | int VNC_WS_num = -1; | |
145 | ||
146 | ||
147 | ||
148 | /* Variable to hold the frame format details */ | |
149 | vnc_frame_format_t frame_format = {CONFIG_VNC_SERVER_FRAME_WIDTH, CONFIG_VNC_SERVER_FRAME_HEIGHT, frame_buffer, | |
150 | 1, // RGB332 server native. | |
151 | 0, // RGB555 | |
152 | 0, // RGB565 | |
153 | 0, // BGR233 | |
154 | 0, // TRUECOLOR0888 | |
155 | }; | |
156 | ||
157 | ||
158 | ||
159 | #if 0 | |
160 | void dump_msg(char *buf, uint16_t buflen, char *mode) | |
161 | { | |
162 | int i, l = 0; | |
163 | ||
164 | printf("%s %d", mode, buflen); | |
165 | for (i = 0; i < buflen; i++) { | |
166 | if ((i % 16) == 0) { | |
167 | printf("\n%02d: ", l); | |
168 | l++; | |
169 | } | |
170 | printf("%02x ", buf[i]); | |
171 | } | |
172 | printf("\n"); | |
173 | } | |
174 | #endif | |
175 | ||
176 | ||
177 | /* Structure to hold the encoding type details */ | |
178 | volatile struct encoding_type_struct | |
179 | { | |
180 | uint8_t raw; | |
181 | uint8_t copy_rectangle; | |
182 | uint8_t rre; | |
183 | uint8_t corre; | |
184 | uint8_t hextile; | |
185 | } encoding_type; | |
186 | ||
187 | ||
188 | static int GetMessageData(int, char *, int); | |
189 | static int GenTileUpdateData(uint8_t *); | |
190 | void task_VNCserver(void *); | |
191 | void task_frame(void *); | |
192 | void task_WS(void *); | |
193 | ||
194 | ||
195 | ||
196 | /** | |
197 | * @brief Serve a new VNC client. | |
198 | */ | |
199 | void vnc_netconn_serve(void); | |
200 | ||
201 | ||
202 | /* VNC startup. */ | |
203 | void VncStartup(void) | |
204 | { | |
205 | /* Initialise mutex & cond vars */ | |
206 | xEventGroupVNC = xEventGroupCreate(); | |
207 | SoundBell_lock = xSemaphoreCreateMutex(); | |
208 | ||
209 | xTaskCreate(&task_VNCserver, "VNCserver", MIN_STACK_SIZE, NULL, 5, &xTaskClientHandler); | |
210 | xTaskCreate(&task_frame, "frame_update", MIN_STACK_SIZE, NULL, 6, &xTaskFrameUpdate); | |
211 | } | |
212 | ||
213 | ||
214 | /** | |
215 | * @brief Client Handler Task. | |
216 | * | |
217 | * This task handles the client initialisation sequence. Once the client | |
218 | * is initialised this task handles all received messages from the client, | |
219 | * but does not send any data to the client. | |
220 | */ | |
221 | void task_VNCserver(void *pvParameter) | |
222 | { | |
223 | int server_sock; | |
224 | struct sockaddr_in server_addr; | |
225 | struct sockaddr_in client_addr; | |
226 | socklen_t client_addr_size; | |
227 | ||
228 | ESP_LOGI(TAG, "Starting VNC server"); | |
229 | ||
230 | /* Clear the encoding type structure */ | |
231 | encoding_type.raw = 0; | |
232 | encoding_type.copy_rectangle = 0; | |
233 | encoding_type.rre = 0; | |
234 | encoding_type.corre = 0; | |
235 | encoding_type.hextile = 0; | |
236 | ||
237 | /* Clear the sound bell counter */ | |
238 | SoundBellCount = 0; | |
239 | VNC_pointer_button = 0; | |
240 | VNC_pointer_x = VNC_pointer_y = 0; | |
241 | ||
242 | /* Create socket for incomming connections */ | |
243 | if ((server_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { | |
244 | ESP_LOGE(TAG, "socket() VNC function failed"); | |
245 | exit(1); | |
246 | } | |
247 | ||
248 | /* Construct the server address structure */ | |
249 | memset(&server_addr, 0, sizeof(server_addr)); /* Fill entire structure with 0's */ | |
250 | server_addr.sin_family = AF_INET; /* Internet address family */ | |
251 | server_addr.sin_addr.s_addr = INADDR_ANY; /* Autofill with my IP address */ | |
252 | server_addr.sin_port = htons(CONFIG_VNC_SERVER_PORT); | |
253 | ||
254 | /* Bind socket to local address */ | |
255 | if (bind(server_sock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { | |
256 | ESP_LOGE(TAG, "bind() VNC function failed"); | |
257 | exit(1); | |
258 | } | |
259 | ||
260 | /* Set the socket to listen for incoming connections */ | |
261 | if (listen(server_sock, BACKLOG) < 0) { | |
262 | ESP_LOGE(TAG, "listen() VNC function failed"); | |
263 | exit(1); | |
264 | } | |
265 | ||
266 | do { | |
267 | vnc_client_sock = (accept(server_sock, (struct sockaddr *) &client_addr, &client_addr_size)); | |
268 | if (vnc_client_sock >= 0) { | |
269 | vnc_client_connected = true; | |
270 | vnc_netconn_serve(); | |
271 | vnc_client_connected = false; | |
272 | } | |
273 | vTaskDelay((TickType_t)10); /* allows the freeRTOS scheduler to take over if needed */ | |
274 | } while (vnc_client_sock >= 0); | |
275 | } | |
276 | ||
277 | ||
278 | ||
279 | /** | |
280 | * @brief Serve a new VNC client and handle the whole session. | |
281 | */ | |
282 | void vnc_netconn_serve(void) | |
283 | { | |
284 | long int temp_long; | |
285 | char protocol_ver[8], message_buffer[MESSAGE_BUFFER_SIZE]; | |
286 | int i, j, message_len, ProtocolOkay; | |
287 | uint32_t *ptr_to_uint32; | |
288 | uint16_t *ptr_to_uint16; | |
289 | struct sockaddr_storage addr; | |
290 | socklen_t len = sizeof addr; | |
291 | char ipstr[INET6_ADDRSTRLEN]; | |
292 | ||
293 | getpeername(vnc_client_sock, (struct sockaddr*)&addr, &len); | |
294 | if (addr.ss_family == AF_INET) { | |
295 | struct sockaddr_in *s = (struct sockaddr_in *)&addr; | |
296 | inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); | |
297 | } | |
298 | ESP_LOGI(TAG, "VNC new client %s socket %d", ipstr, vnc_client_sock); | |
299 | ||
300 | /* ProtocolVersion Handshake - begin */ | |
301 | /* Send ProtocolVersion we want to use to client */ | |
302 | message_len = sprintf(message_buffer, "RFB 003.003\n"); | |
303 | if (send(vnc_client_sock, message_buffer, message_len, 0) != message_len) { | |
304 | goto close_connection; | |
305 | } | |
306 | ||
307 | /* Receive ProtocolVersion the client wants to use */ | |
308 | if (GetMessageData(vnc_client_sock, &(message_buffer[0]), 12) == 0) { | |
309 | goto close_connection; | |
310 | } | |
311 | ||
312 | /* Check this is acceptable (RFB 003.xxx is okay) */ | |
313 | ProtocolOkay = 1; | |
314 | for (i = 0; i < 8; i++) { | |
315 | if (message_buffer[i] != server_ProtocolVersion[i]) { | |
316 | ProtocolOkay = 0; | |
317 | } | |
318 | ||
319 | /* Store the protocol version - ignoring thr 'RFB ' part */ | |
320 | protocol_ver[i] = message_buffer[i + 4]; | |
321 | } | |
322 | protocol_ver[7] = 0; | |
323 | /* ProtocolVersion Handshake - end */ | |
324 | ||
325 | /* Authentication - begin */ | |
326 | /* Send Authentication scheme to be used to client */ | |
327 | if (ProtocolOkay == 0) { | |
328 | /* ProtocolVerion is not okay */ | |
329 | ||
330 | /* Generate the Bad ProtocolVerion message */ | |
331 | ptr_to_uint32 = (uint32_t *) &(message_buffer[0]); | |
332 | *ptr_to_uint32 = htonl(0); | |
333 | ptr_to_uint32 = (uint32_t *) &(message_buffer[4]); | |
334 | *ptr_to_uint32 = htonl(strlen(bad_protocol)); | |
335 | strcpy(&(message_buffer[8]), bad_protocol); | |
336 | ||
337 | if (send(vnc_client_sock, message_buffer, 8 + strlen(bad_protocol), 0) != (8 + strlen(bad_protocol))) { | |
338 | printf("Call to send() 1 failed\n"); | |
339 | } | |
340 | goto close_connection; | |
341 | ||
342 | } else if (VNC_WS_num != -1) { | |
343 | /* Busy with a websocket client */ | |
344 | ptr_to_uint32 = (uint32_t *) &(message_buffer[0]); | |
345 | *ptr_to_uint32 = htonl(0); | |
346 | ptr_to_uint32 = (uint32_t *) &(message_buffer[4]); | |
347 | *ptr_to_uint32 = htonl(strlen(server_busy)); | |
348 | strcpy(&(message_buffer[8]), server_busy); | |
349 | ||
350 | if (send(vnc_client_sock, message_buffer, 8 + strlen(server_busy), 0) != (8 + strlen(server_busy))) { | |
351 | printf("Call to send() 1 failed\n"); | |
352 | } | |
353 | goto close_connection; | |
354 | ||
355 | } else { | |
356 | /* ProtocolVerion is okay - connect with no authentication*/ | |
357 | ||
358 | /* Generate the No Authentication message */ | |
359 | ptr_to_uint32 = (uint32_t *) &(message_buffer[0]); | |
360 | *ptr_to_uint32 = htonl((uint32_t)1); | |
361 | ||
362 | if (send(vnc_client_sock, message_buffer, 4, 0) != 4) { | |
363 | goto close_connection; | |
364 | } | |
365 | } | |
366 | /* Authentication - end */ | |
367 | ||
368 | /* ClientInitialisation - begin */ | |
369 | /* Receive initialisation message from client (1 byte) */ | |
370 | if (GetMessageData(vnc_client_sock, &(message_buffer[0]), 1) == 0) { | |
371 | goto close_connection; | |
372 | } | |
373 | /* Do nothing with this as we only support 1 Client at a time */ | |
374 | /* ClientInitialisation - end */ | |
375 | ||
376 | /* ServerInitialisation - begin */ | |
377 | ||
378 | /* Initial default RGB332 */ | |
379 | Red_Max = RED_MAX; | |
380 | Green_Max = GREEN_MAX; | |
381 | Blue_Max = BLUE_MAX; | |
382 | Red_Shift = RED_SHIFT; | |
383 | Green_Shift = GREEN_SHIFT; | |
384 | Blue_Shift = BLUE_SHIFT; | |
385 | AltPixels = false; | |
386 | ||
387 | /* Create Initialisation message for client */ | |
388 | ptr_to_uint16 = (uint16_t *) &(message_buffer[0]); | |
389 | *ptr_to_uint16 = htons((uint16_t)CONFIG_VNC_SERVER_FRAME_WIDTH); | |
390 | ||
391 | ptr_to_uint16 = (uint16_t *) &(message_buffer[2]); | |
392 | *ptr_to_uint16 = htons((uint16_t)CONFIG_VNC_SERVER_FRAME_HEIGHT); | |
393 | ||
394 | message_buffer[4] = (uint8_t)BITS_PER_PIXEL; | |
395 | message_buffer[5] = (uint8_t)PIXEL_DEPTH; | |
396 | message_buffer[6] = (uint8_t)BIG_ENDIAN_FLAG; | |
397 | message_buffer[7] = (uint8_t)TRUE_COLOUR_FLAG; | |
398 | ||
399 | ptr_to_uint16 = (uint16_t *) &(message_buffer[8]); | |
400 | *ptr_to_uint16 = htons(Red_Max); | |
401 | ||
402 | ptr_to_uint16 = (uint16_t *) &(message_buffer[10]); | |
403 | *ptr_to_uint16 = htons(Green_Max); | |
404 | ||
405 | ptr_to_uint16 = (uint16_t *) &(message_buffer[12]); | |
406 | *ptr_to_uint16 = htons(Blue_Max); | |
407 | ||
408 | message_buffer[14] = Red_Shift; | |
409 | message_buffer[15] = Green_Shift; | |
410 | message_buffer[16] = Blue_Shift; | |
411 | ||
412 | ptr_to_uint32 = (uint32_t *) &(message_buffer[20]); | |
413 | *ptr_to_uint32 = htonl(strlen(desktop_name)); | |
414 | strcpy(&(message_buffer[24]), desktop_name); | |
415 | ||
416 | if (send(vnc_client_sock, message_buffer, 24 + strlen(desktop_name), 0) != (24 + strlen(desktop_name))) { | |
417 | printf("Call to send() 3 failed\n"); | |
418 | } | |
419 | /* ServerInitialisation - end */ | |
420 | ||
421 | /* Cancel any outstanding Sound Bell requests */ | |
422 | if (xSemaphoreTake(SoundBell_lock, 10) == pdTRUE) { | |
423 | SoundBellCount = 0; | |
424 | xSemaphoreGive(SoundBell_lock); | |
425 | } | |
426 | ||
427 | ESP_LOGI(TAG, "VNC client connected (RFB Protocol Ver: %s)", protocol_ver); | |
428 | ||
429 | /* Main message handling loop */ | |
430 | while(1) { | |
431 | int num_of_encodings; | |
432 | ||
433 | /* Receive 1st byte of message from client */ | |
434 | if (GetMessageData(vnc_client_sock, &(message_buffer[0]), 1) == 0) { | |
435 | goto close_connection; /* Connection lost */ | |
436 | } | |
437 | ||
438 | switch(message_buffer[0]) { | |
439 | case SET_PIXEL_FORMAT: | |
440 | ||
441 | /* Get the remainder (19 bytes) of message */ | |
442 | if (GetMessageData(vnc_client_sock, &(message_buffer[1]), 19) == 0) { | |
443 | goto close_connection; | |
444 | } | |
445 | ||
446 | /* Check pixel format is as expected */ | |
447 | i = 0; | |
448 | if (message_buffer[4] != BITS_PER_PIXEL) { | |
449 | ESP_LOGI(TAG, "SetPixelFormat client wants %d bits-per-pixel", message_buffer[4]); | |
450 | i++; | |
451 | } | |
452 | ||
453 | if (message_buffer[5] != PIXEL_DEPTH) { | |
454 | ESP_LOGI(TAG, "SetPixelFormat client wants %d pixel-depth", message_buffer[5]); | |
455 | i++; | |
456 | } | |
457 | ||
458 | if ((message_buffer[7] & 0x01) != TRUE_COLOUR_FLAG) { | |
459 | ESP_LOGI(TAG, "SetPixelFormat client wants %d true-colour-flag", message_buffer[7]); | |
460 | i++; | |
461 | } | |
462 | ||
463 | ptr_to_uint16 = (uint16_t *)&(message_buffer[8]); | |
464 | if (htons(*ptr_to_uint16) != Red_Max) { | |
465 | Red_Max = htons(*ptr_to_uint16); | |
466 | AltPixels = true; | |
467 | ESP_LOGI(TAG, "SetPixelFormat client granted %d red-max", Red_Max); | |
468 | } | |
469 | ||
470 | ptr_to_uint16 = (uint16_t *)&(message_buffer[10]); | |
471 | if (htons(*ptr_to_uint16) != Green_Max) { | |
472 | Green_Max = htons(*ptr_to_uint16); | |
473 | AltPixels = true; | |
474 | ESP_LOGI(TAG, "SetPixelFormat client granted %d green-max", Green_Max); | |
475 | } | |
476 | ||
477 | ptr_to_uint16 = (uint16_t *)&(message_buffer[12]); | |
478 | if (htons(*ptr_to_uint16) != Blue_Max) { | |
479 | Blue_Max = htons(*ptr_to_uint16); | |
480 | AltPixels = true; | |
481 | ESP_LOGI(TAG, "SetPixelFormat client granted %d blue-max", Blue_Max); | |
482 | } | |
483 | ||
484 | if (message_buffer[14] != Red_Shift) { | |
485 | Red_Shift = message_buffer[14]; | |
486 | AltPixels = true; | |
487 | ESP_LOGI(TAG, "SetPixelFormat client granted %d red-shift", Red_Shift); | |
488 | } | |
489 | ||
490 | if (message_buffer[15] != Green_Shift) { | |
491 | Green_Shift = message_buffer[15]; | |
492 | AltPixels = true; | |
493 | ESP_LOGI(TAG, "SetPixelFormat client granted %d green-shift", Green_Shift); | |
494 | } | |
495 | ||
496 | if (message_buffer[16] != Blue_Shift) { | |
497 | Blue_Shift = message_buffer[16]; | |
498 | AltPixels = true; | |
499 | ESP_LOGI(TAG, "SetPixelFormat client granted %d blue-shift", Blue_Shift); | |
500 | } | |
501 | ||
502 | if (i) { | |
503 | ESP_LOGI(TAG, "SetPixelFormat %d errors, disconnect client", i); | |
504 | ESP_LOGI(TAG, "Ensure the 'Auto select' is not enabled in your vncviewer options,"); | |
505 | goto close_connection_quietly; | |
506 | } | |
507 | ||
508 | break; | |
509 | ||
510 | case FIX_COLOUR_MAP_ENTRIES: | |
511 | printf("fix colormap entries\n"); | |
512 | /* Not supported, just get the data from the buffer and ignore it */ | |
513 | ||
514 | /* Get the next 5 bytes of message */ | |
515 | if (GetMessageData(vnc_client_sock, &(message_buffer[1]), 5) == 0) { | |
516 | goto close_connection; | |
517 | } | |
518 | ||
519 | /* Calculate how many colour entries are in the buffer */ | |
520 | i = message_buffer[4]*255 + message_buffer[5]; | |
521 | i *= 6; | |
522 | ||
523 | /* Get this amount of data from the buffer */ | |
524 | for (j = 0; j < i; j++) { | |
525 | if (GetMessageData(vnc_client_sock, &(message_buffer[6]), 6) == 0) { | |
526 | goto close_connection; | |
527 | } | |
528 | } | |
529 | ||
530 | break; | |
531 | ||
532 | case SET_ENCODINGS: | |
533 | ||
534 | /* Get the next 3 bytes of message */ | |
535 | if (GetMessageData(vnc_client_sock, &(message_buffer[1]), 3) == 0) { | |
536 | goto close_connection; | |
537 | } | |
538 | ||
539 | num_of_encodings = message_buffer[2]*255 + message_buffer[3]; | |
540 | ||
541 | /* Get the remainder of message */ | |
542 | if (GetMessageData(vnc_client_sock, &(message_buffer[0]), 4 * num_of_encodings) == 0) { | |
543 | goto close_connection; | |
544 | } | |
545 | ||
546 | /* Clear the encoding type structure */ | |
547 | encoding_type.raw = 0; | |
548 | encoding_type.copy_rectangle = 0; | |
549 | encoding_type.rre = 0; | |
550 | encoding_type.corre = 0; | |
551 | encoding_type.hextile = 0; | |
552 | ||
553 | for (i = 0; i < num_of_encodings; i++) { | |
554 | switch(message_buffer[3 + (i*4)]) { | |
555 | case 0: /* Raw encoding */ | |
556 | encoding_type.raw = i + 1; | |
557 | break; | |
558 | case 1: /* Copy rectangle encoding */ | |
559 | encoding_type.copy_rectangle = i + 1; | |
560 | break; | |
561 | case 2: /* RRE encoding */ | |
562 | encoding_type.rre = i + 1; | |
563 | break; | |
564 | case 4: /* CoRRE encoding */ | |
565 | encoding_type.corre = i + 1; | |
566 | break; | |
567 | case 5: /* Hextile encoding */ | |
568 | encoding_type.hextile = i + 1; | |
569 | break; | |
570 | default: /* Unknown coding type - do nothing */ | |
571 | break; | |
572 | } | |
573 | } | |
574 | ||
575 | if (encoding_type.corre) { | |
576 | ESP_LOGI(TAG, "SetEncodings use CORRE"); | |
577 | } else { | |
578 | ESP_LOGI(TAG, "SetEncodings use RAW"); | |
579 | } | |
580 | break; | |
581 | ||
582 | case FRAME_BUFFER_UPDATE_REQ: | |
583 | /* Get the remainder of message (9 bytes) */ | |
584 | if (GetMessageData(vnc_client_sock, &(message_buffer[1]), 9) == 0) { | |
585 | goto close_connection; | |
586 | } | |
587 | ||
588 | if (!message_buffer[1]) { | |
589 | /* Non-incremental mode - mark the squares that need to be updated */ | |
590 | for (i = (message_buffer[2]*255 + message_buffer[3])/TILE_SIZE; | |
591 | i <= (message_buffer[2]*255 + message_buffer[3] + message_buffer[6]*255 + message_buffer[7])/TILE_SIZE; | |
592 | i++) { | |
593 | for (j = (message_buffer[4]*255 + message_buffer[5])/TILE_SIZE; | |
594 | j <= (message_buffer[4]*255 + message_buffer[5] + message_buffer[8]*255 + message_buffer[9])/TILE_SIZE; | |
595 | j++) { | |
596 | tile_updated[j][i] = 1; | |
597 | } | |
598 | } | |
599 | } | |
600 | ||
601 | /* Signal that there is now a pending update request */ | |
602 | xEventGroupSetBits(xEventGroupVNC, VNC_CLIENT_UPDATE_REQ); | |
603 | break; | |
604 | ||
605 | case KEY_EVENT: | |
606 | /* Handle the key event, ignored for brewboard */ | |
607 | /* Get the remainder of message (7 bytes) */ | |
608 | if (GetMessageData(vnc_client_sock, &(message_buffer[1]), 7) == 0) { | |
609 | goto close_connection; | |
610 | } | |
611 | break; | |
612 | ||
613 | case POINTER_EVENT: | |
614 | /* Handle the pointer event, simulate the touch screen. */ | |
615 | /* Get the remainder of message (5 bytes) */ | |
616 | if (GetMessageData(vnc_client_sock, &(message_buffer[1]), 5) == 0) { | |
617 | goto close_connection; | |
618 | } | |
619 | /* Set global variables that will be read by another task. */ | |
620 | VNC_pointer_button = message_buffer[1]; | |
621 | VNC_pointer_x = message_buffer[2]*255 + message_buffer[3]; | |
622 | VNC_pointer_y = message_buffer[4]*255 + message_buffer[5]; | |
623 | break; | |
624 | ||
625 | case CLIENT_CUT_TEXT: | |
626 | /* Handle the client has cut text event */ | |
627 | /* Current we just get and discard the data */ | |
628 | /* Get the next 7 bytes of the message */ | |
629 | if (GetMessageData(vnc_client_sock, &(message_buffer[1]), 7) == 0) { | |
630 | goto close_connection; | |
631 | } | |
632 | ||
633 | ptr_to_uint32 = (uint32_t *)&(message_buffer[4]); | |
634 | temp_long = htonl(*ptr_to_uint32); | |
635 | ||
636 | while (temp_long > 0) { | |
637 | /* Get the text in chunks MESSAGE_BUFFER_SIZE-1 characters */ | |
638 | if (temp_long > MESSAGE_BUFFER_SIZE-2) { | |
639 | if (GetMessageData(vnc_client_sock, &(message_buffer[0]), MESSAGE_BUFFER_SIZE-1) == 0) { | |
640 | goto close_connection; | |
641 | } | |
642 | ||
643 | message_buffer[MESSAGE_BUFFER_SIZE-1] = 0; | |
644 | temp_long -= (MESSAGE_BUFFER_SIZE-1); | |
645 | } else { | |
646 | if (GetMessageData(vnc_client_sock, &(message_buffer[0]), temp_long) == 0) { | |
647 | goto close_connection; | |
648 | } | |
649 | ||
650 | message_buffer[temp_long] = 0; | |
651 | temp_long = 0; | |
652 | } | |
653 | } | |
654 | ||
655 | break; | |
656 | ||
657 | default: | |
658 | ESP_LOGI(TAG, "Unknown message %d from client", message_buffer[0]); | |
659 | } | |
660 | ||
661 | vTaskDelay( (TickType_t)1); | |
662 | } | |
663 | ||
664 | close_connection: | |
665 | ESP_LOGI(TAG, "VNC client disconnected"); | |
666 | ||
667 | close_connection_quietly: | |
668 | ||
669 | /* Cancel any outstanding update requests */ | |
670 | xEventGroupClearBits(xEventGroupVNC, VNC_CLIENT_UPDATE_REQ); | |
671 | close(vnc_client_sock); | |
672 | } | |
673 | ||
674 | ||
675 | ||
676 | /** | |
677 | * @brief Frame update task. This thread handles the sending of all frame | |
678 | * update data to the client and sends the 'ring bell' message to | |
679 | * the client when required. | |
680 | * Works for VNC port connected clients and Websocket clients. | |
681 | * @param pvParameter Ignored | |
682 | */ | |
683 | void task_frame(void *pvParameter) | |
684 | { | |
685 | int i, j, x_pos, y_pos, packet_length, num_updated_tiles; | |
686 | uint16_t *ptr_to_uint16; | |
687 | /* These are declared static so they don't use thread stack memory */ | |
688 | static uint8_t FramebufferUpdate_msg[4 + 12 + TILE_SIZE*TILE_SIZE*BITS_PER_PIXEL/8 + 1460]; | |
689 | static int FrameBufferPtr; /* Pointer to next space in buffer */ | |
690 | static int tile_updated_local[NUM_TILES_Y_AXIS][NUM_TILES_X_AXIS]; | |
691 | ||
692 | ESP_LOGI(TAG, "Starting VNC frame updater"); | |
693 | ||
694 | while(1) { | |
695 | /* Wait until client sends a frame update request */ | |
696 | wait_for_client: | |
697 | xEventGroupWaitBits(xEventGroupVNC, VNC_CLIENT_UPDATE_REQ, pdFALSE, pdFALSE, portMAX_DELAY); | |
698 | ||
699 | /* Copy tile_updated array to local version and clear copied tiles */ | |
700 | vTaskSuspendAll(); | |
701 | num_updated_tiles = 0; | |
702 | for (i = 0; i < NUM_TILES_Y_AXIS; i++) { | |
703 | for (j = 0; j < NUM_TILES_X_AXIS; j++) { | |
704 | if (tile_updated[i][j]) { | |
705 | tile_updated_local[i][j] = 1; | |
706 | tile_updated[i][j] = 0; | |
707 | num_updated_tiles++; /* Keep count of the updated tiles */ | |
708 | } | |
709 | } | |
710 | } | |
711 | xTaskResumeAll(); | |
712 | ||
713 | if (num_updated_tiles) { | |
714 | /* Cancel update request */ | |
715 | xEventGroupClearBits(xEventGroupVNC, VNC_CLIENT_UPDATE_REQ); | |
716 | /* Fill in constant parts of FramebufferUpdate Message */ | |
717 | FramebufferUpdate_msg[0] = 0; /* Message-type */ | |
718 | FramebufferUpdate_msg[1] = 0; /* Padding */ | |
719 | ptr_to_uint16 = (uint16_t *) &(FramebufferUpdate_msg[2]); | |
720 | *ptr_to_uint16 = htons(num_updated_tiles); /* Number-of-rectangles */ | |
721 | FrameBufferPtr = 4; | |
722 | ||
723 | for (y_pos = 0; y_pos < NUM_TILES_Y_AXIS; y_pos++) { | |
724 | for (x_pos = 0; x_pos < NUM_TILES_X_AXIS; x_pos++) { | |
725 | if (tile_updated_local[y_pos][x_pos]) { | |
726 | /* Send current square data to client */ | |
727 | ||
728 | /* x-position */ | |
729 | FramebufferUpdate_msg[FrameBufferPtr+0] = (x_pos * TILE_SIZE) / 256; | |
730 | FramebufferUpdate_msg[FrameBufferPtr+1] = (x_pos * TILE_SIZE) % 256; | |
731 | ||
732 | /* y-position */ | |
733 | FramebufferUpdate_msg[FrameBufferPtr+2] = (y_pos * TILE_SIZE) / 256; | |
734 | FramebufferUpdate_msg[FrameBufferPtr+3] = (y_pos * TILE_SIZE) % 256; | |
735 | ||
736 | ||
737 | /* Set width of tile in packet */ | |
738 | if (x_pos == (NUM_TILES_X_AXIS -1)) { | |
739 | /* Last tile in X-axis */ | |
740 | FramebufferUpdate_msg[FrameBufferPtr+4] = LAST_TILE_WIDTH / 256; | |
741 | FramebufferUpdate_msg[FrameBufferPtr+5] = LAST_TILE_WIDTH % 256; | |
742 | } else { | |
743 | FramebufferUpdate_msg[FrameBufferPtr+4] = TILE_SIZE / 256; | |
744 | FramebufferUpdate_msg[FrameBufferPtr+5] = TILE_SIZE % 256; | |
745 | } | |
746 | ||
747 | if (y_pos == (NUM_TILES_Y_AXIS -1)) { | |
748 | /* Last tile in Y-axis */ | |
749 | FramebufferUpdate_msg[FrameBufferPtr+6] = LAST_TILE_HEIGHT / 256; | |
750 | FramebufferUpdate_msg[FrameBufferPtr+7] = LAST_TILE_HEIGHT % 256; | |
751 | } else { | |
752 | FramebufferUpdate_msg[FrameBufferPtr+6] = TILE_SIZE / 256; | |
753 | FramebufferUpdate_msg[FrameBufferPtr+7] = TILE_SIZE % 256; | |
754 | } | |
755 | ||
756 | /* Generate the packet data for this tile */ | |
757 | packet_length = GenTileUpdateData(&(FramebufferUpdate_msg[FrameBufferPtr])); | |
758 | ||
759 | /* Send the packet data for this tile to the client */ | |
760 | FrameBufferPtr += packet_length; | |
761 | ||
762 | if (FrameBufferPtr > 1460) { | |
763 | /* Send the data to the client */ | |
764 | if (VNC_WS_num != -1) { | |
765 | ws_server_send_bin_client(VNC_WS_num, (char *)FramebufferUpdate_msg, FrameBufferPtr); | |
766 | } | |
767 | if (vnc_client_connected) { | |
768 | if (send(vnc_client_sock, FramebufferUpdate_msg, FrameBufferPtr, 0) != FrameBufferPtr) { | |
769 | goto wait_for_client; | |
770 | } | |
771 | } | |
772 | FrameBufferPtr = 0; | |
773 | } | |
774 | ||
775 | tile_updated_local[y_pos][x_pos] = 0; /* Clear the update bit for this square */ | |
776 | } | |
777 | } | |
778 | } | |
779 | ||
780 | if (FrameBufferPtr > 0) { | |
781 | /* Last data for this update, send it to the client */ | |
782 | if (VNC_WS_num != -1) { | |
783 | ws_server_send_bin_client(VNC_WS_num, (char *)FramebufferUpdate_msg, FrameBufferPtr); | |
784 | } | |
785 | if (vnc_client_connected) { | |
786 | if (send(vnc_client_sock, FramebufferUpdate_msg, FrameBufferPtr, 0) != FrameBufferPtr) { | |
787 | goto wait_for_client; | |
788 | } | |
789 | } | |
790 | ||
791 | FrameBufferPtr = 0; | |
792 | } | |
793 | ||
794 | } else { /* if (num_updated_tiles) */ | |
795 | /* There was no new display data to send to the client */ | |
796 | /* Sleep for 1/20th second before checking again */ | |
797 | vTaskDelay(20 / portTICK_PERIOD_MS); | |
798 | } | |
799 | ||
800 | /* Check for sound bell event */ | |
801 | if (xSemaphoreTake(SoundBell_lock, 10) == pdTRUE) { | |
802 | if (SoundBellCount) { | |
803 | --SoundBellCount; | |
804 | xSemaphoreGive(SoundBell_lock); | |
805 | ||
806 | if (vnc_client_connected) { | |
807 | if (send(vnc_client_sock, sound_bell, 1, 0) != 1) { | |
808 | goto wait_for_client; | |
809 | } | |
810 | } | |
811 | if (VNC_WS_num != -1) { | |
812 | ws_server_send_bin_client(VNC_WS_num, sound_bell, 1); | |
813 | } | |
814 | } else { | |
815 | xSemaphoreGive(SoundBell_lock); | |
816 | } | |
817 | } | |
818 | } | |
819 | } | |
820 | ||
821 | ||
822 | ||
823 | /** | |
824 | * Convert a framebuffer pixel to BGR233 if needed. | |
825 | * @param pixel The 8 bit pixel value | |
826 | * @return Then unchanged or changed pixel. | |
827 | */ | |
828 | vnc_color_t IRAM_ATTR PixelConvert(vnc_color_t pixel) | |
829 | { | |
830 | if (!AltPixels) | |
831 | return pixel; | |
832 | ||
833 | return (((pixel >> RED_SHIFT) & RED_MAX) << Red_Shift) | | |
834 | (((pixel >> GREEN_SHIFT) & GREEN_MAX) << Green_Shift) | | |
835 | (((pixel >> BLUE_SHIFT) & BLUE_MAX) << Blue_Shift); | |
836 | } | |
837 | ||
838 | ||
839 | ||
840 | /** | |
841 | * @brief Generate tile update data function | |
842 | * | |
843 | * This function is called by the frame_update thread to generate the message | |
844 | * data for a tile to send to the client. This function expects the | |
845 | * x-position, y-position, width and height fields of the buffer to be filled | |
846 | * in before it is called. | |
847 | * | |
848 | * The format of the buffer is: | |
849 | * packet_buffer[0:1] - x-position of tile | |
850 | * packet_buffer[2:3] - y-position of tile | |
851 | * packet_buffer[4:5] - width of tile | |
852 | * packet_buffer[6:7] - height of tile | |
853 | * packet_buffer[8 onwards] - Pixel data for the tile | |
854 | * | |
855 | * The pixel data will be encoded with CoRRE encoding (if the CDL option is | |
856 | * enabled and the client can handle it) or RAW encoding if that is smaller | |
857 | * than CoRRE encoding for that particular tile. | |
858 | * | |
859 | * @param packet_buffer Buffer to store tile data | |
860 | * @return Length of generated data in bytes | |
861 | */ | |
862 | static int GenTileUpdateData(uint8_t *packet_buffer) | |
863 | { | |
864 | uint16_t x_pos, y_pos; | |
865 | int i, j; | |
866 | int tile_width, tile_height; | |
867 | int packet_length; | |
868 | static vnc_color_t tile_buffer[TILE_SIZE][TILE_SIZE]; /* Buffer to hold tile to be encoded */ | |
869 | vnc_color_t pixel_colour; | |
870 | vnc_color_t bg_colour; | |
871 | int no_of_subrects, subrect_width, subrect_height; | |
872 | int k, l; | |
873 | ||
874 | no_of_subrects = 0; /* Set to no sub-rectangles to start with */ | |
875 | packet_length = 20-4+(Bits_Per_Pixel/8); /* Set to minimum packet length to start with */ | |
876 | ||
877 | /* Get the X and Y positions of this tile from the packet buffer */ | |
878 | x_pos = packet_buffer[0] * 256 + packet_buffer[1]; | |
879 | y_pos = packet_buffer[2] * 256 + packet_buffer[3]; | |
880 | ||
881 | /* Get the tile width and height from the packet buffer */ | |
882 | tile_width = packet_buffer[4] * 256 + packet_buffer[5]; | |
883 | tile_height = packet_buffer[6] * 256 + packet_buffer[7]; | |
884 | ||
885 | /* Set the encoding type to RRE */ | |
886 | if (!encoding_type.corre) { | |
887 | /* CoRRE encoding is not supported - just use raw encoding */ | |
888 | goto use_raw_encoding; | |
889 | } | |
890 | ||
891 | /* Set encoding type to CoRRE encoding in packet buffer */ | |
892 | packet_buffer[8+0] = 0; | |
893 | packet_buffer[8+1] = 0; | |
894 | packet_buffer[8+2] = 0; | |
895 | packet_buffer[8+3] = 4; | |
896 | ||
897 | /* Copy tile from the main frame buffer to the local tile buffer */ | |
898 | for (i = 0; i < tile_height; i++) { | |
899 | for (j = 0; j < tile_width; j++) { | |
900 | tile_buffer[i][j] = frame_buffer[y_pos + i][x_pos + j]; | |
901 | } | |
902 | } | |
903 | ||
904 | /* Find the background colour */ | |
905 | /* We just assume the (0, 0) pixel in the tile is the bgcolour */ | |
906 | /* Its quick!!! */ | |
907 | bg_colour = frame_buffer[y_pos][x_pos]; | |
908 | ||
909 | /* Set the background colour in the packet buffer */ | |
910 | if (Bits_Per_Pixel == 8) { | |
911 | packet_buffer[16] = PixelConvert(bg_colour); /* (vnc_color_t) bg_colour; */ | |
912 | } else { | |
913 | packet_buffer[16] = COLOUR2BYTE0(bg_colour); | |
914 | packet_buffer[16+1] = COLOUR2BYTE1(bg_colour); | |
915 | } | |
916 | ||
917 | #ifdef CYGNUM_VNC_SERVER_CORRE_ENCODING_HACK | |
918 | /* Add an initial sub-rectangle to paint the background the background colour */ | |
919 | /* This is required because of a known bug in the VNC viewer (x86 version) */ | |
920 | //#if BITS_PER_PIXEL == 8 | |
921 | packet_buffer[packet_length] = (vnc_color_t) bg_colour; | |
922 | packet_length++; | |
923 | //#endif | |
924 | //#if BITS_PER_PIXEL == 16 | |
925 | // packet_buffer[packet_length] = packet_buffer[16]; | |
926 | // packet_buffer[packet_length+1] = packet_buffer[16+1]; | |
927 | // packet_length += 2; | |
928 | //#endif | |
929 | packet_buffer[packet_length] = (uint8_t) 0; /* Sub-rect x-pos */ | |
930 | packet_buffer[packet_length+1] = (uint8_t) 0; /* Sub-rect y-pos*/ | |
931 | packet_buffer[packet_length+2] = (uint8_t) tile_width; /* Sub-rect width*/ | |
932 | packet_buffer[packet_length+3] = (uint8_t) tile_height; /* Sub-rect height*/ | |
933 | packet_length += 4; | |
934 | no_of_subrects++; /* Increment sub-rectangle count */ | |
935 | #endif | |
936 | ||
937 | /* Scan trough tile and find sub-rectangles */ | |
938 | for (i = 0; i < tile_height; i++) { | |
939 | for (j = 0; j < tile_width; j++) { | |
940 | if (tile_buffer[i][j] != bg_colour) { | |
941 | /* This is a non-background pixel */ | |
942 | subrect_width = 1; | |
943 | pixel_colour = tile_buffer[i][j]; | |
944 | ||
945 | /* Extend the sub-rectangle to its maximum width */ | |
946 | for (subrect_width = 1; subrect_width <= tile_width-j-1; subrect_width++) { | |
947 | if (tile_buffer[i][j+subrect_width] != pixel_colour) { | |
948 | goto got_subrect_width; | |
949 | } | |
950 | } | |
951 | ||
952 | got_subrect_width: | |
953 | ||
954 | /* Extend the sub-rectangle to its maximum height */ | |
955 | for (subrect_height=1; subrect_height <= tile_height-i-1; subrect_height++) { | |
956 | for (k = j; k < j+subrect_width; k++) { | |
957 | if (tile_buffer[i+subrect_height][k] != pixel_colour) { | |
958 | goto got_subrect_height; | |
959 | } | |
960 | } | |
961 | } | |
962 | ||
963 | got_subrect_height: | |
964 | ||
965 | /* Delete the pixels for the sub-rectangle from the sub-rectangle */ | |
966 | for (k = i; k < i+subrect_height; k++) { | |
967 | for (l = j; l < j+subrect_width; l++) { | |
968 | tile_buffer[k][l] = bg_colour; | |
969 | } | |
970 | } | |
971 | ||
972 | /* Append new sub-rectangle data to the packet buffer */ | |
973 | if (Bits_Per_Pixel == 8) { | |
974 | packet_buffer[packet_length] = PixelConvert(pixel_colour); // (vnc_color_t) pixel_colour; | |
975 | packet_length++; | |
976 | } else { | |
977 | packet_buffer[packet_length] = COLOUR2BYTE0(pixel_colour); | |
978 | packet_buffer[packet_length+1] = COLOUR2BYTE1(pixel_colour); | |
979 | packet_length += 2; | |
980 | } | |
981 | ||
982 | packet_buffer[packet_length] = (uint8_t) j; /* Sub-rect x-pos */ | |
983 | packet_length++; | |
984 | ||
985 | packet_buffer[packet_length] = (uint8_t) i; /* Sub-rect y-pos*/ | |
986 | packet_length++; | |
987 | ||
988 | packet_buffer[packet_length] = (uint8_t) subrect_width; /* Sub-rect width*/ | |
989 | packet_length++; | |
990 | ||
991 | packet_buffer[packet_length] = (uint8_t) subrect_height; /* Sub-rect height*/ | |
992 | packet_length++; | |
993 | ||
994 | no_of_subrects++; /* Increment sub-rectangle count */ | |
995 | ||
996 | if (packet_length >= 12 + tile_height*tile_width*(BITS_PER_PIXEL/8) - 6) { | |
997 | /* The next sub-rectangle will make the packet size */ | |
998 | /* larger than a rew encoded packet - so just use raw */ | |
999 | goto use_raw_encoding; | |
1000 | } | |
1001 | } | |
1002 | } | |
1003 | } | |
1004 | ||
1005 | /* Fill in no_of_sub-rectangles field in packet buffer */ | |
1006 | packet_buffer[12+0] = 0; | |
1007 | packet_buffer[12+1] = 0; | |
1008 | packet_buffer[12+2] = no_of_subrects / 256; | |
1009 | packet_buffer[12+3] = no_of_subrects % 256; | |
1010 | ||
1011 | /* CoRRE data encoding for tile complete */ | |
1012 | return packet_length; | |
1013 | ||
1014 | use_raw_encoding: | |
1015 | ||
1016 | /* Create packet data using RAW encoding */ | |
1017 | for (i = 0; i < tile_height; i++) { | |
1018 | for (j = 0; j < tile_width; j++) { | |
1019 | if (Bits_Per_Pixel == 8) { | |
1020 | packet_buffer[12 + tile_width * i + j] = PixelConvert(frame_buffer[y_pos + i][x_pos + j]); | |
1021 | } else { | |
1022 | packet_buffer[12 + 2 * tile_width * i + 2*j] = COLOUR2BYTE0(frame_buffer[y_pos + i][x_pos + j]); | |
1023 | packet_buffer[12 + 2 * tile_width * i + 2*j+ 1] = COLOUR2BYTE1(frame_buffer[y_pos + i][x_pos + j]); | |
1024 | } | |
1025 | } | |
1026 | } | |
1027 | ||
1028 | /* Set the encoding type to raw */ | |
1029 | packet_buffer[8+0] = 0; | |
1030 | packet_buffer[8+1] = 0; | |
1031 | packet_buffer[8+2] = 0; | |
1032 | packet_buffer[8+3] = 0; | |
1033 | ||
1034 | return (12 + tile_width*tile_height*(Bits_Per_Pixel/8)); | |
1035 | } | |
1036 | ||
1037 | ||
1038 | ||
1039 | /** | |
1040 | * @brief Get message data function | |
1041 | * This function is called by the client_handler thread to get data | |
1042 | * from the client's socket. | |
1043 | * | |
1044 | * @param socket_fd File descriptor of the socket to get the data from. | |
1045 | * @param *buffer Buffer to store received data in. | |
1046 | * @param num_bytes Number of bytes to attempt to get. | |
1047 | * | |
1048 | * @return 1 on sucessfull completion - 0 on error. | |
1049 | */ | |
1050 | static int GetMessageData(int socket_fd, char *buffer, int num_bytes) | |
1051 | { | |
1052 | int bytes_rxd; | |
1053 | int message_len = 0; | |
1054 | ||
1055 | while (message_len < num_bytes) { | |
1056 | if ((bytes_rxd = recv(socket_fd, buffer, num_bytes, 0)) <= 0) { | |
1057 | return 0; | |
1058 | } | |
1059 | message_len += bytes_rxd; | |
1060 | } | |
1061 | ||
1062 | return 1; | |
1063 | } | |
1064 | ||
1065 | ||
1066 | ||
1067 | void VncCls(vnc_color_t color) | |
1068 | { | |
1069 | /* Clear the frame buffer */ | |
1070 | int i, j; | |
1071 | ||
1072 | for (i = 0; i < CONFIG_VNC_SERVER_FRAME_HEIGHT; i++) { | |
1073 | for (j = 0; j < CONFIG_VNC_SERVER_FRAME_WIDTH; j++) { | |
1074 | VncDrawPixel(j, i, color); | |
1075 | } | |
1076 | } | |
1077 | } | |
1078 | ||
1079 | ||
1080 | ||
1081 | void IRAM_ATTR VncDrawPixel(uint16_t x, uint16_t y, vnc_color_t color) | |
1082 | { | |
1083 | if (x >= CONFIG_VNC_SERVER_FRAME_WIDTH || y >= CONFIG_VNC_SERVER_FRAME_HEIGHT) { | |
1084 | printf("write_frame(%d, %d) tile %d/%d\n", x, y, x/TILE_SIZE, y/TILE_SIZE); | |
1085 | return; | |
1086 | } | |
1087 | ||
1088 | /* Set that pixel to 'colour' */ | |
1089 | frame_buffer[y][x] = color; | |
1090 | ||
1091 | /* Mark the tile for update */ | |
1092 | tile_updated[y/TILE_SIZE][x/TILE_SIZE] = 1; | |
1093 | } | |
1094 | ||
1095 | ||
1096 | ||
1097 | void IRAM_ATTR VncDrawHorzLine(uint16_t x1, uint16_t x2, uint16_t y, vnc_color_t color) | |
1098 | { | |
1099 | int i; | |
1100 | ||
1101 | /* Draw the line */ | |
1102 | for (i = x1; i <= x2; i++) { | |
1103 | VncDrawPixel(i, y, color); | |
1104 | } | |
1105 | } | |
1106 | ||
1107 | ||
1108 | ||
1109 | void IRAM_ATTR VncDrawVertLine(uint16_t x, uint16_t y1, uint16_t y2, vnc_color_t color) | |
1110 | { | |
1111 | int i; | |
1112 | ||
1113 | /* Draw the line */ | |
1114 | for (i = y1; i <= y2; i++) { | |
1115 | VncDrawPixel(x, i, color); | |
1116 | } | |
1117 | } | |
1118 | ||
1119 | ||
1120 | ||
1121 | void IRAM_ATTR VncFillRect(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, vnc_color_t color) | |
1122 | { | |
1123 | /* Draw a solid rectangle */ | |
1124 | int i, j; | |
1125 | ||
1126 | for (i = y1; i <= y2; i++) { | |
1127 | for (j = x1; j <= x2; j++) { | |
1128 | VncDrawPixel(j, i, color); | |
1129 | } | |
1130 | } | |
1131 | } | |
1132 | ||
1133 | ||
1134 | ||
1135 | void VncSoundBell(void) | |
1136 | { | |
1137 | if (xSemaphoreTake(SoundBell_lock, 10) == pdTRUE) { | |
1138 | SoundBellCount++; | |
1139 | xSemaphoreGive(SoundBell_lock); | |
1140 | } | |
1141 | } | |
1142 | ||
1143 | ||
1144 | ||
1145 | /** | |
1146 | * @brief Websocket task. Created on a websocket connection. | |
1147 | */ | |
1148 | void task_WS(void *pvParameter) | |
1149 | { | |
1150 | int i, j, num_of_encodings, rest_time; | |
1151 | uint16_t *ptr_to_uint16; | |
1152 | struct strMessage _msg; | |
1153 | ||
1154 | // Do pin/pong. | |
1155 | ||
1156 | VNC_WS_run = true; | |
1157 | rest_time = 0; | |
1158 | ||
1159 | while (VNC_WS_run) { | |
1160 | ||
1161 | /* | |
1162 | * Wait until something is received from the client. Exit the queue receiver | |
1163 | * each 5 milliseconds to allow to evaluate the VNC_WS_run flag and to allow | |
1164 | * a clean exit of this task. | |
1165 | */ | |
1166 | if (xQueueReceive(message_queue, &_msg, 5 / portTICK_PERIOD_MS) == pdTRUE) { | |
1167 | // dump_msg((char *)_msg.message, _msg.length, "pull"); | |
1168 | ||
1169 | switch (_msg.message[0]) { | |
1170 | ||
1171 | case SET_PIXEL_FORMAT: | |
1172 | // printf("SET_PIXEL_FORMAT\n"); | |
1173 | /* Check total message length */ | |
1174 | if (_msg.length >= 20) { | |
1175 | /* Check pixel format is as expected */ | |
1176 | i = 0; | |
1177 | if (_msg.message[4] != BITS_PER_PIXEL) { | |
1178 | ESP_LOGI(TAG, "SetPixelFormat client wants %d bits-per-pixel", _msg.message[4]); | |
1179 | i++; | |
1180 | } | |
1181 | ||
1182 | if (_msg.message[5] != PIXEL_DEPTH) { | |
1183 | ESP_LOGI(TAG, "SetPixelFormat client wants %d pixel-depth", _msg.message[5]); | |
1184 | i++; | |
1185 | } | |
1186 | ||
1187 | if ((_msg.message[7] & 0x01) != TRUE_COLOUR_FLAG) { | |
1188 | ESP_LOGI(TAG, "SetPixelFormat client wants %d true-colour-flag", _msg.message[7]); | |
1189 | i++; | |
1190 | } | |
1191 | ||
1192 | ptr_to_uint16 = (uint16_t *)&(_msg.message[8]); | |
1193 | if (htons(*ptr_to_uint16) != Red_Max) { | |
1194 | Red_Max = htons(*ptr_to_uint16); | |
1195 | AltPixels = true; | |
1196 | ESP_LOGI(TAG, "SetPixelFormat client granted %d red-max", Red_Max); | |
1197 | } | |
1198 | ||
1199 | ptr_to_uint16 = (uint16_t *)&(_msg.message[10]); | |
1200 | if (htons(*ptr_to_uint16) != Green_Max) { | |
1201 | Green_Max = htons(*ptr_to_uint16); | |
1202 | AltPixels = true; | |
1203 | ESP_LOGI(TAG, "SetPixelFormat client granted %d green-max", Green_Max); | |
1204 | } | |
1205 | ||
1206 | ptr_to_uint16 = (uint16_t *)&(_msg.message[12]); | |
1207 | if (htons(*ptr_to_uint16) != Blue_Max) { | |
1208 | Blue_Max = htons(*ptr_to_uint16); | |
1209 | AltPixels = true; | |
1210 | ESP_LOGI(TAG, "SetPixelFormat client granted %d blue-max", Blue_Max); | |
1211 | } | |
1212 | ||
1213 | if (_msg.message[14] != Red_Shift) { | |
1214 | Red_Shift = _msg.message[14]; | |
1215 | AltPixels = true; | |
1216 | ESP_LOGI(TAG, "SetPixelFormat client granted %d red-shift", Red_Shift); | |
1217 | } | |
1218 | ||
1219 | if (_msg.message[15] != Green_Shift) { | |
1220 | Green_Shift = _msg.message[15]; | |
1221 | AltPixels = true; | |
1222 | ESP_LOGI(TAG, "SetPixelFormat client granted %d green-shift", Green_Shift); | |
1223 | } | |
1224 | ||
1225 | if (_msg.message[16] != Blue_Shift) { | |
1226 | Blue_Shift = _msg.message[16]; | |
1227 | AltPixels = true; | |
1228 | ESP_LOGI(TAG, "SetPixelFormat client granted %d blue-shift", Blue_Shift); | |
1229 | } | |
1230 | ||
1231 | if (i) { | |
1232 | ESP_LOGI(TAG, "SetPixelFormat %d errors, disconnect client", i); | |
1233 | ESP_LOGI(TAG, "Ensure the 'Auto select' is not enabled in your vncviewer options,"); | |
1234 | _msg.length = 0; | |
1235 | ws_server_remove_client(VNC_WS_num); | |
1236 | } | |
1237 | } | |
1238 | break; | |
1239 | ||
1240 | case FIX_COLOUR_MAP_ENTRIES: | |
1241 | printf("FIX_COLOUR_MAP_ENTRIES\n"); | |
1242 | break; | |
1243 | ||
1244 | case SET_ENCODINGS: | |
1245 | if (_msg.length >= 3) { | |
1246 | // Get the total encodings | |
1247 | num_of_encodings = _msg.message[2]*255 + _msg.message[3]; | |
1248 | ||
1249 | /* Clear the encoding type structure */ | |
1250 | encoding_type.raw = 0; | |
1251 | encoding_type.copy_rectangle = 0; | |
1252 | encoding_type.rre = 0; | |
1253 | encoding_type.corre = 0; | |
1254 | encoding_type.hextile = 0; | |
1255 | ||
1256 | for (i = 0; i < num_of_encodings; i++) { | |
1257 | switch(_msg.message[3 + (i*4)]) { | |
1258 | case 0: /* Raw encoding */ | |
1259 | encoding_type.raw = i + 1; | |
1260 | break; | |
1261 | case 1: /* Copy rectangle encoding */ | |
1262 | encoding_type.copy_rectangle = i + 1; | |
1263 | break; | |
1264 | case 2: /* RRE encoding */ | |
1265 | encoding_type.rre = i + 1; | |
1266 | break; | |
1267 | case 4: /* CoRRE encoding */ | |
1268 | encoding_type.corre = i + 1; | |
1269 | break; | |
1270 | case 5: /* Hextile encoding */ | |
1271 | encoding_type.hextile = i + 1; | |
1272 | break; | |
1273 | default: /* Unknown coding type - do nothing */ | |
1274 | break; | |
1275 | } | |
1276 | } | |
1277 | ||
1278 | if (encoding_type.corre) { | |
1279 | ESP_LOGI(TAG, "SetEncodings use CORRE"); | |
1280 | } else { | |
1281 | ESP_LOGI(TAG, "SetEncodings use RAW"); | |
1282 | } | |
1283 | } | |
1284 | break; | |
1285 | ||
1286 | case FRAME_BUFFER_UPDATE_REQ: | |
1287 | if (_msg.length == 10) { | |
1288 | if (!_msg.message[1]) { | |
1289 | /* Non-incremental mode - mark the squares that need to be updated */ | |
1290 | for (i = (_msg.message[2]*255 + _msg.message[3])/TILE_SIZE; | |
1291 | i <= (_msg.message[2]*255 + _msg.message[3] + _msg.message[6]*255 + _msg.message[7])/TILE_SIZE; | |
1292 | i++) { | |
1293 | for (j = (_msg.message[4]*255 + _msg.message[5])/TILE_SIZE; | |
1294 | j <= (_msg.message[4]*255 + _msg.message[5] + _msg.message[8]*255 + _msg.message[9])/TILE_SIZE; | |
1295 | j++) { | |
1296 | tile_updated[j][i] = 1; | |
1297 | } | |
1298 | } | |
1299 | } | |
1300 | ||
1301 | /* Signal that there is now a pending update request */ | |
1302 | xEventGroupSetBits(xEventGroupVNC, VNC_CLIENT_UPDATE_REQ); | |
1303 | } | |
1304 | break; | |
1305 | ||
1306 | case KEY_EVENT: | |
1307 | printf("KEY_EVENT\n"); | |
1308 | /* Handle the key event, ignored for brewboard */ | |
1309 | /* Get the remainder of message (7 bytes) */ | |
1310 | break; | |
1311 | ||
1312 | case POINTER_EVENT: | |
1313 | /* Handle the pointer event, simulate the touch screen. */ | |
1314 | if (_msg.length == 6) { | |
1315 | /* Set global variables that will be read by another task. */ | |
1316 | VNC_pointer_button = _msg.message[1]; | |
1317 | VNC_pointer_x = _msg.message[2]*255 + _msg.message[3]; | |
1318 | VNC_pointer_y = _msg.message[4]*255 + _msg.message[5]; | |
1319 | // printf("POINTER_EVENT button=%02x x=%d y=%d\n", VNC_pointer_button, VNC_pointer_x, VNC_pointer_y); | |
1320 | } | |
1321 | _msg.length = 0; | |
1322 | break; | |
1323 | ||
1324 | case CLIENT_CUT_TEXT: | |
1325 | printf("CLIENT_CUT_TEXT\n"); | |
1326 | break; | |
1327 | ||
1328 | default: | |
1329 | ESP_LOGI(TAG, "Unknown message %d from client", _msg.message[0]); | |
1330 | } | |
1331 | rest_time = 0; | |
1332 | } else { | |
1333 | rest_time++; // Each 5 milliseconds | |
1334 | if (rest_time == 2000) { | |
1335 | // 10 seconds silence, send a PING | |
1336 | rest_time = 0; | |
1337 | // int rc = ws_server_ping(VNC_WS_num); | |
1338 | // ESP_LOGI(TAG, "Send PING to %d, rc=%d", VNC_WS_num, rc); | |
1339 | } | |
1340 | } | |
1341 | } | |
1342 | ||
1343 | VNC_WS_num = -1; | |
1344 | xEventGroupClearBits(xEventGroupVNC, VNC_CLIENT_UPDATE_REQ); | |
1345 | ESP_LOGI(TAG, "task_WS finished"); | |
1346 | vTaskDelete(NULL); | |
1347 | } | |
1348 | ||
1349 | ||
1350 | ||
1351 | int VncStartWS(int num) | |
1352 | { | |
1353 | char protocol_ver[8], message_buffer[MESSAGE_BUFFER_SIZE]; | |
1354 | int i, ProtocolOkay; | |
1355 | uint32_t *ptr_to_uint32; | |
1356 | uint16_t *ptr_to_uint16; | |
1357 | struct strMessage _msg; | |
1358 | ||
1359 | ESP_LOGI(TAG, "Start VNC WebSocket connection %d", num); | |
1360 | message_queue = xQueueCreate(message_queue_size, sizeof(struct strMessage)); | |
1361 | ||
1362 | /* | |
1363 | * Initial handshake | |
1364 | */ | |
1365 | ws_server_send_bin_client(num, "RFB 003.003\n", 12); | |
1366 | ProtocolOkay = 1; | |
1367 | if (xQueueReceive(message_queue, &_msg, 5000 / portTICK_PERIOD_MS) == pdTRUE) { | |
1368 | // dump_msg((char *)_msg.message, _msg.length, "pull"); | |
1369 | ||
1370 | for (i = 0; i < 8; i++) { | |
1371 | if (_msg.message[i] != server_ProtocolVersion[i]) { | |
1372 | ProtocolOkay = 0; | |
1373 | } | |
1374 | /* Store the protocol version - ignoring thr 'RFB ' part */ | |
1375 | protocol_ver[i] = _msg.message[i + 4]; | |
1376 | } | |
1377 | protocol_ver[7] = 0; | |
1378 | } else { | |
1379 | ESP_LOGE(TAG, "Client timeout after initial message"); | |
1380 | return -5; | |
1381 | } | |
1382 | ||
1383 | if (ProtocolOkay == 0) { | |
1384 | /* Generate the Bad ProtocolVerion message */ | |
1385 | ESP_LOGI(TAG, "Start VNC WebSocket task failed, bad protocol."); | |
1386 | ptr_to_uint32 = (uint32_t *) &(message_buffer[0]); | |
1387 | *ptr_to_uint32 = htonl(0); | |
1388 | ptr_to_uint32 = (uint32_t *) &(message_buffer[4]); | |
1389 | *ptr_to_uint32 = htonl(strlen(bad_protocol)); | |
1390 | strcpy(&(message_buffer[8]), bad_protocol); | |
1391 | ws_server_send_bin_client(num, message_buffer, 8 + strlen(bad_protocol)); | |
1392 | vTaskDelay(500 / portTICK_PERIOD_MS); | |
1393 | ws_server_remove_client(num); | |
1394 | return -2; | |
1395 | } | |
1396 | /* ProtocolVerion is okay - connect with no authentication*/ | |
1397 | ||
1398 | /* Server is busy with another client */ | |
1399 | if (VNC_WS_run || vnc_client_connected) { | |
1400 | ESP_LOGI(TAG, "Start VNC WebSocket task failed, server busy."); | |
1401 | ptr_to_uint32 = (uint32_t *) &(message_buffer[0]); | |
1402 | *ptr_to_uint32 = htonl(0); | |
1403 | ptr_to_uint32 = (uint32_t *) &(message_buffer[4]); | |
1404 | *ptr_to_uint32 = htonl(strlen(server_busy)); | |
1405 | strcpy(&(message_buffer[8]), server_busy); | |
1406 | ws_server_send_bin_client(num, message_buffer, 8 + strlen(server_busy)); | |
1407 | vTaskDelay(500 / portTICK_PERIOD_MS); | |
1408 | ws_server_remove_client(num); | |
1409 | return -1; | |
1410 | } | |
1411 | ||
1412 | /* Generate the No Authentication message */ | |
1413 | ptr_to_uint32 = (uint32_t *) &(message_buffer[0]); | |
1414 | *ptr_to_uint32 = htonl((uint32_t)1); | |
1415 | ws_server_send_bin_client(num, message_buffer, 4); | |
1416 | /* Authentication - end */ | |
1417 | ||
1418 | /* ClientInitialisation - begin */ | |
1419 | /* Receive initialisation message from client (1 byte) */ | |
1420 | if (xQueueReceive(message_queue, &_msg, 5000 / portTICK_PERIOD_MS) == pdFALSE) { | |
1421 | ESP_LOGE(TAG, "Client timeout after auth message"); | |
1422 | return -5; | |
1423 | } | |
1424 | /* Do nothing with this as we only support 1 Client at a time */ | |
1425 | /* ClientInitialisation - end */ | |
1426 | ||
1427 | /* Initial default RGB332 */ | |
1428 | Red_Max = RED_MAX; | |
1429 | Green_Max = GREEN_MAX; | |
1430 | Blue_Max = BLUE_MAX; | |
1431 | Red_Shift = RED_SHIFT; | |
1432 | Green_Shift = GREEN_SHIFT; | |
1433 | Blue_Shift = BLUE_SHIFT; | |
1434 | AltPixels = false; | |
1435 | ||
1436 | /* ServerInitialisation - begin */ | |
1437 | /* Create Initialisation message for client */ | |
1438 | ptr_to_uint16 = (uint16_t *) &(message_buffer[0]); | |
1439 | *ptr_to_uint16 = htons((uint16_t)CONFIG_VNC_SERVER_FRAME_WIDTH); | |
1440 | ||
1441 | ptr_to_uint16 = (uint16_t *) &(message_buffer[2]); | |
1442 | *ptr_to_uint16 = htons((uint16_t)CONFIG_VNC_SERVER_FRAME_HEIGHT); | |
1443 | ||
1444 | message_buffer[4] = (uint8_t)BITS_PER_PIXEL; | |
1445 | message_buffer[5] = (uint8_t)PIXEL_DEPTH; | |
1446 | message_buffer[6] = (uint8_t)BIG_ENDIAN_FLAG; | |
1447 | message_buffer[7] = (uint8_t)TRUE_COLOUR_FLAG; | |
1448 | ||
1449 | ptr_to_uint16 = (uint16_t *) &(message_buffer[8]); | |
1450 | *ptr_to_uint16 = htons(Red_Max); | |
1451 | ||
1452 | ptr_to_uint16 = (uint16_t *) &(message_buffer[10]); | |
1453 | *ptr_to_uint16 = htons(Green_Max); | |
1454 | ||
1455 | ptr_to_uint16 = (uint16_t *) &(message_buffer[12]); | |
1456 | *ptr_to_uint16 = htons(Blue_Max); | |
1457 | ||
1458 | message_buffer[14] = Red_Shift; | |
1459 | message_buffer[15] = Green_Shift; | |
1460 | message_buffer[16] = Blue_Shift; | |
1461 | ||
1462 | ptr_to_uint32 = (uint32_t *) &(message_buffer[20]); | |
1463 | *ptr_to_uint32 = htonl(strlen(desktop_name)); | |
1464 | strcpy(&(message_buffer[24]), desktop_name); | |
1465 | ||
1466 | // dump_msg(message_buffer, 24 + strlen(desktop_name), (char *)"send"); | |
1467 | ws_server_send_bin_client(num, message_buffer, 24 + strlen(desktop_name)); | |
1468 | ||
1469 | /* ServerInitialisation - end */ | |
1470 | ESP_LOGI(TAG, "VNC WebSocket client connected (RFB Protocol Ver: %s)", protocol_ver); | |
1471 | ||
1472 | VNC_WS_num = num; | |
1473 | xTaskCreate(&task_WS, "WSclient", MIN_STACK_SIZE, NULL, 5, &xTaskWSclient); | |
1474 | return 0; | |
1475 | } | |
1476 | ||
1477 | ||
1478 | ||
1479 | void VncStopWS(int num) | |
1480 | { | |
1481 | if (! VNC_WS_run) { | |
1482 | ESP_LOGI(TAG, "Stop VNC WebSocket, not running."); | |
1483 | return; | |
1484 | } | |
1485 | ||
1486 | if (num != VNC_WS_num) { | |
1487 | ESP_LOGI(TAG, "Stop VNC WebSocket, %d is running, requested %d", VNC_WS_num, num); | |
1488 | return; | |
1489 | } | |
1490 | ||
1491 | ESP_LOGI(TAG, "Stop VNC WebSocket task connection %d", num); | |
1492 | VNC_WS_run = false; | |
1493 | xQueueReset(message_queue); | |
1494 | } | |
1495 | ||
1496 | ||
1497 | ||
1498 | void VncGetWSmessage(char *msg, uint16_t len) | |
1499 | { | |
1500 | int max; | |
1501 | struct strMessage _msg; | |
1502 | ||
1503 | if (len == 0) | |
1504 | return; | |
1505 | ||
1506 | max = len; | |
1507 | if (max > MESSAGE_BUFFER_SIZE) { | |
1508 | ESP_LOGE(TAG, "VncGetWSmessage need %d bytes in a %d length buffer", len, MESSAGE_BUFFER_SIZE); | |
1509 | max = MESSAGE_BUFFER_SIZE; | |
1510 | } | |
1511 | ||
1512 | for (int i = 0; i < max; i++) { | |
1513 | _msg.message[i] = msg[i]; | |
1514 | } | |
1515 | _msg.length = max; | |
1516 | if (xQueueSendToBack(message_queue, &_msg, 200 / portTICK_PERIOD_MS) == pdFALSE) { | |
1517 | ESP_LOGE(TAG, "VncGetWSmessage() Queue full, message lost"); | |
1518 | } | |
1519 | } | |
1520 |