2 *Copyright (C) 2003-2004 Harold L Hunt II All Rights Reserved.
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:
12 *The above copyright notice and this permission notice shall be
13 *included in all copies or substantial portions of the Software.
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.
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.
28 * Authors: Harold L Hunt II
31 #ifdef HAVE_XWIN_CONFIG_H
32 #include <xwin-config.h>
34 #include <sys/types.h>
36 #include "winclipboard.h"
38 extern void winFixClipboardChain();
45 #define WIN_CLIPBOARD_PROP "cyg_clipboard_prop"
46 #define WIN_POLL_TIMEOUT 1
50 * References to external symbols
53 extern Bool g_fUseUnicode
;
54 extern Bool g_fUnicodeSupport
;
55 extern void *g_pClipboardDisplay
;
56 extern Window g_iClipboardWindow
;
57 extern Atom g_atomLastOwnedSelection
;
59 /* BPS - g_hwndClipboard needed for X app->Windows paste fix */
60 extern HWND g_hwndClipboard
;
63 * Local function prototypes
67 winProcessXEventsTimeout (HWND hwnd
, int iWindow
, Display
*pDisplay
,
68 Bool fUseUnicode
, int iTimeoutSec
);
72 * Process X events up to specified timeout
76 winProcessXEventsTimeout (HWND hwnd
, int iWindow
, Display
*pDisplay
,
77 Bool fUseUnicode
, int iTimeoutSec
)
82 DWORD dwStopTime
= (GetTickCount () / 1000) + iTimeoutSec
;
84 /* We need to ensure that all pending events are processed */
85 XSync (pDisplay
, FALSE
);
87 /* Get our connection number */
88 iConnNumber
= ConnectionNumber (pDisplay
);
90 /* Loop for X events */
95 /* Setup the file descriptor set */
97 FD_SET (iConnNumber
, &fdsRead
);
100 tv
.tv_sec
= dwStopTime
- (GetTickCount () / 1000);
103 /* Break out if no time left */
105 return WIN_XEVENTS_SUCCESS
;
107 /* Wait for a Windows event or an X event */
108 iReturn
= select (iConnNumber
+ 1,/* Highest fds number */
109 &fdsRead
, /* Read mask */
110 NULL
, /* No write mask */
111 NULL
, /* No exception mask */
112 &tv
); /* No timeout */
115 ErrorF ("winProcessXEventsTimeout - Call to select () failed: %d. "
116 "Bailing.\n", iReturn
);
120 /* Branch on which descriptor became active */
121 if (FD_ISSET (iConnNumber
, &fdsRead
))
123 /* Process X events */
124 /* Exit when we see that server is shutting down */
125 iReturn
= winClipboardFlushXEvents (hwnd
,
129 if (WIN_XEVENTS_NOTIFY
== iReturn
130 || WIN_XEVENTS_CONVERT
== iReturn
)
132 /* Bail out if convert or notify processed */
138 return WIN_XEVENTS_SUCCESS
;
143 * Process a given Windows message
146 /* BPS - Define our own message, which we'll post to ourselves to facilitate
147 * resetting the delayed rendering mechanism after each paste from X app to
148 * Windows app. TODO - Perhaps move to win.h with the other WM_USER messages.
150 #define WM_USER_PASTE_COMPLETE (WM_USER + 1003)
153 winClipboardWindowProc (HWND hwnd
, UINT message
,
154 WPARAM wParam
, LPARAM lParam
)
156 static HWND s_hwndNextViewer
;
157 static Bool s_fCBCInitialized
;
159 /* Branch on message type */
164 winDebug ("winClipboardWindowProc - WM_DESTROY\n");
166 /* Remove ourselves from the clipboard chain */
167 ChangeClipboardChain (hwnd
, s_hwndNextViewer
);
169 s_hwndNextViewer
= NULL
;
179 DWORD error_code
= 0;
180 winDebug ("winClipboardWindowProc - WM_CREATE\n");
182 first
= GetClipboardViewer(); /* Get handle to first viewer in chain. */
183 if (first
== hwnd
) return 0; /* Make sure it's not us! */
184 /* Add ourselves to the clipboard viewer chain */
185 next
= SetClipboardViewer (hwnd
);
186 error_code
= GetLastError();
187 if (SUCCEEDED(error_code
) && (next
== first
)) /* SetClipboardViewer must have succeeded, and the handle */
188 s_hwndNextViewer
= next
; /* it returned must have been the first window in the chain */
190 s_fCBCInitialized
= FALSE
;
195 case WM_CHANGECBCHAIN
:
197 winDebug ("winClipboardWindowProc - WM_CHANGECBCHAIN: wParam(%x) "
198 "lParam(%x) s_hwndNextViewer(%x)\n",
199 wParam
, lParam
, s_hwndNextViewer
);
201 if ((HWND
) wParam
== s_hwndNextViewer
)
203 s_hwndNextViewer
= (HWND
) lParam
;
204 if (s_hwndNextViewer
== hwnd
)
206 s_hwndNextViewer
= NULL
;
207 winErrorFVerb (1, "winClipboardWindowProc - WM_CHANGECBCHAIN: "
208 "attempted to set next window to ourselves.");
211 else if (s_hwndNextViewer
)
212 SendMessage (s_hwndNextViewer
, message
,
216 winDebug ("winClipboardWindowProc - WM_CHANGECBCHAIN: Exit\n");
221 /* Ensure that we're in the clipboard chain. Some apps,
222 * WinXP's remote desktop for one, don't play nice with the
223 * chain. This message is called whenever we receive a
224 * WM_ACTIVATEAPP message to ensure that we continue to
225 * receive clipboard messages.
227 * It might be possible to detect if we're still in the chain
228 * by calling SendMessage (GetClipboardViewer(),
229 * WM_DRAWCLIPBOARD, 0, 0); and then seeing if we get the
230 * WM_DRAWCLIPBOARD message. That, however, might be more
231 * expensive than just putting ourselves back into the chain.
235 DWORD error_code
= 0;
236 winDebug ("winClipboardWindowProc - WM_WM_REINIT: Enter\n");
238 first
= GetClipboardViewer(); /* Get handle to first viewer in chain. */
239 if (first
== hwnd
) return 0; /* Make sure it's not us! */
240 winDebug (" WM_WM_REINIT: Replacing us(%x) with %x at head "
241 "of chain\n", hwnd
, s_hwndNextViewer
);
242 s_fCBCInitialized
= FALSE
;
243 ChangeClipboardChain (hwnd
, s_hwndNextViewer
);
244 s_hwndNextViewer
= NULL
;
245 s_fCBCInitialized
= FALSE
;
246 winDebug (" WM_WM_REINIT: Putting us back at head of chain.\n");
247 first
= GetClipboardViewer(); /* Get handle to first viewer in chain. */
248 if (first
== hwnd
) return 0; /* Make sure it's not us! */
249 next
= SetClipboardViewer (hwnd
);
250 error_code
= GetLastError();
251 if (SUCCEEDED(error_code
) && (next
== first
)) /* SetClipboardViewer must have succeeded, and the handle */
252 s_hwndNextViewer
= next
; /* it returned must have been the first window in the chain */
254 s_fCBCInitialized
= FALSE
;
256 winDebug ("winClipboardWindowProc - WM_WM_REINIT: Exit\n");
260 case WM_DRAWCLIPBOARD
:
262 static Bool s_fProcessingDrawClipboard
= FALSE
;
263 Display
*pDisplay
= g_pClipboardDisplay
;
264 Window iWindow
= g_iClipboardWindow
;
267 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Enter\n");
270 * We've occasionally seen a loop in the clipboard chain.
271 * Try and fix it on the first hint of recursion.
273 if (! s_fProcessingDrawClipboard
)
275 s_fProcessingDrawClipboard
= TRUE
;
279 /* Attempt to break the nesting by getting out of the chain, twice?, and then fix and bail */
280 s_fCBCInitialized
= FALSE
;
281 ChangeClipboardChain (hwnd
, s_hwndNextViewer
);
282 winFixClipboardChain();
283 winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
284 "Nested calls detected. Re-initing.\n");
285 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
286 s_fProcessingDrawClipboard
= FALSE
;
290 /* Bail on first message */
291 if (!s_fCBCInitialized
)
293 s_fCBCInitialized
= TRUE
;
294 s_fProcessingDrawClipboard
= FALSE
;
295 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
300 * NOTE: We cannot bail out when NULL == GetClipboardOwner ()
301 * because some applications deal with the clipboard in a manner
302 * that causes the clipboard owner to be NULL when they are in
303 * fact taking ownership. One example of this is the Win32
304 * native compile of emacs.
307 /* Bail when we still own the clipboard */
308 if (hwnd
== GetClipboardOwner ())
311 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
312 "We own the clipboard, returning.\n");
313 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
314 s_fProcessingDrawClipboard
= FALSE
;
315 if (s_hwndNextViewer
)
316 SendMessage (s_hwndNextViewer
, message
, wParam
, lParam
);
321 * Do not take ownership of the X11 selections when something
322 * other than CF_TEXT or CF_UNICODETEXT has been copied
323 * into the Win32 clipboard.
325 if (!IsClipboardFormatAvailable (CF_TEXT
)
326 && !IsClipboardFormatAvailable (CF_UNICODETEXT
))
329 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
330 "Clipboard does not contain CF_TEXT nor "
331 "CF_UNICODETEXT.\n");
334 * We need to make sure that the X Server has processed
335 * previous XSetSelectionOwner messages.
337 XSync (pDisplay
, FALSE
);
339 /* Release PRIMARY selection if owned */
340 iReturn
= XGetSelectionOwner (pDisplay
, XA_PRIMARY
);
341 if (iReturn
== g_iClipboardWindow
)
343 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
344 "PRIMARY selection is owned by us.\n");
345 XSetSelectionOwner (pDisplay
,
350 else if (BadWindow
== iReturn
|| BadAtom
== iReturn
)
351 winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
352 "XGetSelection failed for PRIMARY: %d\n", iReturn
);
354 /* Release CLIPBOARD selection if owned */
355 iReturn
= XGetSelectionOwner (pDisplay
,
356 XInternAtom (pDisplay
,
359 if (iReturn
== g_iClipboardWindow
)
361 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
362 "CLIPBOARD selection is owned by us.\n");
363 XSetSelectionOwner (pDisplay
,
364 XInternAtom (pDisplay
,
370 else if (BadWindow
== iReturn
|| BadAtom
== iReturn
)
371 winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
372 "XGetSelection failed for CLIPBOARD: %d\n", iReturn
);
374 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
375 s_fProcessingDrawClipboard
= FALSE
;
376 if (s_hwndNextViewer
)
377 SendMessage (s_hwndNextViewer
, message
, wParam
, lParam
);
381 /* Reassert ownership of PRIMARY */
382 iReturn
= XSetSelectionOwner (pDisplay
,
386 if (iReturn
== BadAtom
|| iReturn
== BadWindow
)
388 winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
389 "Could not reassert ownership of PRIMARY\n");
393 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
394 "Reasserted ownership of PRIMARY\n");
397 /* Reassert ownership of the CLIPBOARD */
398 iReturn
= XSetSelectionOwner (pDisplay
,
399 XInternAtom (pDisplay
,
404 if (iReturn
== BadAtom
|| iReturn
== BadWindow
)
406 winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
407 "Could not reassert ownership of CLIPBOARD\n");
411 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
412 "Reasserted ownership of CLIPBOARD\n");
415 /* Flush the pending SetSelectionOwner event now */
418 s_fProcessingDrawClipboard
= FALSE
;
420 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
421 /* Pass the message on the next window in the clipboard viewer chain */
422 if (s_hwndNextViewer
)
423 SendMessage (s_hwndNextViewer
, message
, wParam
, lParam
);
427 case WM_DESTROYCLIPBOARD
:
429 * NOTE: Intentionally do nothing.
430 * Changes in the Win32 clipboard are handled by WM_DRAWCLIPBOARD
431 * above. We only process this message to conform to the specs
432 * for delayed clipboard rendering in Win32. You might think
433 * that we need to release ownership of the X11 selections, but
434 * we do not, because a WM_DRAWCLIPBOARD message will closely
435 * follow this message and reassert ownership of the X11
436 * selections, handling the issue for us.
441 case WM_RENDERFORMAT
:
442 case WM_RENDERALLFORMATS
:
445 Display
*pDisplay
= g_pClipboardDisplay
;
446 Window iWindow
= g_iClipboardWindow
;
447 Bool fConvertToUnicode
;
449 winDebug ("winClipboardWindowProc - WM_RENDER*FORMAT - Hello.\n");
451 /* Flag whether to convert to Unicode or not */
452 if (message
== WM_RENDERALLFORMATS
)
453 fConvertToUnicode
= FALSE
;
455 fConvertToUnicode
= g_fUnicodeSupport
&& (CF_UNICODETEXT
== wParam
);
457 /* Request the selection contents */
458 iReturn
= XConvertSelection (pDisplay
,
459 g_atomLastOwnedSelection
,
460 XInternAtom (pDisplay
,
461 "COMPOUND_TEXT", False
),
462 XInternAtom (pDisplay
,
463 "CYGX_CUT_BUFFER", False
),
466 if (iReturn
== BadAtom
|| iReturn
== BadWindow
)
468 winErrorFVerb (1, "winClipboardWindowProc - WM_RENDER*FORMAT - "
469 "XConvertSelection () failed\n");
473 /* Special handling for WM_RENDERALLFORMATS */
474 if (message
== WM_RENDERALLFORMATS
)
476 /* We must open and empty the clipboard */
478 /* Close clipboard if we have it open already */
479 if (GetOpenClipboardWindow () == hwnd
)
484 if (!OpenClipboard (hwnd
))
486 winErrorFVerb (1, "winClipboardWindowProc - WM_RENDER*FORMATS - "
487 "OpenClipboard () failed: %08x\n",
492 if (!EmptyClipboard ())
494 winErrorFVerb (1, "winClipboardWindowProc - WM_RENDER*FORMATS - "
495 "EmptyClipboard () failed: %08x\n",
501 /* Process the SelectionNotify event */
502 iReturn
= winProcessXEventsTimeout (hwnd
,
507 if (WIN_XEVENTS_CONVERT
== iReturn
)
510 * The selection was offered for conversion first, so we have
511 * to process a second SelectionNotify event to get the actual
512 * data in the selection.
514 iReturn
= winProcessXEventsTimeout (hwnd
,
522 * The last of the up-to two calls to winProcessXEventsTimeout
523 * from above had better have seen a notify event, or else we
524 * are dealing with a buggy or old X11 app. In these cases we
525 * have to paste some fake data to the Win32 clipboard to
526 * satisfy the requirement that we write something to it.
528 if (WIN_XEVENTS_NOTIFY
!= iReturn
)
530 /* Paste no data, to satisfy required call to SetClipboardData */
531 if (g_fUnicodeSupport
)
532 SetClipboardData (CF_UNICODETEXT
, NULL
);
533 SetClipboardData (CF_TEXT
, NULL
);
536 /* BPS - Post ourselves a user message whose handler will reset the
537 * delayed rendering mechanism after the paste is complete. This is
538 * necessary because calling SetClipboardData() with a NULL argument
539 * here will cause the data we just put on the clipboard to be lost!
541 PostMessage(g_hwndClipboard
, WM_USER_PASTE_COMPLETE
, 0, 0);
543 /* Special handling for WM_RENDERALLFORMATS */
544 if (message
== WM_RENDERALLFORMATS
)
546 /* We must close the clipboard */
548 if (!CloseClipboard ())
550 winErrorFVerb (1, "winClipboardWindowProc - WM_RENDERALLFORMATS - "
551 "CloseClipboard () failed: %08x\n",
557 winDebug ("winClipboardWindowProc - WM_RENDER*FORMAT - Returning.\n");
560 /* BPS - This WM_USER message is posted by us. It gives us the opportunity
561 * to reset the delayed rendering mechanism after each and every paste
562 * from an X app to a Windows app. Without such a mechanism, subsequent
563 * changes of selection in the X app owning the selection are not
564 * reflected in pastes into Windows apps, since Windows won't send us the
565 * WM_RENDERFORMAT message unless someone has set changed data (or NULL)
566 * on the clipboard. */
567 case WM_USER_PASTE_COMPLETE
:
569 if (hwnd
!= GetClipboardOwner ())
570 /* In case we've lost the selection since posting the message */
572 winDebug ("winClipboardWindowProc - WM_USER_PASTE_COMPLETE\n");
574 /* Set up for another delayed rendering callback */
575 OpenClipboard (g_hwndClipboard
);
577 /* Take ownership of the Windows clipboard */
580 /* Advertise Unicode if we support it */
581 if (g_fUnicodeSupport
)
582 SetClipboardData (CF_UNICODETEXT
, NULL
);
584 /* Always advertise regular text */
585 SetClipboardData (CF_TEXT
, NULL
);
587 /* Release the clipboard */
593 /* Let Windows perform default processing for unhandled messages */
594 return DefWindowProc (hwnd
, message
, wParam
, lParam
);
599 * Process any pending Windows messages
603 winClipboardFlushWindowsMessageQueue (HWND hwnd
)
607 /* Flush the messaging window queue */
608 /* NOTE: Do not pass the hwnd of our messaging window to PeekMessage,
609 * as this will filter out many non-window-specific messages that
610 * are sent to our thread, such as WM_QUIT.
612 while (PeekMessage (&msg
, NULL
, 0, 0, PM_REMOVE
))
614 /* Dispatch the message if not WM_QUIT */
615 if (msg
.message
== WM_QUIT
)
618 DispatchMessage (&msg
);