aboutsummaryrefslogtreecommitdiff
path: root/xorg-server/hw/xwin/winclipboardwndproc.c
diff options
context:
space:
mode:
Diffstat (limited to 'xorg-server/hw/xwin/winclipboardwndproc.c')
-rw-r--r--xorg-server/hw/xwin/winclipboardwndproc.c1179
1 files changed, 550 insertions, 629 deletions
diff --git a/xorg-server/hw/xwin/winclipboardwndproc.c b/xorg-server/hw/xwin/winclipboardwndproc.c
index 9394b04db..dbfc61e4e 100644
--- a/xorg-server/hw/xwin/winclipboardwndproc.c
+++ b/xorg-server/hw/xwin/winclipboardwndproc.c
@@ -1,629 +1,550 @@
-/*
- *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 <xwin-config.h>
-#endif
-#include <sys/types.h>
-#include <sys/time.h>
-#include "winclipboard.h"
-#include "misc.h"
-
-/*
- * Constants
- */
-
-#define WIN_CLIPBOARD_PROP "cyg_clipboard_prop"
-#define WIN_POLL_TIMEOUT 1
-
-
-/*
- * References to external symbols
- */
-
-extern Bool g_fUseUnicode;
-extern Bool g_fUnicodeSupport;
-extern void *g_pClipboardDisplay;
-extern Window g_iClipboardWindow;
-extern Atom g_atomLastOwnedSelection;
-
-/* BPS - g_hwndClipboard needed for X app->Windows paste fix */
-extern HWND g_hwndClipboard;
-
-/*
- * Local function prototypes
- */
-
-static int
-winProcessXEventsTimeout (HWND hwnd, int iWindow, Display *pDisplay,
- Bool fUseUnicode, int iTimeoutSec);
-
-
-/*
- * Process X events up to specified timeout
- */
-
-static int
-winProcessXEventsTimeout (HWND hwnd, int iWindow, Display *pDisplay,
- Bool fUseUnicode, int iTimeoutSec)
-{
- int iConnNumber;
- struct timeval tv;
- int iReturn;
- DWORD dwStopTime = (GetTickCount () / 1000) + iTimeoutSec;
-
- /* We need to ensure that all pending events are processed */
- XSync (pDisplay, FALSE);
-
- /* Get our connection number */
- iConnNumber = ConnectionNumber (pDisplay);
-
- /* Loop for X events */
- while (1)
- {
- fd_set fdsRead;
-
- /* Setup the file descriptor set */
- FD_ZERO (&fdsRead);
- FD_SET (iConnNumber, &fdsRead);
-
- /* Adjust timeout */
- tv.tv_sec = dwStopTime - (GetTickCount () / 1000);
- tv.tv_usec = 0;
-
- /* Break out if no time left */
- if (tv.tv_sec < 0)
- return WIN_XEVENTS_SUCCESS;
-
- /* Wait for an X event */
- iReturn = select (iConnNumber + 1,/* Highest fds number */
- &fdsRead, /* Read mask */
- NULL, /* No write mask */
- NULL, /* No exception mask */
- &tv); /* No timeout */
- if (iReturn < 0)
- {
- ErrorF ("winProcessXEventsTimeout - Call to select () failed: %d. "
- "Bailing.\n", iReturn);
- break;
- }
-
- /* Branch on which descriptor became active */
- if (FD_ISSET (iConnNumber, &fdsRead))
- {
- /* Process X events */
- /* Exit when we see that server is shutting down */
- iReturn = winClipboardFlushXEvents (hwnd,
- iWindow,
- pDisplay,
- fUseUnicode);
- if (WIN_XEVENTS_NOTIFY == iReturn
- || WIN_XEVENTS_CONVERT == iReturn)
- {
- /* Bail out if convert or notify processed */
- return iReturn;
- }
- }
- }
-
- return WIN_XEVENTS_SUCCESS;
-}
-
-
-/*
- * Process a given Windows message
- */
-
-/* BPS - Define our own message, which we'll post to ourselves to facilitate
- * resetting the delayed rendering mechanism after each paste from X app to
- * Windows app. TODO - Perhaps move to win.h with the other WM_USER messages.
- */
-#define WM_USER_PASTE_COMPLETE (WM_USER + 1003)
-
-LRESULT CALLBACK
-winClipboardWindowProc (HWND hwnd, UINT message,
- WPARAM wParam, LPARAM lParam)
-{
- static HWND s_hwndNextViewer;
- static Bool s_fCBCInitialized;
-
- /* Branch on message type */
- switch (message)
- {
- case WM_DESTROY:
- {
- winDebug ("winClipboardWindowProc - WM_DESTROY\n");
-
- /* Remove ourselves from the clipboard chain */
- ChangeClipboardChain (hwnd, s_hwndNextViewer);
-
- s_hwndNextViewer = NULL;
-
- PostQuitMessage (0);
- }
- return 0;
-
-
- case WM_CREATE:
- {
- HWND first, next;
- DWORD error_code = 0;
- winDebug ("winClipboardWindowProc - WM_CREATE\n");
-
- first = GetClipboardViewer(); /* Get handle to first viewer in chain. */
- if (first == hwnd) return 0; /* Make sure it's not us! */
- /* Add ourselves to the clipboard viewer chain */
- next = SetClipboardViewer (hwnd);
- error_code = GetLastError();
- if (SUCCEEDED(error_code) && (next == first)) /* SetClipboardViewer must have succeeded, and the handle */
- s_hwndNextViewer = next; /* it returned must have been the first window in the chain */
- else
- s_fCBCInitialized = FALSE;
- }
- return 0;
-
-
- case WM_CHANGECBCHAIN:
- {
- winDebug ("winClipboardWindowProc - WM_CHANGECBCHAIN: wParam(%x) "
- "lParam(%x) s_hwndNextViewer(%x)\n",
- wParam, lParam, s_hwndNextViewer);
-
- if ((HWND) wParam == s_hwndNextViewer)
- {
- s_hwndNextViewer = (HWND) lParam;
- if (s_hwndNextViewer == hwnd)
- {
- s_hwndNextViewer = NULL;
- winErrorFVerb (1, "winClipboardWindowProc - WM_CHANGECBCHAIN: "
- "attempted to set next window to ourselves.");
- }
- }
- else if (s_hwndNextViewer)
- SendMessage (s_hwndNextViewer, message,
- wParam, lParam);
-
- }
- winDebug ("winClipboardWindowProc - WM_CHANGECBCHAIN: Exit\n");
- return 0;
-
- case WM_WM_REINIT:
- {
- /* Ensure that we're in the clipboard chain. Some apps,
- * WinXP's remote desktop for one, don't play nice with the
- * chain. This message is called whenever we receive a
- * WM_ACTIVATEAPP message to ensure that we continue to
- * receive clipboard messages.
- *
- * It might be possible to detect if we're still in the chain
- * by calling SendMessage (GetClipboardViewer(),
- * WM_DRAWCLIPBOARD, 0, 0); and then seeing if we get the
- * WM_DRAWCLIPBOARD message. That, however, might be more
- * expensive than just putting ourselves back into the chain.
- */
-
- HWND first, next;
- DWORD error_code = 0;
- winDebug ("winClipboardWindowProc - WM_WM_REINIT: Enter\n");
-
- first = GetClipboardViewer(); /* Get handle to first viewer in chain. */
- if (first == hwnd) return 0; /* Make sure it's not us! */
- winDebug (" WM_WM_REINIT: Replacing us(%x) with %x at head "
- "of chain\n", hwnd, s_hwndNextViewer);
- s_fCBCInitialized = FALSE;
- ChangeClipboardChain (hwnd, s_hwndNextViewer);
- s_hwndNextViewer = NULL;
- s_fCBCInitialized = FALSE;
- winDebug (" WM_WM_REINIT: Putting us back at head of chain.\n");
- first = GetClipboardViewer(); /* Get handle to first viewer in chain. */
- if (first == hwnd) return 0; /* Make sure it's not us! */
- next = SetClipboardViewer (hwnd);
- error_code = GetLastError();
- if (SUCCEEDED(error_code) && (next == first)) /* SetClipboardViewer must have succeeded, and the handle */
- s_hwndNextViewer = next; /* it returned must have been the first window in the chain */
- else
- s_fCBCInitialized = FALSE;
- }
- winDebug ("winClipboardWindowProc - WM_WM_REINIT: Exit\n");
- return 0;
-
-
- case WM_DRAWCLIPBOARD:
- {
- static Atom atomClipboard;
- static int generation;
- static Bool s_fProcessingDrawClipboard = FALSE;
- Display *pDisplay = g_pClipboardDisplay;
- Window iWindow = g_iClipboardWindow;
- int iReturn;
-
- winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Enter\n");
-
- if (generation != serverGeneration)
- {
- generation = serverGeneration;
- atomClipboard = XInternAtom (pDisplay, "CLIPBOARD", False);
- }
-
- /*
- * We've occasionally seen a loop in the clipboard chain.
- * Try and fix it on the first hint of recursion.
- */
- if (! s_fProcessingDrawClipboard)
- {
- s_fProcessingDrawClipboard = TRUE;
- }
- else
- {
- /* Attempt to break the nesting by getting out of the chain, twice?, and then fix and bail */
- s_fCBCInitialized = FALSE;
- ChangeClipboardChain (hwnd, s_hwndNextViewer);
- winFixClipboardChain();
- winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
- "Nested calls detected. Re-initing.\n");
- winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
- s_fProcessingDrawClipboard = FALSE;
- return 0;
- }
-
- /* Bail on first message */
- if (!s_fCBCInitialized)
- {
- s_fCBCInitialized = TRUE;
- s_fProcessingDrawClipboard = FALSE;
- winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
- return 0;
- }
-
- /*
- * NOTE: We cannot bail out when NULL == GetClipboardOwner ()
- * because some applications deal with the clipboard in a manner
- * that causes the clipboard owner to be NULL when they are in
- * fact taking ownership. One example of this is the Win32
- * native compile of emacs.
- */
-
- /* Bail when we still own the clipboard */
- if (hwnd == GetClipboardOwner ())
- {
-
- winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
- "We own the clipboard, returning.\n");
- winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
- s_fProcessingDrawClipboard = FALSE;
- if (s_hwndNextViewer)
- SendMessage (s_hwndNextViewer, message, wParam, lParam);
- return 0;
- }
-
- /*
- * Do not take ownership of the X11 selections when something
- * other than CF_TEXT or CF_UNICODETEXT has been copied
- * into the Win32 clipboard.
- */
- if (!IsClipboardFormatAvailable (CF_TEXT)
- && !IsClipboardFormatAvailable (CF_UNICODETEXT))
- {
-
- winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
- "Clipboard does not contain CF_TEXT nor "
- "CF_UNICODETEXT.\n");
-
- /*
- * We need to make sure that the X Server has processed
- * previous XSetSelectionOwner messages.
- */
- XSync (pDisplay, FALSE);
-
- /* Release PRIMARY selection if owned */
- iReturn = XGetSelectionOwner (pDisplay, XA_PRIMARY);
- if (iReturn == g_iClipboardWindow)
- {
- winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
- "PRIMARY selection is owned by us.\n");
- XSetSelectionOwner (pDisplay,
- XA_PRIMARY,
- None,
- CurrentTime);
- }
- else if (BadWindow == iReturn || BadAtom == iReturn)
- winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
- "XGetSelection failed for PRIMARY: %d\n", iReturn);
-
- /* Release CLIPBOARD selection if owned */
- iReturn = XGetSelectionOwner (pDisplay,
- atomClipboard);
- if (iReturn == g_iClipboardWindow)
- {
- winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
- "CLIPBOARD selection is owned by us.\n");
- XSetSelectionOwner (pDisplay,
- atomClipboard,
- None,
- CurrentTime);
- }
- else if (BadWindow == iReturn || BadAtom == iReturn)
- winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
- "XGetSelection failed for CLIPBOARD: %d\n", iReturn);
-
- winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
- s_fProcessingDrawClipboard = FALSE;
- if (s_hwndNextViewer)
- SendMessage (s_hwndNextViewer, message, wParam, lParam);
- return 0;
- }
-
- /* Reassert ownership of PRIMARY */
- iReturn = XSetSelectionOwner (pDisplay,
- XA_PRIMARY,
- iWindow,
- CurrentTime);
- if (iReturn == BadAtom || iReturn == BadWindow ||
- XGetSelectionOwner (pDisplay, XA_PRIMARY) != iWindow)
- {
- winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
- "Could not reassert ownership of PRIMARY\n");
- }
- else
- {
- winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
- "Reasserted ownership of PRIMARY\n");
- }
-
- /* Reassert ownership of the CLIPBOARD */
- iReturn = XSetSelectionOwner (pDisplay,
- atomClipboard,
- iWindow,
- CurrentTime);
-
- if (iReturn == BadAtom || iReturn == BadWindow ||
- XGetSelectionOwner (pDisplay, atomClipboard) != iWindow)
- {
- winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
- "Could not reassert ownership of CLIPBOARD\n");
- }
- else
- {
- winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
- "Reasserted ownership of CLIPBOARD\n");
- }
-
- /* Flush the pending SetSelectionOwner event now */
- XFlush (pDisplay);
-
- s_fProcessingDrawClipboard = FALSE;
- }
- winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
- /* Pass the message on the next window in the clipboard viewer chain */
- if (s_hwndNextViewer)
- SendMessage (s_hwndNextViewer, message, wParam, lParam);
- return 0;
-
-
- case WM_DESTROYCLIPBOARD:
- /*
- * NOTE: Intentionally do nothing.
- * Changes in the Win32 clipboard are handled by WM_DRAWCLIPBOARD
- * above. We only process this message to conform to the specs
- * for delayed clipboard rendering in Win32. You might think
- * that we need to release ownership of the X11 selections, but
- * we do not, because a WM_DRAWCLIPBOARD message will closely
- * follow this message and reassert ownership of the X11
- * selections, handling the issue for us.
- */
- winDebug ("winClipboardWindowProc - WM_DESTROYCLIPBOARD - Ignored.\n");
- return 0;
-
- case WM_RENDERFORMAT:
- case WM_RENDERALLFORMATS:
- {
- int iReturn;
- Display *pDisplay = g_pClipboardDisplay;
- Window iWindow = g_iClipboardWindow;
- Bool fConvertToUnicode;
-
- winDebug ("winClipboardWindowProc - WM_RENDER*FORMAT - Hello.\n");
-
- /* Flag whether to convert to Unicode or not */
- if (message == WM_RENDERALLFORMATS)
- fConvertToUnicode = FALSE;
- else
- fConvertToUnicode = g_fUnicodeSupport && (CF_UNICODETEXT == wParam);
-
- /* Request the selection contents */
- iReturn = XConvertSelection (pDisplay,
- g_atomLastOwnedSelection,
- XInternAtom (pDisplay,
- "COMPOUND_TEXT", False),
- XInternAtom (pDisplay,
- "CYGX_CUT_BUFFER", False),
- iWindow,
- CurrentTime);
- if (iReturn == BadAtom || iReturn == BadWindow)
- {
- winErrorFVerb (1, "winClipboardWindowProc - WM_RENDER*FORMAT - "
- "XConvertSelection () failed\n");
- break;
- }
-
- /* Special handling for WM_RENDERALLFORMATS */
- if (message == WM_RENDERALLFORMATS)
- {
- /* We must open and empty the clipboard */
-
- /* Close clipboard if we have it open already */
- if (GetOpenClipboardWindow () == hwnd)
- {
- CloseClipboard ();
- }
-
- if (!OpenClipboard (hwnd))
- {
- winErrorFVerb (1, "winClipboardWindowProc - WM_RENDER*FORMATS - "
- "OpenClipboard () failed: %08x\n",
- GetLastError ());
- break;
- }
-
- if (!EmptyClipboard ())
- {
- winErrorFVerb (1, "winClipboardWindowProc - WM_RENDER*FORMATS - "
- "EmptyClipboard () failed: %08x\n",
- GetLastError ());
- break;
- }
- }
-
- /* Process the SelectionNotify event */
- iReturn = winProcessXEventsTimeout (hwnd,
- iWindow,
- pDisplay,
- fConvertToUnicode,
- WIN_POLL_TIMEOUT);
- if (WIN_XEVENTS_CONVERT == iReturn)
- {
- /*
- * The selection was offered for conversion first, so we have
- * to process a second SelectionNotify event to get the actual
- * data in the selection.
- */
- iReturn = winProcessXEventsTimeout (hwnd,
- iWindow,
- pDisplay,
- fConvertToUnicode,
- WIN_POLL_TIMEOUT);
- }
-
- /*
- * The last of the up-to two calls to winProcessXEventsTimeout
- * from above had better have seen a notify event, or else we
- * are dealing with a buggy or old X11 app. In these cases we
- * have to paste some fake data to the Win32 clipboard to
- * satisfy the requirement that we write something to it.
- */
- if (WIN_XEVENTS_NOTIFY != iReturn)
- {
- /* Paste no data, to satisfy required call to SetClipboardData */
- if (g_fUnicodeSupport)
- SetClipboardData (CF_UNICODETEXT, NULL);
- SetClipboardData (CF_TEXT, NULL);
-
- ErrorF("winClipboardWindowProc - timed out waiting for WIN_XEVENTS_NOTIFY\n");
- }
-
- /* BPS - Post ourselves a user message whose handler will reset the
- * delayed rendering mechanism after the paste is complete. This is
- * necessary because calling SetClipboardData() with a NULL argument
- * here will cause the data we just put on the clipboard to be lost!
- */
- PostMessage(g_hwndClipboard, WM_USER_PASTE_COMPLETE, 0, 0);
-
- /* Special handling for WM_RENDERALLFORMATS */
- if (message == WM_RENDERALLFORMATS)
- {
- /* We must close the clipboard */
-
- if (!CloseClipboard ())
- {
- winErrorFVerb (1, "winClipboardWindowProc - WM_RENDERALLFORMATS - "
- "CloseClipboard () failed: %08x\n",
- GetLastError ());
- break;
- }
- }
-
- winDebug ("winClipboardWindowProc - WM_RENDER*FORMAT - Returning.\n");
- return 0;
- }
- /* BPS - This WM_USER message is posted by us. It gives us the opportunity
- * to reset the delayed rendering mechanism after each and every paste
- * from an X app to a Windows app. Without such a mechanism, subsequent
- * changes of selection in the X app owning the selection are not
- * reflected in pastes into Windows apps, since Windows won't send us the
- * WM_RENDERFORMAT message unless someone has set changed data (or NULL)
- * on the clipboard. */
- case WM_USER_PASTE_COMPLETE:
- {
- if (hwnd != GetClipboardOwner ())
- /* In case we've lost the selection since posting the message */
- return 0;
- winDebug ("winClipboardWindowProc - WM_USER_PASTE_COMPLETE\n");
-
- /* Set up for another delayed rendering callback */
- OpenClipboard (g_hwndClipboard);
-
- /* Take ownership of the Windows clipboard */
- EmptyClipboard ();
-
- /* Advertise Unicode if we support it */
- if (g_fUnicodeSupport)
- SetClipboardData (CF_UNICODETEXT, NULL);
-
- /* Always advertise regular text */
- SetClipboardData (CF_TEXT, NULL);
-
- /* Release the clipboard */
- CloseClipboard ();
- }
- return 0;
- }
-
- /* Let Windows perform default processing for unhandled messages */
- return DefWindowProc (hwnd, message, wParam, lParam);
-}
-
-
-/*
- * Process any pending Windows messages
- */
-
-BOOL
-winClipboardFlushWindowsMessageQueue (HWND hwnd)
-{
- MSG msg;
-
- /* Flush the messaging window queue */
- /* NOTE: Do not pass the hwnd of our messaging window to PeekMessage,
- * as this will filter out many non-window-specific messages that
- * are sent to our thread, such as WM_QUIT.
- */
- while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
- {
- /* Dispatch the message if not WM_QUIT */
- if (msg.message == WM_QUIT)
- return FALSE;
- else
- DispatchMessage (&msg);
- }
-
- return TRUE;
-}
+/*
+ *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 <xwin-config.h>
+#endif
+#include <sys/types.h>
+#include <sys/time.h>
+#include "winclipboard.h"
+#include "misc.h"
+#include "winmsg.h"
+
+/*
+ * Constants
+ */
+
+#define WIN_CLIPBOARD_PROP "cyg_clipboard_prop"
+#define WIN_POLL_TIMEOUT 1
+
+/*
+ * References to external symbols
+ */
+
+extern Bool g_fUseUnicode;
+extern Bool g_fUnicodeSupport;
+extern void *g_pClipboardDisplay;
+extern Window g_iClipboardWindow;
+extern Atom g_atomLastOwnedSelection;
+extern Bool g_fClipboardStarted;
+/* BPS - g_hwndClipboard needed for X app->Windows paste fix */
+extern HWND g_hwndClipboard;
+
+/*
+ * Local function prototypes
+ */
+
+static int
+winProcessXEventsTimeout (HWND hwnd, int iWindow, Display *pDisplay,
+ Bool fUseUnicode, int iTimeoutSec);
+
+
+/*
+ * Process X events up to specified timeout
+ */
+
+static int
+winProcessXEventsTimeout (HWND hwnd, int iWindow, Display *pDisplay,
+ Bool fUseUnicode, int iTimeoutSec)
+{
+ int iConnNumber;
+ struct timeval tv;
+ int iReturn;
+ DWORD dwStopTime = (GetTickCount () / 1000) + iTimeoutSec;
+
+ /* Make sure the output messages are sent before waiting on a response. */
+ iReturn = winClipboardFlushXEvents (hwnd,
+ iWindow,
+ pDisplay,
+ fUseUnicode,
+ TRUE);
+ if (WIN_XEVENTS_NOTIFY == iReturn || WIN_XEVENTS_CONVERT == iReturn)
+ {
+ /* Bail out if convert or notify processed */
+ return iReturn;
+ }
+
+ /* Get our connection number */
+ iConnNumber = ConnectionNumber (pDisplay);
+
+ /* Loop for X events */
+ while (1)
+ {
+ fd_set fdsRead;
+
+ /* Setup the file descriptor set */
+ FD_ZERO (&fdsRead);
+ FD_SET (iConnNumber, &fdsRead);
+
+ /* Adjust timeout */
+ tv.tv_sec = dwStopTime - (GetTickCount () / 1000);
+ tv.tv_usec = 0;
+
+ /* Break out if no time left */
+ if (tv.tv_sec < 0)
+ return WIN_XEVENTS_SUCCESS;
+
+ /* Wait for an X event */
+ iReturn = select (iConnNumber + 1,/* Highest fds number */
+ &fdsRead, /* Read mask */
+ NULL, /* No write mask */
+ NULL, /* No exception mask */
+ &tv); /* No timeout */
+ if (iReturn < 0)
+ {
+ ErrorF ("winProcessXEventsTimeout - Call to select () failed: %d (%x). "
+ "Bailing.\n", iReturn, WSAGetLastError());
+ break;
+ }
+
+ /* Branch on which descriptor became active */
+ if (FD_ISSET (iConnNumber, &fdsRead))
+ {
+ /* Process X events */
+ /* Exit when we see that server is shutting down */
+ iReturn = winClipboardFlushXEvents (hwnd,
+ iWindow,
+ pDisplay,
+ fUseUnicode,
+ TRUE);
+ if (WIN_XEVENTS_NOTIFY == iReturn
+ || WIN_XEVENTS_CONVERT == iReturn)
+ {
+ /* Bail out if convert or notify processed */
+ return iReturn;
+ }
+ }
+ }
+
+ return WIN_XEVENTS_SUCCESS;
+}
+
+/*
+ * Process a given Windows message
+ */
+
+LRESULT CALLBACK
+winClipboardWindowProc (HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam)
+{
+ static HWND s_hwndNextViewer;
+
+ /* Branch on message type */
+ switch (message)
+ {
+ case WM_DESTROY:
+ {
+ winDebug ("winClipboardWindowProc - WM_DESTROY\n");
+
+ /* Remove ourselves from the clipboard chain */
+ ChangeClipboardChain (hwnd, s_hwndNextViewer);
+
+ s_hwndNextViewer = NULL;
+ g_hwndClipboard = NULL;
+ PostQuitMessage (0);
+ }
+ return 0;
+
+
+ case WM_CREATE:
+ {
+ HWND first, next;
+ DWORD error_code = 0;
+ winDebug ("winClipboardWindowProc - WM_CREATE\n");
+
+ /* Add ourselves to the clipboard viewer chain */
+ s_hwndNextViewer = SetClipboardViewer (hwnd);
+ }
+ return 0;
+
+
+ case WM_CHANGECBCHAIN:
+ {
+ winDebug ("winClipboardWindowProc - WM_CHANGECBCHAIN: wParam(%x) "
+ "lParam(%x) s_hwndNextViewer(%x)\n",
+ wParam, lParam, s_hwndNextViewer);
+
+ if ((HWND) wParam == s_hwndNextViewer)
+ {
+ s_hwndNextViewer = (HWND) lParam;
+ }
+ else if (s_hwndNextViewer)
+ SendMessage (s_hwndNextViewer, message,
+ wParam, lParam);
+
+ }
+ winDebug ("winClipboardWindowProc - WM_CHANGECBCHAIN: Exit\n");
+ return 0;
+
+ case WM_WM_REINIT:
+ {
+ /* Ensure that we're in the clipboard chain. Some apps,
+ * WinXP's remote desktop for one, don't play nice with the
+ * chain. This message is called whenever we receive a
+ * WM_ACTIVATEAPP message to ensure that we continue to
+ * receive clipboard messages.
+ *
+ * It might be possible to detect if we're still in the chain
+ * by calling SendMessage (GetClipboardViewer(),
+ * WM_DRAWCLIPBOARD, 0, 0); and then seeing if we get the
+ * WM_DRAWCLIPBOARD message. That, however, might be more
+ * expensive than just putting ourselves back into the chain.
+ */
+
+ HWND first, next;
+ DWORD error_code = 0;
+ if (!g_hwndClipboard)
+ return 0;
+ winDebug ("winClipboardWindowProc - WM_WM_REINIT: Enter\n");
+
+ first = GetClipboardViewer(); /* Get handle to first viewer in chain. */
+ if (first != hwnd)
+ {
+ winDebug (" WM_WM_REINIT: Replacing us(%x) with %x at head "
+ "of chain\n", hwnd, s_hwndNextViewer);
+ ChangeClipboardChain (hwnd, s_hwndNextViewer);
+ winDebug (" WM_WM_REINIT: Putting us back at head of chain.\n");
+ s_hwndNextViewer = SetClipboardViewer (hwnd);
+ }
+ winDebug ("winClipboardWindowProc - WM_WM_REINIT: Exit\n");
+ }
+ return 0;
+
+
+ case WM_DRAWCLIPBOARD:
+ {
+ static Atom atomClipboard;
+ static int generation;
+ static Bool s_fProcessingDrawClipboard = FALSE;
+ Display *pDisplay = g_pClipboardDisplay;
+ Window iWindow = g_iClipboardWindow;
+ int iReturn;
+
+ winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD 0x%x 0x%x 0x%x: Enter\n",hwnd,wParam,lParam);
+
+ if (!g_fClipboardStarted)
+ {
+ winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit with no processing\n");
+ if (s_hwndNextViewer)
+ SendMessage (s_hwndNextViewer, message, wParam, lParam);
+ return 0;
+ }
+
+ if (generation != serverGeneration)
+ {
+ generation = serverGeneration;
+ atomClipboard = XInternAtom (pDisplay, "CLIPBOARD", False);
+ }
+
+ /*
+ * We've occasionally seen a loop in the clipboard chain.
+ * Try and fix it on the first hint of recursion.
+ */
+ if (! s_fProcessingDrawClipboard)
+ {
+ s_fProcessingDrawClipboard = TRUE;
+ }
+ else
+ {
+ /* Attempt to break the nesting by getting out of the chain, twice?, and then fix and bail */
+ ChangeClipboardChain (hwnd, s_hwndNextViewer);
+ winFixClipboardChain();
+ ErrorF ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
+ "Nested calls detected. Re-initing.\n");
+ winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
+ s_fProcessingDrawClipboard = FALSE;
+ return 0;
+ }
+
+ /*
+ * Do not take ownership of the X11 selections when something
+ * other than CF_TEXT or CF_UNICODETEXT has been copied
+ * into the Win32 clipboard.
+ */
+ if (!IsClipboardFormatAvailable (CF_TEXT)
+ && !IsClipboardFormatAvailable (CF_UNICODETEXT))
+ {
+
+ winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
+ "Clipboard does not contain CF_TEXT nor "
+ "CF_UNICODETEXT.\n");
+
+ /*
+ * We need to make sure that the X Server has processed
+ * previous XSetSelectionOwner messages.
+ */
+ XSync (pDisplay, FALSE);
+
+ /* Release PRIMARY selection if owned */
+ iReturn = XGetSelectionOwner (pDisplay, XA_PRIMARY);
+ if (iReturn == g_iClipboardWindow)
+ {
+ winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
+ "PRIMARY selection is owned by us.\n");
+ XSetSelectionOwner (pDisplay,
+ XA_PRIMARY,
+ None,
+ CurrentTime);
+ }
+ else if (BadWindow == iReturn || BadAtom == iReturn)
+ ErrorF ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
+ "XGetSelection failed for PRIMARY: %d\n", iReturn);
+
+ /* Release CLIPBOARD selection if owned */
+ iReturn = XGetSelectionOwner (pDisplay,
+ atomClipboard);
+ if (iReturn == g_iClipboardWindow)
+ {
+ winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
+ "CLIPBOARD selection is owned by us.\n");
+ XSetSelectionOwner (pDisplay,
+ atomClipboard,
+ None,
+ CurrentTime);
+ }
+ else if (BadWindow == iReturn || BadAtom == iReturn)
+ ErrorF ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
+ "XGetSelection failed for CLIPBOARD: %d\n", iReturn);
+
+ winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
+ s_fProcessingDrawClipboard = FALSE;
+ if (s_hwndNextViewer)
+ SendMessage (s_hwndNextViewer, message, wParam, lParam);
+ return 0;
+ }
+ /* Only reassert ownership when we did not change the clipboard ourselves */
+ if (hwnd!=(HWND)wParam)
+ {
+ /* Reassert ownership of PRIMARY */
+ iReturn = XSetSelectionOwner (pDisplay,
+ XA_PRIMARY,
+ iWindow,
+ CurrentTime);
+ if (iReturn == BadAtom || iReturn == BadWindow ||
+ XGetSelectionOwner (pDisplay, XA_PRIMARY) != iWindow)
+ {
+ ErrorF ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
+ "Could not reassert ownership of PRIMARY\n");
+ }
+ else
+ {
+ winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
+ "Reasserted ownership of PRIMARY\n");
+ }
+
+ /* Reassert ownership of the CLIPBOARD */
+ iReturn = XSetSelectionOwner (pDisplay,
+ atomClipboard,
+ iWindow,
+ CurrentTime);
+
+ if (iReturn == BadAtom || iReturn == BadWindow ||
+ XGetSelectionOwner (pDisplay, atomClipboard) != iWindow)
+ {
+ ErrorF ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
+ "Could not reassert ownership of CLIPBOARD\n");
+ }
+ else
+ {
+ winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
+ "Reasserted ownership of CLIPBOARD\n");
+ }
+
+ /* Flush the pending SetSelectionOwner event now */
+ XFlush (pDisplay);
+ }
+
+ s_fProcessingDrawClipboard = FALSE;
+ winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
+ /* Pass the message on the next window in the clipboard viewer chain */
+ if (s_hwndNextViewer)
+ SendMessage (s_hwndNextViewer, message, wParam, lParam);
+ return 0;
+
+ }
+
+ case WM_DESTROYCLIPBOARD:
+ /*
+ * NOTE: Intentionally do nothing.
+ * Changes in the Win32 clipboard are handled by WM_DRAWCLIPBOARD
+ * above. We only process this message to conform to the specs
+ * for delayed clipboard rendering in Win32. You might think
+ * that we need to release ownership of the X11 selections, but
+ * we do not, because a WM_DRAWCLIPBOARD message will closely
+ * follow this message and reassert ownership of the X11
+ * selections, handling the issue for us.
+ */
+ winDebug ("winClipboardWindowProc - WM_DESTROYCLIPBOARD - Ignored.\n");
+ return 0;
+
+ case WM_RENDERFORMAT:
+ case WM_RENDERALLFORMATS:
+ {
+ int iReturn;
+ Display *pDisplay = g_pClipboardDisplay;
+ Window iWindow = g_iClipboardWindow;
+ Bool fConvertToUnicode;
+
+ winDebug ("winClipboardWindowProc - WM_RENDER*FORMAT - Hello.\n");
+
+ /* Flag whether to convert to Unicode or not */
+ if (message == WM_RENDERALLFORMATS)
+ fConvertToUnicode = FALSE;
+ else
+ fConvertToUnicode = g_fUnicodeSupport && (CF_UNICODETEXT == wParam);
+
+ /* Request the selection contents */
+ iReturn = XConvertSelection (pDisplay,
+ g_atomLastOwnedSelection,
+ XInternAtom (pDisplay,
+ "COMPOUND_TEXT", False),
+ XInternAtom (pDisplay,
+ WIN_LOCAL_PROPERTY, False),
+ iWindow,
+ CurrentTime);
+ if (iReturn == BadAtom || iReturn == BadWindow)
+ {
+ ErrorF ("winClipboardWindowProc - WM_RENDER*FORMAT - "
+ "XConvertSelection () failed\n");
+ break;
+ }
+
+ /* Special handling for WM_RENDERALLFORMATS */
+ if (message == WM_RENDERALLFORMATS)
+ {
+ /* We must open and empty the clipboard */
+ if (!OpenClipboard (hwnd))
+ {
+ ErrorF ("winClipboardWindowProc - WM_RENDER*FORMATS - "
+ "OpenClipboard () failed: %08x\n",
+ GetLastError ());
+ break;
+ }
+
+ if (!EmptyClipboard ())
+ {
+ ErrorF ("winClipboardWindowProc - WM_RENDER*FORMATS - "
+ "EmptyClipboard () failed: %08x\n",
+ GetLastError ());
+ CloseClipboard ();
+ break;
+ }
+ }
+
+ /* Process the SelectionNotify event */
+ iReturn = winProcessXEventsTimeout (hwnd,
+ iWindow,
+ pDisplay,
+ fConvertToUnicode,
+ WIN_POLL_TIMEOUT);
+ if (WIN_XEVENTS_CONVERT == iReturn)
+ {
+ /*
+ * The selection was offered for conversion first, so we have
+ * to process a second SelectionNotify event to get the actual
+ * data in the selection.
+ */
+ winDebug("winClipboardWindowProc - Previous winProcessXEventsTimeout returned WIN_XEVENTS_CONVERT, calling it again\n");
+ iReturn = winProcessXEventsTimeout (hwnd,
+ iWindow,
+ pDisplay,
+ fConvertToUnicode,
+ WIN_POLL_TIMEOUT);
+ }
+
+ /*
+ * The last of the up-to two calls to winProcessXEventsTimeout
+ * from above had better have seen a notify event, or else we
+ * are dealing with a buggy or old X11 app. In these cases we
+ * have to paste some fake data to the Win32 clipboard to
+ * satisfy the requirement that we write something to it.
+ */
+ if (WIN_XEVENTS_NOTIFY != iReturn)
+ {
+ ErrorF("winClipboardWindowProc - winProcessXEventsTimeout should have returned WIN_XEVENTS_NOTIFY was %d\n",iReturn);
+ /* Paste no data, to satisfy required call to SetClipboardData */
+ if (g_fUnicodeSupport)
+ SetClipboardData (CF_UNICODETEXT, NULL);
+ SetClipboardData (CF_TEXT, NULL);
+
+ ErrorF("winClipboardWindowProc - timed out waiting for WIN_XEVENTS_NOTIFY\n");
+ }
+
+ /* Special handling for WM_RENDERALLFORMATS */
+ if (message == WM_RENDERALLFORMATS)
+ {
+ /* We must close the clipboard */
+
+ if (!CloseClipboard ())
+ {
+ ErrorF ("winClipboardWindowProc - WM_RENDERALLFORMATS - "
+ "CloseClipboard () failed: %08x\n",
+ GetLastError ());
+ break;
+ }
+ }
+
+ winDebug ("winClipboardWindowProc - WM_RENDER*FORMAT - Returning.\n");
+ return 0;
+ }
+ }
+
+ /* Let Windows perform default processing for unhandled messages */
+ return DefWindowProc (hwnd, message, wParam, lParam);
+}
+
+
+/*
+ * Process any pending Windows messages
+ */
+
+BOOL
+winClipboardFlushWindowsMessageQueue (HWND hwnd)
+{
+ MSG msg;
+
+ /* Flush the messaging window queue */
+ /* NOTE: Do not pass the hwnd of our messaging window to PeekMessage,
+ * as this will filter out many non-window-specific messages that
+ * are sent to our thread, such as WM_QUIT.
+ */
+ while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ /* Dispatch the message if not WM_QUIT */
+ if (msg.message == WM_QUIT)
+ return FALSE;
+ else
+ DispatchMessage (&msg);
+ }
+
+ return TRUE;
+}