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#define WINVER 0x0600 34 35#ifdef HAVE_XWIN_CONFIG_H 36#include <xwin-config.h> 37#endif 38 39#include <sys/types.h> 40#include <sys/time.h> 41#include <limits.h> 42 43#include <xcb/xproto.h> 44#include <xcb/xcb_aux.h> 45 46#include "internal.h" 47#include "winclipboard.h" 48 49/* 50 * Constants 51 */ 52 53#define WIN_POLL_TIMEOUT 1 54 55/* 56 * Process X events up to specified timeout 57 */ 58 59static int 60winProcessXEventsTimeout(HWND hwnd, xcb_window_t iWindow, xcb_connection_t *conn, 61 ClipboardConversionData *data, ClipboardAtoms *atoms, int iTimeoutSec) 62{ 63 int iConnNumber; 64 struct timeval tv; 65 int iReturn; 66 67 winDebug("winProcessXEventsTimeout () - pumping X events, timeout %d seconds\n", 68 iTimeoutSec); 69 70 /* Get our connection number */ 71 iConnNumber = xcb_get_file_descriptor(conn); 72 73 /* Loop for X events */ 74 while (1) { 75 fd_set fdsRead; 76 long remainingTime; 77 78 /* Process X events */ 79 iReturn = winClipboardFlushXEvents(hwnd, iWindow, conn, data, atoms); 80 81 winDebug("winProcessXEventsTimeout () - winClipboardFlushXEvents returned %d\n", iReturn); 82 83 if ((WIN_XEVENTS_NOTIFY_DATA == iReturn) || (WIN_XEVENTS_NOTIFY_TARGETS == iReturn) || (WIN_XEVENTS_FAILED == iReturn)) { 84 /* Bail out */ 85 return iReturn; 86 } 87 88 /* We need to ensure that all pending requests are sent */ 89 xcb_flush(conn); 90 91 /* Setup the file descriptor set */ 92 FD_ZERO(&fdsRead); 93 FD_SET(iConnNumber, &fdsRead); 94 95 /* Adjust timeout */ 96 remainingTime = iTimeoutSec * 1000; 97 tv.tv_sec = remainingTime / 1000; 98 tv.tv_usec = (remainingTime % 1000) * 1000; 99 100 /* Break out if no time left */ 101 if (remainingTime <= 0) 102 return WIN_XEVENTS_SUCCESS; 103 104 /* Wait for an X event */ 105 iReturn = select(iConnNumber + 1, /* Highest fds number */ 106 &fdsRead, /* Read mask */ 107 NULL, /* No write mask */ 108 NULL, /* No exception mask */ 109 &tv); /* Timeout */ 110 if (iReturn < 0) { 111 ErrorF("winProcessXEventsTimeout - Call to select () failed: %d. " 112 "Bailing.\n", iReturn); 113 break; 114 } 115 116 if (!FD_ISSET(iConnNumber, &fdsRead)) { 117 winDebug("winProcessXEventsTimeout - Spurious wake, select() returned %d\n", iReturn); 118 } 119 } 120 121 return WIN_XEVENTS_SUCCESS; 122} 123 124/* 125 * Process a given Windows message 126 */ 127 128LRESULT CALLBACK 129winClipboardWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 130{ 131 static xcb_connection_t *conn; 132 static xcb_window_t iWindow; 133 static ClipboardAtoms *atoms; 134 static BOOL fRunning; 135 136 /* Branch on message type */ 137 switch (message) { 138 case WM_DESTROY: 139 { 140 winDebug("winClipboardWindowProc - WM_DESTROY\n"); 141 142 /* Remove clipboard listener */ 143 RemoveClipboardFormatListener(hwnd); 144 } 145 return 0; 146 147 case WM_WM_QUIT: 148 { 149 winDebug("winClipboardWindowProc - WM_WM_QUIT\n"); 150 fRunning = FALSE; 151 PostQuitMessage(0); 152 } 153 return 0; 154 155 case WM_CREATE: 156 { 157 ClipboardWindowCreationParams *cwcp = (ClipboardWindowCreationParams *)((CREATESTRUCT *)lParam)->lpCreateParams; 158 159 winDebug("winClipboardWindowProc - WM_CREATE\n"); 160 161 conn = cwcp->pClipboardDisplay; 162 iWindow = cwcp->iClipboardWindow; 163 atoms = cwcp->atoms; 164 fRunning = TRUE; 165 166 AddClipboardFormatListener(hwnd); 167 } 168 return 0; 169 170 case WM_CLIPBOARDUPDATE: 171 { 172 xcb_generic_error_t *error; 173 xcb_void_cookie_t cookie_set; 174 175 winDebug("winClipboardWindowProc - WM_CLIPBOARDUPDATE: Enter\n"); 176 177 /* 178 * NOTE: We cannot bail out when NULL == GetClipboardOwner () 179 * because some applications deal with the clipboard in a manner 180 * that causes the clipboard owner to be NULL when they are in 181 * fact taking ownership. One example of this is the Win32 182 * native compile of emacs. 183 */ 184 185 /* Bail when we still own the clipboard */ 186 if (hwnd == GetClipboardOwner()) { 187 188 winDebug("winClipboardWindowProc - WM_CLIPBOARDUPDATE - " 189 "We own the clipboard, returning.\n"); 190 winDebug("winClipboardWindowProc - WM_CLIPBOARDUPDATE: Exit\n"); 191 192 return 0; 193 } 194 195 /* Bail when shutting down */ 196 if (!fRunning) 197 return 0; 198 199 /* 200 * Do not take ownership of the X11 selections when something 201 * other than CF_TEXT or CF_UNICODETEXT has been copied 202 * into the Win32 clipboard. 203 */ 204 if (!IsClipboardFormatAvailable(CF_TEXT) 205 && !IsClipboardFormatAvailable(CF_UNICODETEXT)) { 206 207 xcb_get_selection_owner_cookie_t cookie_get; 208 xcb_get_selection_owner_reply_t *reply; 209 210 winDebug("winClipboardWindowProc - WM_CLIPBOARDUPDATE - " 211 "Clipboard does not contain CF_TEXT nor " 212 "CF_UNICODETEXT.\n"); 213 214 /* 215 * We need to make sure that the X Server has processed 216 * previous XSetSelectionOwner messages. 217 */ 218 xcb_aux_sync(conn); 219 220 winDebug("winClipboardWindowProc - XSync done.\n"); 221 222 /* Release PRIMARY selection if owned */ 223 cookie_get = xcb_get_selection_owner(conn, XCB_ATOM_PRIMARY); 224 reply = xcb_get_selection_owner_reply(conn, cookie_get, NULL); 225 if (reply) { 226 if (reply->owner == iWindow) { 227 winDebug("winClipboardWindowProc - WM_CLIPBOARDUPDATE - " 228 "PRIMARY selection is owned by us, releasing.\n"); 229 xcb_set_selection_owner(conn, XCB_NONE, XCB_ATOM_PRIMARY, XCB_CURRENT_TIME); 230 } 231 free(reply); 232 } 233 234 /* Release CLIPBOARD selection if owned */ 235 cookie_get = xcb_get_selection_owner(conn, atoms->atomClipboard); 236 reply = xcb_get_selection_owner_reply(conn, cookie_get, NULL); 237 if (reply) { 238 if (reply->owner == iWindow) { 239 winDebug("winClipboardWindowProc - WM_CLIPBOARDUPDATE - " 240 "CLIPBOARD selection is owned by us, releasing\n"); 241 xcb_set_selection_owner(conn, XCB_NONE, atoms->atomClipboard, XCB_CURRENT_TIME); 242 } 243 free(reply); 244 } 245 246 winDebug("winClipboardWindowProc - WM_CLIPBOARDUPDATE: Exit\n"); 247 248 return 0; 249 } 250 251 /* Reassert ownership of PRIMARY */ 252 cookie_set = xcb_set_selection_owner_checked(conn, iWindow, XCB_ATOM_PRIMARY, XCB_CURRENT_TIME); 253 error = xcb_request_check(conn, cookie_set); 254 if (error) { 255 ErrorF("winClipboardWindowProc - WM_CLIPBOARDUPDATE - " 256 "Could not reassert ownership of PRIMARY\n"); 257 free(error); 258 } else { 259 winDebug("winClipboardWindowProc - WM_CLIPBOARDUPDATE - " 260 "Reasserted ownership of PRIMARY\n"); 261 } 262 263 /* Reassert ownership of the CLIPBOARD */ 264 cookie_set = xcb_set_selection_owner_checked(conn, iWindow, atoms->atomClipboard, XCB_CURRENT_TIME); 265 error = xcb_request_check(conn, cookie_set); 266 if (error) { 267 ErrorF("winClipboardWindowProc - WM_CLIPBOARDUPDATE - " 268 "Could not reassert ownership of CLIPBOARD\n"); 269 free(error); 270 } 271 else { 272 winDebug("winClipboardWindowProc - WM_CLIPBOARDUPDATE - " 273 "Reasserted ownership of CLIPBOARD\n"); 274 } 275 276 /* Flush the pending SetSelectionOwner event now */ 277 xcb_flush(conn); 278 } 279 winDebug("winClipboardWindowProc - WM_CLIPBOARDUPDATE: Exit\n"); 280 return 0; 281 282 case WM_DESTROYCLIPBOARD: 283 /* 284 * NOTE: Intentionally do nothing. 285 * Changes in the Win32 clipboard are handled by WM_CLIPBOARDUPDATE 286 * above. We only process this message to conform to the specs 287 * for delayed clipboard rendering in Win32. You might think 288 * that we need to release ownership of the X11 selections, but 289 * we do not, because a WM_CLIPBOARDUPDATE message will closely 290 * follow this message and reassert ownership of the X11 291 * selections, handling the issue for us. 292 */ 293 winDebug("winClipboardWindowProc - WM_DESTROYCLIPBOARD - Ignored.\n"); 294 return 0; 295 296 case WM_RENDERALLFORMATS: 297 winDebug("winClipboardWindowProc - WM_RENDERALLFORMATS - Hello.\n"); 298 299 /* 300 WM_RENDERALLFORMATS is sent as we are shutting down, to render the 301 clipboard so its contents remains available to other applications. 302 303 Unfortunately, this can't work without major changes. The server is 304 already waiting for us to stop, so we can't ask for the rendering of 305 clipboard text now. 306 */ 307 308 return 0; 309 310 case WM_RENDERFORMAT: 311 { 312 int iReturn; 313 BOOL pasted = FALSE; 314 xcb_atom_t selection; 315 ClipboardConversionData data; 316 int best_target = 0; 317 318 winDebug("winClipboardWindowProc - WM_RENDERFORMAT %d - Hello.\n", 319 (int)wParam); 320 321 selection = winClipboardGetLastOwnedSelectionAtom(atoms); 322 if (selection == XCB_NONE) { 323 ErrorF("winClipboardWindowProc - no monitored selection is owned\n"); 324 goto fake_paste; 325 } 326 327 winDebug("winClipboardWindowProc - requesting targets for selection from owner\n"); 328 329 /* Request the selection's supported conversion targets */ 330 xcb_convert_selection(conn, iWindow, selection, atoms->atomTargets, 331 atoms->atomLocalProperty, XCB_CURRENT_TIME); 332 333 /* Process X events */ 334 data.incr = NULL; 335 data.incrsize = 0; 336 337 iReturn = winProcessXEventsTimeout(hwnd, 338 iWindow, 339 conn, 340 &data, 341 atoms, 342 WIN_POLL_TIMEOUT); 343 344 if (WIN_XEVENTS_NOTIFY_TARGETS != iReturn) { 345 ErrorF 346 ("winClipboardWindowProc - timed out waiting for WIN_XEVENTS_NOTIFY_TARGETS\n"); 347 goto fake_paste; 348 } 349 350 /* Choose the most preferred target */ 351 { 352 struct target_priority 353 { 354 xcb_atom_t target; 355 unsigned int priority; 356 }; 357 358 struct target_priority target_priority_table[] = 359 { 360 { atoms->atomUTF8String, 0 }, 361 // { atoms->atomCompoundText, 1 }, not implemented (yet?) 362 { XCB_ATOM_STRING, 2 }, 363 }; 364 365 int best_priority = INT_MAX; 366 367 int i,j; 368 for (i = 0 ; data.targetList[i] != 0; i++) 369 { 370 for (j = 0; j < ARRAY_SIZE(target_priority_table); j ++) 371 { 372 if ((data.targetList[i] == target_priority_table[j].target) && 373 (target_priority_table[j].priority < best_priority)) 374 { 375 best_target = target_priority_table[j].target; 376 best_priority = target_priority_table[j].priority; 377 } 378 } 379 } 380 } 381 382 free(data.targetList); 383 data.targetList = 0; 384 385 winDebug("winClipboardWindowProc - best target is %d\n", best_target); 386 387 /* No useful targets found */ 388 if (best_target == 0) 389 goto fake_paste; 390 391 winDebug("winClipboardWindowProc - requesting selection from owner\n"); 392 393 /* Request the selection contents */ 394 xcb_convert_selection(conn, iWindow, selection, best_target, 395 atoms->atomLocalProperty, XCB_CURRENT_TIME); 396 397 /* Process X events */ 398 iReturn = winProcessXEventsTimeout(hwnd, 399 iWindow, 400 conn, 401 &data, 402 atoms, 403 WIN_POLL_TIMEOUT); 404 405 /* 406 * winProcessXEventsTimeout had better have seen a notify event, 407 * or else we are dealing with a buggy or old X11 app. 408 */ 409 if (WIN_XEVENTS_NOTIFY_DATA != iReturn) { 410 ErrorF 411 ("winClipboardWindowProc - timed out waiting for WIN_XEVENTS_NOTIFY_DATA\n"); 412 } 413 else { 414 pasted = TRUE; 415 } 416 417 /* 418 * If we couldn't get the data from the X clipboard, we 419 * have to paste some fake data to the Win32 clipboard to 420 * satisfy the requirement that we write something to it. 421 */ 422 fake_paste: 423 if (!pasted) 424 { 425 /* Paste no data, to satisfy required call to SetClipboardData */ 426 SetClipboardData(CF_UNICODETEXT, NULL); 427 SetClipboardData(CF_TEXT, NULL); 428 } 429 430 winDebug("winClipboardWindowProc - WM_RENDERFORMAT - Returning.\n"); 431 return 0; 432 } 433 } 434 435 /* Let Windows perform default processing for unhandled messages */ 436 return DefWindowProc(hwnd, message, wParam, lParam); 437} 438 439/* 440 * Process any pending Windows messages 441 */ 442 443BOOL 444winClipboardFlushWindowsMessageQueue(HWND hwnd) 445{ 446 MSG msg; 447 448 /* Flush the messaging window queue */ 449 /* NOTE: Do not pass the hwnd of our messaging window to PeekMessage, 450 * as this will filter out many non-window-specific messages that 451 * are sent to our thread, such as WM_QUIT. 452 */ 453 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { 454 /* Dispatch the message if not WM_QUIT */ 455 if (msg.message == WM_QUIT) 456 return FALSE; 457 else 458 DispatchMessage(&msg); 459 } 460 461 return TRUE; 462} 463