First import
[xorg_rtime.git] / xorg-server-1.4 / hw / xwin / winclipboardwndproc.c
blob802a7403598f80ead5097c67eee9dec12015f08a
1 /*
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>
33 #endif
34 #include <sys/types.h>
35 #include <sys/time.h>
36 #include "winclipboard.h"
38 extern void winFixClipboardChain();
42 * Constants
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;
62 /*
63 * Local function prototypes
66 static Bool
67 winProcessXEventsTimeout (HWND hwnd, int iWindow, Display *pDisplay,
68 Bool fUseUnicode, int iTimeoutSec);
72 * Process X events up to specified timeout
75 static int
76 winProcessXEventsTimeout (HWND hwnd, int iWindow, Display *pDisplay,
77 Bool fUseUnicode, int iTimeoutSec)
79 int iConnNumber;
80 struct timeval tv;
81 int iReturn;
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 */
91 while (1)
93 fd_set fdsRead;
95 /* Setup the file descriptor set */
96 FD_ZERO (&fdsRead);
97 FD_SET (iConnNumber, &fdsRead);
99 /* Adjust timeout */
100 tv.tv_sec = dwStopTime - (GetTickCount () / 1000);
101 tv.tv_usec = 0;
103 /* Break out if no time left */
104 if (tv.tv_sec < 0)
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 */
113 if (iReturn <= 0)
115 ErrorF ("winProcessXEventsTimeout - Call to select () failed: %d. "
116 "Bailing.\n", iReturn);
117 break;
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,
126 iWindow,
127 pDisplay,
128 fUseUnicode);
129 if (WIN_XEVENTS_NOTIFY == iReturn
130 || WIN_XEVENTS_CONVERT == iReturn)
132 /* Bail out if convert or notify processed */
133 return iReturn;
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)
152 LRESULT CALLBACK
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 */
160 switch (message)
162 case WM_DESTROY:
164 winDebug ("winClipboardWindowProc - WM_DESTROY\n");
166 /* Remove ourselves from the clipboard chain */
167 ChangeClipboardChain (hwnd, s_hwndNextViewer);
169 s_hwndNextViewer = NULL;
171 PostQuitMessage (0);
173 return 0;
176 case WM_CREATE:
178 HWND first, next;
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 */
189 else
190 s_fCBCInitialized = FALSE;
192 return 0;
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,
213 wParam, lParam);
216 winDebug ("winClipboardWindowProc - WM_CHANGECBCHAIN: Exit\n");
217 return 0;
219 case WM_WM_REINIT:
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.
234 HWND first, next;
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 */
253 else
254 s_fCBCInitialized = FALSE;
256 winDebug ("winClipboardWindowProc - WM_WM_REINIT: Exit\n");
257 return 0;
260 case WM_DRAWCLIPBOARD:
262 static Bool s_fProcessingDrawClipboard = FALSE;
263 Display *pDisplay = g_pClipboardDisplay;
264 Window iWindow = g_iClipboardWindow;
265 int iReturn;
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;
277 else
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;
287 return 0;
290 /* Bail on first message */
291 if (!s_fCBCInitialized)
293 s_fCBCInitialized = TRUE;
294 s_fProcessingDrawClipboard = FALSE;
295 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
296 return 0;
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);
317 return 0;
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,
346 XA_PRIMARY,
347 None,
348 CurrentTime);
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,
357 "CLIPBOARD",
358 False));
359 if (iReturn == g_iClipboardWindow)
361 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
362 "CLIPBOARD selection is owned by us.\n");
363 XSetSelectionOwner (pDisplay,
364 XInternAtom (pDisplay,
365 "CLIPBOARD",
366 False),
367 None,
368 CurrentTime);
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);
378 return 0;
381 /* Reassert ownership of PRIMARY */
382 iReturn = XSetSelectionOwner (pDisplay,
383 XA_PRIMARY,
384 iWindow,
385 CurrentTime);
386 if (iReturn == BadAtom || iReturn == BadWindow)
388 winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
389 "Could not reassert ownership of PRIMARY\n");
391 else
393 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
394 "Reasserted ownership of PRIMARY\n");
397 /* Reassert ownership of the CLIPBOARD */
398 iReturn = XSetSelectionOwner (pDisplay,
399 XInternAtom (pDisplay,
400 "CLIPBOARD",
401 False),
402 iWindow,
403 CurrentTime);
404 if (iReturn == BadAtom || iReturn == BadWindow)
406 winErrorFVerb (1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
407 "Could not reassert ownership of CLIPBOARD\n");
409 else
411 winDebug ("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
412 "Reasserted ownership of CLIPBOARD\n");
415 /* Flush the pending SetSelectionOwner event now */
416 XFlush (pDisplay);
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);
424 return 0;
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.
438 return 0;
441 case WM_RENDERFORMAT:
442 case WM_RENDERALLFORMATS:
444 int iReturn;
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;
454 else
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),
464 iWindow,
465 CurrentTime);
466 if (iReturn == BadAtom || iReturn == BadWindow)
468 winErrorFVerb (1, "winClipboardWindowProc - WM_RENDER*FORMAT - "
469 "XConvertSelection () failed\n");
470 break;
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)
481 CloseClipboard ();
484 if (!OpenClipboard (hwnd))
486 winErrorFVerb (1, "winClipboardWindowProc - WM_RENDER*FORMATS - "
487 "OpenClipboard () failed: %08x\n",
488 GetLastError ());
489 break;
492 if (!EmptyClipboard ())
494 winErrorFVerb (1, "winClipboardWindowProc - WM_RENDER*FORMATS - "
495 "EmptyClipboard () failed: %08x\n",
496 GetLastError ());
497 break;
501 /* Process the SelectionNotify event */
502 iReturn = winProcessXEventsTimeout (hwnd,
503 iWindow,
504 pDisplay,
505 fConvertToUnicode,
506 WIN_POLL_TIMEOUT);
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,
515 iWindow,
516 pDisplay,
517 fConvertToUnicode,
518 WIN_POLL_TIMEOUT);
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",
552 GetLastError ());
553 break;
557 winDebug ("winClipboardWindowProc - WM_RENDER*FORMAT - Returning.\n");
558 return 0;
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 */
571 return 0;
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 */
578 EmptyClipboard ();
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 */
588 CloseClipboard ();
590 return 0;
593 /* Let Windows perform default processing for unhandled messages */
594 return DefWindowProc (hwnd, message, wParam, lParam);
599 * Process any pending Windows messages
602 BOOL
603 winClipboardFlushWindowsMessageQueue (HWND hwnd)
605 MSG msg;
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)
616 return FALSE;
617 else
618 DispatchMessage (&msg);
621 return TRUE;