nuttx-apps/system/vncviewer/rfb_protocol.c
wangjianyu3 62f3d37041 examples/vncviewer: add VNC viewer for LCD display
Add a minimal VNC viewer application that connects to a VNC server
and renders the remote desktop on a local LCD.

Features:
- RFB 3.8 protocol with VNC Authentication (DES, pure software)
- Raw encoding with pixel format auto-detected from LCD driver
- Row-by-row rendering via LCDDEVIO_PUTAREA (minimal RAM usage)
- LCD resolution and format queried at runtime via LCDDEVIO_GETVIDEOINFO
- Auto-reconnect on connection loss with 2-second retry

Usage:
  vncviewer <host> [port]
  vncviewer -p <password> <host> [port]
  vncviewer -p <password> -d <lcd_devno> <host> [port]

Assisted-by: GitHubCopilot:claude-4.6-opus
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
2026-04-24 14:58:49 +02:00

1051 lines
26 KiB
C

/****************************************************************************
* apps/system/vncviewer/rfb_protocol.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "rfb_protocol.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Data - DES tables for VNC authentication
****************************************************************************/
/* Initial permutation */
static const uint8_t g_ip[64] =
{
58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7
};
/* Final permutation (IP inverse) */
static const uint8_t g_fp[64] =
{
40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25
};
/* Expansion permutation */
static const uint8_t g_expand[48] =
{
32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,
8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1
};
/* Permuted choice 1 */
static const uint8_t g_pc1[56] =
{
57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18,
10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36,
63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22,
14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4
};
/* Permuted choice 2 */
static const uint8_t g_pc2[48] =
{
14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10,
23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2,
41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
};
/* P-box permutation */
static const uint8_t g_pbox[32] =
{
16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26,
5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9,
19, 13, 30, 6, 22, 11, 4, 25
};
/* S-boxes */
static const uint8_t g_sbox[8][64] =
{
{
14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
},
{
15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
},
{
10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
},
{
7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
},
{
2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
},
{
12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
},
{
4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
},
{
13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
}
};
/* Key schedule left shifts */
static const uint8_t g_shifts[] =
{
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: des_getbit / des_setbit
*
* Description:
* Get/set a single bit in a byte array (1-indexed, MSB first).
*
****************************************************************************/
static int des_getbit(const uint8_t *data, int bitnum)
{
return (data[(bitnum - 1) / 8] >> (7 - ((bitnum - 1) % 8))) & 1;
}
static void des_setbit(uint8_t *data, int bitnum, int val)
{
int byte_idx = (bitnum - 1) / 8;
int bit_idx = 7 - ((bitnum - 1) % 8);
if (val)
{
data[byte_idx] |= (uint8_t)(1 << bit_idx);
}
else
{
data[byte_idx] &= (uint8_t)~(1 << bit_idx);
}
}
/****************************************************************************
* Name: des_permute
*
* Description:
* Apply a permutation table to a bit array.
*
****************************************************************************/
static void des_permute(const uint8_t *in, uint8_t *out,
const uint8_t *table, int n)
{
int i;
memset(out, 0, (n + 7) / 8);
for (i = 0; i < n; i++)
{
des_setbit(out, i + 1, des_getbit(in, table[i]));
}
}
/****************************************************************************
* Name: des_encrypt_block
*
* Description:
* DES ECB encrypt a single 8-byte block in place.
* Straightforward implementation using standard tables.
*
****************************************************************************/
static void des_encrypt_block(uint8_t *block, const uint8_t *key)
{
uint8_t state[8];
uint8_t cd[7];
uint8_t l[4];
uint8_t r[4];
uint8_t er[6];
uint8_t subkey[6];
uint8_t sout[4];
uint8_t f[4];
uint8_t newl[4];
uint8_t combined[8];
uint8_t result[8];
uint32_t c;
uint32_t d;
int round;
int i;
int s;
/* Initial permutation */
des_permute(block, state, g_ip, 64);
memcpy(l, state, 4);
memcpy(r, state + 4, 4);
/* Key schedule: apply PC1 to key */
des_permute(key, cd, g_pc1, 56);
/* Extract C (bits 1-28) and D (bits 29-56) as 28-bit values */
for (round = 0; round < 16; round++)
{
int shift = g_shifts[round];
c = ((uint32_t)cd[0] << 20) | ((uint32_t)cd[1] << 12) |
((uint32_t)cd[2] << 4) | ((uint32_t)cd[3] >> 4);
d = (((uint32_t)cd[3] & 0x0f) << 24) | ((uint32_t)cd[4] << 16) |
((uint32_t)cd[5] << 8) | (uint32_t)cd[6];
/* Left rotate C and D by shift positions */
c = ((c << shift) | (c >> (28 - shift))) & 0x0fffffff;
d = ((d << shift) | (d >> (28 - shift))) & 0x0fffffff;
/* Pack back into cd[] */
cd[0] = (uint8_t)(c >> 20);
cd[1] = (uint8_t)(c >> 12);
cd[2] = (uint8_t)(c >> 4);
cd[3] = (uint8_t)((c << 4) | (d >> 24));
cd[4] = (uint8_t)(d >> 16);
cd[5] = (uint8_t)(d >> 8);
cd[6] = (uint8_t)d;
/* Apply PC2 to get 48-bit subkey */
des_permute(cd, subkey, g_pc2, 48);
/* Expand R from 32 to 48 bits */
des_permute(r, er, g_expand, 48);
/* XOR with subkey */
for (i = 0; i < 6; i++)
{
er[i] ^= subkey[i];
}
/* S-box substitution: 48 bits -> 32 bits */
memset(sout, 0, 4);
for (s = 0; s < 8; s++)
{
int off = s * 6;
int b0 = des_getbit(er, off + 1);
int b1 = des_getbit(er, off + 2);
int b2 = des_getbit(er, off + 3);
int b3 = des_getbit(er, off + 4);
int b4 = des_getbit(er, off + 5);
int b5 = des_getbit(er, off + 6);
int row = (b0 << 1) | b5;
int col = (b1 << 3) | (b2 << 2) | (b3 << 1) | b4;
int val = g_sbox[s][row * 16 + col];
sout[s / 2] |= (uint8_t)(val << (((s & 1) == 0) ? 4 : 0));
}
/* P-box permutation */
des_permute(sout, f, g_pbox, 32);
/* newR = L XOR f(R, K); L = old R */
for (i = 0; i < 4; i++)
{
newl[i] = l[i] ^ f[i];
}
memcpy(l, r, 4);
memcpy(r, newl, 4);
}
/* Final: combine R16 | L16 (note the swap) and apply FP */
memcpy(combined, r, 4);
memcpy(combined + 4, l, 4);
des_permute(combined, result, g_fp, 64);
memcpy(block, result, 8);
}
/****************************************************************************
* Name: vnc_encrypt_challenge
*
* Description:
* Encrypt 16-byte VNC challenge using password as DES key.
* VNC reverses bits in each byte of the password.
*
****************************************************************************/
static void vnc_encrypt_challenge(uint8_t *challenge,
const char *password)
{
uint8_t key[8];
int i;
memset(key, 0, 8);
for (i = 0; i < 8 && password[i] != '\0'; i++)
{
uint8_t c = (uint8_t)password[i];
/* Reverse bits in byte (VNC quirk) */
key[i] = (uint8_t)(((c & 0x01) << 7) | ((c & 0x02) << 5) |
((c & 0x04) << 3) | ((c & 0x08) << 1) |
((c & 0x10) >> 1) | ((c & 0x20) >> 3) |
((c & 0x40) >> 5) | ((c & 0x80) >> 7));
}
/* Encrypt two 8-byte blocks */
des_encrypt_block(challenge, key);
des_encrypt_block(challenge + 8, key);
}
/****************************************************************************
* Name: rfb_recv_exact
*
* Description:
* Receive exactly 'len' bytes from socket.
*
****************************************************************************/
static int rfb_recv_exact(int sockfd, void *buf, size_t len)
{
uint8_t *p = (uint8_t *)buf;
ssize_t n;
while (len > 0)
{
n = recv(sockfd, p, len, 0);
if (n <= 0)
{
if (n == 0)
{
return -ECONNRESET;
}
if (errno == EINTR)
{
continue;
}
return -errno;
}
p += n;
len -= n;
}
return OK;
}
/****************************************************************************
* Name: rfb_send_exact
*
* Description:
* Send exactly 'len' bytes to socket.
*
****************************************************************************/
static int rfb_send_exact(int sockfd, const void *buf, size_t len)
{
const uint8_t *p = (const uint8_t *)buf;
ssize_t n;
while (len > 0)
{
n = send(sockfd, p, len, 0);
if (n <= 0)
{
if (errno == EINTR)
{
continue;
}
return -errno;
}
p += n;
len -= n;
}
return OK;
}
/****************************************************************************
* Name: rfb_negotiate_version
*
* Description:
* Exchange RFB protocol version strings.
*
****************************************************************************/
static int rfb_negotiate_version(int sockfd)
{
char buf[RFB_VERSION_LEN + 1];
int ret;
/* Receive server version */
ret = rfb_recv_exact(sockfd, buf, RFB_VERSION_LEN);
if (ret < 0)
{
printf("vncviewer: failed to receive server version\n");
return ret;
}
buf[RFB_VERSION_LEN] = '\0';
printf("vncviewer: server version: %s", buf);
/* Send our version (3.8) */
ret = rfb_send_exact(sockfd, RFB_VERSION_STRING, RFB_VERSION_LEN);
if (ret < 0)
{
printf("vncviewer: failed to send client version\n");
return ret;
}
return OK;
}
/****************************************************************************
* Name: rfb_negotiate_security
*
* Description:
* Negotiate security type. Supports None and VNC Authentication.
*
****************************************************************************/
static int rfb_negotiate_security(int sockfd, const char *password)
{
uint8_t num_types;
uint8_t types[16];
uint8_t chosen;
uint32_t result;
int ret;
int i;
/* Receive number of security types */
ret = rfb_recv_exact(sockfd, &num_types, 1);
if (ret < 0)
{
return ret;
}
if (num_types == 0)
{
/* Connection failed - read reason string */
uint32_t reason_len;
rfb_recv_exact(sockfd, &reason_len, 4);
reason_len = ntohl(reason_len);
printf("vncviewer: connection refused by server\n");
return -ECONNREFUSED;
}
/* Receive security type list */
ret = rfb_recv_exact(sockfd, types, num_types);
if (ret < 0)
{
return ret;
}
/* Choose security type: prefer None, fallback to VNC Auth */
chosen = RFB_SEC_INVALID;
for (i = 0; i < num_types; i++)
{
if (types[i] == RFB_SEC_NONE)
{
chosen = RFB_SEC_NONE;
break;
}
if (types[i] == RFB_SEC_VNC_AUTH)
{
chosen = RFB_SEC_VNC_AUTH;
}
}
if (chosen == RFB_SEC_INVALID)
{
printf("vncviewer: no supported security type\n");
return -ENOTSUP;
}
/* Send chosen type */
ret = rfb_send_exact(sockfd, &chosen, 1);
if (ret < 0)
{
return ret;
}
printf("vncviewer: security type: %s\n",
chosen == RFB_SEC_NONE ? "None" : "VNC Auth");
if (chosen == RFB_SEC_VNC_AUTH)
{
/* VNC Authentication: receive 16-byte challenge */
uint8_t challenge[16];
ret = rfb_recv_exact(sockfd, challenge, 16);
if (ret < 0)
{
return ret;
}
/* Encrypt challenge with password using DES */
if (password == NULL || password[0] == '\0')
{
printf("vncviewer: WARNING: VNC Auth required but no password\n");
}
else
{
vnc_encrypt_challenge(challenge, password);
}
ret = rfb_send_exact(sockfd, challenge, 16);
if (ret < 0)
{
return ret;
}
}
/* Receive SecurityResult (for both None and VNC Auth in RFB 3.8) */
ret = rfb_recv_exact(sockfd, &result, 4);
if (ret < 0)
{
return ret;
}
result = ntohl(result);
if (result != 0)
{
printf("vncviewer: authentication failed (result=%u)\n",
(unsigned)result);
return -EACCES;
}
printf("vncviewer: authentication OK\n");
return OK;
}
/****************************************************************************
* Name: rfb_client_init
*
* Description:
* Send ClientInit and receive ServerInit.
*
****************************************************************************/
static int rfb_client_init(int sockfd, struct rfb_conn_s *conn)
{
uint8_t shared = 1; /* shared session */
uint8_t buf[24];
uint32_t name_len;
int ret;
/* Send ClientInit */
ret = rfb_send_exact(sockfd, &shared, 1);
if (ret < 0)
{
return ret;
}
/* Receive ServerInit: 2+2+16+4 = 24 bytes header + name */
ret = rfb_recv_exact(sockfd, buf, 24);
if (ret < 0)
{
return ret;
}
conn->fb_width = ntohs(*(uint16_t *)&buf[0]);
conn->fb_height = ntohs(*(uint16_t *)&buf[2]);
/* bytes 4..19 = server pixel format (we'll override it) */
name_len = ntohl(*(uint32_t *)&buf[20]);
if (name_len > sizeof(conn->name) - 1)
{
name_len = sizeof(conn->name) - 1;
}
if (name_len > 0)
{
ret = rfb_recv_exact(sockfd, conn->name, name_len);
if (ret < 0)
{
return ret;
}
}
conn->name[name_len] = '\0';
printf("vncviewer: server desktop: \"%s\" %ux%u\n",
conn->name, conn->fb_width, conn->fb_height);
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: rfb_connect
****************************************************************************/
int rfb_connect(const char *host, uint16_t port)
{
struct sockaddr_in addr;
int sockfd;
int ret;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
printf("vncviewer: socket() failed: %d\n", errno);
return -errno;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
ret = inet_pton(AF_INET, host, &addr.sin_addr);
if (ret <= 0)
{
printf("vncviewer: invalid address: %s\n", host);
close(sockfd);
return -EINVAL;
}
printf("vncviewer: connecting to %s:%u...\n", host, port);
ret = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0)
{
printf("vncviewer: connect() failed: %d\n", errno);
close(sockfd);
return -errno;
}
printf("vncviewer: connected\n");
return sockfd;
}
/****************************************************************************
* Name: rfb_handshake
****************************************************************************/
int rfb_handshake(struct rfb_conn_s *conn, const char *password)
{
int ret;
ret = rfb_negotiate_version(conn->sockfd);
if (ret < 0)
{
return ret;
}
ret = rfb_negotiate_security(conn->sockfd, password);
if (ret < 0)
{
return ret;
}
ret = rfb_client_init(conn->sockfd, conn);
if (ret < 0)
{
return ret;
}
return OK;
}
/****************************************************************************
* Name: rfb_set_pixel_format
****************************************************************************/
int rfb_set_pixel_format(struct rfb_conn_s *conn, uint8_t fmt)
{
uint8_t msg[20];
uint8_t bits;
uint8_t depth;
uint16_t rmax;
uint16_t gmax;
uint16_t bmax;
uint8_t rshift;
uint8_t gshift;
uint8_t bshift;
switch (fmt)
{
case 11: /* FB_FMT_RGB16_565 */
bits = 16; depth = 16;
rmax = 31; gmax = 63; bmax = 31;
rshift = 11; gshift = 5; bshift = 0;
conn->bpp = 2;
break;
case 10: /* FB_FMT_RGB16_555 */
bits = 16; depth = 15;
rmax = 31; gmax = 31; bmax = 31;
rshift = 10; gshift = 5; bshift = 0;
conn->bpp = 2;
break;
case 12: /* FB_FMT_RGB24 */
bits = 32; depth = 24;
rmax = 255; gmax = 255; bmax = 255;
rshift = 16; gshift = 8; bshift = 0;
conn->bpp = 4;
break;
case 13: /* FB_FMT_RGB32 */
bits = 32; depth = 24;
rmax = 255; gmax = 255; bmax = 255;
rshift = 16; gshift = 8; bshift = 0;
conn->bpp = 4;
break;
default:
printf("vncviewer: unsupported LCD format %d\n", fmt);
return -ENOTSUP;
}
memset(msg, 0, sizeof(msg));
/* Message type + 3 bytes padding */
msg[0] = RFB_SET_PIXEL_FORMAT;
/* Pixel format at offset 4 */
msg[4] = bits; /* bits-per-pixel */
msg[5] = depth; /* depth */
msg[6] = 0; /* big-endian = false */
msg[7] = 1; /* true-color = true */
msg[8] = (rmax >> 8) & 0xff;
msg[9] = rmax & 0xff;
msg[10] = (gmax >> 8) & 0xff;
msg[11] = gmax & 0xff;
msg[12] = (bmax >> 8) & 0xff;
msg[13] = bmax & 0xff;
msg[14] = rshift;
msg[15] = gshift;
msg[16] = bshift;
return rfb_send_exact(conn->sockfd, msg, sizeof(msg));
}
/****************************************************************************
* Name: rfb_set_encodings
****************************************************************************/
int rfb_set_encodings(struct rfb_conn_s *conn)
{
uint8_t msg[8];
msg[0] = RFB_SET_ENCODINGS;
msg[1] = 0; /* padding */
/* number-of-encodings = 1 */
msg[2] = 0;
msg[3] = 1;
/* encoding: Raw = 0 */
msg[4] = 0;
msg[5] = 0;
msg[6] = 0;
msg[7] = 0;
return rfb_send_exact(conn->sockfd, msg, sizeof(msg));
}
/****************************************************************************
* Name: rfb_request_update
****************************************************************************/
int rfb_request_update(struct rfb_conn_s *conn, bool incremental,
uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
uint8_t msg[10];
msg[0] = RFB_FB_UPDATE_REQUEST;
msg[1] = incremental ? 1 : 0;
*(uint16_t *)&msg[2] = htons(x);
*(uint16_t *)&msg[4] = htons(y);
*(uint16_t *)&msg[6] = htons(w);
*(uint16_t *)&msg[8] = htons(h);
return rfb_send_exact(conn->sockfd, msg, sizeof(msg));
}
/****************************************************************************
* Name: rfb_recv_update
****************************************************************************/
int rfb_recv_update(struct rfb_conn_s *conn, rfb_rect_cb_t rect_cb,
void *arg)
{
uint8_t msg_type;
int ret;
/* Read message type */
ret = rfb_recv_exact(conn->sockfd, &msg_type, 1);
if (ret < 0)
{
return ret;
}
switch (msg_type)
{
case RFB_FB_UPDATE:
{
uint8_t hdr[3];
uint16_t num_rects;
uint16_t i;
/* Read padding(1) + number-of-rectangles(2) */
ret = rfb_recv_exact(conn->sockfd, hdr, 3);
if (ret < 0)
{
return ret;
}
num_rects = ntohs(*(uint16_t *)&hdr[1]);
for (i = 0; i < num_rects; i++)
{
struct rfb_rect_hdr_s rect;
uint8_t rect_buf[12];
uint32_t row_bytes;
uint32_t row;
/* Read rectangle header: x(2)+y(2)+w(2)+h(2)+encoding(4) */
ret = rfb_recv_exact(conn->sockfd, rect_buf, 12);
if (ret < 0)
{
return ret;
}
rect.x = ntohs(*(uint16_t *)&rect_buf[0]);
rect.y = ntohs(*(uint16_t *)&rect_buf[2]);
rect.w = ntohs(*(uint16_t *)&rect_buf[4]);
rect.h = ntohs(*(uint16_t *)&rect_buf[6]);
rect.encoding = (int32_t)ntohl(*(uint32_t *)&rect_buf[8]);
if (rect.encoding != RFB_ENCODING_RAW)
{
printf("vncviewer: unsupported encoding %d\n",
(int)rect.encoding);
return -ENOTSUP;
}
/* Read pixel data row by row to limit memory usage */
row_bytes = rect.w * conn->bpp;
FAR uint8_t *rowbuf = malloc(row_bytes);
if (rowbuf == NULL)
{
return -ENOMEM;
}
for (row = 0; row < rect.h; row++)
{
struct rfb_rect_hdr_s row_rect;
ret = rfb_recv_exact(conn->sockfd, rowbuf, row_bytes);
if (ret < 0)
{
free(rowbuf);
return ret;
}
/* Callback with single-row rectangle */
row_rect.x = rect.x;
row_rect.y = rect.y + row;
row_rect.w = rect.w;
row_rect.h = 1;
row_rect.encoding = RFB_ENCODING_RAW;
if (rect_cb)
{
rect_cb(&row_rect, rowbuf, arg);
}
}
free(rowbuf);
}
}
break;
case RFB_SET_COLORMAP:
{
/* Skip: padding(1) + first-color(2) + num-colors(2) */
uint8_t cm_hdr[5];
uint16_t num_colors;
ret = rfb_recv_exact(conn->sockfd, cm_hdr, 5);
if (ret < 0)
{
return ret;
}
num_colors = ntohs(*(uint16_t *)&cm_hdr[3]);
/* Skip color entries: num_colors * 6 bytes (R+G+B each 2B) */
uint32_t skip = num_colors * 6;
uint8_t tmp[256];
while (skip > 0)
{
uint32_t chunk = skip > sizeof(tmp) ? sizeof(tmp) : skip;
ret = rfb_recv_exact(conn->sockfd, tmp, chunk);
if (ret < 0)
{
return ret;
}
skip -= chunk;
}
}
break;
case RFB_BELL:
/* No payload */
break;
case RFB_SERVER_CUT_TEXT:
{
uint8_t ct_hdr[7];
uint32_t text_len;
ret = rfb_recv_exact(conn->sockfd, ct_hdr, 7);
if (ret < 0)
{
return ret;
}
text_len = ntohl(*(uint32_t *)&ct_hdr[3]);
/* Skip text content */
uint8_t tmp[256];
while (text_len > 0)
{
uint32_t chunk = text_len > sizeof(tmp)
? sizeof(tmp) : text_len;
ret = rfb_recv_exact(conn->sockfd, tmp, chunk);
if (ret < 0)
{
return ret;
}
text_len -= chunk;
}
}
break;
default:
printf("vncviewer: unknown message type %u\n", msg_type);
return -EPROTO;
}
return OK;
}