winclipboardthread.c revision 706f2543
1706f2543Smrg/* 2706f2543Smrg *Copyright (C) 2003-2004 Harold L Hunt II All Rights Reserved. 3706f2543Smrg *Copyright (C) Colin Harrison 2005-2008 4706f2543Smrg * 5706f2543Smrg *Permission is hereby granted, free of charge, to any person obtaining 6706f2543Smrg * a copy of this software and associated documentation files (the 7706f2543Smrg *"Software"), to deal in the Software without restriction, including 8706f2543Smrg *without limitation the rights to use, copy, modify, merge, publish, 9706f2543Smrg *distribute, sublicense, and/or sell copies of the Software, and to 10706f2543Smrg *permit persons to whom the Software is furnished to do so, subject to 11706f2543Smrg *the following conditions: 12706f2543Smrg * 13706f2543Smrg *The above copyright notice and this permission notice shall be 14706f2543Smrg *included in all copies or substantial portions of the Software. 15706f2543Smrg * 16706f2543Smrg *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17706f2543Smrg *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18706f2543Smrg *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19706f2543Smrg *NONINFRINGEMENT. IN NO EVENT SHALL HAROLD L HUNT II BE LIABLE FOR 20706f2543Smrg *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 21706f2543Smrg *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22706f2543Smrg *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23706f2543Smrg * 24706f2543Smrg *Except as contained in this notice, the name of the copyright holder(s) 25706f2543Smrg *and author(s) shall not be used in advertising or otherwise to promote 26706f2543Smrg *the sale, use or other dealings in this Software without prior written 27706f2543Smrg *authorization from the copyright holder(s) and author(s). 28706f2543Smrg * 29706f2543Smrg * Authors: Harold L Hunt II 30706f2543Smrg * Colin Harrison 31706f2543Smrg */ 32706f2543Smrg 33706f2543Smrg#ifdef HAVE_XWIN_CONFIG_H 34706f2543Smrg#include <xwin-config.h> 35706f2543Smrg#endif 36706f2543Smrg#include <sys/types.h> 37706f2543Smrg#include "winclipboard.h" 38706f2543Smrg#ifdef __CYGWIN__ 39706f2543Smrg#include <errno.h> 40706f2543Smrg#endif 41706f2543Smrg#include "misc.h" 42706f2543Smrg 43706f2543Smrg 44706f2543Smrg/* 45706f2543Smrg * References to external symbols 46706f2543Smrg */ 47706f2543Smrg 48706f2543Smrgextern Bool g_fUnicodeClipboard; 49706f2543Smrgextern unsigned long serverGeneration; 50706f2543Smrgextern Bool g_fClipboardStarted; 51706f2543Smrgextern HWND g_hwndClipboard; 52706f2543Smrgextern void *g_pClipboardDisplay; 53706f2543Smrgextern Window g_iClipboardWindow; 54706f2543Smrg 55706f2543Smrg 56706f2543Smrg/* 57706f2543Smrg * Global variables 58706f2543Smrg */ 59706f2543Smrg 60706f2543Smrgstatic jmp_buf g_jmpEntry; 61706f2543SmrgBool g_fUnicodeSupport = FALSE; 62706f2543SmrgBool g_fUseUnicode = FALSE; 63706f2543Smrg 64706f2543Smrg 65706f2543Smrg/* 66706f2543Smrg * Local function prototypes 67706f2543Smrg */ 68706f2543Smrg 69706f2543Smrgstatic int 70706f2543SmrgwinClipboardErrorHandler (Display *pDisplay, XErrorEvent *pErr); 71706f2543Smrg 72706f2543Smrgstatic int 73706f2543SmrgwinClipboardIOErrorHandler (Display *pDisplay); 74706f2543Smrg 75706f2543Smrg 76706f2543Smrg/* 77706f2543Smrg * Main thread function 78706f2543Smrg */ 79706f2543Smrg 80706f2543Smrgvoid * 81706f2543SmrgwinClipboardProc (void *pvNotUsed) 82706f2543Smrg{ 83706f2543Smrg Atom atomClipboard, atomClipboardManager; 84706f2543Smrg int iReturn; 85706f2543Smrg HWND hwnd = NULL; 86706f2543Smrg int iConnectionNumber = 0; 87706f2543Smrg#ifdef HAS_DEVWINDOWS 88706f2543Smrg int fdMessageQueue = 0; 89706f2543Smrg#else 90706f2543Smrg struct timeval tvTimeout; 91706f2543Smrg#endif 92706f2543Smrg fd_set fdsRead; 93706f2543Smrg int iMaxDescriptor; 94706f2543Smrg Display *pDisplay = NULL; 95706f2543Smrg Window iWindow = None; 96706f2543Smrg int iRetries; 97706f2543Smrg Bool fUseUnicode; 98706f2543Smrg char szDisplay[512]; 99706f2543Smrg int iSelectError; 100706f2543Smrg 101706f2543Smrg ErrorF ("winClipboardProc - Hello\n"); 102706f2543Smrg 103706f2543Smrg /* Do we have Unicode support? */ 104706f2543Smrg g_fUnicodeSupport = winClipboardDetectUnicodeSupport (); 105706f2543Smrg 106706f2543Smrg /* Do we use Unicode clipboard? */ 107706f2543Smrg fUseUnicode = g_fUnicodeClipboard && g_fUnicodeSupport; 108706f2543Smrg 109706f2543Smrg /* Save the Unicode support flag in a global */ 110706f2543Smrg g_fUseUnicode = fUseUnicode; 111706f2543Smrg 112706f2543Smrg /* Allow multiple threads to access Xlib */ 113706f2543Smrg if (XInitThreads () == 0) 114706f2543Smrg { 115706f2543Smrg ErrorF ("winClipboardProc - XInitThreads failed.\n"); 116706f2543Smrg pthread_exit (NULL); 117706f2543Smrg } 118706f2543Smrg 119706f2543Smrg /* See if X supports the current locale */ 120706f2543Smrg if (XSupportsLocale () == False) 121706f2543Smrg { 122706f2543Smrg ErrorF ("winClipboardProc - Warning: Locale not supported by X.\n"); 123706f2543Smrg } 124706f2543Smrg 125706f2543Smrg /* Set jump point for Error exits */ 126706f2543Smrg iReturn = setjmp (g_jmpEntry); 127706f2543Smrg 128706f2543Smrg /* Check if we should continue operations */ 129706f2543Smrg if (iReturn != WIN_JMP_ERROR_IO 130706f2543Smrg && iReturn != WIN_JMP_OKAY) 131706f2543Smrg { 132706f2543Smrg /* setjmp returned an unknown value, exit */ 133706f2543Smrg ErrorF ("winClipboardProc - setjmp returned: %d exiting\n", 134706f2543Smrg iReturn); 135706f2543Smrg pthread_exit (NULL); 136706f2543Smrg } 137706f2543Smrg else if (iReturn == WIN_JMP_ERROR_IO) 138706f2543Smrg { 139706f2543Smrg /* TODO: Cleanup the Win32 window and free any allocated memory */ 140706f2543Smrg ErrorF ("winClipboardProc - setjmp returned for IO Error Handler.\n"); 141706f2543Smrg pthread_exit (NULL); 142706f2543Smrg } 143706f2543Smrg 144706f2543Smrg /* Use our generated cookie for authentication */ 145706f2543Smrg winSetAuthorization(); 146706f2543Smrg 147706f2543Smrg /* Set error handler */ 148706f2543Smrg XSetErrorHandler (winClipboardErrorHandler); 149706f2543Smrg XSetIOErrorHandler (winClipboardIOErrorHandler); 150706f2543Smrg 151706f2543Smrg /* Initialize retry count */ 152706f2543Smrg iRetries = 0; 153706f2543Smrg 154706f2543Smrg /* Setup the display connection string x */ 155706f2543Smrg /* 156706f2543Smrg * NOTE: Always connect to screen 0 since we require that screen 157706f2543Smrg * numbers start at 0 and increase without gaps. We only need 158706f2543Smrg * to connect to one screen on the display to get events 159706f2543Smrg * for all screens on the display. That is why there is only 160706f2543Smrg * one clipboard client thread. 161706f2543Smrg */ 162706f2543Smrg snprintf (szDisplay, 163706f2543Smrg 512, 164706f2543Smrg "127.0.0.1:%s.0", 165706f2543Smrg display); 166706f2543Smrg 167706f2543Smrg /* Print the display connection string */ 168706f2543Smrg ErrorF ("winClipboardProc - DISPLAY=%s\n", szDisplay); 169706f2543Smrg 170706f2543Smrg /* Open the X display */ 171706f2543Smrg do 172706f2543Smrg { 173706f2543Smrg pDisplay = XOpenDisplay (szDisplay); 174706f2543Smrg if (pDisplay == NULL) 175706f2543Smrg { 176706f2543Smrg ErrorF ("winClipboardProc - Could not open display, " 177706f2543Smrg "try: %d, sleeping: %d\n", 178706f2543Smrg iRetries + 1, WIN_CONNECT_DELAY); 179706f2543Smrg ++iRetries; 180706f2543Smrg sleep (WIN_CONNECT_DELAY); 181706f2543Smrg continue; 182706f2543Smrg } 183706f2543Smrg else 184706f2543Smrg break; 185706f2543Smrg } 186706f2543Smrg while (pDisplay == NULL && iRetries < WIN_CONNECT_RETRIES); 187706f2543Smrg 188706f2543Smrg /* Make sure that the display opened */ 189706f2543Smrg if (pDisplay == NULL) 190706f2543Smrg { 191706f2543Smrg ErrorF ("winClipboardProc - Failed opening the display, giving up\n"); 192706f2543Smrg pthread_exit (NULL); 193706f2543Smrg } 194706f2543Smrg 195706f2543Smrg /* Save the display in the screen privates */ 196706f2543Smrg g_pClipboardDisplay = pDisplay; 197706f2543Smrg 198706f2543Smrg ErrorF ("winClipboardProc - XOpenDisplay () returned and " 199706f2543Smrg "successfully opened the display.\n"); 200706f2543Smrg 201706f2543Smrg /* Get our connection number */ 202706f2543Smrg iConnectionNumber = ConnectionNumber (pDisplay); 203706f2543Smrg 204706f2543Smrg#ifdef HAS_DEVWINDOWS 205706f2543Smrg /* Open a file descriptor for the windows message queue */ 206706f2543Smrg fdMessageQueue = open (WIN_MSG_QUEUE_FNAME, O_RDONLY); 207706f2543Smrg if (fdMessageQueue == -1) 208706f2543Smrg { 209706f2543Smrg ErrorF ("winClipboardProc - Failed opening %s\n", WIN_MSG_QUEUE_FNAME); 210706f2543Smrg pthread_exit (NULL); 211706f2543Smrg } 212706f2543Smrg 213706f2543Smrg /* Find max of our file descriptors */ 214706f2543Smrg iMaxDescriptor = max (fdMessageQueue, iConnectionNumber) + 1; 215706f2543Smrg#else 216706f2543Smrg iMaxDescriptor = iConnectionNumber + 1; 217706f2543Smrg#endif 218706f2543Smrg 219706f2543Smrg /* Create atoms */ 220706f2543Smrg atomClipboard = XInternAtom (pDisplay, "CLIPBOARD", False); 221706f2543Smrg atomClipboardManager = XInternAtom (pDisplay, "CLIPBOARD_MANAGER", False); 222706f2543Smrg 223706f2543Smrg /* Create a messaging window */ 224706f2543Smrg iWindow = XCreateSimpleWindow (pDisplay, 225706f2543Smrg DefaultRootWindow (pDisplay), 226706f2543Smrg 1, 1, 227706f2543Smrg 500, 500, 228706f2543Smrg 0, 229706f2543Smrg BlackPixel (pDisplay, 0), 230706f2543Smrg BlackPixel (pDisplay, 0)); 231706f2543Smrg if (iWindow == 0) 232706f2543Smrg { 233706f2543Smrg ErrorF ("winClipboardProc - Could not create an X window.\n"); 234706f2543Smrg pthread_exit (NULL); 235706f2543Smrg } 236706f2543Smrg 237706f2543Smrg /* Select event types to watch */ 238706f2543Smrg if (XSelectInput (pDisplay, 239706f2543Smrg iWindow, 240706f2543Smrg PropertyChangeMask) == BadWindow) 241706f2543Smrg ErrorF ("winClipboardProc - XSelectInput generated BadWindow " 242706f2543Smrg "on messaging window\n"); 243706f2543Smrg 244706f2543Smrg /* Save the window in the screen privates */ 245706f2543Smrg g_iClipboardWindow = iWindow; 246706f2543Smrg 247706f2543Smrg /* Create Windows messaging window */ 248706f2543Smrg hwnd = winClipboardCreateMessagingWindow (); 249706f2543Smrg 250706f2543Smrg /* Save copy of HWND in screen privates */ 251706f2543Smrg g_hwndClipboard = hwnd; 252706f2543Smrg 253706f2543Smrg /* Assert ownership of selections if Win32 clipboard is owned */ 254706f2543Smrg if (NULL != GetClipboardOwner ()) 255706f2543Smrg { 256706f2543Smrg /* PRIMARY */ 257706f2543Smrg iReturn = XSetSelectionOwner (pDisplay, XA_PRIMARY, 258706f2543Smrg iWindow, CurrentTime); 259706f2543Smrg if (iReturn == BadAtom || iReturn == BadWindow || 260706f2543Smrg XGetSelectionOwner (pDisplay, XA_PRIMARY) != iWindow) 261706f2543Smrg { 262706f2543Smrg ErrorF ("winClipboardProc - Could not set PRIMARY owner\n"); 263706f2543Smrg pthread_exit (NULL); 264706f2543Smrg } 265706f2543Smrg 266706f2543Smrg /* CLIPBOARD */ 267706f2543Smrg iReturn = XSetSelectionOwner (pDisplay, atomClipboard, 268706f2543Smrg iWindow, CurrentTime); 269706f2543Smrg if (iReturn == BadAtom || iReturn == BadWindow || 270706f2543Smrg XGetSelectionOwner (pDisplay, atomClipboard) != iWindow) 271706f2543Smrg { 272706f2543Smrg ErrorF ("winClipboardProc - Could not set CLIPBOARD owner\n"); 273706f2543Smrg pthread_exit (NULL); 274706f2543Smrg } 275706f2543Smrg } 276706f2543Smrg 277706f2543Smrg /* Pre-flush X events */ 278706f2543Smrg /* 279706f2543Smrg * NOTE: Apparently you'll freeze if you don't do this, 280706f2543Smrg * because there may be events in local data structures 281706f2543Smrg * already. 282706f2543Smrg */ 283706f2543Smrg winClipboardFlushXEvents (hwnd, 284706f2543Smrg iWindow, 285706f2543Smrg pDisplay, 286706f2543Smrg fUseUnicode); 287706f2543Smrg 288706f2543Smrg /* Pre-flush Windows messages */ 289706f2543Smrg if (!winClipboardFlushWindowsMessageQueue (hwnd)) 290706f2543Smrg return 0; 291706f2543Smrg 292706f2543Smrg /* Signal that the clipboard client has started */ 293706f2543Smrg g_fClipboardStarted = TRUE; 294706f2543Smrg 295706f2543Smrg /* Loop for X events */ 296706f2543Smrg while (1) 297706f2543Smrg { 298706f2543Smrg /* Setup the file descriptor set */ 299706f2543Smrg /* 300706f2543Smrg * NOTE: You have to do this before every call to select 301706f2543Smrg * because select modifies the mask to indicate 302706f2543Smrg * which descriptors are ready. 303706f2543Smrg */ 304706f2543Smrg FD_ZERO (&fdsRead); 305706f2543Smrg FD_SET (iConnectionNumber, &fdsRead); 306706f2543Smrg#ifdef HAS_DEVWINDOWS 307706f2543Smrg FD_SET (fdMessageQueue, &fdsRead); 308706f2543Smrg#else 309706f2543Smrg tvTimeout.tv_sec = 0; 310706f2543Smrg tvTimeout.tv_usec = 100; 311706f2543Smrg#endif 312706f2543Smrg 313706f2543Smrg /* Wait for a Windows event or an X event */ 314706f2543Smrg iReturn = select (iMaxDescriptor, /* Highest fds number */ 315706f2543Smrg &fdsRead, /* Read mask */ 316706f2543Smrg NULL, /* No write mask */ 317706f2543Smrg NULL, /* No exception mask */ 318706f2543Smrg#ifdef HAS_DEVWINDOWS 319706f2543Smrg NULL /* No timeout */ 320706f2543Smrg#else 321706f2543Smrg &tvTimeout /* Set timeout */ 322706f2543Smrg#endif 323706f2543Smrg ); 324706f2543Smrg 325706f2543Smrg#ifndef HAS_WINSOCK 326706f2543Smrg iSelectError = errno; 327706f2543Smrg#else 328706f2543Smrg iSelectError = WSAGetLastError(); 329706f2543Smrg#endif 330706f2543Smrg 331706f2543Smrg if (iReturn < 0) 332706f2543Smrg { 333706f2543Smrg#ifndef HAS_WINSOCK 334706f2543Smrg if (iSelectError == EINTR) 335706f2543Smrg#else 336706f2543Smrg if (iSelectError == WSAEINTR) 337706f2543Smrg#endif 338706f2543Smrg continue; 339706f2543Smrg 340706f2543Smrg ErrorF ("winClipboardProc - Call to select () failed: %d. " 341706f2543Smrg "Bailing.\n", iReturn); 342706f2543Smrg break; 343706f2543Smrg } 344706f2543Smrg 345706f2543Smrg /* Branch on which descriptor became active */ 346706f2543Smrg if (FD_ISSET (iConnectionNumber, &fdsRead)) 347706f2543Smrg { 348706f2543Smrg /* Process X events */ 349706f2543Smrg /* Exit when we see that server is shutting down */ 350706f2543Smrg iReturn = winClipboardFlushXEvents (hwnd, 351706f2543Smrg iWindow, 352706f2543Smrg pDisplay, 353706f2543Smrg fUseUnicode); 354706f2543Smrg if (WIN_XEVENTS_SHUTDOWN == iReturn) 355706f2543Smrg { 356706f2543Smrg ErrorF ("winClipboardProc - winClipboardFlushXEvents " 357706f2543Smrg "trapped shutdown event, exiting main loop.\n"); 358706f2543Smrg break; 359706f2543Smrg } 360706f2543Smrg } 361706f2543Smrg 362706f2543Smrg#ifdef HAS_DEVWINDOWS 363706f2543Smrg /* Check for Windows event ready */ 364706f2543Smrg if (FD_ISSET (fdMessageQueue, &fdsRead)) 365706f2543Smrg#else 366706f2543Smrg if (1) 367706f2543Smrg#endif 368706f2543Smrg { 369706f2543Smrg /* Process Windows messages */ 370706f2543Smrg if (!winClipboardFlushWindowsMessageQueue (hwnd)) 371706f2543Smrg { 372706f2543Smrg ErrorF ("winClipboardProc - " 373706f2543Smrg "winClipboardFlushWindowsMessageQueue trapped " 374706f2543Smrg "WM_QUIT message, exiting main loop.\n"); 375706f2543Smrg break; 376706f2543Smrg } 377706f2543Smrg } 378706f2543Smrg } 379706f2543Smrg 380706f2543Smrg /* Close our X window */ 381706f2543Smrg if (pDisplay && iWindow) 382706f2543Smrg { 383706f2543Smrg iReturn = XDestroyWindow (pDisplay, iWindow); 384706f2543Smrg if (iReturn == BadWindow) 385706f2543Smrg ErrorF ("winClipboardProc - XDestroyWindow returned BadWindow.\n"); 386706f2543Smrg else 387706f2543Smrg ErrorF ("winClipboardProc - XDestroyWindow succeeded.\n"); 388706f2543Smrg } 389706f2543Smrg 390706f2543Smrg 391706f2543Smrg#ifdef HAS_DEVWINDOWS 392706f2543Smrg /* Close our Win32 message handle */ 393706f2543Smrg if (fdMessageQueue) 394706f2543Smrg close (fdMessageQueue); 395706f2543Smrg#endif 396706f2543Smrg 397706f2543Smrg#if 0 398706f2543Smrg /* 399706f2543Smrg * FIXME: XCloseDisplay hangs if we call it, as of 2004/03/26. The 400706f2543Smrg * XSync and XSelectInput calls did not help. 401706f2543Smrg */ 402706f2543Smrg 403706f2543Smrg /* Discard any remaining events */ 404706f2543Smrg XSync (pDisplay, TRUE); 405706f2543Smrg 406706f2543Smrg /* Select event types to watch */ 407706f2543Smrg XSelectInput (pDisplay, 408706f2543Smrg DefaultRootWindow (pDisplay), 409706f2543Smrg None); 410706f2543Smrg 411706f2543Smrg /* Close our X display */ 412706f2543Smrg if (pDisplay) 413706f2543Smrg { 414706f2543Smrg XCloseDisplay (pDisplay); 415706f2543Smrg } 416706f2543Smrg#endif 417706f2543Smrg 418706f2543Smrg g_iClipboardWindow = None; 419706f2543Smrg g_pClipboardDisplay = NULL; 420706f2543Smrg g_hwndClipboard = NULL; 421706f2543Smrg 422706f2543Smrg return NULL; 423706f2543Smrg} 424706f2543Smrg 425706f2543Smrg 426706f2543Smrg/* 427706f2543Smrg * winClipboardErrorHandler - Our application specific error handler 428706f2543Smrg */ 429706f2543Smrg 430706f2543Smrgstatic int 431706f2543SmrgwinClipboardErrorHandler (Display *pDisplay, XErrorEvent *pErr) 432706f2543Smrg{ 433706f2543Smrg char pszErrorMsg[100]; 434706f2543Smrg 435706f2543Smrg XGetErrorText (pDisplay, 436706f2543Smrg pErr->error_code, 437706f2543Smrg pszErrorMsg, 438706f2543Smrg sizeof (pszErrorMsg)); 439706f2543Smrg ErrorF ("winClipboardErrorHandler - ERROR: \n\t%s\n" 440706f2543Smrg "\tSerial: %lu, Request Code: %d, Minor Code: %d\n", 441706f2543Smrg pszErrorMsg, 442706f2543Smrg pErr->serial, 443706f2543Smrg pErr->request_code, 444706f2543Smrg pErr->minor_code); 445706f2543Smrg return 0; 446706f2543Smrg} 447706f2543Smrg 448706f2543Smrg 449706f2543Smrg/* 450706f2543Smrg * winClipboardIOErrorHandler - Our application specific IO error handler 451706f2543Smrg */ 452706f2543Smrg 453706f2543Smrgstatic int 454706f2543SmrgwinClipboardIOErrorHandler (Display *pDisplay) 455706f2543Smrg{ 456706f2543Smrg ErrorF ("winClipboardIOErrorHandler!\n\n"); 457706f2543Smrg 458706f2543Smrg /* Restart at the main entry point */ 459706f2543Smrg longjmp (g_jmpEntry, WIN_JMP_ERROR_IO); 460706f2543Smrg 461706f2543Smrg return 0; 462706f2543Smrg} 463