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