Browse Source

Use UTF-8 in clipboard API

In prepartion for better clipboard extensions that can send Unicode
data between the client and server.
tags/v1.9.90
Pierre Ossman 5 years ago
parent
commit
5fbbe10b6c

+ 4
- 2
common/rfb/CConnection.cxx View File

strFree(serverClipboard); strFree(serverClipboard);
serverClipboard = NULL; serverClipboard = NULL;


serverClipboard = strDup(str);
serverClipboard = latin1ToUTF8(str);


handleClipboardAnnounce(true); handleClipboardAnnounce(true);
} }


void CConnection::sendClipboardData(const char* data) void CConnection::sendClipboardData(const char* data)
{ {
writer()->writeClientCutText(data);
CharArray latin1(utf8ToLatin1(data));

writer()->writeClientCutText(latin1.buf);
} }


void CConnection::refreshFramebuffer() void CConnection::refreshFramebuffer()

+ 4
- 2
common/rfb/SConnection.cxx View File

strFree(clientClipboard); strFree(clientClipboard);
clientClipboard = NULL; clientClipboard = NULL;


clientClipboard = strDup(str);
clientClipboard = latin1ToUTF8(str);


handleClipboardAnnounce(true); handleClipboardAnnounce(true);
} }


void SConnection::sendClipboardData(const char* data) void SConnection::sendClipboardData(const char* data)
{ {
writer()->writeServerCutText(data);
CharArray latin1(utf8ToLatin1(data));

writer()->writeServerCutText(latin1.buf);
} }


void SConnection::writeFakeColourMap(void) void SConnection::writeFakeColourMap(void)

+ 218
- 0
common/rfb/util.cxx View File

delete [] s; delete [] s;
} }


void strFree(wchar_t* s) {
delete [] s;
}



bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd) { bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd) {
CharArray out1old, out2old; CharArray out1old, out2old;
return buffer; return buffer;
} }


char* convertCRLF(const char* src, size_t bytes)
{
char* buffer;
size_t sz;

char* out;
const char* in;
size_t in_len;

// Always include space for a NULL
sz = 1;

// Compute output size
in = src;
in_len = bytes;
while ((*in != '\0') && (in_len > 0)) {
sz++;

if (*in == '\r') {
if ((in_len == 0) || (*(in+1) != '\n'))
sz++;
} else if (*in == '\n') {
if ((in == src) || (*(in-1) != '\r'))
sz++;
}

in++;
in_len--;
}

// Alloc
buffer = new char[sz];
memset(buffer, 0, sz);

// And convert
out = buffer;
in = src;
in_len = bytes;
while ((*in != '\0') && (in_len > 0)) {
if (*in == '\n') {
if ((in == src) || (*(in-1) != '\r'))
*out++ = '\r';
}

*out = *in;

if (*in == '\r') {
if ((in_len == 0) || (*(in+1) != '\n')) {
out++;
*out = '\n';
}
}

out++;
in++;
in_len--;
}

return buffer;
}

