2 *Copyright (C) 2003-2004 Harold L Hunt II All Rights Reserved.
3 *Copyright (C) Colin Harrison 2005-2008
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:
13 *The above copyright notice and this permission notice shall be
14 *included in all copies or substantial portions of the Software.
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.
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).
29 * Authors: Harold L Hunt II
33 #ifdef HAVE_XWIN_CONFIG_H
34 #include <xwin-config.h>
36 #include <sys/types.h>
38 #include "winclipboard.h"
45 #define WIN_POLL_TIMEOUT 1
48 * References to external symbols
51 extern void *g_pClipboardDisplay
;
52 extern Window g_iClipboardWindow
;
53 extern Atom g_atomLastOwnedSelection
;
56 * Process X events up to specified timeout
60 winProcessXEventsTimeout(HWND hwnd
, int iWindow
, Display
* pDisplay
,
61 Bool fUseUnicode
, int iTimeoutSec
)
66 DWORD dwStopTime
= GetTickCount() + iTimeoutSec
* 1000;
68 winDebug("winProcessXEventsTimeout () - pumping X events for %d seconds\n",
71 /* Get our connection number */
72 iConnNumber
= ConnectionNumber(pDisplay
);
74 /* Loop for X events */
79 /* We need to ensure that all pending events are processed */
80 XSync(pDisplay
, FALSE
);
82 /* Setup the file descriptor set */
84 FD_SET(iConnNumber
, &fdsRead
);
87 remainingTime
= dwStopTime
- GetTickCount();
88 tv
.tv_sec
= remainingTime
/ 1000;
89 tv
.tv_usec
= (remainingTime
% 1000) * 1000;
90 winDebug("winProcessXEventsTimeout () - %d milliseconds left\n",
93 /* Break out if no time left */
94 if (remainingTime
<= 0)
95 return WIN_XEVENTS_SUCCESS
;
97 /* Wait for an X event */
98 iReturn
= select(iConnNumber
+ 1, /* Highest fds number */
99 &fdsRead
, /* Read mask */
100 NULL
, /* No write mask */
101 NULL
, /* No exception mask */
104 ErrorF("winProcessXEventsTimeout - Call to select () failed: %d. "
105 "Bailing.\n", iReturn
);
109 /* Branch on which descriptor became active */
110 if (FD_ISSET(iConnNumber
, &fdsRead
)) {
111 /* Process X events */
112 /* Exit when we see that server is shutting down */
113 iReturn
= winClipboardFlushXEvents(hwnd
,
114 iWindow
, pDisplay
, fUseUnicode
);
117 ("winProcessXEventsTimeout () - winClipboardFlushXEvents returned %d\n",
120 if (WIN_XEVENTS_NOTIFY
== iReturn
) {
121 /* Bail out if notify processed */
126 winDebug("winProcessXEventsTimeout - Spurious wake\n");
130 return WIN_XEVENTS_SUCCESS
;
134 * Process a given Windows message
138 winClipboardWindowProc(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
140 static HWND s_hwndNextViewer
;
141 static Bool s_fCBCInitialized
;
143 /* Branch on message type */
147 winDebug("winClipboardWindowProc - WM_DESTROY\n");
149 /* Remove ourselves from the clipboard chain */
150 ChangeClipboardChain(hwnd
, s_hwndNextViewer
);
152 s_hwndNextViewer
= NULL
;
161 DWORD error_code
= 0;
163 winDebug("winClipboardWindowProc - WM_CREATE\n");
165 first
= GetClipboardViewer(); /* Get handle to first viewer in chain. */
167 return 0; /* Make sure it's not us! */
168 /* Add ourselves to the clipboard viewer chain */
169 next
= SetClipboardViewer(hwnd
);
170 error_code
= GetLastError();
171 if (SUCCEEDED(error_code
) && (next
== first
)) /* SetClipboardViewer must have succeeded, and the handle */
172 s_hwndNextViewer
= next
; /* it returned must have been the first window in the chain */
174 s_fCBCInitialized
= FALSE
;
178 case WM_CHANGECBCHAIN
:
180 winDebug("winClipboardWindowProc - WM_CHANGECBCHAIN: wParam(%x) "
181 "lParam(%x) s_hwndNextViewer(%x)\n",
182 wParam
, lParam
, s_hwndNextViewer
);
184 if ((HWND
) wParam
== s_hwndNextViewer
) {
185 s_hwndNextViewer
= (HWND
) lParam
;
186 if (s_hwndNextViewer
== hwnd
) {
187 s_hwndNextViewer
= NULL
;
188 winErrorFVerb(1, "winClipboardWindowProc - WM_CHANGECBCHAIN: "
189 "attempted to set next window to ourselves.");
192 else if (s_hwndNextViewer
)
193 SendMessage(s_hwndNextViewer
, message
, wParam
, lParam
);
196 winDebug("winClipboardWindowProc - WM_CHANGECBCHAIN: Exit\n");
201 /* Ensure that we're in the clipboard chain. Some apps,
202 * WinXP's remote desktop for one, don't play nice with the
203 * chain. This message is called whenever we receive a
204 * WM_ACTIVATEAPP message to ensure that we continue to
205 * receive clipboard messages.
207 * It might be possible to detect if we're still in the chain
208 * by calling SendMessage (GetClipboardViewer(),
209 * WM_DRAWCLIPBOARD, 0, 0); and then seeing if we get the
210 * WM_DRAWCLIPBOARD message. That, however, might be more
211 * expensive than just putting ourselves back into the chain.
215 DWORD error_code
= 0;
217 winDebug("winClipboardWindowProc - WM_WM_REINIT: Enter\n");
219 first
= GetClipboardViewer(); /* Get handle to first viewer in chain. */
221 return 0; /* Make sure it's not us! */
222 winDebug(" WM_WM_REINIT: Replacing us(%x) with %x at head "
223 "of chain\n", hwnd
, s_hwndNextViewer
);
224 s_fCBCInitialized
= FALSE
;
225 ChangeClipboardChain(hwnd
, s_hwndNextViewer
);
226 s_hwndNextViewer
= NULL
;
227 s_fCBCInitialized
= FALSE
;
228 winDebug(" WM_WM_REINIT: Putting us back at head of chain.\n");
229 first
= GetClipboardViewer(); /* Get handle to first viewer in chain. */
231 return 0; /* Make sure it's not us! */
232 next
= SetClipboardViewer(hwnd
);
233 error_code
= GetLastError();
234 if (SUCCEEDED(error_code
) && (next
== first
)) /* SetClipboardViewer must have succeeded, and the handle */
235 s_hwndNextViewer
= next
; /* it returned must have been the first window in the chain */
237 s_fCBCInitialized
= FALSE
;
239 winDebug("winClipboardWindowProc - WM_WM_REINIT: Exit\n");
242 case WM_DRAWCLIPBOARD
:
244 static Atom atomClipboard
;
245 static int generation
;
246 static Bool s_fProcessingDrawClipboard
= FALSE
;
247 Display
*pDisplay
= g_pClipboardDisplay
;
248 Window iWindow
= g_iClipboardWindow
;
251 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD: Enter\n");
253 if (generation
!= serverGeneration
) {
254 generation
= serverGeneration
;
255 atomClipboard
= XInternAtom(pDisplay
, "CLIPBOARD", False
);
259 * We've occasionally seen a loop in the clipboard chain.
260 * Try and fix it on the first hint of recursion.
262 if (!s_fProcessingDrawClipboard
) {
263 s_fProcessingDrawClipboard
= TRUE
;
266 /* Attempt to break the nesting by getting out of the chain, twice?, and then fix and bail */
267 s_fCBCInitialized
= FALSE
;
268 ChangeClipboardChain(hwnd
, s_hwndNextViewer
);
269 winFixClipboardChain();
270 winErrorFVerb(1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
271 "Nested calls detected. Re-initing.\n");
272 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
273 s_fProcessingDrawClipboard
= FALSE
;
277 /* Bail on first message */
278 if (!s_fCBCInitialized
) {
279 s_fCBCInitialized
= TRUE
;
280 s_fProcessingDrawClipboard
= FALSE
;
281 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
286 * NOTE: We cannot bail out when NULL == GetClipboardOwner ()
287 * because some applications deal with the clipboard in a manner
288 * that causes the clipboard owner to be NULL when they are in
289 * fact taking ownership. One example of this is the Win32
290 * native compile of emacs.
293 /* Bail when we still own the clipboard */
294 if (hwnd
== GetClipboardOwner()) {
296 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
297 "We own the clipboard, returning.\n");
298 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
299 s_fProcessingDrawClipboard
= FALSE
;
300 if (s_hwndNextViewer
)
301 SendMessage(s_hwndNextViewer
, message
, wParam
, lParam
);
306 * Do not take ownership of the X11 selections when something
307 * other than CF_TEXT or CF_UNICODETEXT has been copied
308 * into the Win32 clipboard.
310 if (!IsClipboardFormatAvailable(CF_TEXT
)
311 && !IsClipboardFormatAvailable(CF_UNICODETEXT
)) {
313 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
314 "Clipboard does not contain CF_TEXT nor "
315 "CF_UNICODETEXT.\n");
318 * We need to make sure that the X Server has processed
319 * previous XSetSelectionOwner messages.
321 XSync(pDisplay
, FALSE
);
323 winDebug("winClipboardWindowProc - XSync done.\n");
325 /* Release PRIMARY selection if owned */
326 iReturn
= XGetSelectionOwner(pDisplay
, XA_PRIMARY
);
327 if (iReturn
== g_iClipboardWindow
) {
328 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
329 "PRIMARY selection is owned by us.\n");
330 XSetSelectionOwner(pDisplay
, XA_PRIMARY
, None
, CurrentTime
);
332 else if (BadWindow
== iReturn
|| BadAtom
== iReturn
)
333 winErrorFVerb(1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
334 "XGetSelection failed for PRIMARY: %d\n",
337 /* Release CLIPBOARD selection if owned */
338 iReturn
= XGetSelectionOwner(pDisplay
, atomClipboard
);
339 if (iReturn
== g_iClipboardWindow
) {
340 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
341 "CLIPBOARD selection is owned by us.\n");
342 XSetSelectionOwner(pDisplay
, atomClipboard
, None
, CurrentTime
);
344 else if (BadWindow
== iReturn
|| BadAtom
== iReturn
)
345 winErrorFVerb(1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
346 "XGetSelection failed for CLIPBOARD: %d\n",
349 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
350 s_fProcessingDrawClipboard
= FALSE
;
351 if (s_hwndNextViewer
)
352 SendMessage(s_hwndNextViewer
, message
, wParam
, lParam
);
356 /* Reassert ownership of PRIMARY */
357 iReturn
= XSetSelectionOwner(pDisplay
,
358 XA_PRIMARY
, iWindow
, CurrentTime
);
359 if (iReturn
== BadAtom
|| iReturn
== BadWindow
||
360 XGetSelectionOwner(pDisplay
, XA_PRIMARY
) != iWindow
) {
361 winErrorFVerb(1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
362 "Could not reassert ownership of PRIMARY\n");
365 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
366 "Reasserted ownership of PRIMARY\n");
369 /* Reassert ownership of the CLIPBOARD */
370 iReturn
= XSetSelectionOwner(pDisplay
,
371 atomClipboard
, iWindow
, CurrentTime
);
373 if (iReturn
== BadAtom
|| iReturn
== BadWindow
||
374 XGetSelectionOwner(pDisplay
, atomClipboard
) != iWindow
) {
375 winErrorFVerb(1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
376 "Could not reassert ownership of CLIPBOARD\n");
379 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
380 "Reasserted ownership of CLIPBOARD\n");
383 /* Flush the pending SetSelectionOwner event now */
386 s_fProcessingDrawClipboard
= FALSE
;
388 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
389 /* Pass the message on the next window in the clipboard viewer chain */
390 if (s_hwndNextViewer
)
391 SendMessage(s_hwndNextViewer
, message
, wParam
, lParam
);
394 case WM_DESTROYCLIPBOARD
:
396 * NOTE: Intentionally do nothing.
397 * Changes in the Win32 clipboard are handled by WM_DRAWCLIPBOARD
398 * above. We only process this message to conform to the specs
399 * for delayed clipboard rendering in Win32. You might think
400 * that we need to release ownership of the X11 selections, but
401 * we do not, because a WM_DRAWCLIPBOARD message will closely
402 * follow this message and reassert ownership of the X11
403 * selections, handling the issue for us.
405 winDebug("winClipboardWindowProc - WM_DESTROYCLIPBOARD - Ignored.\n");
408 case WM_RENDERFORMAT
:
409 case WM_RENDERALLFORMATS
:
412 Display
*pDisplay
= g_pClipboardDisplay
;
413 Window iWindow
= g_iClipboardWindow
;
414 Bool fConvertToUnicode
;
416 winDebug("winClipboardWindowProc - WM_RENDER*FORMAT - Hello.\n");
418 /* Flag whether to convert to Unicode or not */
419 if (message
== WM_RENDERALLFORMATS
)
420 fConvertToUnicode
= FALSE
;
422 fConvertToUnicode
= (CF_UNICODETEXT
== wParam
);
424 /* Request the selection contents */
425 iReturn
= XConvertSelection(pDisplay
,
426 g_atomLastOwnedSelection
,
427 XInternAtom(pDisplay
,
428 "COMPOUND_TEXT", False
),
429 XInternAtom(pDisplay
,
430 "CYGX_CUT_BUFFER", False
),
431 iWindow
, CurrentTime
);
432 if (iReturn
== BadAtom
|| iReturn
== BadWindow
) {
433 winErrorFVerb(1, "winClipboardWindowProc - WM_RENDER*FORMAT - "
434 "XConvertSelection () failed\n");
438 /* Special handling for WM_RENDERALLFORMATS */
439 if (message
== WM_RENDERALLFORMATS
) {
440 /* We must open and empty the clipboard */
442 /* Close clipboard if we have it open already */
443 if (GetOpenClipboardWindow() == hwnd
) {
447 if (!OpenClipboard(hwnd
)) {
448 winErrorFVerb(1, "winClipboardWindowProc - WM_RENDER*FORMATS - "
449 "OpenClipboard () failed: %08x\n",
454 if (!EmptyClipboard()) {
455 winErrorFVerb(1, "winClipboardWindowProc - WM_RENDER*FORMATS - "
456 "EmptyClipboard () failed: %08x\n",
462 /* Process the SelectionNotify event */
463 iReturn
= winProcessXEventsTimeout(hwnd
,
466 fConvertToUnicode
, WIN_POLL_TIMEOUT
);
469 * The last call to winProcessXEventsTimeout
470 * from above had better have seen a notify event, or else we
471 * are dealing with a buggy or old X11 app. In these cases we
472 * have to paste some fake data to the Win32 clipboard to
473 * satisfy the requirement that we write something to it.
475 if (WIN_XEVENTS_NOTIFY
!= iReturn
) {
476 /* Paste no data, to satisfy required call to SetClipboardData */
477 SetClipboardData(CF_UNICODETEXT
, NULL
);
478 SetClipboardData(CF_TEXT
, NULL
);
481 ("winClipboardWindowProc - timed out waiting for WIN_XEVENTS_NOTIFY\n");
484 /* Special handling for WM_RENDERALLFORMATS */
485 if (message
== WM_RENDERALLFORMATS
) {
486 /* We must close the clipboard */
488 if (!CloseClipboard()) {
490 "winClipboardWindowProc - WM_RENDERALLFORMATS - "
491 "CloseClipboard () failed: %08x\n",
497 winDebug("winClipboardWindowProc - WM_RENDER*FORMAT - Returning.\n");
502 /* Let Windows perform default processing for unhandled messages */
503 return DefWindowProc(hwnd
, message
, wParam
, lParam
);
507 * Process any pending Windows messages
511 winClipboardFlushWindowsMessageQueue(HWND hwnd
)
515 /* Flush the messaging window queue */
516 /* NOTE: Do not pass the hwnd of our messaging window to PeekMessage,
517 * as this will filter out many non-window-specific messages that
518 * are sent to our thread, such as WM_QUIT.
520 while (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
)) {
521 /* Dispatch the message if not WM_QUIT */
522 if (msg
.message
== WM_QUIT
)
525 DispatchMessage(&msg
);