/* *Copyright (C) 1994-2000 The XFree86 Project, Inc. All Rights Reserved. *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 THE AUTHORS OR COPYRIGHT HOLDERS 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_CONFIG_H #include #endif /* Standard library headers */ #include #include #include #include #ifndef HAS_WINSOCK #include #endif #include #include /* X headers */ #include #include #include /* Windows headers */ #include /* Local headers */ #include "xwinclip.h" #include "textconv.h" #include "wndproc.h" #include "xevents.h" #include "debug.h" /* Application constants */ #define WINDOW_CLASS "xwinclip" #define WINDOW_TITLE "xwinclip" #define WIN_MSG_QUEUE_FNAME "/dev/windows" /* * Local variables */ static HWND g_hwndClipboard; static jmp_buf g_jmpEntry; static XIOErrorHandler g_ClipboardOldIOErrorHandler; static pthread_t g_ClipboardProcThread; /* * Global variables */ int xfixes_event_base; int xfixes_error_base; /* * Local function prototypes */ static HWND CreateMessagingWindow (Display *display, Window window); /* * ClipboardErrorHandler - Our application specific error handler */ static int ClipboardErrorHandler(Display *pDisplay, XErrorEvent *pErr) { char pszErrorMsg[100]; XGetErrorText(pDisplay, pErr->error_code, pszErrorMsg, sizeof(pszErrorMsg)); winError("ClipboardErrorHandler - ERROR: %s Serial: %lu, Request Code: %d, Minor Code: %d\n", pszErrorMsg, pErr->serial, pErr->request_code, pErr->minor_code); return 0; } /* * ClipboardIOErrorHandler - Our application specific IO error handler */ static int ClipboardIOErrorHandler(Display *pDisplay) { winError("ClipboardIOErrorHandler!\n"); if (pthread_equal(pthread_self(), g_ClipboardProcThread)) { /* Restart at the main entry point */ longjmp(g_jmpEntry, 2); } if (g_ClipboardOldIOErrorHandler) g_ClipboardOldIOErrorHandler(pDisplay); return 0; } /* * Create X11 and Win32 messaging windows, and run message processing loop * * returns TRUE if shutdown was signalled to loop, FALSE is some error occurred */ int ClipboardProc(Bool fUnicodeSupport, char *pszDisplay) { Atom atomLocalProperty, atomCompoundText; Atom atomUTF8String, atomTargets; Atom atomClipboard; 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; /* Set error handler */ static Bool fErrorHandlerSet = FALSE; g_ClipboardProcThread = pthread_self(); if (!fErrorHandlerSet) { XSetErrorHandler(ClipboardErrorHandler); g_ClipboardOldIOErrorHandler = XSetIOErrorHandler(ClipboardIOErrorHandler); fErrorHandlerSet = TRUE; } /* Set jump point for error exits */ if (setjmp(g_jmpEntry)) { winError("ClipboardProc - setjmp returned for IO Error Handler.\n"); /* Cleanup and exit */ goto ClipboardProc_Done; } /* Open the X display */ pDisplay = XOpenDisplay (pszDisplay); if (pDisplay == NULL) { winError ("Could not open display\n"); goto ClipboardProc_Done; } /* Get our connection number */ iConnectionNumber = ConnectionNumber (pDisplay); #ifdef HAS_DEVWINDOWS /* Open a file descriptor for the windows message queue */ fdMessageQueue = open(WIN_MSG_QUEUE_FNAME, O_RDONLY); if (fdMessageQueue == -1) { winError ("Failed opening %s\n", WIN_MSG_QUEUE_FNAME); goto ClipboardProc_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)) winError ("XFixes extension not present\n"); /* Create a messaging window */ iWindow = XCreateSimpleWindow (pDisplay, DefaultRootWindow (pDisplay), 1, 1, 500, 500, 0, BlackPixel (pDisplay, 0), BlackPixel (pDisplay, 0)); if (iWindow == 0) { winError ("Could not create an X window\n"); goto ClipboardProc_Done; } /* Print out our window number */ winDebug("Window number: 0x%x\n", iWindow); XStoreName(pDisplay, iWindow, "xwinclip"); /* Select event types to watch */ if (XSelectInput(pDisplay, iWindow, PropertyChangeMask) == BadWindow) winError("XSelectInput generated BadWindow on messaging window\n"); XFixesSelectSelectionInput (pDisplay, iWindow, XA_PRIMARY, XFixesSetSelectionOwnerNotifyMask | XFixesSelectionWindowDestroyNotifyMask | XFixesSelectionClientCloseNotifyMask); XFixesSelectSelectionInput (pDisplay, iWindow, XInternAtom (pDisplay, "CLIPBOARD", False), XFixesSetSelectionOwnerNotifyMask | XFixesSelectionWindowDestroyNotifyMask | XFixesSelectionClientCloseNotifyMask); /* Create Windows messaging window */ hwnd = CreateMessagingWindow (pDisplay, iWindow); if (!hwnd) { winError ("Could not create a Windows window\n"); goto ClipboardProc_Done; } /* Save a copy of the HWND */ g_hwndClipboard = hwnd; /* Create an atom for CLIPBOARD */ atomClipboard = XInternAtom (pDisplay, "CLIPBOARD", False); if (atomClipboard == None) { winError ("Could not create CLIPBOARD atom\n"); goto ClipboardProc_Done; } /* 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) { winError("Could not set PRIMARY owner\n"); } /* CLIPBOARD */ iReturn = XSetSelectionOwner(pDisplay, atomClipboard, iWindow, CurrentTime); if (iReturn == BadAtom || iReturn == BadWindow || XGetSelectionOwner(pDisplay, atomClipboard) != iWindow) { winError("Could not set CLIPBOARD owner\n"); } } /* Create an atom for Local property to hold pasted data */ atomLocalProperty = XInternAtom (pDisplay, "CYGX_CUT_BUFFER", False); if (atomLocalProperty == None) { winError ("Could not create CYGX_CUT_BUFFER atom\n"); goto ClipboardProc_Done; } /* Create an atom for UTF8_STRING */ atomUTF8String = XInternAtom (pDisplay, "UTF8_STRING", False); if (atomUTF8String == None) { winError ("Could not create UTF8_STRING atom\n"); goto ClipboardProc_Done; } /* Create an atom for COMPOUND_TEXT */ atomCompoundText = XInternAtom (pDisplay, "COMPOUND_TEXT", False); if (atomCompoundText == None) { winError ("Could not create COMPOUND_TEXT atom\n"); goto ClipboardProc_Done; } /* Create an atom for TARGETS */ atomTargets = XInternAtom (pDisplay, "TARGETS", False); if (atomTargets == None) { winError ("Could not create TARGETS atom\n"); goto ClipboardProc_Done; } /* Pre-flush X events */ /* * NOTE: Apparently you'll freeze if you don't do this, * because there may be events in local data structures * already. */ FlushXEvents (hwnd, atomLocalProperty, atomUTF8String, atomCompoundText, atomTargets, iWindow, pDisplay, fUnicodeSupport); /* Pre-flush Windows messages */ if (!FlushWindowsMessageQueue()) { winError("ClipboardFlushWindowsMessageQueue failed\n"); goto ClipboardProc_Done; } /* Loop for X events */ while (1) { /* 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 /* 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; winError ("Call to select () failed: %d. Bailing.\n", iReturn); break; } winDebug("select returned %d\n", iReturn); /* Branch on which descriptor became active */ if (FD_ISSET (iConnectionNumber, &fdsRead)) { /* X event ready */ winDebug("X connection ready, pumping X event queue\n"); /* Process X events */ /* Exit when we see that server is shutting down */ iReturn = FlushXEvents (hwnd, atomLocalProperty, atomUTF8String, atomCompoundText, atomTargets, iWindow, pDisplay, fUnicodeSupport); } #ifdef HAS_DEVWINDOWS /* Check for Windows event ready */ if (FD_ISSET (fdMessageQueue, &fdsRead)) #else if (1) #endif { /* Windows event ready */ winDebug ("/dev/windows ready, pumping Windows message queue\n"); /* Process Windows messages */ if (!FlushWindowsMessageQueue()) { winError("FlushWindowsMessageQueue trapped WM_QUIT message, exiting main loop.\n"); break; } } #ifdef HAS_DEVWINDOWS if (!(FD_ISSET(iConnectionNumber, &fdsRead)) && !(FD_ISSET(fdMessageQueue, &fdsRead))) { winDebug("Spurious wake\n"); } #endif } /* broke out of while loop on a shutdown message */ fShutdown = TRUE; ClipboardProc_Done: /* Close our Windows window */ if (hwnd) { /* Destroy the Window window (hwnd) */ winDebug("Destroy Windows window\n"); PostMessage(hwnd, WM_DESTROY, 0, 0); FlushWindowsMessageQueue(); } /* Close our X window */ if (pDisplay && iWindow) { iReturn = XDestroyWindow(pDisplay, iWindow); if (iReturn == BadWindow) winError("XDestroyWindow returned BadWindow.\n"); else winDebug("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. */ /* Close our X display */ if (pDisplay) { XCloseDisplay(pDisplay); } #endif return fShutdown; } /* * Create the Windows window that we use to recieve Windows messages */ static HWND CreateMessagingWindow (Display *display, Window window) { WNDCLASSEX wc; HWND hwnd; /* Setup our window class */ wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WindowProc; 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 = WINDOW_CLASS; wc.hIconSm = 0; RegisterClassEx(&wc); WindowCreationParams wcp; wcp.pClipboardDisplay = display; wcp.iClipboardWindow = window; /* Create the window */ hwnd = CreateWindowExA(0, /* Extended styles */ WINDOW_CLASS, /* Class name */ 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 */ &wcp); /* 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; } void ClipboardWindowDestroy(void) { if (g_hwndClipboard) { SendMessage(g_hwndClipboard, WM_DESTROY, 0, 0); } } void ClipboardFixClipboardChain(void) { if (g_hwndClipboard) { PostMessage(g_hwndClipboard, WM_WM_REINIT, 0, 0); } }