Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / xwin / winclipboardwndproc.c
CommitLineData
a09e091a
JB
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#ifdef HAVE_XWIN_CONFIG_H
34#include <xwin-config.h>
35#endif
36#include <sys/types.h>
37#include <sys/time.h>
38#include "winclipboard.h"
39#include "misc.h"
40
41/*
42 * Constants
43 */
44
45#define WIN_POLL_TIMEOUT 1
46
47/*
48 * References to external symbols
49 */
50
51extern void *g_pClipboardDisplay;
52extern Window g_iClipboardWindow;
53extern Atom g_atomLastOwnedSelection;
54
55/*
56 * Process X events up to specified timeout
57 */
58
59static int
60winProcessXEventsTimeout(HWND hwnd, int iWindow, Display * pDisplay,
61 Bool fUseUnicode, int iTimeoutSec)
62{
63 int iConnNumber;
64 struct timeval tv;
65 int iReturn;
66 DWORD dwStopTime = GetTickCount() + iTimeoutSec * 1000;
67
68 winDebug("winProcessXEventsTimeout () - pumping X events for %d seconds\n",
69 iTimeoutSec);
70
71 /* Get our connection number */
72 iConnNumber = ConnectionNumber(pDisplay);
73
74 /* Loop for X events */
75 while (1) {
76 fd_set fdsRead;
77 long remainingTime;
78
79 /* We need to ensure that all pending events are processed */
80 XSync(pDisplay, FALSE);
81
82 /* Setup the file descriptor set */
83 FD_ZERO(&fdsRead);
84 FD_SET(iConnNumber, &fdsRead);
85
86 /* Adjust timeout */
87 remainingTime = dwStopTime - GetTickCount();
88 tv.tv_sec = remainingTime / 1000;
89 tv.tv_usec = (remainingTime % 1000) * 1000;
90 winDebug("winProcessXEventsTimeout () - %d milliseconds left\n",
91 remainingTime);
92
93 /* Break out if no time left */
94 if (remainingTime <= 0)
95 return WIN_XEVENTS_SUCCESS;
96
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 */
102 &tv); /* Timeout */
103 if (iReturn < 0) {
104 ErrorF("winProcessXEventsTimeout - Call to select () failed: %d. "
105 "Bailing.\n", iReturn);
106 break;
107 }
108
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);
115
116 winDebug
117 ("winProcessXEventsTimeout () - winClipboardFlushXEvents returned %d\n",
118 iReturn);
119
120 if (WIN_XEVENTS_NOTIFY == iReturn) {
121 /* Bail out if notify processed */
122 return iReturn;
123 }
124 }
125 else {
126 winDebug("winProcessXEventsTimeout - Spurious wake\n");
127 }
128 }
129
130 return WIN_XEVENTS_SUCCESS;
131}
132
133/*
134 * Process a given Windows message
135 */
136
137LRESULT CALLBACK
138winClipboardWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
139{
140 static HWND s_hwndNextViewer;
141 static Bool s_fCBCInitialized;
142
143 /* Branch on message type */
144 switch (message) {
145 case WM_DESTROY:
146 {
147 winDebug("winClipboardWindowProc - WM_DESTROY\n");
148
149 /* Remove ourselves from the clipboard chain */
150 ChangeClipboardChain(hwnd, s_hwndNextViewer);
151
152 s_hwndNextViewer = NULL;
153
154 PostQuitMessage(0);
155 }
156 return 0;
157
158 case WM_CREATE:
159 {
160 HWND first, next;
161 DWORD error_code = 0;
162
163 winDebug("winClipboardWindowProc - WM_CREATE\n");
164
165 first = GetClipboardViewer(); /* Get handle to first viewer in chain. */
166 if (first == hwnd)
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 */
173 else
174 s_fCBCInitialized = FALSE;
175 }
176 return 0;
177
178 case WM_CHANGECBCHAIN:
179 {
180 winDebug("winClipboardWindowProc - WM_CHANGECBCHAIN: wParam(%x) "
181 "lParam(%x) s_hwndNextViewer(%x)\n",
182 wParam, lParam, s_hwndNextViewer);
183
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.");
190 }
191 }
192 else if (s_hwndNextViewer)
193 SendMessage(s_hwndNextViewer, message, wParam, lParam);
194
195 }
196 winDebug("winClipboardWindowProc - WM_CHANGECBCHAIN: Exit\n");
197 return 0;
198
199 case WM_WM_REINIT:
200 {
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.
206 *
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.
212 */
213
214 HWND first, next;
215 DWORD error_code = 0;
216
217 winDebug("winClipboardWindowProc - WM_WM_REINIT: Enter\n");
218
219 first = GetClipboardViewer(); /* Get handle to first viewer in chain. */
220 if (first == hwnd)
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. */
230 if (first == hwnd)
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 */
236 else
237 s_fCBCInitialized = FALSE;
238 }
239 winDebug("winClipboardWindowProc - WM_WM_REINIT: Exit\n");
240 return 0;
241
242 case WM_DRAWCLIPBOARD:
243 {
244 static Atom atomClipboard;
245 static int generation;
246 static Bool s_fProcessingDrawClipboard = FALSE;
247 Display *pDisplay = g_pClipboardDisplay;
248 Window iWindow = g_iClipboardWindow;
249 int iReturn;
250
251 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD: Enter\n");
252
253 if (generation != serverGeneration) {
254 generation = serverGeneration;
255 atomClipboard = XInternAtom(pDisplay, "CLIPBOARD", False);
256 }
257
258 /*
259 * We've occasionally seen a loop in the clipboard chain.
260 * Try and fix it on the first hint of recursion.
261 */
262 if (!s_fProcessingDrawClipboard) {
263 s_fProcessingDrawClipboard = TRUE;
264 }
265 else {
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;
274 return 0;
275 }
276
277 /* Bail on first message */
278 if (!s_fCBCInitialized) {
279 s_fCBCInitialized = TRUE;
280 s_fProcessingDrawClipboard = FALSE;
281 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
282 return 0;
283 }
284
285 /*
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.
291 */
292
293 /* Bail when we still own the clipboard */
294 if (hwnd == GetClipboardOwner()) {
295
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);
302 return 0;
303 }
304
305 /*
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.
309 */
310 if (!IsClipboardFormatAvailable(CF_TEXT)
311 && !IsClipboardFormatAvailable(CF_UNICODETEXT)) {
312
313 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
314 "Clipboard does not contain CF_TEXT nor "
315 "CF_UNICODETEXT.\n");
316
317 /*
318 * We need to make sure that the X Server has processed
319 * previous XSetSelectionOwner messages.
320 */
321 XSync(pDisplay, FALSE);
322
323 winDebug("winClipboardWindowProc - XSync done.\n");
324
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);
331 }
332 else if (BadWindow == iReturn || BadAtom == iReturn)
333 winErrorFVerb(1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
334 "XGetSelection failed for PRIMARY: %d\n",
335 iReturn);
336
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);
343 }
344 else if (BadWindow == iReturn || BadAtom == iReturn)
345 winErrorFVerb(1, "winClipboardWindowProc - WM_DRAWCLIPBOARD - "
346 "XGetSelection failed for CLIPBOARD: %d\n",
347 iReturn);
348
349 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD: Exit\n");
350 s_fProcessingDrawClipboard = FALSE;
351 if (s_hwndNextViewer)
352 SendMessage(s_hwndNextViewer, message, wParam, lParam);
353 return 0;
354 }
355
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");
363 }
364 else {
365 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
366 "Reasserted ownership of PRIMARY\n");
367 }
368
369 /* Reassert ownership of the CLIPBOARD */
370 iReturn = XSetSelectionOwner(pDisplay,
371 atomClipboard, iWindow, CurrentTime);
372
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");
377 }
378 else {
379 winDebug("winClipboardWindowProc - WM_DRAWCLIPBOARD - "
380 "Reasserted ownership of CLIPBOARD\n");
381 }
382
383 /* Flush the pending SetSelectionOwner event now */
384 XFlush(pDisplay);
385
386 s_fProcessingDrawClipboard = FALSE;
387 }
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);
392 return 0;
393
394 case WM_DESTROYCLIPBOARD:
395 /*
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.
404 */
405 winDebug("winClipboardWindowProc - WM_DESTROYCLIPBOARD - Ignored.\n");
406 return 0;
407
408 case WM_RENDERFORMAT:
409 case WM_RENDERALLFORMATS:
410 {
411 int iReturn;
412 Display *pDisplay = g_pClipboardDisplay;
413 Window iWindow = g_iClipboardWindow;
414 Bool fConvertToUnicode;
415
416 winDebug("winClipboardWindowProc - WM_RENDER*FORMAT - Hello.\n");
417
418 /* Flag whether to convert to Unicode or not */
419 if (message == WM_RENDERALLFORMATS)
420 fConvertToUnicode = FALSE;
421 else
422 fConvertToUnicode = (CF_UNICODETEXT == wParam);
423
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");
435 break;
436 }
437
438 /* Special handling for WM_RENDERALLFORMATS */
439 if (message == WM_RENDERALLFORMATS) {
440 /* We must open and empty the clipboard */
441
442 /* Close clipboard if we have it open already */
443 if (GetOpenClipboardWindow() == hwnd) {
444 CloseClipboard();
445 }
446
447 if (!OpenClipboard(hwnd)) {
448 winErrorFVerb(1, "winClipboardWindowProc - WM_RENDER*FORMATS - "
449 "OpenClipboard () failed: %08x\n",
450 GetLastError());
451 break;
452 }
453
454 if (!EmptyClipboard()) {
455 winErrorFVerb(1, "winClipboardWindowProc - WM_RENDER*FORMATS - "
456 "EmptyClipboard () failed: %08x\n",
457 GetLastError());
458 break;
459 }
460 }
461
462 /* Process the SelectionNotify event */
463 iReturn = winProcessXEventsTimeout(hwnd,
464 iWindow,
465 pDisplay,
466 fConvertToUnicode, WIN_POLL_TIMEOUT);
467
468 /*
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.
474 */
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);
479
480 ErrorF
481 ("winClipboardWindowProc - timed out waiting for WIN_XEVENTS_NOTIFY\n");
482 }
483
484 /* Special handling for WM_RENDERALLFORMATS */
485 if (message == WM_RENDERALLFORMATS) {
486 /* We must close the clipboard */
487
488 if (!CloseClipboard()) {
489 winErrorFVerb(1,
490 "winClipboardWindowProc - WM_RENDERALLFORMATS - "
491 "CloseClipboard () failed: %08x\n",
492 GetLastError());
493 break;
494 }
495 }
496
497 winDebug("winClipboardWindowProc - WM_RENDER*FORMAT - Returning.\n");
498 return 0;
499 }
500 }
501
502 /* Let Windows perform default processing for unhandled messages */
503 return DefWindowProc(hwnd, message, wParam, lParam);
504}
505
506/*
507 * Process any pending Windows messages
508 */
509
510BOOL
511winClipboardFlushWindowsMessageQueue(HWND hwnd)
512{
513 MSG msg;
514
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.
519 */
520 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
521 /* Dispatch the message if not WM_QUIT */
522 if (msg.message == WM_QUIT)
523 return FALSE;
524 else
525 DispatchMessage(&msg);
526 }
527
528 return TRUE;
529}