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