1/* 2 *Copyright (C) 2003-2004 Harold L Hunt II All Rights Reserved. 3 *Copyright (C) Colin Harrison 2005-2008 4 * 5 *Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files (the 7 *"Software"), to deal in the Software without restriction, including 8 *without limitation the rights to use, copy, modify, merge, publish, 9 *distribute, sublicense, and/or sell copies of the Software, and to 10 *permit persons to whom the Software is furnished to do so, subject to 11 *the following conditions: 12 * 13 *The above copyright notice and this permission notice shall be 14 *included in all copies or substantial portions of the Software. 15 * 16 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 *NONINFRINGEMENT. IN NO EVENT SHALL HAROLD L HUNT II BE LIABLE FOR 20 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 21 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 * 24 *Except as contained in this notice, the name of the copyright holder(s) 25 *and author(s) shall not be used in advertising or otherwise to promote 26 *the sale, use or other dealings in this Software without prior written 27 *authorization from the copyright holder(s) and author(s). 28 * 29 * Authors: Harold L Hunt II 30 * Colin Harrison 31 */ 32 33#ifdef HAVE_XWIN_CONFIG_H 34#include <xwin-config.h> 35#endif 36#include <sys/types.h> 37#include "winclipboard.h" 38#ifdef __CYGWIN__ 39#include <errno.h> 40#endif 41#include "misc.h" 42 43 44/* 45 * References to external symbols 46 */ 47 48extern Bool g_fUnicodeClipboard; 49extern unsigned long serverGeneration; 50extern Bool g_fClipboardStarted; 51extern HWND g_hwndClipboard; 52extern void *g_pClipboardDisplay; 53extern Window g_iClipboardWindow; 54 55 56/* 57 * Global variables 58 */ 59 60static jmp_buf g_jmpEntry; 61Bool g_fUnicodeSupport = FALSE; 62Bool g_fUseUnicode = FALSE; 63 64 65/* 66 * Local function prototypes 67 */ 68 69static int 70winClipboardErrorHandler (Display *pDisplay, XErrorEvent *pErr); 71 72static int 73winClipboardIOErrorHandler (Display *pDisplay); 74 75 76/* 77 * Main thread function 78 */ 79 80void * 81winClipboardProc (void *pvNotUsed) 82{ 83 Atom atomClipboard, atomClipboardManager; 84 int iReturn; 85 HWND hwnd = NULL; 86 int iConnectionNumber = 0; 87#ifdef HAS_DEVWINDOWS 88 int fdMessageQueue = 0; 89#else 90 struct timeval tvTimeout; 91#endif 92 fd_set fdsRead; 93 int iMaxDescriptor; 94 Display *pDisplay = NULL; 95 Window iWindow = None; 96 int iRetries; 97 Bool fUseUnicode; 98 char szDisplay[512]; 99 int iSelectError; 100 101 ErrorF ("winClipboardProc - Hello\n"); 102 103 /* Do we have Unicode support? */ 104 g_fUnicodeSupport = winClipboardDetectUnicodeSupport (); 105 106 /* Do we use Unicode clipboard? */ 107 fUseUnicode = g_fUnicodeClipboard && g_fUnicodeSupport; 108 109 /* Save the Unicode support flag in a global */ 110 g_fUseUnicode = fUseUnicode; 111 112 /* Allow multiple threads to access Xlib */ 113 if (XInitThreads () == 0) 114 { 115 ErrorF ("winClipboardProc - XInitThreads failed.\n"); 116 pthread_exit (NULL); 117 } 118 119 /* See if X supports the current locale */ 120 if (XSupportsLocale () == False) 121 { 122 ErrorF ("winClipboardProc - Warning: Locale not supported by X.\n"); 123 } 124 125 /* Set jump point for Error exits */ 126 iReturn = setjmp (g_jmpEntry); 127 128 /* Check if we should continue operations */ 129 if (iReturn != WIN_JMP_ERROR_IO 130 && iReturn != WIN_JMP_OKAY) 131 { 132 /* setjmp returned an unknown value, exit */ 133 ErrorF ("winClipboardProc - setjmp returned: %d exiting\n", 134 iReturn); 135 pthread_exit (NULL); 136 } 137 else if (iReturn == WIN_JMP_ERROR_IO) 138 { 139 /* TODO: Cleanup the Win32 window and free any allocated memory */ 140 ErrorF ("winClipboardProc - setjmp returned for IO Error Handler.\n"); 141 pthread_exit (NULL); 142 } 143 144 /* Use our generated cookie for authentication */ 145 winSetAuthorization(); 146 147 /* Set error handler */ 148 XSetErrorHandler (winClipboardErrorHandler); 149 XSetIOErrorHandler (winClipboardIOErrorHandler); 150 151 /* Initialize retry count */ 152 iRetries = 0; 153 154 /* Setup the display connection string x */ 155 /* 156 * NOTE: Always connect to screen 0 since we require that screen 157 * numbers start at 0 and increase without gaps. We only need 158 * to connect to one screen on the display to get events 159 * for all screens on the display. That is why there is only 160 * one clipboard client thread. 161 */ 162 snprintf (szDisplay, 163 512, 164 "127.0.0.1:%s.0", 165 display); 166 167 /* Print the display connection string */ 168 ErrorF ("winClipboardProc - DISPLAY=%s\n", szDisplay); 169 170 /* Open the X display */ 171 do 172 { 173 pDisplay = XOpenDisplay (szDisplay); 174 if (pDisplay == NULL) 175 { 176 ErrorF ("winClipboardProc - Could not open display, " 177 "try: %d, sleeping: %d\n", 178 iRetries + 1, WIN_CONNECT_DELAY); 179 ++iRetries; 180 sleep (WIN_CONNECT_DELAY); 181 continue; 182 } 183 else 184 break; 185 } 186 while (pDisplay == NULL && iRetries < WIN_CONNECT_RETRIES); 187 188 /* Make sure that the display opened */ 189 if (pDisplay == NULL) 190 { 191 ErrorF ("winClipboardProc - Failed opening the display, giving up\n"); 192 pthread_exit (NULL); 193 } 194 195 /* Save the display in the screen privates */ 196 g_pClipboardDisplay = pDisplay; 197 198 ErrorF ("winClipboardProc - XOpenDisplay () returned and " 199 "successfully opened the display.\n"); 200 201 /* Get our connection number */ 202 iConnectionNumber = ConnectionNumber (pDisplay); 203 204#ifdef HAS_DEVWINDOWS 205 /* Open a file descriptor for the windows message queue */ 206 fdMessageQueue = open (WIN_MSG_QUEUE_FNAME, O_RDONLY); 207 if (fdMessageQueue == -1) 208 { 209 ErrorF ("winClipboardProc - Failed opening %s\n", WIN_MSG_QUEUE_FNAME); 210 pthread_exit (NULL); 211 } 212 213 /* Find max of our file descriptors */ 214 iMaxDescriptor = max (fdMessageQueue, iConnectionNumber) + 1; 215#else 216 iMaxDescriptor = iConnectionNumber + 1; 217#endif 218 219 /* Create atoms */ 220 atomClipboard = XInternAtom (pDisplay, "CLIPBOARD", False); 221 atomClipboardManager = XInternAtom (pDisplay, "CLIPBOARD_MANAGER", False); 222 223 /* Create a messaging window */ 224 iWindow = XCreateSimpleWindow (pDisplay, 225 DefaultRootWindow (pDisplay), 226 1, 1, 227 500, 500, 228 0, 229 BlackPixel (pDisplay, 0), 230 BlackPixel (pDisplay, 0)); 231 if (iWindow == 0) 232 { 233 ErrorF ("winClipboardProc - Could not create an X window.\n"); 234 pthread_exit (NULL); 235 } 236 237 /* Select event types to watch */ 238 if (XSelectInput (pDisplay, 239 iWindow, 240 PropertyChangeMask) == BadWindow) 241 ErrorF ("winClipboardProc - XSelectInput generated BadWindow " 242 "on messaging window\n"); 243 244 /* Save the window in the screen privates */ 245 g_iClipboardWindow = iWindow; 246 247 /* Create Windows messaging window */ 248 hwnd = winClipboardCreateMessagingWindow (); 249 250 /* Save copy of HWND in screen privates */ 251 g_hwndClipboard = hwnd; 252 253 /* Assert ownership of selections if Win32 clipboard is owned */ 254 if (NULL != GetClipboardOwner ()) 255 { 256 /* PRIMARY */ 257 iReturn = XSetSelectionOwner (pDisplay, XA_PRIMARY, 258 iWindow, CurrentTime); 259 if (iReturn == BadAtom || iReturn == BadWindow || 260 XGetSelectionOwner (pDisplay, XA_PRIMARY) != iWindow) 261 { 262 ErrorF ("winClipboardProc - Could not set PRIMARY owner\n"); 263 pthread_exit (NULL); 264 } 265 266 /* CLIPBOARD */ 267 iReturn = XSetSelectionOwner (pDisplay, atomClipboard, 268 iWindow, CurrentTime); 269 if (iReturn == BadAtom || iReturn == BadWindow || 270 XGetSelectionOwner (pDisplay, atomClipboard) != iWindow) 271 { 272 ErrorF ("winClipboardProc - Could not set CLIPBOARD owner\n"); 273 pthread_exit (NULL); 274 } 275 } 276 277 /* Pre-flush X events */ 278 /* 279 * NOTE: Apparently you'll freeze if you don't do this, 280 * because there may be events in local data structures 281 * already. 282 */ 283 winClipboardFlushXEvents (hwnd, 284 iWindow, 285 pDisplay, 286 fUseUnicode); 287 288 /* Pre-flush Windows messages */ 289 if (!winClipboardFlushWindowsMessageQueue (hwnd)) 290 return 0; 291 292 /* Signal that the clipboard client has started */ 293 g_fClipboardStarted = TRUE; 294 295 /* Loop for X events */ 296 while (1) 297 { 298 /* Setup the file descriptor set */ 299 /* 300 * NOTE: You have to do this before every call to select 301 * because select modifies the mask to indicate 302 * which descriptors are ready. 303 */ 304 FD_ZERO (&fdsRead); 305 FD_SET (iConnectionNumber, &fdsRead); 306#ifdef HAS_DEVWINDOWS 307 FD_SET (fdMessageQueue, &fdsRead); 308#else 309 tvTimeout.tv_sec = 0; 310 tvTimeout.tv_usec = 100; 311#endif 312 313 /* Wait for a Windows event or an X event */ 314 iReturn = select (iMaxDescriptor, /* Highest fds number */ 315 &fdsRead, /* Read mask */ 316 NULL, /* No write mask */ 317 NULL, /* No exception mask */ 318#ifdef HAS_DEVWINDOWS 319 NULL /* No timeout */ 320#else 321 &tvTimeout /* Set timeout */ 322#endif 323 ); 324 325#ifndef HAS_WINSOCK 326 iSelectError = errno; 327#else 328 iSelectError = WSAGetLastError(); 329#endif 330 331 if (iReturn < 0) 332 { 333#ifndef HAS_WINSOCK 334 if (iSelectError == EINTR) 335#else 336 if (iSelectError == WSAEINTR) 337#endif 338 continue; 339 340 ErrorF ("winClipboardProc - Call to select () failed: %d. " 341 "Bailing.\n", iReturn); 342 break; 343 } 344 345 /* Branch on which descriptor became active */ 346 if (FD_ISSET (iConnectionNumber, &fdsRead)) 347 { 348 /* Process X events */ 349 /* Exit when we see that server is shutting down */ 350 iReturn = winClipboardFlushXEvents (hwnd, 351 iWindow, 352 pDisplay, 353 fUseUnicode); 354 if (WIN_XEVENTS_SHUTDOWN == iReturn) 355 { 356 ErrorF ("winClipboardProc - winClipboardFlushXEvents " 357 "trapped shutdown event, exiting main loop.\n"); 358 break; 359 } 360 } 361 362#ifdef HAS_DEVWINDOWS 363 /* Check for Windows event ready */ 364 if (FD_ISSET (fdMessageQueue, &fdsRead)) 365#else 366 if (1) 367#endif 368 { 369 /* Process Windows messages */ 370 if (!winClipboardFlushWindowsMessageQueue (hwnd)) 371 { 372 ErrorF ("winClipboardProc - " 373 "winClipboardFlushWindowsMessageQueue trapped " 374 "WM_QUIT message, exiting main loop.\n"); 375 break; 376 } 377 } 378 } 379 380 /* Close our X window */ 381 if (pDisplay && iWindow) 382 { 383 iReturn = XDestroyWindow (pDisplay, iWindow); 384 if (iReturn == BadWindow) 385 ErrorF ("winClipboardProc - XDestroyWindow returned BadWindow.\n"); 386 else 387 ErrorF ("winClipboardProc - XDestroyWindow succeeded.\n"); 388 } 389 390 391#ifdef HAS_DEVWINDOWS 392 /* Close our Win32 message handle */ 393 if (fdMessageQueue) 394 close (fdMessageQueue); 395#endif 396 397#if 0 398 /* 399 * FIXME: XCloseDisplay hangs if we call it, as of 2004/03/26. The 400 * XSync and XSelectInput calls did not help. 401 */ 402 403 /* Discard any remaining events */ 404 XSync (pDisplay, TRUE); 405 406 /* Select event types to watch */ 407 XSelectInput (pDisplay, 408 DefaultRootWindow (pDisplay), 409 None); 410 411 /* Close our X display */ 412 if (pDisplay) 413 { 414 XCloseDisplay (pDisplay); 415 } 416#endif 417 418 g_iClipboardWindow = None; 419 g_pClipboardDisplay = NULL; 420 g_hwndClipboard = NULL; 421 422 return NULL; 423} 424 425 426/* 427 * winClipboardErrorHandler - Our application specific error handler 428 */ 429 430static int 431winClipboardErrorHandler (Display *pDisplay, XErrorEvent *pErr) 432{ 433 char pszErrorMsg[100]; 434 435 XGetErrorText (pDisplay, 436 pErr->error_code, 437 pszErrorMsg, 438 sizeof (pszErrorMsg)); 439 ErrorF ("winClipboardErrorHandler - ERROR: \n\t%s\n" 440 "\tSerial: %lu, Request Code: %d, Minor Code: %d\n", 441 pszErrorMsg, 442 pErr->serial, 443 pErr->request_code, 444 pErr->minor_code); 445 return 0; 446} 447 448 449/* 450 * winClipboardIOErrorHandler - Our application specific IO error handler 451 */ 452 453static int 454winClipboardIOErrorHandler (Display *pDisplay) 455{ 456 ErrorF ("winClipboardIOErrorHandler!\n\n"); 457 458 /* Restart at the main entry point */ 459 longjmp (g_jmpEntry, WIN_JMP_ERROR_IO); 460 461 return 0; 462} 463