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