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"
43
#define WIN_CLIPBOARD_PROP "cyg_clipboard_prop"
44
#define WIN_POLL_TIMEOUT 1
48
* References to external symbols
51
extern Bool g_fUseUnicode;
52
extern Bool g_fUnicodeSupport;
53
extern void *g_pClipboardDisplay;
54
extern Window g_iClipboardWindow;
55
extern Atom g_atomLastOwnedSelection;
59
* Local function prototypes
63
winProcessXEventsTimeout (HWND hwnd, int iWindow, Display *pDisplay,
64
Bool fUseUnicode, int iTimeoutSec);
68
* Process X events up to specified timeout
72
winProcessXEventsTimeout (HWND hwnd, int iWindow, Display *pDisplay,
73
Bool fUseUnicode, int iTimeoutSec)
78
DWORD dwStopTime = (GetTickCount () / 1000) + iTimeoutSec;
80
/* We need to ensure that all pending events are processed */
81
XSync (pDisplay, FALSE);
83
/* Get our connection number */
84
iConnNumber = ConnectionNumber (pDisplay);
86
/* Loop for X events */
91
/* Setup the file descriptor set */
93
FD_SET (iConnNumber, &fdsRead);
96
tv.tv_sec = dwStopTime - (GetTickCount () / 1000);
99
/* Break out if no time left */
101
return WIN_XEVENTS_SUCCESS;
103
/* Wait for a Windows event or an X event */
104
iReturn = select (iConnNumber + 1,/* Highest fds number */
105
&fdsRead, /* Read mask */
106
NULL, /* No write mask */
107
NULL, /* No exception mask */
108
&tv); /* No timeout */
111
ErrorF ("winProcessXEventsTimeout - Call to select () failed: %d. "
112
"Bailing.\n", iReturn);
116
/* Branch on which descriptor became active */
117
if (FD_ISSET (iConnNumber, &fdsRead))
119
/* Process X events */
120
/* Exit when we see that server is shutting down */
121
iReturn = winClipboardFlushXEvents (hwnd,
125
if (WIN_XEVENTS_NOTIFY == iReturn
126
|| WIN_XEVENTS_CONVERT == iReturn)
128
/* Bail out if convert or notify processed */
134
return WIN_XEVENTS_SUCCESS;
139
* Process a given Windows message
143
winClipboardWindowProc (HWND hwnd, UINT message,
144
WPARAM wParam, LPARAM lParam)
146
static HWND s_hwndNextViewer;
147
static Bool s_fCBCInitialized;
149
/* Branch on message type */
154
winDebug ("winClipboardWindowProc - WM_DESTROY\n");
156
/* Remove ourselves from the clipboard chain */
157
ChangeClipboardChain (hwnd, s_hwndNextViewer);
159
s_hwndNextViewer = NULL;
168
winDebug ("winClipboardWindowProc - WM_CREATE\n");
170
/* Add ourselves to the clipboard viewer chain */
171
s_hwndNextViewer = SetClipboardViewer (hwnd);
172
if (s_hwndNextViewer == hwnd)
174
s_hwndNextViewer = NULL;
175
winErrorFVerb (1, "winClipboardWindowProc - WM_CREATE: "
176
"attempted to set next window to ourselves.");
182
case WM_CHANGECBCHAIN:
184
static Bool s_fProcessingChangeCBChain = FALSE;
185
winDebug ("winClipboardWindowProc - WM_CHANGECBCHAIN: wParam(%x) "
186
"lParam(%x) s_hwndNextViewer(%x)\n",
187
wParam, lParam, s_hwndNextViewer);
191
* We've occasionally seen a loop in the clipboard chain. Break
192
* it on the first hint of recursion.
194
if (! s_fProcessingChangeCBChain)
196
s_fProcessingChangeCBChain = TRUE;
200
winErrorFVerb (1, "winClipboardWindowProc - WM_CHANGECBCHAIN - "
201
"Nested calls detected. Bailing.\n");
202
winDebug ("winClipboardWindowProc - WM_CHANGECBCHAIN: Exit\n");
206
if ((HWND) wParam == s_hwndNextViewer)
208
s_hwndNextViewer = (HWND) lParam;
209
if (s_hwndNextViewer == hwnd)
211
s_hwndNextViewer = NULL;
212
winErrorFVerb (1, "winClipboardWindowProc - WM_CHANGECBCHAIN: "
213
"attempted to set next window to ourselves.");
216
else if (s_hwndNextViewer)
217
SendMessage (s_hwndNextViewer, message,
220
s_fProcessingChangeCBChain = FALSE;
222
winDebug ("winClipboardWindowProc - WM_CHANGECBCHAIN: Exit\n");
227
/* Ensure that we're in the clipboard chain. Some apps,
228
* WinXP's remote desktop for one, don't play nice with the
229
* chain. This message is called whenever we receive a
230
* WM_ACTIVATEAPP message to ensure that we continue to
231
* receive clipboard messages.
233
* It might be possible to detect if we're still in the chain
234
* by calling SendMessage (GetClipboardViewer(),
235
* WM_DRAWCLIPBOARD, 0, 0); and then seeing if we get the
236
* WM_DRAWCLIPBOARD message. That, however, might be more
237
* expensive than just putting ourselves back into the chain.
240
winDebug ("winClipboardWindowProc - WM_WM_REINIT: Enter\n");
241
if (hwnd != GetClipboardViewer ())
243
winDebug (" WM_WM_REINIT: Replacing us(%x) with %x at head "
244
"of chain\n", hwnd, s_hwndNextViewer);
245
s_fCBCInitialized = FALSE;
246
ChangeClipboardChain (hwnd, s_hwndNextViewer);
247
s_hwndNextViewer = NULL;
248
s_fCBCInitialized = FALSE;
249
winDebug (" WM_WM_REINIT: Putting us back at head of chain.\n");
250
s_hwndNextViewer = SetClipboardViewer (hwnd);
251
if (s_hwndNextViewer == hwnd)
253
s_hwndNextViewer = NULL;
254
winErrorFVerb (1, "winClipboardWindowProc - WM_WM_REINIT: "
255
"attempted to set next window to ourselves.\n");
260
winDebug (" WM_WM_REINIT: already at head of viewer chain.\n");
263
winDebug ("winClipboardWindowProc - WM_WM_REINIT: Exit\n");
267
case WM_DRAWCLIPBOARD:
269
static Bool s_fProcessingDrawClipboard = FALSE;
270
Display *pDisplay = g_pClipboardDisplay;
271
Window iWindow = g_iClipboardWindow;
274
winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Enter\n");
277
* We've occasionally seen a loop in the clipboard chain. Break
278
* it on the first hint of recursion.
280
if (! s_fProcessingDrawClipboard)
282
s_fProcessingDrawClipboard = TRUE;
286
winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
287
"Nested calls detected. Bailing.\n");
288
winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
292
/* Pass the message on the next window in the clipboard viewer chain */
293
if (s_hwndNextViewer)
294
SendMessage (s_hwndNextViewer, message, 0, 0);
296
/* Bail on first message */
297
if (!s_fCBCInitialized)
299
s_fCBCInitialized = TRUE;
300
s_fProcessingDrawClipboard = FALSE;
301
winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
306
* NOTE: We cannot bail out when NULL == GetClipboardOwner ()
307
* because some applications deal with the clipboard in a manner
308
* that causes the clipboard owner to be NULL when they are in
309
* fact taking ownership. One example of this is the Win32
310
* native compile of emacs.
313
/* Bail when we still own the clipboard */
314
if (hwnd == GetClipboardOwner ())
317
winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
318
"We own the clipboard, returning.\n");
319
winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
320
s_fProcessingDrawClipboard = FALSE;
325
* Do not take ownership of the X11 selections when something
326
* other than CF_TEXT or CF_UNICODETEXT has been copied
327
* into the Win32 clipboard.
329
if (!IsClipboardFormatAvailable (CF_TEXT)
330
&& !IsClipboardFormatAvailable (CF_UNICODETEXT))
333
winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
334
"Clipboard does not contain CF_TEXT nor "
335
"CF_UNICODETEXT.\n");
338
* We need to make sure that the X Server has processed
339
* previous XSetSelectionOwner messages.
341
XSync (pDisplay, FALSE);
343
winDebug("winClipboardWindowProc - XSync done.\n");
345
/* Release PRIMARY selection if owned */
346
iReturn = XGetSelectionOwner (pDisplay, XA_PRIMARY);
347
if (iReturn == g_iClipboardWindow)
349
winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
350
"PRIMARY selection is owned by us.\n");
351
XSetSelectionOwner (pDisplay,
356
else if (BadWindow == iReturn || BadAtom == iReturn)
357
winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
358
"XGetSelection failed for PRIMARY: %d\n", iReturn);
360
/* Release CLIPBOARD selection if owned */
361
iReturn = XGetSelectionOwner (pDisplay,
362
XInternAtom (pDisplay,
365
if (iReturn == g_iClipboardWindow)
367
winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
368
"CLIPBOARD selection is owned by us.\n");
369
XSetSelectionOwner (pDisplay,
370
XInternAtom (pDisplay,
376
else if (BadWindow == iReturn || BadAtom == iReturn)
377
winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
378
"XGetSelection failed for CLIPBOARD: %d\n", iReturn);
380
winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
381
s_fProcessingDrawClipboard = FALSE;
385
/* Reassert ownership of PRIMARY */
386
iReturn = XSetSelectionOwner (pDisplay,
390
if (iReturn == BadAtom || iReturn == BadWindow)
392
winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
393
"Could not reassert ownership of PRIMARY\n");
397
winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
398
"Reasserted ownership of PRIMARY\n");
401
/* Reassert ownership of the CLIPBOARD */
402
iReturn = XSetSelectionOwner (pDisplay,
403
XInternAtom (pDisplay,
408
if (iReturn == BadAtom || iReturn == BadWindow)
410
winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
411
"Could not reassert ownership of CLIPBOARD\n");
415
winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
416
"Reasserted ownership of CLIPBOARD\n");
419
/* Flush the pending SetSelectionOwner event now */
422
s_fProcessingDrawClipboard = FALSE;
424
winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
428
case WM_DESTROYCLIPBOARD:
430
* NOTE: Intentionally do nothing.
431
* Changes in the Win32 clipboard are handled by WM_DRAWCLIPBOARD
432
* above. We only process this message to conform to the specs
433
* for delayed clipboard rendering in Win32. You might think
434
* that we need to release ownership of the X11 selections, but
435
* we do not, because a WM_DRAWCLIPBOARD message will closely
436
* follow this message and reassert ownership of the X11
437
* selections, handling the issue for us.
442
case WM_RENDERFORMAT:
443
case WM_RENDERALLFORMATS:
446
Display *pDisplay = g_pClipboardDisplay;
447
Window iWindow = g_iClipboardWindow;
448
Bool fConvertToUnicode;
450
winDebug ("winClipboardWindowProc - WM_RENDER*FORMAT - Hello.\n");
452
/* Flag whether to convert to Unicode or not */
453
if (message == WM_RENDERALLFORMATS)
454
fConvertToUnicode = FALSE;
456
fConvertToUnicode = g_fUnicodeSupport && (CF_UNICODETEXT == wParam);
458
/* Request the selection contents */
459
iReturn = XConvertSelection (pDisplay,
460
g_atomLastOwnedSelection,
461
XInternAtom (pDisplay,
462
"COMPOUND_TEXT", False),
463
XInternAtom (pDisplay,
464
"CYGX_CUT_BUFFER", False),
467
if (iReturn == BadAtom || iReturn == BadWindow)
469
winErrorFVerb (1, "winClipboardWindowProc - WM_RENDER*FORMAT - "
470
"XConvertSelection () failed\n");
474
/* Special handling for WM_RENDERALLFORMATS */
475
if (message == WM_RENDERALLFORMATS)
477
/* We must open and empty the clipboard */
479
/* Close clipboard if we have it open already */
480
if (GetOpenClipboardWindow () == hwnd)
485
if (!OpenClipboard (hwnd))
487
winErrorFVerb (1, "winClipboardWindowProc - WM_RENDER*FORMATS - "
488
"OpenClipboard () failed: %08x\n",
493
if (!EmptyClipboard ())
495
winErrorFVerb (1, "winClipboardWindowProc - WM_RENDER*FORMATS - "
496
"EmptyClipboard () failed: %08x\n",
502
/* Process the SelectionNotify event */
503
iReturn = winProcessXEventsTimeout (hwnd,
508
if (WIN_XEVENTS_CONVERT == iReturn)
511
* The selection was offered for conversion first, so we have
512
* to process a second SelectionNotify event to get the actual
513
* data in the selection.
515
iReturn = winProcessXEventsTimeout (hwnd,
523
* The last of the up-to two calls to winProcessXEventsTimeout
524
* from above had better have seen a notify event, or else we
525
* are dealing with a buggy or old X11 app. In these cases we
526
* have to paste some fake data to the Win32 clipboard to
527
* satisfy the requirement that we write something to it.
529
if (WIN_XEVENTS_NOTIFY != iReturn)
531
/* Paste no data, to satisfy required call to SetClipboardData */
532
if (g_fUnicodeSupport)
533
SetClipboardData (CF_UNICODETEXT, NULL);
534
SetClipboardData (CF_TEXT, NULL);
537
/* Special handling for WM_RENDERALLFORMATS */
538
if (message == WM_RENDERALLFORMATS)
540
/* We must close the clipboard */
542
if (!CloseClipboard ())
544
winErrorFVerb (1, "winClipboardWindowProc - WM_RENDERALLFORMATS - "
545
"CloseClipboard () failed: %08x\n",
551
winDebug ("winClipboardWindowProc - WM_RENDER*FORMAT - Returning.\n");
556
/* Let Windows perform default processing for unhandled messages */
557
return DefWindowProc (hwnd, message, wParam, lParam);
562
* Process any pending Windows messages
566
winClipboardFlushWindowsMessageQueue (HWND hwnd)
570
/* Flush the messaging window queue */
571
/* NOTE: Do not pass the hwnd of our messaging window to PeekMessage,
572
* as this will filter out many non-window-specific messages that
573
* are sent to our thread, such as WM_QUIT.
575
while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
577
/* Dispatch the message if not WM_QUIT */
578
if (msg.message == WM_QUIT)
581
DispatchMessage (&msg);