components/vnc_server/vnc-server.c

changeset 0
b74b0e4902c3
child 1
ad2c8b13eb88
equal deleted inserted replaced
-1:000000000000 0:b74b0e4902c3
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

mercurial