In prepartion for better clipboard extensions that can send Unicode data between the client and server.tags/v1.9.90
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() |
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) |
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) | ||||
{ | { |
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 |
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; | ||||
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)); | ||||
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; | ||||