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