Ok guys... this is my solution for unicode clipboard support in Windows VM... It works fine... I can make some improvements in memory management but it's ok for now...
/****************************************************************************/ /* Clipboard */ /****************************************************************************/
int clipboardSize(void) { HANDLE h, h2; WCHAR *src; unsigned char *csrc; int len, bytesNeeded;
// Do we have text in the clipboard? if(!IsClipboardFormatAvailable(CF_TEXT)) return 0; if(!OpenClipboard(stWindow)) return 0;
// Get it in unicode format. h = GetClipboardData(CF_UNICODETEXT);
src = GlobalLock(h);
// How many bytes do we want to store those unicode chars in UTF8 format? bytesNeeded = WideCharToMultiByte( CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL );
h2 = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, bytesNeeded); csrc = GlobalLock(h2);
// Convert Unicode text to UTF8. WideCharToMultiByte( CP_UTF8, 0, src, -1, csrc, bytesNeeded, NULL, NULL );
// Consider CrLf as a 1-byte character. len = bytesNeeded; while(len--) if(((*csrc == 13) && (csrc[1] == 10))) { bytesNeeded--; csrc++; } else { csrc++; }
// Clipboard size is bytesNeeded excluding the terminating character. len = bytesNeeded - 1;
GlobalUnlock(h); GlobalUnlock(h2);
return len; }
/* send the given string to the clipboard */ int clipboardWriteFromAt(int count, int byteArrayIndex, int startIndex) { HANDLE h, h2; unsigned char *src; int wcharsNeeded, len; WCHAR *out, *tmp;
if(!OpenClipboard(stWindow)) return 0;
// Get the pointer to the byte array. src = (unsigned char *)byteArrayIndex + startIndex;
// How many WCHARs do we need to store the UTF8 represented bytes from Squeak? wcharsNeeded = MultiByteToWideChar( CP_UTF8, 0, src, count+1, NULL, 0 );
// If we have Cr only "returns" then we will need another character for Lf. len = wcharsNeeded * sizeof(WCHAR); while(len--) if ((*src++ == 13) && (*src != 10)) wcharsNeeded++;
// Point to start of byte aray. src = (unsigned char *)byteArrayIndex + startIndex;
// Allocate needed memory for wcharsNeeded WCHARs. h = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, wcharsNeeded * sizeof(WCHAR)); out = GlobalLock(h);
// Convert them to Unicode UTF16. MultiByteToWideChar( CP_UTF8, 0, src, count+1, out, wcharsNeeded );
// Allocate needed memory for intermediate buffer. // OK, i could do some more work here in order to avoid a whole new buffer just for // fixing the CrLf thing, but it's ok for now... h2 = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, wcharsNeeded * sizeof(WCHAR)); tmp = GlobalLock(h2);
// Add missing Lf characters... len = wcharsNeeded; while(len--) if((*out == 13)) { /* special case: crlf translation */ *tmp = 13; tmp++; *tmp = 10; tmp++; out++; } else { /* regular case: lookup translation */ *tmp = *out; tmp++; out++; }
EmptyClipboard(); GlobalUnlock(h); GlobalUnlock(h2);
// Send the Unicode text to the clipboard. SetClipboardData(CF_UNICODETEXT, h2);
*src=0; *tmp=0;
/* Note: After setting clipboard data, the memory block previously allocated belongs to the clipboard - not to the app. */ CloseClipboard(); return 1; }
/* transfer the clipboard data into the given byte array */ int clipboardReadIntoAt(int count, int byteArrayIndex, int startIndex) { HANDLE h, h2; unsigned char *dst, *tmp; WCHAR *src; int tel,len, bytesNeeded;
if(!IsClipboardFormatAvailable(CF_TEXT)) /* check for format CF_TEXT */ return 0;
if(!OpenClipboard(stWindow)) return 0;
// Get clipboard data in Unicode format h = GetClipboardData(CF_UNICODETEXT); src = GlobalLock(h);
// How many bytes do we want to store the Unicode chars to UTF8 representation? bytesNeeded = WideCharToMultiByte( CP_UTF8, 0, src, -1, tmp, 0, NULL, NULL ) + 1;
h2 = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, bytesNeeded); tmp = GlobalLock(h2);
// Convert Unicode text to UTF8. WideCharToMultiByte( CP_UTF8, 0, src, -1, tmp, bytesNeeded, NULL, NULL);
len = bytesNeeded;
// Get the pointer to the byte array. dst= (unsigned char *)byteArrayIndex + startIndex;
while(len--) if(((*tmp == 13) && (tmp[1] == 10))) { /* RvL 17-04-1998 drop the line feed after a carriage return but leave lone line feeds alone may transfer less than 'len' bytes but who cares i.e. if they use the clipboardSize there should be no problem.*/ *dst = 13; dst++; tmp++; tmp++; } else { /* regular case: lookup translation */ *dst = *tmp; dst++; tmp++; }
GlobalUnlock(h); GlobalUnlock(h2); CloseClipboard(); return bytesNeeded; }
The only thing is that in the image side you need a UTF8TextConverter before sending text to the clipboard or getting from it...so, my unicode clipboard interpreter has these methods...
fromSystemClipboard: aString ^ aString convertFromWithConverter: (UTF8TextConverter new).
toSystemClipboard: aString | result utfString | " aString isOctetString ifTrue: [^ aString asOctetString]." utfString := aString convertToWithConverter: (UTF8TextConverter new). result _ WriteStream on: (String new: utfString size). utfString do: [:each | result nextPut: each]. ^ result contents.
I am pretty sure it will need some minor fixes... I'll post links to ready made builds of this version so others can try it too, soon.
Any comments, suggestions, C-related fixes are most welcome...
Christos
Hi Chris -
This looks good; one question though: Do you know whether the windows clipboard automatically provides the CF_UNICODETEXT -> CF_TEXT translation?
Cheers, - Andreas
Chris Petsos wrote:
Ok guys... this is my solution for unicode clipboard support in Windows VM... It works fine... I can make some improvements in memory management but it's ok for now...
/****************************************************************************/ /* Clipboard */ /****************************************************************************/
int clipboardSize(void) { HANDLE h, h2; WCHAR *src; unsigned char *csrc; int len, bytesNeeded;
// Do we have text in the clipboard? if(!IsClipboardFormatAvailable(CF_TEXT)) return 0; if(!OpenClipboard(stWindow)) return 0;
// Get it in unicode format. h = GetClipboardData(CF_UNICODETEXT);
src = GlobalLock(h);
// How many bytes do we want to store those unicode chars in UTF8 format? bytesNeeded = WideCharToMultiByte( CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL );
h2 = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, bytesNeeded); csrc = GlobalLock(h2);
// Convert Unicode text to UTF8. WideCharToMultiByte( CP_UTF8, 0, src, -1, csrc, bytesNeeded, NULL, NULL );
// Consider CrLf as a 1-byte character. len = bytesNeeded; while(len--) if(((*csrc == 13) && (csrc[1] == 10))) { bytesNeeded--; csrc++; } else { csrc++; }
// Clipboard size is bytesNeeded excluding the terminating character. len = bytesNeeded - 1;
GlobalUnlock(h); GlobalUnlock(h2);
return len; }
/* send the given string to the clipboard */ int clipboardWriteFromAt(int count, int byteArrayIndex, int startIndex) { HANDLE h, h2; unsigned char *src; int wcharsNeeded, len; WCHAR *out, *tmp;
if(!OpenClipboard(stWindow)) return 0;
// Get the pointer to the byte array. src = (unsigned char *)byteArrayIndex + startIndex;
// How many WCHARs do we need to store the UTF8 represented bytes
from Squeak?
wcharsNeeded = MultiByteToWideChar( CP_UTF8, 0, src, count+1, NULL, 0 );
// If we have Cr only "returns" then we will need another character for Lf. len = wcharsNeeded * sizeof(WCHAR); while(len--) if ((*src++ == 13) && (*src != 10)) wcharsNeeded++;
// Point to start of byte aray. src = (unsigned char *)byteArrayIndex + startIndex;
// Allocate needed memory for wcharsNeeded WCHARs. h = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, wcharsNeeded * sizeof(WCHAR)); out = GlobalLock(h);
// Convert them to Unicode UTF16. MultiByteToWideChar( CP_UTF8, 0, src, count+1, out, wcharsNeeded );
// Allocate needed memory for intermediate buffer. // OK, i could do some more work here in order to avoid a whole new buffer just for // fixing the CrLf thing, but it's ok for now... h2 = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, wcharsNeeded * sizeof(WCHAR)); tmp = GlobalLock(h2);
// Add missing Lf characters... len = wcharsNeeded; while(len--) if((*out == 13)) { /* special case: crlf translation */ *tmp = 13; tmp++; *tmp = 10; tmp++; out++; } else { /* regular case: lookup translation */ *tmp = *out; tmp++; out++; }
EmptyClipboard(); GlobalUnlock(h); GlobalUnlock(h2);
// Send the Unicode text to the clipboard. SetClipboardData(CF_UNICODETEXT, h2);
*src=0; *tmp=0;
/* Note: After setting clipboard data, the memory block previously allocated belongs to the clipboard - not to the app. */ CloseClipboard(); return 1; }
/* transfer the clipboard data into the given byte array */ int clipboardReadIntoAt(int count, int byteArrayIndex, int startIndex) { HANDLE h, h2; unsigned char *dst, *tmp; WCHAR *src; int tel,len, bytesNeeded;
if(!IsClipboardFormatAvailable(CF_TEXT)) /* check for format CF_TEXT */ return 0;
if(!OpenClipboard(stWindow)) return 0;
// Get clipboard data in Unicode format h = GetClipboardData(CF_UNICODETEXT); src = GlobalLock(h);
// How many bytes do we want to store the Unicode chars to UTF8 representation? bytesNeeded = WideCharToMultiByte( CP_UTF8, 0, src, -1, tmp, 0, NULL, NULL ) + 1;
h2 = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, bytesNeeded); tmp = GlobalLock(h2);
// Convert Unicode text to UTF8. WideCharToMultiByte( CP_UTF8, 0, src, -1, tmp, bytesNeeded, NULL, NULL);
len = bytesNeeded;
// Get the pointer to the byte array. dst= (unsigned char *)byteArrayIndex + startIndex;
while(len--) if(((*tmp == 13) && (tmp[1] == 10))) { /* RvL 17-04-1998 drop the line feed after a carriage return but leave lone line feeds alone may transfer less than 'len' bytes but who cares i.e. if they use the clipboardSize there should be no problem.*/ *dst = 13; dst++; tmp++; tmp++; } else { /* regular case: lookup translation */ *dst = *tmp; dst++; tmp++; }
GlobalUnlock(h); GlobalUnlock(h2); CloseClipboard(); return bytesNeeded; }
The only thing is that in the image side you need a UTF8TextConverter before sending text to the clipboard or getting from it...so, my unicode clipboard interpreter has these methods...
fromSystemClipboard: aString ^ aString convertFromWithConverter: (UTF8TextConverter new).
toSystemClipboard: aString | result utfString | " aString isOctetString ifTrue: [^ aString asOctetString]." utfString := aString convertToWithConverter: (UTF8TextConverter new). result _ WriteStream on: (String new: utfString size). utfString do: [:each | result nextPut: each]. ^ result contents.
I am pretty sure it will need some minor fixes... I'll post links to ready made builds of this version so others can try it too, soon.
Any comments, suggestions, C-related fixes are most welcome...
Christos
This looks good; one question though: Do you know whether the windows clipboard automatically provides the CF_UNICODETEXT -> CF_TEXT translation?
I suppose you are asking if IsClipboardFormatAvailable(CF_TEXT) returns TRUE when we have CF_UNICODETEXT on the clipboard... Well... from what i saw it does...even the vice versa is again evaluated to TRUE. Actually i couldn' see any difference in asking IsClipboardFormatAvailable(CF_TEXT) from IsClipboardFormatAvailable(CF_UNICODETEXT) when transporting UTF-8 chars to/from the clipboard.
Christos.
vm-dev@lists.squeakfoundation.org