winclipboardwrappers.c revision 4642e01f
1/*
2 *Copyright (C) 2003-2004 Harold L Hunt II All Rights Reserved.
3 *
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:
11 *
12 *The above copyright notice and this permission notice shall be
13 *included in all copies or substantial portions of the Software.
14 *
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.
22 *
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.
27 *
28 * Authors:	Harold L Hunt II
29 */
30
31#ifdef HAVE_XWIN_CONFIG_H
32#include <xwin-config.h>
33#endif
34#include "win.h"
35#include "dixstruct.h"
36#include <X11/Xatom.h>
37
38
39/*
40 * Constants
41 */
42
43#define CLIP_NUM_SELECTIONS		2
44#define CLIP_OWN_PRIMARY		0
45#define CLIP_OWN_CLIPBOARD		1
46
47
48/*
49 * Local function prototypes
50 */
51
52DISPATCH_PROC(winProcEstablishConnection);
53DISPATCH_PROC(winProcQueryTree);
54DISPATCH_PROC(winProcSetSelectionOwner);
55
56
57/*
58 * References to external symbols
59 */
60
61extern Bool		g_fUnicodeSupport;
62extern int		g_iNumScreens;
63extern unsigned int	g_uiAuthDataLen;
64extern char		*g_pAuthData;
65extern Bool		g_fXdmcpEnabled;
66extern Bool		g_fClipboardLaunched;
67extern Bool		g_fClipboardStarted;
68extern Bool		g_fClipboard;
69extern Window		g_iClipboardWindow;
70extern Atom		g_atomLastOwnedSelection;
71extern HWND		g_hwndClipboard;
72
73extern winDispatchProcPtr	winProcEstablishConnectionOrig;
74extern winDispatchProcPtr	winProcQueryTreeOrig;
75extern winDispatchProcPtr	winProcSetSelectionOwnerOrig;
76
77
78/*
79 * Wrapper for internal QueryTree function.
80 * Hides the clipboard client when it is the only client remaining.
81 */
82
83int
84winProcQueryTree (ClientPtr client)
85{
86  int			iReturn;
87
88  /*
89   * This procedure is only used for initialization.
90   * We can unwrap the original procedure at this point
91   * so that this function is no longer called until the
92   * server resets and the function is wrapped again.
93   */
94  ProcVector[X_QueryTree] = winProcQueryTreeOrig;
95
96  /*
97   * Call original function and bail if it fails.
98   * NOTE: We must do this first, since we need XdmcpOpenDisplay
99   * to be called before we initialize our clipboard client.
100   */
101  iReturn = (*winProcQueryTreeOrig) (client);
102  if (iReturn != 0)
103    {
104      ErrorF ("winProcQueryTree - ProcQueryTree failed, bailing.\n");
105      return iReturn;
106    }
107
108  /* Make errors more obvious */
109  winProcQueryTreeOrig = NULL;
110
111  /* Do nothing if clipboard is not enabled */
112  if (!g_fClipboard)
113    {
114      ErrorF ("winProcQueryTree - Clipboard is not enabled, "
115	      "returning.\n");
116      return iReturn;
117    }
118
119  /* If the clipboard client has already been started, abort */
120  if (g_fClipboardLaunched)
121    {
122      ErrorF ("winProcQueryTree - Clipboard client already "
123	      "launched, returning.\n");
124      return iReturn;
125    }
126
127  /* Startup the clipboard client if clipboard mode is being used */
128  if (g_fXdmcpEnabled && g_fClipboard)
129    {
130      /*
131       * NOTE: The clipboard client is started here for a reason:
132       * 1) Assume you are using XDMCP (e.g. XWin -query %hostname%)
133       * 2) If the clipboard client attaches during X Server startup,
134       *    then it becomes the "magic client" that causes the X Server
135       *    to reset if it exits.
136       * 3) XDMCP calls KillAllClients when it starts up.
137       * 4) The clipboard client is a client, so it is killed.
138       * 5) The clipboard client is the "magic client", so the X Server
139       *    resets itself.
140       * 6) This repeats ad infinitum.
141       * 7) We avoid this by waiting until at least one client (could
142       *    be XDM, could be another client) connects, which makes it
143       *    almost certain that the clipboard client will not connect
144       *    until after XDM when using XDMCP.
145       * 8) Unfortunately, there is another problem.
146       * 9) XDM walks the list of windows with XQueryTree,
147       *    killing any client it finds with a window.
148       * 10)Thus, when using XDMCP we wait until the first call
149       *    to ProcQueryTree before we startup the clipboard client.
150       *    This should prevent XDM from finding the clipboard client,
151       *    since it has not yet created a window.
152       * 11)Startup when not using XDMCP is handled in
153       *    winProcEstablishConnection.
154       */
155
156      /* Create the clipboard client thread */
157      if (!winInitClipboard ())
158	{
159	  ErrorF ("winProcQueryTree - winClipboardInit "
160		  "failed.\n");
161	  return iReturn;
162	}
163
164      ErrorF ("winProcQueryTree - winInitClipboard returned.\n");
165    }
166
167  /* Flag that clipboard client has been launched */
168  g_fClipboardLaunched = TRUE;
169
170  return iReturn;
171}
172
173
174/*
175 * Wrapper for internal EstablishConnection function.
176 * Initializes internal clients that must not be started until
177 * an external client has connected.
178 */
179
180int
181winProcEstablishConnection (ClientPtr client)
182{
183  int			iReturn;
184  static int		s_iCallCount = 0;
185  static unsigned long	s_ulServerGeneration = 0;
186
187  ErrorF ("winProcEstablishConnection - Hello\n");
188
189  /* Do nothing if clipboard is not enabled */
190  if (!g_fClipboard)
191    {
192      ErrorF ("winProcEstablishConnection - Clipboard is not enabled, "
193	      "returning.\n");
194
195      /* Unwrap the original function, call it, and return */
196      InitialVector[2] = winProcEstablishConnectionOrig;
197      iReturn = (*winProcEstablishConnectionOrig) (client);
198      winProcEstablishConnectionOrig = NULL;
199      return iReturn;
200    }
201
202  /* Watch for server reset */
203  if (s_ulServerGeneration != serverGeneration)
204    {
205      /* Save new generation number */
206      s_ulServerGeneration = serverGeneration;
207
208      /* Reset call count */
209      s_iCallCount = 0;
210    }
211
212  /* Increment call count */
213  ++s_iCallCount;
214
215  /* Wait for second call when Xdmcp is enabled */
216  if (g_fXdmcpEnabled
217      && !g_fClipboardLaunched
218      && s_iCallCount < 4)
219    {
220      ErrorF ("winProcEstablishConnection - Xdmcp enabled, waiting to "
221	      "start clipboard client until fourth call.\n");
222      return (*winProcEstablishConnectionOrig) (client);
223    }
224
225  /*
226   * This procedure is only used for initialization.
227   * We can unwrap the original procedure at this point
228   * so that this function is no longer called until the
229   * server resets and the function is wrapped again.
230   */
231  InitialVector[2] = winProcEstablishConnectionOrig;
232
233  /*
234   * Call original function and bail if it fails.
235   * NOTE: We must do this first, since we need XdmcpOpenDisplay
236   * to be called before we initialize our clipboard client.
237   */
238  iReturn = (*winProcEstablishConnectionOrig) (client);
239  if (iReturn != 0)
240    {
241      ErrorF ("winProcEstablishConnection - ProcEstablishConnection "
242	      "failed, bailing.\n");
243      return iReturn;
244    }
245
246  /* Clear original function pointer */
247  winProcEstablishConnectionOrig = NULL;
248
249  /* If the clipboard client has already been started, abort */
250  if (g_fClipboardLaunched)
251    {
252      ErrorF ("winProcEstablishConnection - Clipboard client already "
253	      "launched, returning.\n");
254      return iReturn;
255    }
256
257  /* Startup the clipboard client if clipboard mode is being used */
258  if (g_fClipboard)
259    {
260      /*
261       * NOTE: The clipboard client is started here for a reason:
262       * 1) Assume you are using XDMCP (e.g. XWin -query %hostname%)
263       * 2) If the clipboard client attaches during X Server startup,
264       *    then it becomes the "magic client" that causes the X Server
265       *    to reset if it exits.
266       * 3) XDMCP calls KillAllClients when it starts up.
267       * 4) The clipboard client is a client, so it is killed.
268       * 5) The clipboard client is the "magic client", so the X Server
269       *    resets itself.
270       * 6) This repeats ad infinitum.
271       * 7) We avoid this by waiting until at least one client (could
272       *    be XDM, could be another client) connects, which makes it
273       *    almost certain that the clipboard client will not connect
274       *    until after XDM when using XDMCP.
275       * 8) Unfortunately, there is another problem.
276       * 9) XDM walks the list of windows with XQueryTree,
277       *    killing any client it finds with a window.
278       * 10)Thus, when using XDMCP we wait until the second call
279       *    to ProcEstablishCeonnection before we startup the clipboard
280       *    client.  This should prevent XDM from finding the clipboard
281       *    client, since it has not yet created a window.
282       */
283
284      /* Create the clipboard client thread */
285      if (!winInitClipboard ())
286	{
287	  ErrorF ("winProcEstablishConnection - winClipboardInit "
288		  "failed.\n");
289	  return iReturn;
290	}
291
292      ErrorF ("winProcEstablishConnection - winInitClipboard returned.\n");
293    }
294
295  /* Flag that clipboard client has been launched */
296  g_fClipboardLaunched = TRUE;
297
298  return iReturn;
299}
300
301
302/*
303 * Wrapper for internal SetSelectionOwner function.
304 * Grabs ownership of Windows clipboard when X11 clipboard owner changes.
305 */
306
307int
308winProcSetSelectionOwner (ClientPtr client)
309{
310  int			i;
311  DrawablePtr		pDrawable;
312  WindowPtr		pWindow = None;
313  Bool			fOwnedToNotOwned = FALSE;
314  static Window		s_iOwners[CLIP_NUM_SELECTIONS] = {None};
315  static unsigned long	s_ulServerGeneration = 0;
316  REQUEST(xSetSelectionOwnerReq);
317
318  REQUEST_SIZE_MATCH(xSetSelectionOwnerReq);
319
320#if 0
321  ErrorF ("winProcSetSelectionOwner - Hello.\n");
322#endif
323
324  /* Watch for server reset */
325  if (s_ulServerGeneration != serverGeneration)
326    {
327      /* Save new generation number */
328      s_ulServerGeneration = serverGeneration;
329
330      /* Initialize static variables */
331      for (i = 0; i < CLIP_NUM_SELECTIONS; ++i)
332	s_iOwners[i] = None;
333    }
334
335  /* Abort if clipboard not completely initialized yet */
336  if (!g_fClipboardStarted)
337    {
338      ErrorF ("winProcSetSelectionOwner - Clipboard not yet started, "
339	      "aborting.\n");
340      goto winProcSetSelectionOwner_Done;
341    }
342
343  /* Grab window if we have one */
344  if (None != stuff->window)
345    {
346      /* Grab the Window from the request */
347      int rc = dixLookupWindow(&pWindow, stuff->window, client, DixReadAccess);
348      if (rc != Success) {
349	  ErrorF ("winProcSetSelectionOwner - Found BadWindow, aborting.\n");
350	  goto winProcSetSelectionOwner_Done;
351      }
352    }
353
354  /* Now we either have a valid window or None */
355
356  /* Save selection owners for monitored selections, ignore other selections */
357  if (XA_PRIMARY == stuff->selection)
358    {
359      /* Look for owned -> not owned transition */
360      if (None == stuff->window
361	  && None != s_iOwners[CLIP_OWN_PRIMARY])
362	{
363	  fOwnedToNotOwned = TRUE;
364
365#if 0
366	  ErrorF ("winProcSetSelectionOwner - PRIMARY - Going from "
367		  "owned to not owned.\n");
368#endif
369
370	  /* Adjust last owned selection */
371	  if (None != s_iOwners[CLIP_OWN_CLIPBOARD])
372	    g_atomLastOwnedSelection = MakeAtom ("CLIPBOARD", 9, TRUE);
373	  else
374	    g_atomLastOwnedSelection = None;
375	}
376
377      /* Save new selection owner or None */
378      s_iOwners[CLIP_OWN_PRIMARY] = stuff->window;
379
380#if 0
381      ErrorF ("winProcSetSelectionOwner - PRIMARY - Now owned by: %d\n",
382	      stuff->window);
383#endif
384    }
385  else if (MakeAtom ("CLIPBOARD", 9, TRUE) == stuff->selection)
386    {
387      /* Look for owned -> not owned transition */
388      if (None == stuff->window
389	  && None != s_iOwners[CLIP_OWN_CLIPBOARD])
390	{
391	  fOwnedToNotOwned = TRUE;
392
393#if 0
394	  ErrorF ("winProcSetSelectionOwner - CLIPBOARD - Going from "
395		  "owned to not owned.\n");
396#endif
397
398	  /* Adjust last owned selection */
399	  if (None != s_iOwners[CLIP_OWN_PRIMARY])
400	    g_atomLastOwnedSelection = XA_PRIMARY;
401	  else
402	    g_atomLastOwnedSelection = None;
403	}
404
405      /* Save new selection owner or None */
406      s_iOwners[CLIP_OWN_CLIPBOARD] = stuff->window;
407
408#if 0
409      ErrorF ("winProcSetSelectionOwner - CLIPBOARD - Now owned by: %d\n",
410	      stuff->window);
411#endif
412    }
413  else
414    goto winProcSetSelectionOwner_Done;
415
416  /*
417   * At this point, if one of the selections is still owned by the
418   * clipboard manager then it should be marked as unowned since
419   * we will be taking ownership of the Win32 clipboard.
420   */
421  if (g_iClipboardWindow == s_iOwners[CLIP_OWN_PRIMARY])
422    s_iOwners[CLIP_OWN_PRIMARY] = None;
423  if (g_iClipboardWindow == s_iOwners[CLIP_OWN_CLIPBOARD])
424    s_iOwners[CLIP_OWN_CLIPBOARD] = None;
425
426  /*
427   * Handle case when selection is being disowned,
428   * WM_DRAWCLIPBOARD did not do the disowning,
429   * both monitored selections are no longer owned,
430   * an owned to not owned transition was detected,
431   * and we currently own the Win32 clipboard.
432   */
433  if (None == stuff->window
434      && (None == s_iOwners[CLIP_OWN_PRIMARY]
435	  || g_iClipboardWindow == s_iOwners[CLIP_OWN_PRIMARY])
436      && (None == s_iOwners[CLIP_OWN_CLIPBOARD]
437	  || g_iClipboardWindow == s_iOwners[CLIP_OWN_CLIPBOARD])
438      && fOwnedToNotOwned
439      && g_hwndClipboard != NULL
440      && g_hwndClipboard == GetClipboardOwner ())
441    {
442#if 0
443      ErrorF ("winProcSetSelectionOwner - We currently own the "
444	      "clipboard and neither the PRIMARY nor the CLIPBOARD "
445	      "selections are owned, releasing ownership of Win32 "
446	      "clipboard.\n");
447#endif
448
449      /* Release ownership of the Windows clipboard */
450      OpenClipboard (NULL);
451      EmptyClipboard ();
452      CloseClipboard ();
453
454      /* Clear X selection ownership (might still be marked as us owning) */
455      s_iOwners[CLIP_OWN_PRIMARY] = None;
456      s_iOwners[CLIP_OWN_CLIPBOARD] = None;
457
458      goto winProcSetSelectionOwner_Done;
459    }
460
461  /* Abort if no window at this point */
462  if (None == stuff->window)
463    {
464#if 0
465      ErrorF ("winProcSetSelectionOwner - No window, returning.\n");
466#endif
467      goto winProcSetSelectionOwner_Done;
468    }
469
470  /* Abort if invalid selection */
471  if (!ValidAtom (stuff->selection))
472    {
473      ErrorF ("winProcSetSelectionOwner - Found BadAtom, aborting.\n");
474      goto winProcSetSelectionOwner_Done;
475    }
476
477  /* Cast Window to Drawable */
478  pDrawable = (DrawablePtr) pWindow;
479
480  /* Abort if clipboard manager is owning the selection */
481  if (pDrawable->id == g_iClipboardWindow)
482    {
483#if 0
484      ErrorF ("winProcSetSelectionOwner - We changed ownership, "
485	      "aborting.\n");
486#endif
487      goto winProcSetSelectionOwner_Done;
488    }
489
490  /* Abort if root window is taking ownership */
491  if (pDrawable->id == 0)
492    {
493      ErrorF ("winProcSetSelectionOwner - Root window taking ownership, "
494	      "aborting\n");
495      goto winProcSetSelectionOwner_Done;
496    }
497
498  /* Close clipboard if we have it open already */
499  if (GetOpenClipboardWindow () == g_hwndClipboard)
500    {
501      CloseClipboard ();
502    }
503
504  /* Access the Windows clipboard */
505  if (!OpenClipboard (g_hwndClipboard))
506    {
507      ErrorF ("winProcSetSelectionOwner - OpenClipboard () failed: %08x\n",
508	      (int) GetLastError ());
509      goto winProcSetSelectionOwner_Done;
510    }
511
512  /* Take ownership of the Windows clipboard */
513  if (!EmptyClipboard ())
514    {
515      ErrorF ("winProcSetSelectionOwner - EmptyClipboard () failed: %08x\n",
516	      (int) GetLastError ());
517      goto winProcSetSelectionOwner_Done;
518    }
519
520  /* Advertise Unicode if we support it */
521  if (g_fUnicodeSupport)
522    SetClipboardData (CF_UNICODETEXT, NULL);
523
524  /* Always advertise regular text */
525  SetClipboardData (CF_TEXT, NULL);
526
527  /* Save handle to last owned selection */
528  g_atomLastOwnedSelection = stuff->selection;
529
530  /* Release the clipboard */
531  if (!CloseClipboard ())
532    {
533      ErrorF ("winProcSetSelectionOwner - CloseClipboard () failed: "
534	      "%08x\n",
535	      (int) GetLastError ());
536      goto winProcSetSelectionOwner_Done;
537    }
538
539 winProcSetSelectionOwner_Done:
540  return (*winProcSetSelectionOwnerOrig) (client);
541}
542