/* *Copyright (C) 2003-2004 Harold L Hunt II All Rights Reserved. *Copyright (C) Colin Harrison 2005-2008 * *Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the *"Software"), to deal in the Software without restriction, including *without limitation the rights to use, copy, modify, merge, publish, *distribute, sublicense, and/or sell copies of the Software, and to *permit persons to whom the Software is furnished to do so, subject to *the following conditions: * *The above copyright notice and this permission notice shall be *included in all copies or substantial portions of the Software. * *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND *NONINFRINGEMENT. IN NO EVENT SHALL HAROLD L HUNT II BE LIABLE FOR *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * *Except as contained in this notice, the name of the copyright holder(s) *and author(s) shall not be used in advertising or otherwise to promote *the sale, use or other dealings in this Software without prior written *authorization from the copyright holder(s) and author(s). * * Authors: Harold L Hunt II * Colin Harrison */ #ifdef HAVE_XWIN_CONFIG_H #include #endif #include "winclipboard.h" #include "misc.h" /* * Process any pending X events */ int winClipboardFlushXEvents (HWND hwnd, int iWindow, Display *pDisplay, Bool fUseUnicode) { static Atom atomLocalProperty; static Atom atomCompoundText; static Atom atomUTF8String; static Atom atomTargets; static int generation; if (generation != serverGeneration) { generation = serverGeneration; atomLocalProperty = XInternAtom (pDisplay, WIN_LOCAL_PROPERTY, False); atomUTF8String = XInternAtom (pDisplay, "UTF8_STRING", False); atomCompoundText = XInternAtom (pDisplay, "COMPOUND_TEXT", False); atomTargets = XInternAtom (pDisplay, "TARGETS", False); } /* Process all pending events */ while (XPending (pDisplay)) { XTextProperty xtpText = {0}; XEvent event; XSelectionEvent eventSelection; unsigned long ulReturnBytesLeft; char *pszReturnData = NULL; char *pszGlobalData = NULL; int iReturn; HGLOBAL hGlobal = NULL; XICCEncodingStyle xiccesStyle; int iConvertDataLen = 0; char *pszConvertData = NULL; char *pszTextList[2] = {NULL}; int iCount; char **ppszTextList = NULL; wchar_t *pwszUnicodeStr = NULL; int iUnicodeLen = 0; int iReturnDataLen = 0; int i; Bool fAbort = FALSE; Bool fCloseClipboard = FALSE; Bool fSetClipboardData = TRUE; /* Get the next event - will not block because one is ready */ XNextEvent (pDisplay, &event); /* Branch on the event type */ switch (event.type) { /* * SelectionRequest */ case SelectionRequest: { char *pszAtomName = NULL; winDebug("SelectionRequest - target %d\n", event.xselectionrequest.target); pszAtomName = XGetAtomName (pDisplay, event.xselectionrequest.target); winDebug("SelectionRequest - Target atom name %s\n", pszAtomName); XFree (pszAtomName); pszAtomName = NULL; } /* Abort if invalid target type */ if (event.xselectionrequest.target != XA_STRING && event.xselectionrequest.target != atomUTF8String && event.xselectionrequest.target != atomCompoundText && event.xselectionrequest.target != atomTargets) { /* Abort */ fAbort = TRUE; goto winClipboardFlushXEvents_SelectionRequest_Done; } /* Handle targets type of request */ if (event.xselectionrequest.target == atomTargets) { Atom atomTargetArr[] = {atomTargets, atomCompoundText, atomUTF8String, XA_STRING}; winDebug("SelectionRequest - populating targets\n"); /* Try to change the property */ iReturn = XChangeProperty (pDisplay, event.xselectionrequest.requestor, event.xselectionrequest.property, XA_ATOM, 32, PropModeReplace, (unsigned char *) atomTargetArr, (sizeof (atomTargetArr) / sizeof (atomTargetArr[0]))); if (iReturn == BadAlloc || iReturn == BadAtom || iReturn == BadMatch || iReturn == BadValue || iReturn == BadWindow) { ErrorF ("winClipboardFlushXEvents - SelectionRequest - " "XChangeProperty failed: %d\n", iReturn); } /* Setup selection notify xevent */ eventSelection.type = SelectionNotify; eventSelection.send_event = True; eventSelection.display = pDisplay; eventSelection.requestor = event.xselectionrequest.requestor; eventSelection.selection = event.xselectionrequest.selection; eventSelection.target = event.xselectionrequest.target; eventSelection.property = event.xselectionrequest.property; eventSelection.time = event.xselectionrequest.time; /* * Notify the requesting window that * the operation has completed */ iReturn = XSendEvent (pDisplay, eventSelection.requestor, False, 0L, (XEvent *) &eventSelection); if (iReturn == BadValue || iReturn == BadWindow) { ErrorF ("winClipboardFlushXEvents - SelectionRequest - " "XSendEvent () failed\n"); } break; } /* Close clipboard if we have it open already */ if (GetOpenClipboardWindow () == hwnd) { CloseClipboard (); } /* Access the clipboard */ if (!OpenClipboard (hwnd)) { ErrorF ("winClipboardFlushXEvents - SelectionRequest - " "OpenClipboard () failed: %08lx\n", GetLastError ()); /* Abort */ fAbort = TRUE; goto winClipboardFlushXEvents_SelectionRequest_Done; } /* Indicate that clipboard was opened */ fCloseClipboard = TRUE; /* Check that clipboard format is available */ if (fUseUnicode && !IsClipboardFormatAvailable (CF_UNICODETEXT)) { static int count; /* Hack to stop acroread spamming the log */ static HWND lasthwnd; /* I've not seen any other client get here repeatedly? */ if (hwnd != lasthwnd) count = 0; count++; if (count < 6) ErrorF ("winClipboardFlushXEvents - CF_UNICODETEXT is not " "available from Win32 clipboard. Aborting %d.\n", count); lasthwnd = hwnd; /* Abort */ fAbort = TRUE; goto winClipboardFlushXEvents_SelectionRequest_Done; } else if (!fUseUnicode && !IsClipboardFormatAvailable (CF_TEXT)) { ErrorF ("winClipboardFlushXEvents - CF_TEXT is not " "available from Win32 clipboard. Aborting.\n"); /* Abort */ fAbort = TRUE; goto winClipboardFlushXEvents_SelectionRequest_Done; } /* Setup the string style */ if (event.xselectionrequest.target == XA_STRING) xiccesStyle = XStringStyle; #ifdef X_HAVE_UTF8_STRING else if (event.xselectionrequest.target == atomUTF8String) xiccesStyle = XUTF8StringStyle; #endif else if (event.xselectionrequest.target == atomCompoundText) xiccesStyle = XCompoundTextStyle; else xiccesStyle = XStringStyle; /* Get a pointer to the clipboard text, in desired format */ if (fUseUnicode) { /* Retrieve clipboard data */ hGlobal = GetClipboardData (CF_UNICODETEXT); } else { /* Retrieve clipboard data */ hGlobal = GetClipboardData (CF_TEXT); } if (!hGlobal) { ErrorF ("winClipboardFlushXEvents - SelectionRequest - " "GetClipboardData () failed: %08lx\n", GetLastError ()); /* Abort */ fAbort = TRUE; goto winClipboardFlushXEvents_SelectionRequest_Done; } pszGlobalData = (char *) GlobalLock (hGlobal); /* Convert the Unicode string to UTF8 (MBCS) */ if (fUseUnicode) { iConvertDataLen = WideCharToMultiByte (CP_UTF8, 0, (LPCWSTR)pszGlobalData, -1, NULL, 0, NULL, NULL); /* NOTE: iConvertDataLen includes space for null terminator */ pszConvertData = (char *) malloc (iConvertDataLen); WideCharToMultiByte (CP_UTF8, 0, (LPCWSTR)pszGlobalData, -1, pszConvertData, iConvertDataLen, NULL, NULL); } else { pszConvertData = strdup (pszGlobalData); iConvertDataLen = strlen (pszConvertData) + 1; } /* Convert DOS string to UNIX string */ winClipboardDOStoUNIX (pszConvertData, strlen (pszConvertData)); /* Setup our text list */ pszTextList[0] = pszConvertData; pszTextList[1] = NULL; /* Initialize the text property */ xtpText.value = NULL; xtpText.nitems = 0; /* Create the text property from the text list */ if (fUseUnicode) { #ifdef X_HAVE_UTF8_STRING iReturn = Xutf8TextListToTextProperty (pDisplay, pszTextList, 1, xiccesStyle, &xtpText); #endif } else { iReturn = XmbTextListToTextProperty (pDisplay, pszTextList, 1, xiccesStyle, &xtpText); } if (iReturn == XNoMemory || iReturn == XLocaleNotSupported) { ErrorF ("winClipboardFlushXEvents - SelectionRequest - " "X*TextListToTextProperty failed: %d\n", iReturn); /* Abort */ fAbort = TRUE; goto winClipboardFlushXEvents_SelectionRequest_Done; } /* Free the converted string */ free (pszConvertData); pszConvertData = NULL; /* Copy the clipboard text to the requesting window */ iReturn = XChangeProperty (pDisplay, event.xselectionrequest.requestor, event.xselectionrequest.property, event.xselectionrequest.target, 8, PropModeReplace, xtpText.value, xtpText.nitems); if (iReturn == BadAlloc || iReturn == BadAtom || iReturn == BadMatch || iReturn == BadValue || iReturn == BadWindow) { ErrorF ("winClipboardFlushXEvents - SelectionRequest - " "XChangeProperty failed: %d\n", iReturn); /* Abort */ fAbort = TRUE; goto winClipboardFlushXEvents_SelectionRequest_Done; } /* Release the clipboard data */ GlobalUnlock (hGlobal); pszGlobalData = NULL; fCloseClipboard = FALSE; CloseClipboard (); /* Clean up */ XFree (xtpText.value); xtpText.value = NULL; xtpText.nitems = 0; /* Setup selection notify event */ eventSelection.type = SelectionNotify; eventSelection.send_event = True; eventSelection.display = pDisplay; eventSelection.requestor = event.xselectionrequest.requestor; eventSelection.selection = event.xselectionrequest.selection; eventSelection.target = event.xselectionrequest.target; eventSelection.property = event.xselectionrequest.property; eventSelection.time = event.xselectionrequest.time; /* Notify the requesting window that the operation has completed */ iReturn = XSendEvent (pDisplay, eventSelection.requestor, False, 0L, (XEvent *) &eventSelection); if (iReturn == BadValue || iReturn == BadWindow) { ErrorF ("winClipboardFlushXEvents - SelectionRequest - " "XSendEvent () failed\n"); /* Abort */ fAbort = TRUE; goto winClipboardFlushXEvents_SelectionRequest_Done; } winClipboardFlushXEvents_SelectionRequest_Done: /* Free allocated resources */ if (xtpText.value) { XFree (xtpText.value); xtpText.value = NULL; xtpText.nitems = 0; } free(pszConvertData); if (hGlobal && pszGlobalData) GlobalUnlock (hGlobal); /* * Send a SelectionNotify event to the requesting * client when we abort. */ if (fAbort) { winDebug("SelectionRequest - aborting\n"); /* Setup selection notify event */ eventSelection.type = SelectionNotify; eventSelection.send_event = True; eventSelection.display = pDisplay; eventSelection.requestor = event.xselectionrequest.requestor; eventSelection.selection = event.xselectionrequest.selection; eventSelection.target = event.xselectionrequest.target; eventSelection.property = None; eventSelection.time = event.xselectionrequest.time; /* Notify the requesting window that the operation is complete */ iReturn = XSendEvent (pDisplay, eventSelection.requestor, False, 0L, (XEvent *) &eventSelection); if (iReturn == BadValue || iReturn == BadWindow) { /* * Should not be a problem if XSendEvent fails because * the client may simply have exited. */ ErrorF ("winClipboardFlushXEvents - SelectionRequest - " "XSendEvent () failed for abort event.\n"); } } /* Close clipboard if it was opened */ if (fCloseClipboard) { fCloseClipboard = FALSE; CloseClipboard (); } break; /* * SelectionNotify */ case SelectionNotify: winDebug ("winClipboardFlushXEvents - SelectionNotify\n"); { char *pszAtomName; pszAtomName = XGetAtomName (pDisplay, event.xselection.selection); winDebug("winClipboardFlushXEvents - SelectionNotify - ATOM: %s\n", pszAtomName); XFree (pszAtomName); } /* * Request conversion of UTF8 and CompoundText targets. */ if (event.xselection.property == None) { if (event.xselection.target == XA_STRING) { winDebug ("winClipboardFlushXEvents - SelectionNotify - " "XA_STRING\n"); return WIN_XEVENTS_CONVERT; } else if (event.xselection.target == atomUTF8String) { winDebug("winClipboardFlushXEvents - SelectionNotify - " "Requesting conversion of UTF8 target.\n"); XConvertSelection (pDisplay, event.xselection.selection, XA_STRING, atomLocalProperty, iWindow, CurrentTime); /* Process the ConvertSelection event */ XFlush (pDisplay); return WIN_XEVENTS_CONVERT; } #ifdef X_HAVE_UTF8_STRING else if (event.xselection.target == atomCompoundText) { winDebug("winClipboardFlushXEvents - SelectionNotify - " "Requesting conversion of CompoundText target.\n"); XConvertSelection (pDisplay, event.xselection.selection, atomUTF8String, atomLocalProperty, iWindow, CurrentTime); /* Process the ConvertSelection event */ XFlush (pDisplay); return WIN_XEVENTS_CONVERT; } #endif else { ErrorF ("winClipboardFlushXEvents - SelectionNotify - " "Unknown format. Cannot request conversion, " "aborting.\n"); break; } } case SelectionClear: winDebug("SelectionClear - doing nothing\n"); break; case PropertyNotify: { char *pszAtomName; pszAtomName = XGetAtomName (pDisplay, event.xproperty.atom); winDebug("winClipboardFlushXEvents - PropertyNotify - ATOM: %s\n", pszAtomName); XFree (pszAtomName); } if (event.xproperty.atom != atomLocalProperty) break; /* Retrieve the size of the stored data */ iReturn = XGetWindowProperty (pDisplay, iWindow, atomLocalProperty, 0, 0, /* Don't get data, just size */ False, AnyPropertyType, &xtpText.encoding, &xtpText.format, &xtpText.nitems, &ulReturnBytesLeft, &xtpText.value); if (iReturn != Success) { ErrorF ("winClipboardFlushXEvents - PropertyNotify - " "XGetWindowProperty () failed, aborting: %d\n", iReturn); break; } winDebug("PropertyNotify - returned data %d left %d\n", xtpText.nitems, ulReturnBytesLeft); /* Request the selection data */ iReturn = XGetWindowProperty (pDisplay, iWindow, atomLocalProperty, 0, ulReturnBytesLeft, False, AnyPropertyType, &xtpText.encoding, &xtpText.format, &xtpText.nitems, &ulReturnBytesLeft, &xtpText.value); if (iReturn != Success) { ErrorF ("winClipboardFlushXEvents - PropertyNotify - " "XGetWindowProperty () failed, aborting: %d\n", iReturn); break; } { char *pszAtomName = NULL; winDebug("PropertyNotify - returned data %d left %d\n", xtpText.nitems, ulReturnBytesLeft); pszAtomName = XGetAtomName(pDisplay, xtpText.encoding); winDebug("SelectionNotify - encoding atom name %s\n", pszAtomName); XFree (pszAtomName); pszAtomName = NULL; } if (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) { 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 - PropertyNotify - " "X*TextPropertyToTextList list_return is NULL.\n"); pszReturnData = malloc (1); pszReturnData[0] = '\0'; } } else { ErrorF ("winClipboardFlushXEvents - PropertyNotify - " "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 (fUseUnicode) { /* Find out how much space needed to convert MBCS to Unicode */ iUnicodeLen = MultiByteToWideChar (CP_UTF8, 0, pszReturnData, -1, NULL, 0); /* Allocate memory for the Unicode string */ pwszUnicodeStr = (wchar_t*) malloc (sizeof (wchar_t) * (iUnicodeLen + 1)); if (!pwszUnicodeStr) { ErrorF ("winClipboardFlushXEvents - PropertyNotify " "malloc failed for pwszUnicodeStr, aborting.\n"); /* Abort */ fAbort = TRUE; goto winClipboardFlushXEvents_PropertyNotify_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 + 1)); } else { 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 - PropertyNotify " "GlobalAlloc failed, aborting: %ld\n", GetLastError ()); /* Abort */ fAbort = TRUE; goto winClipboardFlushXEvents_PropertyNotify_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_PropertyNotify_Done; } /* Copy the returned string into the global memory */ if (fUseUnicode) { memcpy (pszGlobalData, pwszUnicodeStr, sizeof (wchar_t) * (iUnicodeLen + 1)); 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 (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_PropertyNotify_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; case MappingNotify: break; default: ErrorF ("winClipboardFlushXEvents - unexpected event type %d\n", event.type); break; } } return WIN_XEVENTS_SUCCESS; }