| 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 | * Constants |
| 42 | */ |
| 43 | |
| 44 | #define CLIP_NUM_SELECTIONS 2 |
| 45 | #define CLIP_OWN_PRIMARY 0 |
| 46 | #define CLIP_OWN_CLIPBOARD 1 |
| 47 | |
| 48 | /* |
| 49 | * Local function prototypes |
| 50 | */ |
| 51 | |
| 52 | DISPATCH_PROC(winProcEstablishConnection); |
| 53 | DISPATCH_PROC(winProcSetSelectionOwner); |
| 54 | |
| 55 | /* |
| 56 | * References to external symbols |
| 57 | */ |
| 58 | |
| 59 | extern Bool g_fClipboardLaunched; |
| 60 | extern Bool g_fClipboardStarted; |
| 61 | extern Bool g_fClipboard; |
| 62 | extern Window g_iClipboardWindow; |
| 63 | extern Atom g_atomLastOwnedSelection; |
| 64 | extern HWND g_hwndClipboard; |
| 65 | |
| 66 | |
| 67 | /* |
| 68 | * Wrapper for internal EstablishConnection function. |
| 69 | * Initializes internal clients that must not be started until |
| 70 | * an external client has connected. |
| 71 | */ |
| 72 | |
| 73 | int |
| 74 | winProcEstablishConnection(ClientPtr client) |
| 75 | { |
| 76 | int iReturn; |
| 77 | static int s_iCallCount = 0; |
| 78 | static unsigned long s_ulServerGeneration = 0; |
| 79 | |
| 80 | if (s_iCallCount == 0) |
| 81 | winDebug("winProcEstablishConnection - Hello\n"); |
| 82 | |
| 83 | /* Do nothing if clipboard is not enabled */ |
| 84 | if (!g_fClipboard) { |
| 85 | ErrorF("winProcEstablishConnection - Clipboard is not enabled, " |
| 86 | "returning.\n"); |
| 87 | |
| 88 | /* Unwrap the original function, call it, and return */ |
| 89 | InitialVector[2] = winProcEstablishConnectionOrig; |
| 90 | iReturn = (*winProcEstablishConnectionOrig) (client); |
| 91 | winProcEstablishConnectionOrig = NULL; |
| 92 | return iReturn; |
| 93 | } |
| 94 | |
| 95 | /* Watch for server reset */ |
| 96 | if (s_ulServerGeneration != serverGeneration) { |
| 97 | /* Save new generation number */ |
| 98 | s_ulServerGeneration = serverGeneration; |
| 99 | |
| 100 | /* Reset call count */ |
| 101 | s_iCallCount = 0; |
| 102 | } |
| 103 | |
| 104 | /* Increment call count */ |
| 105 | ++s_iCallCount; |
| 106 | |
| 107 | /* |
| 108 | * This procedure is only used for initialization. |
| 109 | * We can unwrap the original procedure at this point |
| 110 | * so that this function is no longer called until the |
| 111 | * server resets and the function is wrapped again. |
| 112 | */ |
| 113 | InitialVector[2] = winProcEstablishConnectionOrig; |
| 114 | |
| 115 | /* |
| 116 | * Call original function and bail if it fails. |
| 117 | * NOTE: We must do this first, since we need XdmcpOpenDisplay |
| 118 | * to be called before we initialize our clipboard client. |
| 119 | */ |
| 120 | iReturn = (*winProcEstablishConnectionOrig) (client); |
| 121 | if (iReturn != 0) { |
| 122 | ErrorF("winProcEstablishConnection - ProcEstablishConnection " |
| 123 | "failed, bailing.\n"); |
| 124 | return iReturn; |
| 125 | } |
| 126 | |
| 127 | /* Clear original function pointer */ |
| 128 | winProcEstablishConnectionOrig = NULL; |
| 129 | |
| 130 | /* If the clipboard client has already been started, abort */ |
| 131 | if (g_fClipboardLaunched) { |
| 132 | ErrorF("winProcEstablishConnection - Clipboard client already " |
| 133 | "launched, returning.\n"); |
| 134 | return iReturn; |
| 135 | } |
| 136 | |
| 137 | /* Startup the clipboard client if clipboard mode is being used */ |
| 138 | if (g_fClipboard) { |
| 139 | /* |
| 140 | * NOTE: The clipboard client is started here for a reason: |
| 141 | * 1) Assume you are using XDMCP (e.g. XWin -query %hostname%) |
| 142 | * 2) If the clipboard client attaches during X Server startup, |
| 143 | * then it becomes the "magic client" that causes the X Server |
| 144 | * to reset if it exits. |
| 145 | * 3) XDMCP calls KillAllClients when it starts up. |
| 146 | * 4) The clipboard client is a client, so it is killed. |
| 147 | * 5) The clipboard client is the "magic client", so the X Server |
| 148 | * resets itself. |
| 149 | * 6) This repeats ad infinitum. |
| 150 | * 7) We avoid this by waiting until at least one client (could |
| 151 | * be XDM, could be another client) connects, which makes it |
| 152 | * almost certain that the clipboard client will not connect |
| 153 | * until after XDM when using XDMCP. |
| 154 | */ |
| 155 | |
| 156 | /* Create the clipboard client thread */ |
| 157 | if (!winInitClipboard()) { |
| 158 | ErrorF("winProcEstablishConnection - winClipboardInit " |
| 159 | "failed.\n"); |
| 160 | return iReturn; |
| 161 | } |
| 162 | |
| 163 | ErrorF("winProcEstablishConnection - winInitClipboard returned.\n"); |
| 164 | } |
| 165 | |
| 166 | /* Flag that clipboard client has been launched */ |
| 167 | g_fClipboardLaunched = TRUE; |
| 168 | |
| 169 | return iReturn; |
| 170 | } |
| 171 | |
| 172 | /* |
| 173 | * Wrapper for internal SetSelectionOwner function. |
| 174 | * Grabs ownership of Windows clipboard when X11 clipboard owner changes. |
| 175 | */ |
| 176 | |
| 177 | int |
| 178 | winProcSetSelectionOwner(ClientPtr client) |
| 179 | { |
| 180 | int i; |
| 181 | DrawablePtr pDrawable; |
| 182 | WindowPtr pWindow = None; |
| 183 | Bool fOwnedToNotOwned = FALSE; |
| 184 | static Window s_iOwners[CLIP_NUM_SELECTIONS] = { None }; |
| 185 | static unsigned long s_ulServerGeneration = 0; |
| 186 | |
| 187 | REQUEST(xSetSelectionOwnerReq); |
| 188 | |
| 189 | REQUEST_SIZE_MATCH(xSetSelectionOwnerReq); |
| 190 | |
| 191 | winDebug("winProcSetSelectionOwner - Hello.\n"); |
| 192 | |
| 193 | /* Watch for server reset */ |
| 194 | if (s_ulServerGeneration != serverGeneration) { |
| 195 | /* Save new generation number */ |
| 196 | s_ulServerGeneration = serverGeneration; |
| 197 | |
| 198 | /* Initialize static variables */ |
| 199 | for (i = 0; i < CLIP_NUM_SELECTIONS; ++i) |
| 200 | s_iOwners[i] = None; |
| 201 | } |
| 202 | |
| 203 | /* Abort if clipboard not completely initialized yet */ |
| 204 | if (!g_fClipboardStarted) { |
| 205 | /* ErrorF ("winProcSetSelectionOwner - Clipboard not yet started, " |
| 206 | "aborting.\n"); */ |
| 207 | goto winProcSetSelectionOwner_Done; |
| 208 | } |
| 209 | |
| 210 | /* Grab window if we have one */ |
| 211 | if (None != stuff->window) { |
| 212 | /* Grab the Window from the request */ |
| 213 | int rc = |
| 214 | dixLookupWindow(&pWindow, stuff->window, client, DixReadAccess); |
| 215 | if (rc != Success) { |
| 216 | ErrorF("winProcSetSelectionOwner - Found BadWindow, aborting.\n"); |
| 217 | goto winProcSetSelectionOwner_Done; |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | /* Now we either have a valid window or None */ |
| 222 | |
| 223 | /* Save selection owners for monitored selections, ignore other selections */ |
| 224 | if (XA_PRIMARY == stuff->selection) { |
| 225 | /* Look for owned -> not owned transition */ |
| 226 | if (None == stuff->window && None != s_iOwners[CLIP_OWN_PRIMARY]) { |
| 227 | fOwnedToNotOwned = TRUE; |
| 228 | |
| 229 | winDebug("winProcSetSelectionOwner - PRIMARY - Going from " |
| 230 | "owned to not owned.\n"); |
| 231 | |
| 232 | /* Adjust last owned selection */ |
| 233 | if (None != s_iOwners[CLIP_OWN_CLIPBOARD]) |
| 234 | g_atomLastOwnedSelection = MakeAtom("CLIPBOARD", 9, TRUE); |
| 235 | else |
| 236 | g_atomLastOwnedSelection = None; |
| 237 | } |
| 238 | |
| 239 | /* Save new selection owner or None */ |
| 240 | s_iOwners[CLIP_OWN_PRIMARY] = stuff->window; |
| 241 | |
| 242 | winDebug("winProcSetSelectionOwner - PRIMARY - Now owned by: %d\n", |
| 243 | stuff->window); |
| 244 | } |
| 245 | else if (MakeAtom("CLIPBOARD", 9, TRUE) == stuff->selection) { |
| 246 | /* Look for owned -> not owned transition */ |
| 247 | if (None == stuff->window && None != s_iOwners[CLIP_OWN_CLIPBOARD]) { |
| 248 | fOwnedToNotOwned = TRUE; |
| 249 | |
| 250 | winDebug("winProcSetSelectionOwner - CLIPBOARD - Going from " |
| 251 | "owned to not owned.\n"); |
| 252 | |
| 253 | /* Adjust last owned selection */ |
| 254 | if (None != s_iOwners[CLIP_OWN_PRIMARY]) |
| 255 | g_atomLastOwnedSelection = XA_PRIMARY; |
| 256 | else |
| 257 | g_atomLastOwnedSelection = None; |
| 258 | } |
| 259 | |
| 260 | /* Save new selection owner or None */ |
| 261 | s_iOwners[CLIP_OWN_CLIPBOARD] = stuff->window; |
| 262 | |
| 263 | winDebug("winProcSetSelectionOwner - CLIPBOARD - Now owned by: %d\n", |
| 264 | stuff->window); |
| 265 | |
| 266 | } |
| 267 | else |
| 268 | goto winProcSetSelectionOwner_Done; |
| 269 | |
| 270 | /* |
| 271 | * At this point, if one of the selections is still owned by the |
| 272 | * clipboard manager then it should be marked as unowned since |
| 273 | * we will be taking ownership of the Win32 clipboard. |
| 274 | */ |
| 275 | if (g_iClipboardWindow == s_iOwners[CLIP_OWN_PRIMARY]) |
| 276 | s_iOwners[CLIP_OWN_PRIMARY] = None; |
| 277 | if (g_iClipboardWindow == s_iOwners[CLIP_OWN_CLIPBOARD]) |
| 278 | s_iOwners[CLIP_OWN_CLIPBOARD] = None; |
| 279 | |
| 280 | /* |
| 281 | * Handle case when selection is being disowned, |
| 282 | * WM_DRAWCLIPBOARD did not do the disowning, |
| 283 | * both monitored selections are no longer owned, |
| 284 | * an owned to not owned transition was detected, |
| 285 | * and we currently own the Win32 clipboard. |
| 286 | */ |
| 287 | if (stuff->window == None |
| 288 | && s_iOwners[CLIP_OWN_PRIMARY] == None |
| 289 | && s_iOwners[CLIP_OWN_CLIPBOARD] == None |
| 290 | && fOwnedToNotOwned |
| 291 | && g_hwndClipboard != NULL && g_hwndClipboard == GetClipboardOwner()) { |
| 292 | winDebug("winProcSetSelectionOwner - We currently own the " |
| 293 | "clipboard and neither the PRIMARY nor the CLIPBOARD " |
| 294 | "selections are owned, releasing ownership of Win32 " |
| 295 | "clipboard.\n"); |
| 296 | |
| 297 | /* Release ownership of the Windows clipboard */ |
| 298 | OpenClipboard(NULL); |
| 299 | EmptyClipboard(); |
| 300 | CloseClipboard(); |
| 301 | |
| 302 | goto winProcSetSelectionOwner_Done; |
| 303 | } |
| 304 | |
| 305 | /* Abort if no window at this point */ |
| 306 | if (None == stuff->window) { |
| 307 | winDebug("winProcSetSelectionOwner - No window, returning.\n"); |
| 308 | goto winProcSetSelectionOwner_Done; |
| 309 | } |
| 310 | |
| 311 | /* Abort if invalid selection */ |
| 312 | if (!ValidAtom(stuff->selection)) { |
| 313 | ErrorF("winProcSetSelectionOwner - Found BadAtom, aborting.\n"); |
| 314 | goto winProcSetSelectionOwner_Done; |
| 315 | } |
| 316 | |
| 317 | /* Cast Window to Drawable */ |
| 318 | pDrawable = (DrawablePtr) pWindow; |
| 319 | |
| 320 | /* Abort if clipboard manager is owning the selection */ |
| 321 | if (pDrawable->id == g_iClipboardWindow) { |
| 322 | winDebug("winProcSetSelectionOwner - We changed ownership, " |
| 323 | "aborting.\n"); |
| 324 | goto winProcSetSelectionOwner_Done; |
| 325 | } |
| 326 | |
| 327 | /* Abort if root window is taking ownership */ |
| 328 | if (pDrawable->id == 0) { |
| 329 | ErrorF("winProcSetSelectionOwner - Root window taking ownership, " |
| 330 | "aborting\n"); |
| 331 | goto winProcSetSelectionOwner_Done; |
| 332 | } |
| 333 | |
| 334 | /* Close clipboard if we have it open already */ |
| 335 | if (GetOpenClipboardWindow() == g_hwndClipboard) { |
| 336 | CloseClipboard(); |
| 337 | } |
| 338 | |
| 339 | /* Access the Windows clipboard */ |
| 340 | if (!OpenClipboard(g_hwndClipboard)) { |
| 341 | ErrorF("winProcSetSelectionOwner - OpenClipboard () failed: %08x\n", |
| 342 | (int) GetLastError()); |
| 343 | goto winProcSetSelectionOwner_Done; |
| 344 | } |
| 345 | |
| 346 | /* Take ownership of the Windows clipboard */ |
| 347 | if (!EmptyClipboard()) { |
| 348 | ErrorF("winProcSetSelectionOwner - EmptyClipboard () failed: %08x\n", |
| 349 | (int) GetLastError()); |
| 350 | goto winProcSetSelectionOwner_Done; |
| 351 | } |
| 352 | |
| 353 | /* Advertise regular text and unicode */ |
| 354 | SetClipboardData(CF_UNICODETEXT, NULL); |
| 355 | SetClipboardData(CF_TEXT, NULL); |
| 356 | |
| 357 | /* Save handle to last owned selection */ |
| 358 | g_atomLastOwnedSelection = stuff->selection; |
| 359 | |
| 360 | /* Release the clipboard */ |
| 361 | if (!CloseClipboard()) { |
| 362 | ErrorF("winProcSetSelectionOwner - CloseClipboard () failed: " |
| 363 | "%08x\n", (int) GetLastError()); |
| 364 | goto winProcSetSelectionOwner_Done; |
| 365 | } |
| 366 | |
| 367 | winProcSetSelectionOwner_Done: |
| 368 | return (*winProcSetSelectionOwnerOrig) (client); |
| 369 | } |