Skip to content

Commit c0b3601

Browse files
wuhanckbk138
authored andcommitted
libvncclient: add utf-8 clipboard support
Closes #552
1 parent 52d6522 commit c0b3601

2 files changed

Lines changed: 224 additions & 0 deletions

File tree

libvncclient/rfbproto.c

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,13 @@ SetFormatAndEncodings(rfbClient* client)
14461446
if (se->nEncodings < MAX_ENCODINGS)
14471447
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingQemuExtendedKeyEvent);
14481448

1449+
#ifdef LIBVNCSERVER_HAVE_LIBZ
1450+
/* extendedclipboard */
1451+
if (client->clipboardCap)
1452+
if (se->nEncodings < MAX_ENCODINGS)
1453+
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingExtendedClipboard);
1454+
#endif
1455+
14491456
/* client extensions */
14501457
for(e = rfbClientExtensions; e; e = e->next)
14511458
if(e->encodings) {
@@ -1769,6 +1776,78 @@ SendExtendedKeyEvent(rfbClient* client, uint32_t keysym, uint32_t keycode, rfbBo
17691776
}
17701777

17711778

1779+
#ifdef LIBVNCSERVER_HAVE_LIBZ
1780+
/*
1781+
* sendClientCutTextNotify
1782+
* it is needed when client send utf8 clipboard data
1783+
* please refer to
1784+
* https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#extended-clipboard-pseudo-encoding
1785+
* to supprot utf-8 clipboard we need at least support notify and text
1786+
*/
1787+
1788+
static rfbBool
1789+
sendExtClientCutTextNotify(rfbClient *client)
1790+
{
1791+
rfbClientCutTextMsg cct = {0, };
1792+
const uint32_t be_flags = rfbClientSwap32IfLE(rfbExtendedClipboard_Notify
1793+
| rfbExtendedClipboard_Text); /*text and notify*/
1794+
cct.type = rfbClientCutText;
1795+
cct.length = rfbClientSwap32IfLE(-((uint32_t)sizeof(be_flags)));/*flag*/
1796+
rfbBool ret = WriteToRFBServer(client, (char *)&cct, sz_rfbClientCutTextMsg)
1797+
&& WriteToRFBServer(client, (char *)&be_flags, sizeof(be_flags));
1798+
return ret;
1799+
}
1800+
1801+
1802+
/*
1803+
* sendClientCutTextProvide
1804+
* it need send notify first to grab clipboard (server will check that)
1805+
*/
1806+
1807+
static rfbBool
1808+
sendExtClientCutTextProvide(rfbClient *client, char* data, int len)
1809+
{
1810+
rfbClientCutTextMsg cct = {0, };
1811+
const uint32_t be_flags = rfbClientSwap32IfLE(rfbExtendedClipboard_Provide
1812+
| rfbExtendedClipboard_Text); /*text and provide*/
1813+
const uint32_t be_size = rfbClientSwap32IfLE(len);
1814+
const size_t sz_to_compressed = sizeof(be_size) + len; /*size, data*/
1815+
size_t csz = compressBound(sz_to_compressed + 1); /*tricky, some server need extar byte to flush data*/
1816+
1817+
unsigned char *buf = malloc(sz_to_compressed + 1); /*tricky, some server need extra byte to flush data*/
1818+
if (!buf) {
1819+
rfbClientLog("sendExtClientCutTextProvide. alloc buf failed\n");
1820+
return FALSE;
1821+
}
1822+
memcpy(buf, &be_size, sizeof(be_size));
1823+
memcpy(buf + sizeof(be_size), data, len);
1824+
buf[sz_to_compressed] = 0;
1825+
1826+
unsigned char *cbuf = malloc(sizeof(be_flags) + csz); /*flag, compressed*/
1827+
if (!cbuf) {
1828+
rfbClientLog("sendExtClientCutTextProvide. alloc cbuf failed\n");
1829+
free(buf);
1830+
return FALSE;
1831+
}
1832+
memcpy(cbuf, &be_flags, sizeof(be_flags));
1833+
if (compress(cbuf + sizeof(be_flags), &csz, buf, sz_to_compressed + 1) != Z_OK) {
1834+
rfbClientLog("sendExtClientCutTextProvide: compress cbuf failed\n");
1835+
free(buf);
1836+
free(cbuf);
1837+
return FALSE;
1838+
}
1839+
1840+
cct.type = rfbClientCutText;
1841+
cct.length = rfbClientSwap32IfLE(-(sizeof(be_flags) + csz));/*flag, compressed*/
1842+
rfbBool ret = sendExtClientCutTextNotify(client)
1843+
&& WriteToRFBServer(client, (char *)&cct, sz_rfbClientCutTextMsg)
1844+
&& WriteToRFBServer(client, (char *)cbuf, sizeof(be_flags) + csz);
1845+
free(buf);
1846+
free(cbuf);
1847+
return ret;
1848+
}
1849+
#endif
1850+
17721851
/*
17731852
* SendClientCutText.
17741853
*/
@@ -1780,6 +1859,12 @@ SendClientCutText(rfbClient* client, char *str, int len)
17801859

17811860
if (!SupportsClient2Server(client, rfbClientCutText)) return TRUE;
17821861

1862+
#ifdef LIBVNCSERVER_HAVE_LIBZ
1863+
if (client->clipboardEnabledCap) { /* if enabled extended clipboard, use it */
1864+
return sendExtClientCutTextProvide(client, str, len);
1865+
}
1866+
#endif
1867+
17831868
memset(&cct, 0, sizeof(cct));
17841869
cct.type = rfbClientCutText;
17851870
cct.length = rfbClientSwap32IfLE(len);
@@ -1788,6 +1873,101 @@ SendClientCutText(rfbClient* client, char *str, int len)
17881873
}
17891874

