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 "winclipboard.h"
38#ifdef __CYGWIN__
39#include <errno.h>
40#endif
41#include "misc.h"
42
43
44/*
45 * References to external symbols
46 */
47
48extern Bool		g_fUnicodeClipboard;
49extern unsigned long	serverGeneration;
50extern Bool		g_fClipboardStarted;
51extern HWND		g_hwndClipboard;
52extern void		*g_pClipboardDisplay;
53extern Window		g_iClipboardWindow;
54
55
56/*
57 * Global variables
58 */
59
60static jmp_buf			g_jmpEntry;
61Bool				g_fUnicodeSupport = FALSE;
62Bool				g_fUseUnicode = FALSE;
63
64
65/*
66 * Local function prototypes
67 */
68
69static int
70winClipboardErrorHandler (Display *pDisplay, XErrorEvent *pErr);
71
72static int
73winClipboardIOErrorHandler (Display *pDisplay);
74
75
76/*
77 * Main thread function
78 */
79
80void *
81winClipboardProc (void *pvNotUsed)
82{
83  Atom			atomClipboard, atomClipboardManager;
84  int			iReturn;
85  HWND			hwnd = NULL;
86  int			iConnectionNumber = 0;
87#ifdef HAS_DEVWINDOWS
88  int			fdMessageQueue = 0;
89#else
90  struct timeval        tvTimeout;
91#endif
92  fd_set		fdsRead;
93  int			iMaxDescriptor;
94  Display		*pDisplay = NULL;
95  Window		iWindow = None;
96  int			iRetries;
97  Bool			fUseUnicode;
98  char			szDisplay[512];
99  int			iSelectError;
100
101  ErrorF ("winClipboardProc - Hello\n");
102
103  /* Do we have Unicode support? */
104  g_fUnicodeSupport = winClipboardDetectUnicodeSupport ();
105
106  /* Do we use Unicode clipboard? */
107  fUseUnicode = g_fUnicodeClipboard && g_fUnicodeSupport;
108
109  /* Save the Unicode support flag in a global */
110  g_fUseUnicode = fUseUnicode;
111
112  /* Allow multiple threads to access Xlib */
113  if (XInitThreads () == 0)
114    {
115      ErrorF ("winClipboardProc - XInitThreads failed.\n");
116      pthread_exit (NULL);
117    }
118
119  /* See if X supports the current locale */
120  if (XSupportsLocale () == False)
121    {
122      ErrorF ("winClipboardProc - Warning: Locale not supported by X.\n");
123    }
124
125  /* Set jump point for Error exits */
126  iReturn = setjmp (g_jmpEntry);
127
128  /* Check if we should continue operations */
129  if (iReturn != WIN_JMP_ERROR_IO
130      && iReturn != WIN_JMP_OKAY)
131    {
132      /* setjmp returned an unknown value, exit */
133      ErrorF ("winClipboardProc - setjmp returned: %d exiting\n",
134	      iReturn);
135      pthread_exit (NULL);
136    }
137  else if (iReturn == WIN_JMP_ERROR_IO)
138    {
139      /* TODO: Cleanup the Win32 window and free any allocated memory */
140      ErrorF ("winClipboardProc - setjmp returned for IO Error Handler.\n");
141      pthread_exit (NULL);
142    }
143
144  /* Use our generated cookie for authentication */
145  winSetAuthorization();
146
147  /* Set error handler */
148  XSetErrorHandler (winClipboardErrorHandler);
149  XSetIOErrorHandler (winClipboardIOErrorHandler);
150
151  /* Initialize retry count */
152  iRetries = 0;
153
154  /* Setup the display connection string x */
155  /*
156   * NOTE: Always connect to screen 0 since we require that screen
157   * numbers start at 0 and increase without gaps.  We only need
158   * to connect to one screen on the display to get events
159   * for all screens on the display.  That is why there is only
160   * one clipboard client thread.
161   */
162  snprintf (szDisplay,
163	    512,
164	    "127.0.0.1:%s.0",
165	    display);
166
167  /* Print the display connection string */
168  ErrorF ("winClipboardProc - DISPLAY=%s\n", szDisplay);
169
170  /* Open the X display */
171  do
172    {
173      pDisplay = XOpenDisplay (szDisplay);
174      if (pDisplay == NULL)
175	{
176	  ErrorF ("winClipboardProc - Could not open display, "
177		  "try: %d, sleeping: %d\n",
178		  iRetries + 1, WIN_CONNECT_DELAY);
179	  ++iRetries;
180	  sleep (WIN_CONNECT_DELAY);
181	  continue;
182	}
183      else
184	break;
185    }
186  while (pDisplay == NULL && iRetries < WIN_CONNECT_RETRIES);
187
188  /* Make sure that the display opened */
189  if (pDisplay == NULL)
190    {
191      ErrorF ("winClipboardProc - Failed opening the display, giving up\n");
192      pthread_exit (NULL);
193    }
194
195  /* Save the display in the screen privates */
196  g_pClipboardDisplay = pDisplay;
197
198  ErrorF ("winClipboardProc - XOpenDisplay () returned and "
199	  "successfully opened the display.\n");
200
201  /* Get our connection number */
202  iConnectionNumber = ConnectionNumber (pDisplay);
203
204#ifdef HAS_DEVWINDOWS
205  /* Open a file descriptor for the windows message queue */
206  fdMessageQueue = open (WIN_MSG_QUEUE_FNAME, O_RDONLY);
207  if (fdMessageQueue == -1)
208    {
209      ErrorF ("winClipboardProc - Failed opening %s\n", WIN_MSG_QUEUE_FNAME);
210      pthread_exit (NULL);
211    }
212
213  /* Find max of our file descriptors */
214  iMaxDescriptor = max (fdMessageQueue, iConnectionNumber) + 1;
215#else
216  iMaxDescriptor = iConnectionNumber + 1;
217#endif
218
219  /* Create atoms */
220  atomClipboard = XInternAtom (pDisplay, "CLIPBOARD", False);
221  atomClipboardManager = XInternAtom (pDisplay, "CLIPBOARD_MANAGER", False);
222
223  /* Create a messaging window */
224  iWindow = XCreateSimpleWindow (pDisplay,
225				 DefaultRootWindow (pDisplay),
226				 1, 1,
227				 500, 500,
228				 0,
229				 BlackPixel (pDisplay, 0),
230				 BlackPixel (pDisplay, 0));
231  if (iWindow == 0)
232    {
233      ErrorF ("winClipboardProc - Could not create an X window.\n");
234      pthread_exit (NULL);
235    }
236
237  /* Select event types to watch */
238  if (XSelectInput (pDisplay,
239		    iWindow,
240		    PropertyChangeMask) == BadWindow)
241    ErrorF ("winClipboardProc - XSelectInput generated BadWindow "
242	    "on messaging window\n");
243
244  /* Save the window in the screen privates */
245  g_iClipboardWindow = iWindow;
246
247  /* Create Windows messaging window */
248  hwnd = winClipboardCreateMessagingWindow ();
249
250  /* Save copy of HWND in screen privates */
251  g_hwndClipboard = hwnd;
252
253  /* Assert ownership of selections if Win32 clipboard is owned */
254  if (NULL != GetClipboardOwner ())
255    {
256      /* PRIMARY */
257      iReturn = XSetSelectionOwner (pDisplay, XA_PRIMARY,
258				    iWindow, CurrentTime);
259      if (iReturn == BadAtom || iReturn == BadWindow ||
260	  XGetSelectionOwner (pDisplay, XA_PRIMARY) != iWindow)
261	{
262	  ErrorF ("winClipboardProc - Could not set PRIMARY owner\n");
263	  pthread_exit (NULL);
264	}
265
266      /* CLIPBOARD */
267      iReturn = XSetSelectionOwner (pDisplay, atomClipboard,
268				    iWindow, CurrentTime);
269      if (iReturn == BadAtom || iReturn == BadWindow ||
270	  XGetSelectionOwner (pDisplay, atomClipboard) != iWindow)
271	{
272	  ErrorF ("winClipboardProc - Could not set CLIPBOARD owner\n");
273	  pthread_exit (NULL);
274	}
275    }
276
277  /* Pre-flush X events */
278  /*
279   * NOTE: Apparently you'll freeze if you don't do this,
280   *	   because there may be events in local data structures
281   *	   already.
282   */
283  winClipboardFlushXEvents (hwnd,
284			    iWindow,
285			    pDisplay,
286			    fUseUnicode);
287
288  /* Pre-flush Windows messages */
289  if (!winClipboardFlushWindowsMessageQueue (hwnd))
290    return 0;
291
292  /* Signal that the clipboard client has started */
293  g_fClipboardStarted = TRUE;
294
295  /* Loop for X events */
296  while (1)
297    {
298      /* Setup the file descriptor set */
299      /*
300       * NOTE: You have to do this before every call to select
301       *       because select modifies the mask to indicate
302       *       which descriptors are ready.
303       */
304      FD_ZERO (&fdsRead);
305      FD_SET (iConnectionNumber, &fdsRead);
306#ifdef HAS_DEVWINDOWS
307      FD_SET (fdMessageQueue, &fdsRead);
308#else
309      tvTimeout.tv_sec = 0;
310      tvTimeout.tv_usec = 100;
311#endif
312
313      /* Wait for a Windows event or an X event */
314      iReturn = select (iMaxDescriptor,	/* Highest fds number */
315			&fdsRead,	/* Read mask */
316			NULL,		/* No write mask */
317			NULL,		/* No exception mask */
318#ifdef HAS_DEVWINDOWS
319			NULL		/* No timeout */
320#else
321			&tvTimeout      /* Set timeout */
322#endif
323          );
324
325#ifndef HAS_WINSOCK
326      iSelectError = errno;
327#else
328      iSelectError = WSAGetLastError();
329#endif
330
331      if (iReturn < 0)
332	{
333#ifndef HAS_WINSOCK
334          if (iSelectError == EINTR)
335#else
336          if (iSelectError == WSAEINTR)
337#endif
338            continue;
339
340	  ErrorF ("winClipboardProc - Call to select () failed: %d.  "
341		  "Bailing.\n", iReturn);
342	  break;
343	}
344
345      /* Branch on which descriptor became active */
346      if (FD_ISSET (iConnectionNumber, &fdsRead))
347	{
348	  /* Process X events */
349	  /* Exit when we see that server is shutting down */
350	  iReturn = winClipboardFlushXEvents (hwnd,
351					      iWindow,
352					      pDisplay,
353					      fUseUnicode);
354	  if (WIN_XEVENTS_SHUTDOWN == iReturn)
355	    {
356	      ErrorF ("winClipboardProc - winClipboardFlushXEvents "
357		      "trapped shutdown event, exiting main loop.\n");
358	      break;
359	    }
360	}
361
362#ifdef HAS_DEVWINDOWS
363      /* Check for Windows event ready */
364      if (FD_ISSET (fdMessageQueue, &fdsRead))
365#else
366      if (1)
367#endif
368	{
369	  /* Process Windows messages */
370	  if (!winClipboardFlushWindowsMessageQueue (hwnd))
371	    {
372	      ErrorF ("winClipboardProc - "
373		      "winClipboardFlushWindowsMessageQueue trapped "
374		      "WM_QUIT message, exiting main loop.\n");
375	      break;
376	    }
377	}
378    }
379
380  /* Close our X window */
381  if (pDisplay && iWindow)
382    {
383      iReturn = XDestroyWindow (pDisplay, iWindow);
384      if (iReturn == BadWindow)
385	ErrorF ("winClipboardProc - XDestroyWindow returned BadWindow.\n");
386      else
387	ErrorF ("winClipboardProc - XDestroyWindow succeeded.\n");
388    }
389
390
391#ifdef HAS_DEVWINDOWS
392  /* Close our Win32 message handle */
393  if (fdMessageQueue)
394    close (fdMessageQueue);
395#endif
396
397#if 0
398  /*
399   * FIXME: XCloseDisplay hangs if we call it, as of 2004/03/26.  The
400   * XSync and XSelectInput calls did not help.
401   */
402
403  /* Discard any remaining events */
404  XSync (pDisplay, TRUE);
405
406  /* Select event types to watch */
407  XSelectInput (pDisplay,
408		DefaultRootWindow (pDisplay),
409		None);
410
411  /* Close our X display */
412  if (pDisplay)
413    {
414      XCloseDisplay (pDisplay);
415    }
416#endif
417
418  g_iClipboardWindow = None;
419  g_pClipboardDisplay = NULL;
420  g_hwndClipboard = NULL;
421
422  return NULL;
423}
424
425
426/*
427 * winClipboardErrorHandler - Our application specific error handler
428 */
429
430static int
431winClipboardErrorHandler (Display *pDisplay, XErrorEvent *pErr)
432{
433  char pszErrorMsg[100];
434
435  XGetErrorText (pDisplay,
436		 pErr->error_code,
437		 pszErrorMsg,
438		 sizeof (pszErrorMsg));
439  ErrorF ("winClipboardErrorHandler - ERROR: \n\t%s\n"
440	  "\tSerial: %lu, Request Code: %d, Minor Code: %d\n",
441	  pszErrorMsg,
442	  pErr->serial,
443	  pErr->request_code,
444	  pErr->minor_code);
445  return 0;
446}
447
448
449/*
450 * winClipboardIOErrorHandler - Our application specific IO error handler
451 */
452
453static int
454winClipboardIOErrorHandler (Display *pDisplay)
455{
456  ErrorF ("winClipboardIOErrorHandler!\n\n");
457
458  /* Restart at the main entry point */
459  longjmp (g_jmpEntry, WIN_JMP_ERROR_IO);
460
461  return 0;
462}
463