winclipboardwrappers.c revision 4642e01f
1/* 2 *Copyright (C) 2003-2004 Harold L Hunt II All Rights Reserved. 3 * 4 *Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files (the 6 *"Software"), to deal in the Software without restriction, including 7 *without limitation the rights to use, copy, modify, merge, publish, 8 *distribute, sublicense, and/or sell copies of the Software, and to 9 *permit persons to whom the Software is furnished to do so, subject to 10 *the following conditions: 11 * 12 *The above copyright notice and this permission notice shall be 13 *included in all copies or substantial portions of the Software. 14 * 15 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 *NONINFRINGEMENT. IN NO EVENT SHALL HAROLD L HUNT II BE LIABLE FOR 19 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 20 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 * 23 *Except as contained in this notice, the name of Harold L Hunt II 24 *shall not be used in advertising or otherwise to promote the sale, use 25 *or other dealings in this Software without prior written authorization 26 *from Harold L Hunt II. 27 * 28 * Authors: Harold L Hunt II 29 */ 30 31#ifdef HAVE_XWIN_CONFIG_H 32#include <xwin-config.h> 33#endif 34#include "win.h" 35#include "dixstruct.h" 36#include <X11/Xatom.h> 37 38 39/* 40 * Constants 41 */ 42 43#define CLIP_NUM_SELECTIONS 2 44#define CLIP_OWN_PRIMARY 0 45#define CLIP_OWN_CLIPBOARD 1 46 47 48/* 49 * Local function prototypes 50 */ 51 52DISPATCH_PROC(winProcEstablishConnection); 53DISPATCH_PROC(winProcQueryTree); 54DISPATCH_PROC(winProcSetSelectionOwner); 55 56 57/* 58 * References to external symbols 59 */ 60 61extern Bool g_fUnicodeSupport; 62extern int g_iNumScreens; 63extern unsigned int g_uiAuthDataLen; 64extern char *g_pAuthData; 65extern Bool g_fXdmcpEnabled; 66extern Bool g_fClipboardLaunched; 67extern Bool g_fClipboardStarted; 68extern Bool g_fClipboard; 69extern Window g_iClipboardWindow; 70extern Atom g_atomLastOwnedSelection; 71extern HWND g_hwndClipboard; 72 73extern winDispatchProcPtr winProcEstablishConnectionOrig; 74extern winDispatchProcPtr winProcQueryTreeOrig; 75extern winDispatchProcPtr winProcSetSelectionOwnerOrig; 76 77 78/* 79 * Wrapper for internal QueryTree function. 80 * Hides the clipboard client when it is the only client remaining. 81 */ 82 83int 84winProcQueryTree (ClientPtr client) 85{ 86 int iReturn; 87 88 /* 89 * This procedure is only used for initialization. 90 * We can unwrap the original procedure at this point 91 * so that this function is no longer called until the 92 * server resets and the function is wrapped again. 93 */ 94 ProcVector[X_QueryTree] = winProcQueryTreeOrig; 95 96 /* 97 * Call original function and bail if it fails. 98 * NOTE: We must do this first, since we need XdmcpOpenDisplay 99 * to be called before we initialize our clipboard client. 100 */ 101 iReturn = (*winProcQueryTreeOrig) (client); 102 if (iReturn != 0) 103 { 104 ErrorF ("winProcQueryTree - ProcQueryTree failed, bailing.\n"); 105 return iReturn; 106 } 107 108 /* Make errors more obvious */ 109 winProcQueryTreeOrig = NULL; 110 111 /* Do nothing if clipboard is not enabled */ 112 if (!g_fClipboard) 113 { 114 ErrorF ("winProcQueryTree - Clipboard is not enabled, " 115 "returning.\n"); 116 return iReturn; 117 } 118 119 /* If the clipboard client has already been started, abort */ 120 if (g_fClipboardLaunched) 121 { 122 ErrorF ("winProcQueryTree - Clipboard client already " 123 "launched, returning.\n"); 124 return iReturn; 125 } 126 127 /* Startup the clipboard client if clipboard mode is being used */ 128 if (g_fXdmcpEnabled && g_fClipboard) 129 { 130 /* 131 * NOTE: The clipboard client is started here for a reason: 132 * 1) Assume you are using XDMCP (e.g. XWin -query %hostname%) 133 * 2) If the clipboard client attaches during X Server startup, 134 * then it becomes the "magic client" that causes the X Server 135 * to reset if it exits. 136 * 3) XDMCP calls KillAllClients when it starts up. 137 * 4) The clipboard client is a client, so it is killed. 138 * 5) The clipboard client is the "magic client", so the X Server 139 * resets itself. 140 * 6) This repeats ad infinitum. 141 * 7) We avoid this by waiting until at least one client (could 142 * be XDM, could be another client) connects, which makes it 143 * almost certain that the clipboard client will not connect 144 * until after XDM when using XDMCP. 145 * 8) Unfortunately, there is another problem. 146 * 9) XDM walks the list of windows with XQueryTree, 147 * killing any client it finds with a window. 148 * 10)Thus, when using XDMCP we wait until the first call 149 * to ProcQueryTree before we startup the clipboard client. 150 * This should prevent XDM from finding the clipboard client, 151 * since it has not yet created a window. 152 * 11)Startup when not using XDMCP is handled in 153 * winProcEstablishConnection. 154 */ 155 156 /* Create the clipboard client thread */ 157 if (!winInitClipboard ()) 158 { 159 ErrorF ("winProcQueryTree - winClipboardInit " 160 "failed.\n"); 161 return iReturn; 162 } 163 164 ErrorF ("winProcQueryTree - winInitClipboard returned.\n"); 165 } 166 167 /* Flag that clipboard client has been launched */ 168 g_fClipboardLaunched = TRUE; 169 170 return iReturn; 171} 172 173 174/* 175 * Wrapper for internal EstablishConnection function. 176 * Initializes internal clients that must not be started until 177 * an external client has connected. 178 */ 179 180int 181winProcEstablishConnection (ClientPtr client) 182{ 183 int iReturn; 184 static int s_iCallCount = 0; 185 static unsigned long s_ulServerGeneration = 0; 186 187 ErrorF ("winProcEstablishConnection - Hello\n"); 188 189 /* Do nothing if clipboard is not enabled */ 190 if (!g_fClipboard) 191 { 192 ErrorF ("winProcEstablishConnection - Clipboard is not enabled, " 193 "returning.\n"); 194 195 /* Unwrap the original function, call it, and return */ 196 InitialVector[2] = winProcEstablishConnectionOrig; 197 iReturn = (*winProcEstablishConnectionOrig) (client); 198 winProcEstablishConnectionOrig = NULL; 199 return iReturn; 200 } 201 202 /* Watch for server reset */ 203 if (s_ulServerGeneration != serverGeneration) 204 { 205 /* Save new generation number */ 206 s_ulServerGeneration = serverGeneration; 207 208 /* Reset call count */ 209 s_iCallCount = 0; 210 } 211 212 /* Increment call count */ 213 ++s_iCallCount; 214 215 /* Wait for second call when Xdmcp is enabled */ 216 if (g_fXdmcpEnabled 217 && !g_fClipboardLaunched 218 && s_iCallCount < 4) 219 { 220 ErrorF ("winProcEstablishConnection - Xdmcp enabled, waiting to " 221 "start clipboard client until fourth call.\n"); 222 return (*winProcEstablishConnectionOrig) (client); 223 } 224 225 /* 226 * This procedure is only used for initialization. 227 * We can unwrap the original procedure at this point 228 * so that this function is no longer called until the 229 * server resets and the function is wrapped again. 230 */ 231 InitialVector[2] = winProcEstablishConnectionOrig; 232 233 /* 234 * Call original function and bail if it fails. 235 * NOTE: We must do this first, since we need XdmcpOpenDisplay 236 * to be called before we initialize our clipboard client. 237 */ 238 iReturn = (*winProcEstablishConnectionOrig) (client); 239 if (iReturn != 0) 240 { 241 ErrorF ("winProcEstablishConnection - ProcEstablishConnection " 242 "failed, bailing.\n"); 243 return iReturn; 244 } 245 246 /* Clear original function pointer */ 247 winProcEstablishConnectionOrig = NULL; 248 249 /* If the clipboard client has already been started, abort */ 250 if (g_fClipboardLaunched) 251 { 252 ErrorF ("winProcEstablishConnection - Clipboard client already " 253 "launched, returning.\n"); 254 return iReturn; 255 } 256 257 /* Startup the clipboard client if clipboard mode is being used */ 258 if (g_fClipboard) 259 { 260 /* 261 * NOTE: The clipboard client is started here for a reason: 262 * 1) Assume you are using XDMCP (e.g. XWin -query %hostname%) 263 * 2) If the clipboard client attaches during X Server startup, 264 * then it becomes the "magic client" that causes the X Server 265 * to reset if it exits. 266 * 3) XDMCP calls KillAllClients when it starts up. 267 * 4) The clipboard client is a client, so it is killed. 268 * 5) The clipboard client is the "magic client", so the X Server 269 * resets itself. 270 * 6) This repeats ad infinitum. 271 * 7) We avoid this by waiting until at least one client (could 272 * be XDM, could be another client) connects, which makes it 273 * almost certain that the clipboard client will not connect 274 * until after XDM when using XDMCP. 275 * 8) Unfortunately, there is another problem. 276 * 9) XDM walks the list of windows with XQueryTree, 277 * killing any client it finds with a window. 278 * 10)Thus, when using XDMCP we wait until the second call 279 * to ProcEstablishCeonnection before we startup the clipboard 280 * client. This should prevent XDM from finding the clipboard 281 * client, since it has not yet created a window. 282 */ 283 284 /* Create the clipboard client thread */ 285 if (!winInitClipboard ()) 286 { 287 ErrorF ("winProcEstablishConnection - winClipboardInit " 288 "failed.\n"); 289 return iReturn; 290 } 291 292 ErrorF ("winProcEstablishConnection - winInitClipboard returned.\n"); 293 } 294 295 /* Flag that clipboard client has been launched */ 296 g_fClipboardLaunched = TRUE; 297 298 return iReturn; 299} 300 301 302/* 303 * Wrapper for internal SetSelectionOwner function. 304 * Grabs ownership of Windows clipboard when X11 clipboard owner changes. 305 */ 306 307int 308winProcSetSelectionOwner (ClientPtr client) 309{ 310 int i; 311 DrawablePtr pDrawable; 312 WindowPtr pWindow = None; 313 Bool fOwnedToNotOwned = FALSE; 314 static Window s_iOwners[CLIP_NUM_SELECTIONS] = {None}; 315 static unsigned long s_ulServerGeneration = 0; 316 REQUEST(xSetSelectionOwnerReq); 317 318 REQUEST_SIZE_MATCH(xSetSelectionOwnerReq); 319 320#if 0 321 ErrorF ("winProcSetSelectionOwner - Hello.\n"); 322#endif 323 324 /* Watch for server reset */ 325 if (s_ulServerGeneration != serverGeneration) 326 { 327 /* Save new generation number */ 328 s_ulServerGeneration = serverGeneration; 329 330 /* Initialize static variables */ 331 for (i = 0; i < CLIP_NUM_SELECTIONS; ++i) 332 s_iOwners[i] = None; 333 } 334 335 /* Abort if clipboard not completely initialized yet */ 336 if (!g_fClipboardStarted) 337 { 338 ErrorF ("winProcSetSelectionOwner - Clipboard not yet started, " 339 "aborting.\n"); 340 goto winProcSetSelectionOwner_Done; 341 } 342 343 /* Grab window if we have one */ 344 if (None != stuff->window) 345 { 346 /* Grab the Window from the request */ 347 int rc = dixLookupWindow(&pWindow, stuff->window, client, DixReadAccess); 348 if (rc != Success) { 349 ErrorF ("winProcSetSelectionOwner - Found BadWindow, aborting.\n"); 350 goto winProcSetSelectionOwner_Done; 351 } 352 } 353 354 /* Now we either have a valid window or None */ 355 356 /* Save selection owners for monitored selections, ignore other selections */ 357 if (XA_PRIMARY == stuff->selection) 358 { 359 /* Look for owned -> not owned transition */ 360 if (None == stuff->window 361 && None != s_iOwners[CLIP_OWN_PRIMARY]) 362 { 363 fOwnedToNotOwned = TRUE; 364 365#if 0 366 ErrorF ("winProcSetSelectionOwner - PRIMARY - Going from " 367 "owned to not owned.\n"); 368#endif 369 370 /* Adjust last owned selection */ 371 if (None != s_iOwners[CLIP_OWN_CLIPBOARD]) 372 g_atomLastOwnedSelection = MakeAtom ("CLIPBOARD", 9, TRUE); 373 else 374 g_atomLastOwnedSelection = None; 375 } 376 377 /* Save new selection owner or None */ 378 s_iOwners[CLIP_OWN_PRIMARY] = stuff->window; 379 380#if 0 381 ErrorF ("winProcSetSelectionOwner - PRIMARY - Now owned by: %d\n", 382 stuff->window); 383#endif 384 } 385 else if (MakeAtom ("CLIPBOARD", 9, TRUE) == stuff->selection) 386 { 387 /* Look for owned -> not owned transition */ 388 if (None == stuff->window 389 && None != s_iOwners[CLIP_OWN_CLIPBOARD]) 390 { 391 fOwnedToNotOwned = TRUE; 392 393#if 0 394 ErrorF ("winProcSetSelectionOwner - CLIPBOARD - Going from " 395 "owned to not owned.\n"); 396#endif 397 398 /* Adjust last owned selection */ 399 if (None != s_iOwners[CLIP_OWN_PRIMARY]) 400 g_atomLastOwnedSelection = XA_PRIMARY; 401 else 402 g_atomLastOwnedSelection = None; 403 } 404 405 /* Save new selection owner or None */ 406 s_iOwners[CLIP_OWN_CLIPBOARD] = stuff->window; 407 408#if 0 409 ErrorF ("winProcSetSelectionOwner - CLIPBOARD - Now owned by: %d\n", 410 stuff->window); 411#endif 412 } 413 else 414 goto winProcSetSelectionOwner_Done; 415 416 /* 417 * At this point, if one of the selections is still owned by the 418 * clipboard manager then it should be marked as unowned since 419 * we will be taking ownership of the Win32 clipboard. 420 */ 421 if (g_iClipboardWindow == s_iOwners[CLIP_OWN_PRIMARY]) 422 s_iOwners[CLIP_OWN_PRIMARY] = None; 423 if (g_iClipboardWindow == s_iOwners[CLIP_OWN_CLIPBOARD]) 424 s_iOwners[CLIP_OWN_CLIPBOARD] = None; 425 426 /* 427 * Handle case when selection is being disowned, 428 * WM_DRAWCLIPBOARD did not do the disowning, 429 * both monitored selections are no longer owned, 430 * an owned to not owned transition was detected, 431 * and we currently own the Win32 clipboard. 432 */ 433 if (None == stuff->window 434 && (None == s_iOwners[CLIP_OWN_PRIMARY] 435 || g_iClipboardWindow == s_iOwners[CLIP_OWN_PRIMARY]) 436 && (None == s_iOwners[CLIP_OWN_CLIPBOARD] 437 || g_iClipboardWindow == s_iOwners[CLIP_OWN_CLIPBOARD]) 438 && fOwnedToNotOwned 439 && g_hwndClipboard != NULL 440 && g_hwndClipboard == GetClipboardOwner ()) 441 { 442#if 0 443 ErrorF ("winProcSetSelectionOwner - We currently own the " 444 "clipboard and neither the PRIMARY nor the CLIPBOARD " 445 "selections are owned, releasing ownership of Win32 " 446 "clipboard.\n"); 447#endif 448 449 /* Release ownership of the Windows clipboard */ 450 OpenClipboard (NULL); 451 EmptyClipboard (); 452 CloseClipboard (); 453 454 /* Clear X selection ownership (might still be marked as us owning) */ 455 s_iOwners[CLIP_OWN_PRIMARY] = None; 456 s_iOwners[CLIP_OWN_CLIPBOARD] = None; 457 458 goto winProcSetSelectionOwner_Done; 459 } 460 461 /* Abort if no window at this point */ 462 if (None == stuff->window) 463 { 464#if 0 465 ErrorF ("winProcSetSelectionOwner - No window, returning.\n"); 466#endif 467 goto winProcSetSelectionOwner_Done; 468 } 469 470 /* Abort if invalid selection */ 471 if (!ValidAtom (stuff->selection)) 472 { 473 ErrorF ("winProcSetSelectionOwner - Found BadAtom, aborting.\n"); 474 goto winProcSetSelectionOwner_Done; 475 } 476 477 /* Cast Window to Drawable */ 478 pDrawable = (DrawablePtr) pWindow; 479 480 /* Abort if clipboard manager is owning the selection */ 481 if (pDrawable->id == g_iClipboardWindow) 482 { 483#if 0 484 ErrorF ("winProcSetSelectionOwner - We changed ownership, " 485 "aborting.\n"); 486#endif 487 goto winProcSetSelectionOwner_Done; 488 } 489 490 /* Abort if root window is taking ownership */ 491 if (pDrawable->id == 0) 492 { 493 ErrorF ("winProcSetSelectionOwner - Root window taking ownership, " 494 "aborting\n"); 495 goto winProcSetSelectionOwner_Done; 496 } 497 498 /* Close clipboard if we have it open already */ 499 if (GetOpenClipboardWindow () == g_hwndClipboard) 500 { 501 CloseClipboard (); 502 } 503 504 /* Access the Windows clipboard */ 505 if (!OpenClipboard (g_hwndClipboard)) 506 { 507 ErrorF ("winProcSetSelectionOwner - OpenClipboard () failed: %08x\n", 508 (int) GetLastError ()); 509 goto winProcSetSelectionOwner_Done; 510 } 511 512 /* Take ownership of the Windows clipboard */ 513 if (!EmptyClipboard ()) 514 { 515 ErrorF ("winProcSetSelectionOwner - EmptyClipboard () failed: %08x\n", 516 (int) GetLastError ()); 517 goto winProcSetSelectionOwner_Done; 518 } 519 520 /* Advertise Unicode if we support it */ 521 if (g_fUnicodeSupport) 522 SetClipboardData (CF_UNICODETEXT, NULL); 523 524 /* Always advertise regular text */ 525 SetClipboardData (CF_TEXT, NULL); 526 527 /* Save handle to last owned selection */ 528 g_atomLastOwnedSelection = stuff->selection; 529 530 /* Release the clipboard */ 531 if (!CloseClipboard ()) 532 { 533 ErrorF ("winProcSetSelectionOwner - CloseClipboard () failed: " 534 "%08x\n", 535 (int) GetLastError ()); 536 goto winProcSetSelectionOwner_Done; 537 } 538 539 winProcSetSelectionOwner_Done: 540 return (*winProcSetSelectionOwnerOrig) (client); 541} 542