17901875

1876+
#ifdef LIBVNCSERVER_HAVE_LIBZ
1877+
/*
1878+
* process server clipboard extend text
1879+
*/
1880+
1881+
static rfbBool
1882+
rfbClientProcessExtServerCutText(rfbClient* client, char *data, int len)
1883+
{
1884+
uint32_t flags;
1885+
if (len < sizeof(flags)) {
1886+
rfbClientLog("rfbClientProcessExtServerCutText. len < 4\n");
1887+
return FALSE;
1888+
}
1889+
memcpy(&flags, data, sizeof(flags));
1890+
data += sizeof(flags);
1891+
len -= sizeof(flags);
1892+
flags = rfbClientSwap32IfLE(flags);
1893+
1894+
/*
1895+
* only process (text | provide). Ignore all others
1896+
* modify here if need more types(rtf,html,dib,files)
1897+
*/
1898+
if (!(flags & rfbExtendedClipboard_Text)) {
1899+
rfbClientLog("rfbClientProcessExtServerCutText. not text type. ignore\n");
1900+
return TRUE;
1901+
}
1902+
if (!(flags & rfbExtendedClipboard_Provide)) {
1903+
rfbClientLog("rfbClientProcessExtServerCutText. not provide type. ignore\n");
1904+
return TRUE;
1905+
}
1906+
if (flags & rfbExtendedClipboard_Caps) {
1907+
rfbClientLog("rfbClientProcessExtServerCutText. default cap.\n");
1908+
client->clipboardEnabledCap |= rfbExtendedClipboard_Text; /* for now, only text */
1909+
return TRUE;
1910+
}
1911+
1912+
z_stream stream;
1913+
stream.zalloc = NULL;
1914+
stream.zfree = NULL;
1915+
stream.opaque = NULL;
1916+
stream.avail_in = 0;
1917+
stream.next_in = NULL;
1918+
if (inflateInit(&stream) != Z_OK) {
1919+
rfbClientLog("rfbClientProcessExtServerCutText. inflateInit failed\n");
1920+
return FALSE;
1921+
}
1922+
stream.avail_in = len;
1923+
stream.next_in = (unsigned char *)data;
1924+
1925+
uint32_t size;
1926+
stream.avail_out = sizeof(size);
1927+
stream.next_out = (unsigned char *)&size;
1928+
if (inflate(&stream, Z_SYNC_FLUSH) != Z_OK) {
1929+
rfbClientLog("rfbClientProcessExtServerCutText. inflate size failed\n");
1930+
inflateEnd(&stream);
1931+
return FALSE;
1932+
}
1933+
size = rfbClientSwap32IfLE(size);
1934+
if (size > (1 << 20)) {
1935+
rfbClientLog("rfbClientProcessExtServerCutText. size too large\n");
1936+
inflateEnd(&stream);
1937+
return FALSE;
1938+
}
1939+
1940+
unsigned char *buf = malloc(size);
1941+
if (!buf) {
1942+
rfbClientLog("rfbClientProcessExtServerCutText. alloc buf failed\n");
1943+
inflateEnd(&stream);
1944+
return FALSE;
1945+
}
1946+
stream.avail_out = size;
1947+
stream.next_out = buf;
1948+
uLong out_before = stream.total_out;
1949+
int err = inflate(&stream, Z_SYNC_FLUSH);
1950+
if (err != Z_OK && err != Z_STREAM_END) {
1951+
rfbClientLog("rfbClientProcessExtServerCutText. inflate buf failed\n");
1952+
free(buf);
1953+
inflateEnd(&stream);
1954+
return FALSE;
1955+
}
1956+
if ((stream.total_out - out_before) != size) {
1957+
rfbClientLog("rfbClientProcessExtServerCutText. inflate size error\n");
1958+
free(buf);
1959+
inflateEnd(&stream);
1960+
return FALSE;
1961+
}
1962+
if (client->GotXCutText)
1963+
client->GotXCutText(client, (char *)buf, size);
1964+
free(buf);
1965+
1966+
inflateEnd(&stream);
1967+
return TRUE;
1968+
}
1969+
#endif
1970+
17911971

