Commit 56a91f20 authored by Jon Turney's avatar Jon Turney

hw/xwin: Implement INCR protocol for X clipboard -> Windows clipboard

Also, relax the timeout mechanism so it allows 1 second between events,
rather than 1 second for the entire transfer, as transfers of large
pastes can take more than 1 second.

Also, prefer UTF8_STRING encoding to COMPOUND_TEXT encoding
parent 4f95d87d
/*
*Copyright (C) 2003-2004 Harold L Hunt II All Rights Reserved.
*
......@@ -38,7 +37,7 @@
/* Windows headers */
#include <X11/Xwindows.h>
#define WIN_XEVENTS_SUCCESS 0
#define WIN_XEVENTS_SUCCESS 0 // more like 'CONTINUE'
#define WIN_XEVENTS_FAILED 1
#define WIN_XEVENTS_NOTIFY_DATA 3
#define WIN_XEVENTS_NOTIFY_TARGETS 4
......@@ -77,6 +76,7 @@ typedef struct
Atom atomUTF8String;
Atom atomCompoundText;
Atom atomTargets;
Atom atomIncr;
} ClipboardAtoms;
/* Modern clipboard API functions */
......@@ -111,6 +111,8 @@ typedef struct
{
Bool fUseUnicode;
Atom *targetList;
unsigned char *incr;
unsigned long int incrsize;
} ClipboardConversionData;
int
......
......@@ -199,6 +199,7 @@ winClipboardProc(Bool fUseUnicode, char *szDisplay)
atoms.atomUTF8String = XInternAtom (pDisplay, "UTF8_STRING", False);
atoms.atomCompoundText = XInternAtom (pDisplay, "COMPOUND_TEXT", False);
atoms.atomTargets = XInternAtom (pDisplay, "TARGETS", False);
atoms.atomIncr = XInternAtom (pDisplay, "INCR", False);
/* Create a messaging window */
iWindow = XCreateSimpleWindow(pDisplay,
......@@ -265,6 +266,8 @@ winClipboardProc(Bool fUseUnicode, char *szDisplay)
}
data.fUseUnicode = fUseUnicode;
data.incr = NULL;
data.incrsize = 0;
/* Loop for events */
while (1) {
......
......@@ -73,9 +73,8 @@ winProcessXEventsTimeout(HWND hwnd, Window iWindow, Display * pDisplay,
int iConnNumber;
struct timeval tv;
int iReturn;
DWORD dwStopTime = GetTickCount() + iTimeoutSec * 1000;
winDebug("winProcessXEventsTimeout () - pumping X events for %d seconds\n",
winDebug("winProcessXEventsTimeout () - pumping X events, timeout %d seconds\n",
iTimeoutSec);
/* Get our connection number */
......@@ -104,11 +103,9 @@ winProcessXEventsTimeout(HWND hwnd, Window iWindow, Display * pDisplay,
FD_SET(iConnNumber, &fdsRead);
/* Adjust timeout */
remainingTime = dwStopTime - GetTickCount();
remainingTime = iTimeoutSec * 1000;
tv.tv_sec = remainingTime / 1000;
tv.tv_usec = (remainingTime % 1000) * 1000;
winDebug("winProcessXEventsTimeout () - %ld milliseconds left\n",
remainingTime);
/* Break out if no time left */
if (remainingTime <= 0)
......@@ -494,6 +491,9 @@ winClipboardWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
/* Process X events */
data.fUseUnicode = fConvertToUnicode;
data.incr = NULL;
data.incrsize = 0;
iReturn = winProcessXEventsTimeout(hwnd,
iWindow,
pDisplay,
......@@ -517,10 +517,10 @@ winClipboardWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
struct target_priority target_priority_table[] =
{
{ atoms->atomCompoundText, 0 },
#ifdef X_HAVE_UTF8_STRING
{ atoms->atomUTF8String, 1 },
{ atoms->atomUTF8String, 0 },
#endif
{ atoms->atomCompoundText, 1 },
{ XA_STRING, 2 },
};
......
......@@ -184,6 +184,271 @@ winClipboardSelectionNotifyTargets(HWND hwnd, Window iWindow, Display *pDisplay,
return WIN_XEVENTS_NOTIFY_TARGETS;
}
static int
winClipboardSelectionNotifyData(HWND hwnd, Window iWindow, Display *pDisplay, ClipboardConversionData *data, ClipboardAtoms *atoms)
{
Atom encoding;
int format;
unsigned long int nitems;
unsigned long int after;
unsigned char *value;
XTextProperty xtpText = { 0 };
Bool fSetClipboardData = TRUE;
int iReturn;
char **ppszTextList = NULL;
int iCount;
char *pszReturnData = NULL;
wchar_t *pwszUnicodeStr = NULL;
HGLOBAL hGlobal = NULL;
char *pszConvertData = NULL;
char *pszGlobalData = NULL;
/* Retrieve the selection data and delete the property */
iReturn = XGetWindowProperty(pDisplay,
iWindow,
atoms->atomLocalProperty,
0,
INT_MAX,
True,
AnyPropertyType,
&encoding,
&format,
&nitems,
&after,
&value);
if (iReturn != Success) {
ErrorF("winClipboardFlushXEvents - SelectionNotify - "
"XGetWindowProperty () failed, aborting: %d\n", iReturn);
goto winClipboardFlushXEvents_SelectionNotify_Done;
}
{
char *pszAtomName = NULL;
winDebug("SelectionNotify - returned data %lu left %lu\n", nitems, after);
pszAtomName = XGetAtomName(pDisplay, encoding);
winDebug("Notify atom name %s\n", pszAtomName);
XFree(pszAtomName);
pszAtomName = NULL;
}
/* INCR reply indicates the start of a incremental transfer */
if (encoding == atoms->atomIncr) {
winDebug("winClipboardSelectionNotifyData: starting INCR, anticipated size %d\n", *(int *)value);
data->incrsize = 0;
data->incr = malloc(*(int *)value);
// XXX: if malloc failed, we have an error
return WIN_XEVENTS_SUCCESS;
}
else if (data->incr) {
/* If an INCR transfer is in progress ... */
if (nitems == 0) {
winDebug("winClipboardSelectionNotifyData: ending INCR, actual size %ld\n", data->incrsize);
/* a zero-length property indicates the end of the data */
xtpText.value = data->incr;
xtpText.encoding = encoding;
xtpText.format = format; // XXX: The type of the converted selection is the type of the first partial property. The remaining partial properties must have the same type.
xtpText.nitems = data->incrsize;
}
else {
/* Otherwise, continue appending the INCR data */
winDebug("winClipboardSelectionNotifyData: INCR, %ld bytes\n", nitems);
data->incr = realloc(data->incr, data->incrsize + nitems);
memcpy(data->incr + data->incrsize, value, nitems);
data->incrsize = data->incrsize + nitems;
return WIN_XEVENTS_SUCCESS;
}
}
else {
/* Otherwise, the data is just contained in the property */
winDebug("winClipboardSelectionNotifyData: non-INCR, %ld bytes\n", nitems);
xtpText.value = value;
xtpText.encoding = encoding;
xtpText.format = format;
xtpText.nitems = nitems;
}
if (data->fUseUnicode) {
#ifdef X_HAVE_UTF8_STRING
/* Convert the text property to a text list */
iReturn = Xutf8TextPropertyToTextList(pDisplay,
&xtpText,
&ppszTextList, &iCount);
#endif
}
else {
iReturn = XmbTextPropertyToTextList(pDisplay,
&xtpText,
&ppszTextList, &iCount);
}
if (iReturn == Success || iReturn > 0) {
/* Conversion succeeded or some unconvertible characters */
if (ppszTextList != NULL) {
int i;
int iReturnDataLen = 0;
for (i = 0; i < iCount; i++) {
iReturnDataLen += strlen(ppszTextList[i]);
}
pszReturnData = malloc(iReturnDataLen + 1);
pszReturnData[0] = '\0';
for (i = 0; i < iCount; i++) {
strcat(pszReturnData, ppszTextList[i]);
}
}
else {
ErrorF("winClipboardFlushXEvents - SelectionNotify - "
"X*TextPropertyToTextList list_return is NULL.\n");
pszReturnData = malloc(1);
pszReturnData[0] = '\0';
}
}
else {
ErrorF("winClipboardFlushXEvents - SelectionNotify - "
"X*TextPropertyToTextList returned: ");
switch (iReturn) {
case XNoMemory:
ErrorF("XNoMemory\n");
break;
case XLocaleNotSupported:
ErrorF("XLocaleNotSupported\n");
break;
case XConverterNotFound:
ErrorF("XConverterNotFound\n");
break;
default:
ErrorF("%d\n", iReturn);
break;
}
pszReturnData = malloc(1);
pszReturnData[0] = '\0';
}
if (ppszTextList)
XFreeStringList(ppszTextList);
ppszTextList = NULL;
/* Free the data returned from XGetWindowProperty */
XFree(value);
value = NULL;
nitems = 0;
/* Free any INCR data */
if (data->incr) {
free(data->incr);
data->incr = NULL;
data->incrsize = 0;
}
/* Convert the X clipboard string to DOS format */
winClipboardUNIXtoDOS(&pszReturnData, strlen(pszReturnData));
if (data->fUseUnicode) {
/* Find out how much space needed to convert MBCS to Unicode */
int iUnicodeLen = MultiByteToWideChar(CP_UTF8,
0,
pszReturnData, -1, NULL, 0);
/* NOTE: iUnicodeLen includes space for null terminator */
pwszUnicodeStr = malloc(sizeof(wchar_t) * iUnicodeLen);
if (!pwszUnicodeStr) {
ErrorF("winClipboardFlushXEvents - SelectionNotify "
"malloc failed for pwszUnicodeStr, aborting.\n");
/* Abort */
goto winClipboardFlushXEvents_SelectionNotify_Done;
}
/* Do the actual conversion */
MultiByteToWideChar(CP_UTF8,
0,
pszReturnData,
-1, pwszUnicodeStr, iUnicodeLen);
/* Allocate global memory for the X clipboard data */
hGlobal = GlobalAlloc(GMEM_MOVEABLE,
sizeof(wchar_t) * iUnicodeLen);
}
else {
int iConvertDataLen = 0;
pszConvertData = strdup(pszReturnData);
iConvertDataLen = strlen(pszConvertData) + 1;
/* Allocate global memory for the X clipboard data */
hGlobal = GlobalAlloc(GMEM_MOVEABLE, iConvertDataLen);
}
free(pszReturnData);
/* Check that global memory was allocated */
if (!hGlobal) {
ErrorF("winClipboardFlushXEvents - SelectionNotify "
"GlobalAlloc failed, aborting: %08x\n", (unsigned int)GetLastError());
/* Abort */
goto winClipboardFlushXEvents_SelectionNotify_Done;
}
/* Obtain a pointer to the global memory */
pszGlobalData = GlobalLock(hGlobal);
if (pszGlobalData == NULL) {
ErrorF("winClipboardFlushXEvents - Could not lock global "
"memory for clipboard transfer\n");
/* Abort */
goto winClipboardFlushXEvents_SelectionNotify_Done;
}
/* Copy the returned string into the global memory */
if (data->fUseUnicode) {
wcscpy((wchar_t *)pszGlobalData, pwszUnicodeStr);
free(pwszUnicodeStr);
pwszUnicodeStr = NULL;
}
else {
strcpy(pszGlobalData, pszConvertData);
free(pszConvertData);
pszConvertData = NULL;
}
/* Release the pointer to the global memory */
GlobalUnlock(hGlobal);
pszGlobalData = NULL;
/* Push the selection data to the Windows clipboard */
if (data->fUseUnicode)
SetClipboardData(CF_UNICODETEXT, hGlobal);
else
SetClipboardData(CF_TEXT, hGlobal);
/* Flag that SetClipboardData has been called */
fSetClipboardData = FALSE;
/*
* NOTE: Do not try to free pszGlobalData, it is owned by
* Windows after the call to SetClipboardData ().
*/
winClipboardFlushXEvents_SelectionNotify_Done:
/* Free allocated resources */
if (ppszTextList)
XFreeStringList(ppszTextList);
if (value) {
XFree(value);
value = NULL;
nitems = 0;
}
free(pszConvertData);
free(pwszUnicodeStr);
if (hGlobal && pszGlobalData)
GlobalUnlock(hGlobal);
if (fSetClipboardData) {
SetClipboardData(CF_UNICODETEXT, NULL);
SetClipboardData(CF_TEXT, NULL);
}
return WIN_XEVENTS_NOTIFY_DATA;
}
/*
* Process any pending X events
*/
......@@ -193,7 +458,6 @@ winClipboardFlushXEvents(HWND hwnd,
Window iWindow, Display * pDisplay, ClipboardConversionData *data, ClipboardAtoms *atoms)
{
Atom atomClipboard = atoms->atomClipboard;
Atom atomLocalProperty = atoms->atomLocalProperty;
Atom atomUTF8String = atoms->atomUTF8String;
Atom atomCompoundText = atoms->atomCompoundText;
Atom atomTargets = atoms->atomTargets;
......@@ -203,20 +467,14 @@ winClipboardFlushXEvents(HWND hwnd,
XTextProperty xtpText = { 0 };
XEvent event;
XSelectionEvent eventSelection;
unsigned long ulReturnBytesLeft;
char *pszReturnData = NULL;
char *pszGlobalData = NULL;
int iReturn;
HGLOBAL hGlobal = NULL;
XICCEncodingStyle xiccesStyle;
char *pszConvertData = NULL;
char *pszTextList[2] = { NULL };
int iCount;
char **ppszTextList = NULL;
wchar_t *pwszUnicodeStr = NULL;
Bool fAbort = FALSE;
Bool fCloseClipboard = FALSE;
Bool fSetClipboardData = TRUE;
/* Get the next event - will not block because one is ready */
XNextEvent(pDisplay, &event);
......@@ -569,214 +827,19 @@ winClipboardFlushXEvents(HWND hwnd,
return winClipboardSelectionNotifyTargets(hwnd, iWindow, pDisplay, data, atoms);
}
/* Retrieve the selection data and delete the property */
iReturn = XGetWindowProperty(pDisplay,
iWindow,
atomLocalProperty,
0,
INT_MAX,
True,
AnyPropertyType,
&xtpText.encoding,
&xtpText.format,
&xtpText.nitems,
&ulReturnBytesLeft, &xtpText.value);
if (iReturn != Success) {
ErrorF("winClipboardFlushXEvents - SelectionNotify - "
"XGetWindowProperty () failed, aborting: %d\n", iReturn);
goto winClipboardFlushXEvents_SelectionNotify_Done;
}
{
char *pszAtomName = NULL;
winDebug("SelectionNotify - returned data %lu left %lu\n",
xtpText.nitems, ulReturnBytesLeft);
pszAtomName = XGetAtomName(pDisplay, xtpText.encoding);
winDebug("Notify atom name %s\n", pszAtomName);
XFree(pszAtomName);
pszAtomName = NULL;
}
if (data->fUseUnicode) {
#ifdef X_HAVE_UTF8_STRING
/* Convert the text property to a text list */
iReturn = Xutf8TextPropertyToTextList(pDisplay,
&xtpText,
&ppszTextList, &iCount);
#endif
}
else {
iReturn = XmbTextPropertyToTextList(pDisplay,
&xtpText,
&ppszTextList, &iCount);
}
if (iReturn == Success || iReturn > 0) {
/* Conversion succeeded or some unconvertible characters */
if (ppszTextList != NULL) {
int i;
int iReturnDataLen = 0;
for (i = 0; i < iCount; i++) {
iReturnDataLen += strlen(ppszTextList[i]);
}
pszReturnData = malloc(iReturnDataLen + 1);
pszReturnData[0] = '\0';
for (i = 0; i < iCount; i++) {
strcat(pszReturnData, ppszTextList[i]);
}
}
else {
ErrorF("winClipboardFlushXEvents - SelectionNotify - "
"X*TextPropertyToTextList list_return is NULL.\n");
pszReturnData = malloc(1);
pszReturnData[0] = '\0';
}
}
else {
ErrorF("winClipboardFlushXEvents - SelectionNotify - "
"X*TextPropertyToTextList returned: ");
switch (iReturn) {
case XNoMemory:
ErrorF("XNoMemory\n");
break;
case XLocaleNotSupported:
ErrorF("XLocaleNotSupported\n");
break;
case XConverterNotFound:
ErrorF("XConverterNotFound\n");
break;
default:
ErrorF("%d\n", iReturn);
break;
}
pszReturnData = malloc(1);
pszReturnData[0] = '\0';
}
/* Free the data returned from XGetWindowProperty */
if (ppszTextList)
XFreeStringList(ppszTextList);
ppszTextList = NULL;
XFree(xtpText.value);
xtpText.value = NULL;
xtpText.nitems = 0;
/* Convert the X clipboard string to DOS format */
winClipboardUNIXtoDOS(&pszReturnData, strlen(pszReturnData));
if (data->fUseUnicode) {
/* Find out how much space needed to convert MBCS to Unicode */
int iUnicodeLen = MultiByteToWideChar(CP_UTF8,
0,
pszReturnData, -1, NULL, 0);
/* NOTE: iUnicodeLen includes space for null terminator */
pwszUnicodeStr = malloc(sizeof(wchar_t) * iUnicodeLen);
if (!pwszUnicodeStr) {
ErrorF("winClipboardFlushXEvents - SelectionNotify "
"malloc failed for pwszUnicodeStr, aborting.\n");
/* Abort */
fAbort = TRUE;
goto winClipboardFlushXEvents_SelectionNotify_Done;
}
/* Do the actual conversion */
MultiByteToWideChar(CP_UTF8,
0,
pszReturnData,
-1, pwszUnicodeStr, iUnicodeLen);
/* Allocate global memory for the X clipboard data */
hGlobal = GlobalAlloc(GMEM_MOVEABLE,
sizeof(wchar_t) * iUnicodeLen);
}
else {
int iConvertDataLen = 0;
pszConvertData = strdup(pszReturnData);
iConvertDataLen = strlen(pszConvertData) + 1;
/* Allocate global memory for the X clipboard data */
hGlobal = GlobalAlloc(GMEM_MOVEABLE, iConvertDataLen);
}
free(pszReturnData);
/* Check that global memory was allocated */
if (!hGlobal) {
ErrorF("winClipboardFlushXEvents - SelectionNotify "
"GlobalAlloc failed, aborting: %08x\n", (unsigned int)GetLastError());
/* Abort */
fAbort = TRUE;
goto winClipboardFlushXEvents_SelectionNotify_Done;
}
/* Obtain a pointer to the global memory */
pszGlobalData = GlobalLock(hGlobal);
if (pszGlobalData == NULL) {
ErrorF("winClipboardFlushXEvents - Could not lock global "
"memory for clipboard transfer\n");
/* Abort */
fAbort = TRUE;
goto winClipboardFlushXEvents_SelectionNotify_Done;
}
/* Copy the returned string into the global memory */
if (data->fUseUnicode) {
wcscpy((wchar_t *)pszGlobalData, pwszUnicodeStr);
free(pwszUnicodeStr);
pwszUnicodeStr = NULL;
}
else {
strcpy(pszGlobalData, pszConvertData);
free(pszConvertData);
pszConvertData = NULL;
}
/* Release the pointer to the global memory */
GlobalUnlock(hGlobal);
pszGlobalData = NULL;
/* Push the selection data to the Windows clipboard */
if (data->fUseUnicode)
SetClipboardData(CF_UNICODETEXT, hGlobal);
else
SetClipboardData(CF_TEXT, hGlobal);
/* Flag that SetClipboardData has been called */
fSetClipboardData = FALSE;
/*
* NOTE: Do not try to free pszGlobalData, it is owned by
* Windows after the call to SetClipboardData ().
*/
winClipboardFlushXEvents_SelectionNotify_Done:
/* Free allocated resources */
if (ppszTextList)
XFreeStringList(ppszTextList);
if (xtpText.value) {
XFree(xtpText.value);
xtpText.value = NULL;
xtpText.nitems = 0;
}
free(pszConvertData);
free(pwszUnicodeStr);
if (hGlobal && pszGlobalData)
GlobalUnlock(hGlobal);
if (fSetClipboardData) {
SetClipboardData(CF_UNICODETEXT, NULL);
SetClipboardData(CF_TEXT, NULL);
}
return WIN_XEVENTS_NOTIFY_DATA;
return winClipboardSelectionNotifyData(hwnd, iWindow, pDisplay, data, atoms);
case SelectionClear:
winDebug("SelectionClear - doing nothing\n");
break;
case PropertyNotify:
/* If INCR is in progress, collect the data */
if (data->incr &&
(event.xproperty.atom == atoms->atomLocalProperty) &&
(event.xproperty.state == PropertyNewValue))
return winClipboardSelectionNotifyData(hwnd, iWindow, pDisplay, data, atoms);
break;
case MappingNotify:
......
......@@ -39,8 +39,8 @@ XWin(1)
.SH BUGS
Only text clipboard contents are supported.