summaryrefslogtreecommitdiff
path: root/hw/xwin/winclipboard/thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/xwin/winclipboard/thread.c')
-rw-r--r--hw/xwin/winclipboard/thread.c497
1 files changed, 497 insertions, 0 deletions
diff --git a/hw/xwin/winclipboard/thread.c b/hw/xwin/winclipboard/thread.c
new file mode 100644
index 000000000..1653747ea
--- /dev/null
+++ b/hw/xwin/winclipboard/thread.c
@@ -0,0 +1,497 @@
+/*
+ *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>
+#else
+#define HAS_WINSOCK 1
+#endif
+
+/*
+ * Including any server header might define the macro _XSERVER64 on 64 bit machines.
+ * That macro must _NOT_ be defined for Xlib client code, otherwise bad things happen.
+ * So let's undef that macro if necessary.
+ */
+#ifdef _XSERVER64
+#undef _XSERVER64
+#endif
+
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <pthread.h>
+#include <sys/param.h> // for MAX() macro
+
+#ifdef HAS_WINSOCK
+#include <X11/Xwinsock.h>
+#else
+#include <errno.h>
+#endif
+
+#include <X11/Xatom.h>
+#include <X11/extensions/Xfixes.h>
+#include "winclipboard.h"
+#include "internal.h"
+
+#define WIN_CONNECT_RETRIES 40
+#define WIN_CONNECT_DELAY 4
+
+#define WIN_CLIPBOARD_WINDOW_CLASS "xwinclip"
+#define WIN_CLIPBOARD_WINDOW_TITLE "xwinclip"
+#ifdef HAS_DEVWINDOWS
+#define WIN_MSG_QUEUE_FNAME "/dev/windows"
+#endif
+
+/*
+ * Global variables
+ */
+
+static HWND g_hwndClipboard = NULL;
+static jmp_buf g_jmpEntry;
+static XIOErrorHandler g_winClipboardOldIOErrorHandler;
+static pthread_t g_winClipboardProcThread;
+
+int xfixes_event_base;
+int xfixes_error_base;
+
+/*
+ * Local function prototypes
+ */
+
+static HWND
+winClipboardCreateMessagingWindow(Display *pDisplay, Window iWindow, ClipboardAtoms *atoms);
+
+static int
+ winClipboardErrorHandler(Display * pDisplay, XErrorEvent * pErr);
+
+static int
+ winClipboardIOErrorHandler(Display * pDisplay);
+
+/*
+ * Create X11 and Win32 messaging windows, and run message processing loop
+ *
+ * returns TRUE if shutdown was signalled to loop, FALSE if some error occurred
+ */
+
+Bool
+winClipboardProc(Bool fUseUnicode, char *szDisplay)
+{
+ ClipboardAtoms atoms;
+ int iReturn;
+ HWND hwnd = NULL;
+ int iConnectionNumber = 0;
+
+#ifdef HAS_DEVWINDOWS
+ int fdMessageQueue = 0;
+#else
+ struct timeval tvTimeout;
+#endif
+ fd_set fdsRead;
+ int iMaxDescriptor;
+ Display *pDisplay = NULL;
+ Window iWindow = None;
+ int iSelectError;
+ Bool fShutdown = FALSE;
+
+ winDebug("winClipboardProc - Hello\n");
+
+ /* Set error handler */
+ static Bool fErrorHandlerSet = FALSE;
+
+ g_winClipboardProcThread = pthread_self();
+
+ if (! fErrorHandlerSet) {
+ XSetErrorHandler(winClipboardErrorHandler);
+ g_winClipboardOldIOErrorHandler =
+ XSetIOErrorHandler(winClipboardIOErrorHandler);
+ fErrorHandlerSet = TRUE;
+ }
+
+ /* Set jump point for Error exits */
+ if (setjmp(g_jmpEntry)) {
+ ErrorF("winClipboardProc - setjmp returned for IO Error Handler.\n");
+ goto winClipboardProc_Done;
+ }
+
+ /* Make sure that the display opened */
+ pDisplay = XOpenDisplay(szDisplay);
+ if (pDisplay == NULL) {
+ ErrorF("winClipboardProc - Failed opening the display, giving up\n");
+ goto winClipboardProc_Done;
+ }
+
+ ErrorF("winClipboardProc - XOpenDisplay () returned and "
+ "successfully opened the display.\n");
+
+ /* Get our connection number */
+ iConnectionNumber = XConnectionNumber(pDisplay);
+
+#ifdef HAS_DEVWINDOWS
+ /* Open a file descriptor for the windows message queue */
+ fdMessageQueue = open(WIN_MSG_QUEUE_FNAME, O_RDONLY);
+ if (fdMessageQueue == -1) {
+ ErrorF("winClipboardProc - Failed opening %s\n", WIN_MSG_QUEUE_FNAME);
+ goto winClipboardProc_Done;
+ }
+
+ /* Find max of our file descriptors */
+ iMaxDescriptor = MAX(fdMessageQueue, iConnectionNumber) + 1;
+#else
+ iMaxDescriptor = iConnectionNumber + 1;
+#endif
+
+ if (!XFixesQueryExtension(pDisplay, &xfixes_event_base, &xfixes_error_base))
+ ErrorF ("winClipboardProc - XFixes extension not present\n");
+
+ /* Create atoms */
+ atoms.atomClipboard = XInternAtom(pDisplay, "CLIPBOARD", False);
+ atoms.atomLocalProperty = XInternAtom (pDisplay, "CYGX_CUT_BUFFER", False);
+ atoms.atomUTF8String = XInternAtom (pDisplay, "UTF8_STRING", False);
+ atoms.atomCompoundText = XInternAtom (pDisplay, "COMPOUND_TEXT", False);
+ atoms.atomTargets = XInternAtom (pDisplay, "TARGETS", False);
+
+ /* Create a messaging window */
+ iWindow = XCreateSimpleWindow(pDisplay,
+ XDefaultRootWindow(pDisplay),
+ 1, 1,
+ 500, 500,
+ 0,
+ XBlackPixel(pDisplay, 0),
+ XBlackPixel(pDisplay, 0));
+ if (iWindow == 0) {
+ ErrorF("winClipboardProc - Could not create an X window.\n");
+ goto winClipboardProc_Done;
+ }
+
+ XStoreName(pDisplay, iWindow, "xwinclip");
+
+ /* Select event types to watch */
+ if (XSelectInput(pDisplay, iWindow, PropertyChangeMask) == BadWindow)
+ ErrorF("winClipboardProc - XSelectInput generated BadWindow "
+ "on messaging window\n");
+
+ XFixesSelectSelectionInput (pDisplay,
+ iWindow,
+ XA_PRIMARY,
+ XFixesSetSelectionOwnerNotifyMask |
+ XFixesSelectionWindowDestroyNotifyMask |
+ XFixesSelectionClientCloseNotifyMask);
+
+ XFixesSelectSelectionInput (pDisplay,
+ iWindow,
+ atoms.atomClipboard,
+ XFixesSetSelectionOwnerNotifyMask |
+ XFixesSelectionWindowDestroyNotifyMask |
+ XFixesSelectionClientCloseNotifyMask);
+
+
+ /* Initialize monitored selection state */
+ winClipboardInitMonitoredSelections();
+ /* Create Windows messaging window */
+ hwnd = winClipboardCreateMessagingWindow(pDisplay, iWindow, &atoms);
+
+ /* Save copy of HWND */
+ g_hwndClipboard = hwnd;
+
+ /* Assert ownership of selections if Win32 clipboard is owned */
+ if (NULL != GetClipboardOwner()) {
+ /* PRIMARY */
+ iReturn = XSetSelectionOwner(pDisplay, XA_PRIMARY,
+ iWindow, CurrentTime);
+ if (iReturn == BadAtom || iReturn == BadWindow ||
+ XGetSelectionOwner(pDisplay, XA_PRIMARY) != iWindow) {
+ ErrorF("winClipboardProc - Could not set PRIMARY owner\n");
+ goto winClipboardProc_Done;
+ }
+
+ /* CLIPBOARD */
+ iReturn = XSetSelectionOwner(pDisplay, atoms.atomClipboard,
+ iWindow, CurrentTime);
+ if (iReturn == BadAtom || iReturn == BadWindow ||
+ XGetSelectionOwner(pDisplay, atoms.atomClipboard) != iWindow) {
+ ErrorF("winClipboardProc - Could not set CLIPBOARD owner\n");
+ goto winClipboardProc_Done;
+ }
+ }
+
+ ClipboardConversionData data;
+ data.fUseUnicode = fUseUnicode;
+
+ /* Loop for events */
+ while (1) {
+
+ /* Process X events */
+ winClipboardFlushXEvents(hwnd,
+ iWindow, pDisplay, &data, &atoms);
+
+ /* Process Windows messages */
+ if (!winClipboardFlushWindowsMessageQueue(hwnd)) {
+ ErrorF("winClipboardProc - winClipboardFlushWindowsMessageQueue trapped "
+ "WM_QUIT message, exiting main loop.\n");
+ break;
+ }
+
+ /* We need to ensure that all pending requests are sent */
+ XFlush(pDisplay);
+
+ /* Setup the file descriptor set */
+ /*
+ * NOTE: You have to do this before every call to select
+ * because select modifies the mask to indicate
+ * which descriptors are ready.
+ */
+ FD_ZERO(&fdsRead);
+ FD_SET(iConnectionNumber, &fdsRead);
+#ifdef HAS_DEVWINDOWS
+ FD_SET(fdMessageQueue, &fdsRead);
+#else
+ tvTimeout.tv_sec = 0;
+ tvTimeout.tv_usec = 100;
+#endif
+
+ winDebug("winClipboardProc - Waiting in select\n");
+
+ /* Wait for a Windows event or an X event */
+ iReturn = select(iMaxDescriptor, /* Highest fds number */
+ &fdsRead, /* Read mask */
+ NULL, /* No write mask */
+ NULL, /* No exception mask */
+#ifdef HAS_DEVWINDOWS
+ NULL /* No timeout */
+#else
+ &tvTimeout /* Set timeout */
+#endif
+ );
+
+#ifndef HAS_WINSOCK
+ iSelectError = errno;
+#else
+ iSelectError = WSAGetLastError();
+#endif
+
+ if (iReturn < 0) {
+#ifndef HAS_WINSOCK
+ if (iSelectError == EINTR)
+#else
+ if (iSelectError == WSAEINTR)
+#endif
+ continue;
+
+ ErrorF("winClipboardProc - Call to select () failed: %d. "
+ "Bailing.\n", iReturn);
+ break;
+ }
+
+ winDebug("winClipboardProc - select returned %d\n", iReturn);
+
+ if (FD_ISSET(iConnectionNumber, &fdsRead)) {
+ winDebug
+ ("winClipboardProc - X connection ready, pumping X event queue\n");
+ }
+
+#ifdef HAS_DEVWINDOWS
+ /* Check for Windows event ready */
+ if (FD_ISSET(fdMessageQueue, &fdsRead))
+#else
+ if (1)
+#endif
+ {
+ winDebug
+ ("winClipboardProc - /dev/windows ready, pumping Windows message queue\n");
+ }
+
+#ifdef HAS_DEVWINDOWS
+ if (!(FD_ISSET(iConnectionNumber, &fdsRead)) &&
+ !(FD_ISSET(fdMessageQueue, &fdsRead))) {
+ winDebug("winClipboardProc - Spurious wake, select() returned %d\n", iReturn);
+ }
+#endif
+ }
+
+ /* broke out of while loop on a shutdown message */
+ fShutdown = TRUE;
+
+ winClipboardProc_Done:
+ /* Close our Windows window */
+ if (g_hwndClipboard) {
+ winClipboardWindowDestroy();
+ }
+
+ /* Close our X window */
+ if (pDisplay && iWindow) {
+ iReturn = XDestroyWindow(pDisplay, iWindow);
+ if (iReturn == BadWindow)
+ ErrorF("winClipboardProc - XDestroyWindow returned BadWindow.\n");
+ else
+ ErrorF("winClipboardProc - XDestroyWindow succeeded.\n");
+ }
+
+#ifdef HAS_DEVWINDOWS
+ /* Close our Win32 message handle */
+ if (fdMessageQueue)
+ close(fdMessageQueue);
+#endif
+
+#if 0
+ /*
+ * FIXME: XCloseDisplay hangs if we call it
+ *
+ * XCloseDisplay() calls XSync(), so any outstanding errors are reported.
+ * If we are built into the server, this can deadlock if the server is
+ * in the process of exiting and waiting for this thread to exit.
+ */
+
+ /* Discard any remaining events */
+ XSync(pDisplay, TRUE);
+
+ /* Select event types to watch */
+ XSelectInput(pDisplay, XDefaultRootWindow(pDisplay), None);
+
+ /* Close our X display */
+ if (pDisplay) {
+ XCloseDisplay(pDisplay);
+ }
+#endif
+
+ /* global clipboard variable reset */
+ g_hwndClipboard = NULL;
+
+ return fShutdown;
+}
+
+/*
+ * Create the Windows window that we use to recieve Windows messages
+ */
+
+static HWND
+winClipboardCreateMessagingWindow(Display *pDisplay, Window iWindow, ClipboardAtoms *atoms)
+{
+ WNDCLASSEX wc;
+ HWND hwnd;
+
+ /* Setup our window class */
+ wc.cbSize = sizeof(WNDCLASSEX);
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.lpfnWndProc = winClipboardWindowProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = GetModuleHandle(NULL);
+ wc.hIcon = 0;
+ wc.hCursor = 0;
+ wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = WIN_CLIPBOARD_WINDOW_CLASS;
+ wc.hIconSm = 0;
+ RegisterClassEx(&wc);
+
+ /* Information to be passed to WM_CREATE */
+ ClipboardWindowCreationParams cwcp;
+ cwcp.pClipboardDisplay = pDisplay;
+ cwcp.iClipboardWindow = iWindow;
+ cwcp.atoms = atoms;
+
+ /* Create the window */
+ hwnd = CreateWindowExA(0, /* Extended styles */
+ WIN_CLIPBOARD_WINDOW_CLASS, /* Class name */
+ WIN_CLIPBOARD_WINDOW_TITLE, /* Window name */
+ WS_OVERLAPPED, /* Not visible anyway */
+ CW_USEDEFAULT, /* Horizontal position */
+ CW_USEDEFAULT, /* Vertical position */
+ CW_USEDEFAULT, /* Right edge */
+ CW_USEDEFAULT, /* Bottom edge */
+ (HWND) NULL, /* No parent or owner window */
+ (HMENU) NULL, /* No menu */
+ GetModuleHandle(NULL), /* Instance handle */
+ &cwcp); /* Creation data */
+ assert(hwnd != NULL);
+
+ /* I'm not sure, but we may need to call this to start message processing */
+ ShowWindow(hwnd, SW_HIDE);
+
+ /* Similarly, we may need a call to this even though we don't paint */
+ UpdateWindow(hwnd);
+
+ return hwnd;
+}
+
+/*
+ * winClipboardErrorHandler - Our application specific error handler
+ */
+
+static int
+winClipboardErrorHandler(Display * pDisplay, XErrorEvent * pErr)
+{
+ char pszErrorMsg[100];
+
+ XGetErrorText(pDisplay, pErr->error_code, pszErrorMsg, sizeof(pszErrorMsg));
+ ErrorF("winClipboardErrorHandler - ERROR: \n\t%s\n"
+ "\tSerial: %lu, Request Code: %d, Minor Code: %d\n",
+ pszErrorMsg, pErr->serial, pErr->request_code, pErr->minor_code);
+ return 0;
+}
+
+/*
+ * winClipboardIOErrorHandler - Our application specific IO error handler
+ */
+
+static int
+winClipboardIOErrorHandler(Display * pDisplay)
+{
+ ErrorF("winClipboardIOErrorHandler!\n");
+
+ if (pthread_equal(pthread_self(), g_winClipboardProcThread)) {
+ /* Restart at the main entry point */
+ longjmp(g_jmpEntry, 2);
+ }
+
+ if (g_winClipboardOldIOErrorHandler)
+ g_winClipboardOldIOErrorHandler(pDisplay);
+
+ return 0;
+}
+
+void
+winClipboardWindowDestroy(void)
+{
+ if (g_hwndClipboard) {
+ SendMessage(g_hwndClipboard, WM_DESTROY, 0, 0);
+ }
+}
+
+void
+winFixClipboardChain(void)
+{
+ if (g_hwndClipboard) {
+ PostMessage(g_hwndClipboard, WM_WM_REINIT, 0, 0);
+ }
+}