size_t ucs4ToUTF8(unsigned src, char* dst) { size_t ucs4ToUTF8(unsigned src, char* dst) {
if (src < 0x80) { if (src < 0x80) {
*dst++ = src; *dst++ = src;
return consumed; return consumed;
} }


size_t ucs4ToUTF16(unsigned src, wchar_t* dst) {
if ((src < 0xd800) || ((src >= 0xe000) && (src < 0x10000))) {
*dst++ = src;
*dst++ = L'\0';
return 1;
} else if (src < 0x110000) {
*dst++ = 0xd800 | ((src >> 10) & 0x07ff);
*dst++ = 0xdc00 | (src & 0x07ff);
*dst++ = L'\0';
return 2;
} else {
return ucs4ToUTF16(0xfffd, dst);
}
}

size_t utf16ToUCS4(const wchar_t* src, size_t max, unsigned* dst) {
*dst = 0xfffd;

if (max == 0)
return 0;

if ((*src < 0xd800) || (*src >= 0xe000)) {
*dst = *src;
return 1;
}

if (*src & 0x0400) {
size_t consumed;

// Invalid sequence, consume all continuation characters
consumed = 0;
while ((max > 0) && (*src & 0x0400)) {
src++;
max--;
consumed++;
}

return consumed;
}

*dst = *src++;
max--;

// Invalid or truncated sequence?
if ((max == 0) || ((*src & 0xfc00) != 0xdc00)) {
*dst = 0xfffd;
return 1;
}

*dst = 0x10000 | ((*dst & 0x03ff) << 10);
*dst |= *src & 0x3ff;

return 2;
}

char* latin1ToUTF8(const char* src, size_t bytes) { char* latin1ToUTF8(const char* src, size_t bytes) {
char* buffer; char* buffer;
size_t sz; size_t sz;
return buffer; return buffer;
} }


char* utf16ToUTF8(const wchar_t* src, size_t units)
{
char* buffer;
size_t sz;

char* out;
const wchar_t* in;
size_t in_len;

// Always include space for a NULL
sz = 1;

// Compute output size
in = src;
in_len = units;
while ((*in != '\0') && (in_len > 0)) {
size_t len;
unsigned ucs;
char buf[5];

len = utf16ToUCS4(in, in_len, &ucs);
in += len;
in_len -= len;

sz += ucs4ToUTF8(ucs, buf);
}

// Alloc
buffer = new char[sz];
memset(buffer, 0, sz);

// And convert
out = buffer;
in = src;
in_len = units;
while ((*in != '\0') && (in_len > 0)) {
size_t len;
unsigned ucs;

len = utf16ToUCS4(in, in_len, &ucs);
in += len;
in_len -= len;

out += ucs4ToUTF8(ucs, out);
}

return buffer;
}

wchar_t* utf8ToUTF16(const char* src, size_t bytes)
{
wchar_t* buffer;
size_t sz;

wchar_t* out;
const char* in;
size_t in_len;

// Always include space for a NULL
sz = 1;

// Compute output size
in = src;
in_len = bytes;
while ((*in != '\0') && (in_len > 0)) {
size_t len;
unsigned ucs;
wchar_t buf[3];

len = utf8ToUCS4(in, in_len, &ucs);
in += len;
in_len -= len;

sz += ucs4ToUTF16(ucs, buf);
}

// Alloc
buffer = new wchar_t[sz];
memset(buffer, 0, sz);

// And convert
out = buffer;
in = src;
in_len = bytes;
while ((*in != '\0') && (in_len > 0)) {
size_t len;
unsigned ucs;

len = utf8ToUCS4(in, in_len, &ucs);
in += len;
in_len -= len;

out += ucs4ToUTF16(ucs, out);
}

return buffer;
}

unsigned msBetween(const struct timeval *first, unsigned msBetween(const struct timeval *first,
const struct timeval *second) const struct timeval *second)
{ {

+ 8
- 0
common/rfb/util.h View File



char* strDup(const char* s); char* strDup(const char* s);
void strFree(char* s); void strFree(char* s);
void strFree(wchar_t* s);


// Returns true if split successful. Returns false otherwise. // Returns true if split successful. Returns false otherwise.
// ALWAYS *copies* first part of string to out1 buffer. // ALWAYS *copies* first part of string to out1 buffer.
// Makes sure line endings are in a certain format // Makes sure line endings are in a certain format


char* convertLF(const char* src, size_t bytes = (size_t)-1); char* convertLF(const char* src, size_t bytes = (size_t)-1);
char* convertCRLF(const char* src, size_t bytes = (size_t)-1);


// Convertions between various Unicode formats. The returned strings are // Convertions between various Unicode formats. The returned strings are
// always null terminated and must be freed using strFree(). // always null terminated and must be freed using strFree().
size_t ucs4ToUTF8(unsigned src, char* dst); size_t ucs4ToUTF8(unsigned src, char* dst);
size_t utf8ToUCS4(const char* src, size_t max, unsigned* dst); size_t utf8ToUCS4(const char* src, size_t max, unsigned* dst);


size_t ucs4ToUTF16(unsigned src, wchar_t* dst);
size_t utf16ToUCS4(const wchar_t* src, size_t max, unsigned* dst);

char* latin1ToUTF8(const char* src, size_t bytes = (size_t)-1); char* latin1ToUTF8(const char* src, size_t bytes = (size_t)-1);
char* utf8ToLatin1(const char* src, size_t bytes = (size_t)-1); char* utf8ToLatin1(const char* src, size_t bytes = (size_t)-1);


char* utf16ToUTF8(const wchar_t* src, size_t units = (size_t)-1);
wchar_t* utf8ToUTF16(const char* src, size_t bytes = (size_t)-1);

// HELPER functions for timeout handling // HELPER functions for timeout handling


// soonestTimeout() is a function to help work out the soonest of several // soonestTimeout() is a function to help work out the soonest of several

+ 24
- 23
unix/xserver/hw/vnc/vncSelection.c View File

return Success; return Success;
} else { } else {
if ((target == xaSTRING) || (target == xaTEXT)) { if ((target == xaSTRING) || (target == xaTEXT)) {
char* latin1;

latin1 = vncUTF8ToLatin1(data, (size_t)-1);
if (latin1 == NULL)
return BadAlloc;

rc = dixChangeWindowProperty(serverClient, pWin, realProperty, rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
XA_STRING, 8, PropModeReplace, XA_STRING, 8, PropModeReplace,
strlen(data), (char*)data,
TRUE);
strlen(latin1), latin1, TRUE);

vncStrFree(latin1);

if (rc != Success) if (rc != Success)
return rc; return rc;
} else if (target == xaUTF8_STRING) { } else if (target == xaUTF8_STRING) {
char* buffer;

buffer = vncLatin1ToUTF8(data, (size_t)-1);
if (buffer == NULL)
return BadAlloc;

rc = dixChangeWindowProperty(serverClient, pWin, realProperty, rc = dixChangeWindowProperty(serverClient, pWin, realProperty,
xaUTF8_STRING, 8, PropModeReplace, xaUTF8_STRING, 8, PropModeReplace,
strlen(buffer), buffer, TRUE);
vncStrFree(buffer);
strlen(data), data, TRUE);
if (rc != Success) if (rc != Success)
return rc; return rc;
} else { } else {
vncAnnounceClipboard(TRUE); vncAnnounceClipboard(TRUE);
} }
} else { } else {
if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size))
vncSelectionRequest(selection, xaSTRING);
else if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size))
if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size))
vncSelectionRequest(selection, xaUTF8_STRING); vncSelectionRequest(selection, xaUTF8_STRING);
else if (vncHasAtom(xaSTRING, (const Atom*)prop->data, prop->size))
vncSelectionRequest(selection, xaSTRING);
} }
} else if (target == xaSTRING) { } else if (target == xaSTRING) {
char* filtered; char* filtered;
char* utf8;


if (prop->format != 8) if (prop->format != 8)
return; return;
if (filtered == NULL) if (filtered == NULL)
return; return;


utf8 = vncLatin1ToUTF8(filtered, (size_t)-1);
vncStrFree(filtered);
if (utf8 == NULL)
return;

LOG_DEBUG("Sending clipboard to clients (%d bytes)", LOG_DEBUG("Sending clipboard to clients (%d bytes)",
(int)strlen(filtered));
(int)strlen(utf8));


vncSendClipboardData(filtered);
vncSendClipboardData(utf8);


vncStrFree(filtered);
vncStrFree(utf8);
} else if (target == xaUTF8_STRING) { } else if (target == xaUTF8_STRING) {
char *filtered; char *filtered;
char* buffer;


if (prop->format != 8) if (prop->format != 8)
return; return;
if (prop->type != xaUTF8_STRING) if (prop->type != xaUTF8_STRING)
return; return;


buffer = vncUTF8ToLatin1(prop->data, prop->size);
if (buffer == NULL)
return;

filtered = vncConvertLF(buffer, (size_t)-1);
vncStrFree(buffer);
filtered = vncConvertLF(prop->data, prop->size);
if (filtered == NULL) if (filtered == NULL)
return; return;



+ 5
- 11
vncviewer/Viewport.cxx View File



void Viewport::handleClipboardData(const char* data) void Viewport::handleClipboardData(const char* data)
{ {
char* buffer;
size_t len; size_t len;


if (!hasFocus()) if (!hasFocus())
return; return;


buffer = latin1ToUTF8(data);
len = strlen(buffer);
len = strlen(data);


vlog.debug("Got clipboard data (%d bytes)", (int)len); vlog.debug("Got clipboard data (%d bytes)", (int)len);


// dump the data into both variants. // dump the data into both variants.
#if !defined(WIN32) && !defined(__APPLE__) #if !defined(WIN32) && !defined(__APPLE__)
if (setPrimary) if (setPrimary)
Fl::copy(buffer, len, 0);
Fl::copy(data, len, 0);
#endif #endif
Fl::copy(buffer, len, 1);

strFree(buffer);
Fl::copy(data, len, 1);
} }


void Viewport::setLEDState(unsigned int state) void Viewport::setLEDState(unsigned int state)


int Viewport::handle(int event) int Viewport::handle(int event)
{ {
char *buffer, *filtered;
char *filtered;
int buttonMask, wheelMask; int buttonMask, wheelMask;
DownMap::const_iterator iter; DownMap::const_iterator iter;


switch (event) { switch (event) {
case FL_PASTE: case FL_PASTE:
buffer = utf8ToLatin1(Fl::event_text(), Fl::event_length());
filtered = convertLF(buffer);
strFree(buffer);
filtered = convertLF(Fl::event_text(), Fl::event_length());


vlog.debug("Sending clipboard data (%d bytes)", (int)strlen(filtered)); vlog.debug("Sending clipboard data (%d bytes)", (int)strlen(filtered));



+ 19
- 54
win/rfb_win32/Clipboard.cxx View File



static LogWriter vlog("Clipboard"); static LogWriter vlog("Clipboard");



//
// -=- CR/LF handlers
//

char*
unix2dos(const char* text) {
int len = strlen(text)+1;
char* dos = new char[strlen(text)*2+1];
int i, j=0;
for (i=0; i<len; i++) {
if (text[i] == '\x0a')
dos[j++] = '\x0d';
dos[j++] = text[i];
}
return dos;
}


//
// -=- ISO-8859-1 (Latin 1) filter (in-place)
//

void
removeNonISOLatin1Chars(char* text) {
int len = strlen(text);
int i=0, j=0;
for (; i<len; i++) {
if (((text[i] >= 1) && (text[i] <= 127)) ||
((text[i] >= 160) && (text[i] <= 255)))
text[j++] = text[i];
}
text[j] = 0;
}

// //
// -=- Clipboard object // -=- Clipboard object
// //
if (notifier == NULL) if (notifier == NULL)
vlog.debug("no clipboard notifier registered"); vlog.debug("no clipboard notifier registered");
else else
notifier->notifyClipboardChanged(IsClipboardFormatAvailable(CF_TEXT));
notifier->notifyClipboardChanged(IsClipboardFormatAvailable(CF_UNICODETEXT));
} }
} }
if (next_window) if (next_window)
char* char*
Clipboard::getClipText() { Clipboard::getClipText() {
HGLOBAL cliphandle; HGLOBAL cliphandle;
char* clipdata;
char* filtered;
wchar_t* clipdata;
CharArray utf8;


// Open the clipboard // Open the clipboard
if (!OpenClipboard(getHandle())) if (!OpenClipboard(getHandle()))
return NULL; return NULL;


// Get the clipboard data // Get the clipboard data
cliphandle = GetClipboardData(CF_TEXT);
cliphandle = GetClipboardData(CF_UNICODETEXT);
if (!cliphandle) { if (!cliphandle) {
CloseClipboard(); CloseClipboard();
return NULL; return NULL;
} }


clipdata = (char*) GlobalLock(cliphandle);
clipdata = (wchar_t*) GlobalLock(cliphandle);
if (!clipdata) { if (!clipdata) {
CloseClipboard(); CloseClipboard();
return NULL; return NULL;
} }


// Filter out anything unwanted
filtered = convertLF(clipdata, strlen(clipdata));
removeNonISOLatin1Chars(filtered);
// Convert it to UTF-8
utf8.replaceBuf(utf16ToUTF8(clipdata));


// Release the buffer and close the clipboard // Release the buffer and close the clipboard
GlobalUnlock(cliphandle); GlobalUnlock(cliphandle);
CloseClipboard(); CloseClipboard();


return filtered;
return convertLF(utf8.buf);
} }


void void
if (!OpenClipboard(getHandle())) if (!OpenClipboard(getHandle()))
throw rdr::SystemException("unable to open Win32 clipboard", GetLastError()); throw rdr::SystemException("unable to open Win32 clipboard", GetLastError());


// - Pre-process the supplied clipboard text into DOS format
CharArray dos_text;
dos_text.buf = unix2dos(text);
removeNonISOLatin1Chars(dos_text.buf);
int dos_text_len = strlen(dos_text.buf);
// - Convert the supplied clipboard text into UTF-16 format with CRLF
CharArray filtered(convertCRLF(text));
wchar_t* utf16;
utf16 = utf8ToUTF16(filtered.buf);


// - Allocate global memory for the data // - Allocate global memory for the data
clip_handle = ::GlobalAlloc(GMEM_MOVEABLE, dos_text_len+1);
clip_handle = ::GlobalAlloc(GMEM_MOVEABLE, (wcslen(utf16) + 1) * 2);


char* data = (char*) GlobalLock(clip_handle);
memcpy(data, dos_text.buf, dos_text_len+1);
data[dos_text_len] = 0;
wchar_t* data = (wchar_t*) GlobalLock(clip_handle);
wcscpy(data, utf16);
GlobalUnlock(clip_handle); GlobalUnlock(clip_handle);


strFree(utf16);

// - Next, we must clear out any existing data // - Next, we must clear out any existing data
if (!EmptyClipboard()) if (!EmptyClipboard())
throw rdr::SystemException("unable to empty Win32 clipboard", GetLastError()); throw rdr::SystemException("unable to empty Win32 clipboard", GetLastError());


// - Set the new clipboard data // - Set the new clipboard data
if (!SetClipboardData(CF_TEXT, clip_handle))
if (!SetClipboardData(CF_UNICODETEXT, clip_handle))
throw rdr::SystemException("unable to set Win32 clipboard", GetLastError()); throw rdr::SystemException("unable to set Win32 clipboard", GetLastError());
clip_handle = 0; clip_handle = 0;



Loading…
Cancel
Save