17921972
/*
17931973
* HandleRFBServerMessage.
@@ -2332,12 +2512,31 @@ HandleRFBServerMessage(rfbClient* client)
23322512
case rfbServerCutText:
23332513
{
23342514
char *buffer;
2515+
#ifdef LIBVNCSERVER_HAVE_LIBZ
2516+
int32_t ilen; /* also as a flag, if ilen < 0, it is ext clipboard text */
2517+
rfbBool fallback = FALSE;
2518+
#endif
23352519

23362520
if (!ReadFromRFBServer(client, ((char *)&msg) + 1,
23372521
sz_rfbServerCutTextMsg - 1))
23382522
return FALSE;
23392523

2524+
#ifdef LIBVNCSERVER_HAVE_LIBZ
2525+
ilen = rfbClientSwap32IfLE(msg.sct.length);
2526+
if (client->clipboardEnabledCap && ilen >= 0) {
2527+
if (!client->GotXCutTextFallback) {
2528+
rfbClientLog("extend clipboardCap enabled but msg len:%d >= 0. no fallback callback. ignore\n", ilen);
2529+
return FALSE;
2530+
} else {
2531+
rfbClientLog("extend clipboardCap enabled but msg len:%d >= 0. fallback\n", ilen);
2532+
fallback = TRUE;
2533+
}
2534+
}
2535+
2536+
msg.sct.length = ilen < 0 ? -ilen : ilen;
2537+
#else
23402538
msg.sct.length = rfbClientSwap32IfLE(msg.sct.length);
2539+
#endif
23412540

23422541
if (msg.sct.length > 1<<20) {
23432542
rfbClientErr("Ignoring too big cut text length sent by server: %u B > 1 MB\n", (unsigned int)msg.sct.length);
@@ -2353,8 +2552,20 @@ HandleRFBServerMessage(rfbClient* client)
23532552

23542553
buffer[msg.sct.length] = 0;
23552554

2555+
#ifdef LIBVNCSERVER_HAVE_LIBZ
2556+
if (fallback) {
2557+
client->GotXCutTextFallback(client, buffer, msg.sct.length);
2558+
} else if (ilen < 0) {
2559+
if (!rfbClientProcessExtServerCutText(client, buffer, -ilen)) {
2560+
free(buffer);
2561+
return FALSE;
2562+
}
2563+
} else if (client->GotXCutText)
2564+
client->GotXCutText(client, buffer, msg.sct.length);
2565+
#else
23562566
if (client->GotXCutText)
23572567
client->GotXCutText(client, buffer, msg.sct.length);
2568+
#endif
23582569

23592570
free(buffer);
23602571

rfb/rfbclient.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,19 @@ typedef struct _rfbClient {
477477
* Used for intended dimensions, rfbClient.width and rfbClient.height are used to manage the real framebuffer dimensions.
478478
*/
479479
rfbExtDesktopScreen screen;
480+
481+
#ifdef LIBVNCSERVER_HAVE_LIBZ
482+
uint32_t clipboardCap; /**extended clipboard pseudo-encoding cap */
483+
/**it is bitset as rfb3.8, current only support text */
484+
/**client set it to rfbExtendedClipboard_Text for now */
485+
uint32_t clipboardEnabledCap; /**enabled cap which confirmed by server */
486+
/**client can use it as a check for server cap */
487+
/**
488+
* Used for extended clipboard text fallback
489+
* When server announce cap of utf8, but still send latin-1
490+
*/
491+
GotXCutTextProc GotXCutTextFallback;
492+
#endif
480493
} rfbClient;
481494

482495
/* cursor.c */

0 commit comments

Comments
 (0)