winclipboardthread.c revision 706f2543
1706f2543Smrg/*
2706f2543Smrg *Copyright (C) 2003-2004 Harold L Hunt II All Rights Reserved.
3706f2543Smrg *Copyright (C) Colin Harrison 2005-2008
4706f2543Smrg *
5706f2543Smrg *Permission is hereby granted, free of charge, to any person obtaining
6706f2543Smrg * a copy of this software and associated documentation files (the
7706f2543Smrg *"Software"), to deal in the Software without restriction, including
8706f2543Smrg *without limitation the rights to use, copy, modify, merge, publish,
9706f2543Smrg *distribute, sublicense, and/or sell copies of the Software, and to
10706f2543Smrg *permit persons to whom the Software is furnished to do so, subject to
11706f2543Smrg *the following conditions:
12706f2543Smrg *
13706f2543Smrg *The above copyright notice and this permission notice shall be
14706f2543Smrg *included in all copies or substantial portions of the Software.
15706f2543Smrg *
16706f2543Smrg *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17706f2543Smrg *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18706f2543Smrg *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19706f2543Smrg *NONINFRINGEMENT. IN NO EVENT SHALL HAROLD L HUNT II BE LIABLE FOR
20706f2543Smrg *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
21706f2543Smrg *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22706f2543Smrg *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23706f2543Smrg *
24706f2543Smrg *Except as contained in this notice, the name of the copyright holder(s)
25706f2543Smrg *and author(s) shall not be used in advertising or otherwise to promote
26706f2543Smrg *the sale, use or other dealings in this Software without prior written
27706f2543Smrg *authorization from the copyright holder(s) and author(s).
28706f2543Smrg *
29706f2543Smrg * Authors:	Harold L Hunt II
30706f2543Smrg *              Colin Harrison
31706f2543Smrg */
32706f2543Smrg
33706f2543Smrg#ifdef HAVE_XWIN_CONFIG_H
34706f2543Smrg#include <xwin-config.h>
35706f2543Smrg#endif
36706f2543Smrg#include <sys/types.h>
37706f2543Smrg#include "winclipboard.h"
38706f2543Smrg#ifdef __CYGWIN__
39706f2543Smrg#include <errno.h>
40706f2543Smrg#endif
41706f2543Smrg#include "misc.h"
42706f2543Smrg
43706f2543Smrg
44706f2543Smrg/*
45706f2543Smrg * References to external symbols
46706f2543Smrg */
47706f2543Smrg
48706f2543Smrgextern Bool		g_fUnicodeClipboard;
49706f2543Smrgextern unsigned long	serverGeneration;
50706f2543Smrgextern Bool		g_fClipboardStarted;
51706f2543Smrgextern HWND		g_hwndClipboard;
52706f2543Smrgextern void		*g_pClipboardDisplay;
53706f2543Smrgextern Window		g_iClipboardWindow;
54706f2543Smrg
55706f2543Smrg
56706f2543Smrg/*
57706f2543Smrg * Global variables
58706f2543Smrg */
59706f2543Smrg
60706f2543Smrgstatic jmp_buf			g_jmpEntry;
61706f2543SmrgBool				g_fUnicodeSupport = FALSE;
62706f2543SmrgBool				g_fUseUnicode = FALSE;
63706f2543Smrg
64706f2543Smrg
65706f2543Smrg/*
66706f2543Smrg * Local function prototypes
67706f2543Smrg */
68706f2543Smrg
69706f2543Smrgstatic int
70706f2543SmrgwinClipboardErrorHandler (Display *pDisplay, XErrorEvent *pErr);
71706f2543Smrg
72706f2543Smrgstatic int
73706f2543SmrgwinClipboardIOErrorHandler (Display *pDisplay);
74706f2543Smrg
75706f2543Smrg
76706f2543Smrg/*
77706f2543Smrg * Main thread function
78706f2543Smrg */
79706f2543Smrg
80706f2543Smrgvoid *
81706f2543SmrgwinClipboardProc (void *pvNotUsed)
82706f2543Smrg{
83706f2543Smrg  Atom			atomClipboard, atomClipboardManager;
84706f2543Smrg  int			iReturn;
85706f2543Smrg  HWND			hwnd = NULL;
86706f2543Smrg  int			iConnectionNumber = 0;
87706f2543Smrg#ifdef HAS_DEVWINDOWS
88706f2543Smrg  int			fdMessageQueue = 0;
89706f2543Smrg#else
90706f2543Smrg  struct timeval        tvTimeout;
91706f2543Smrg#endif
92706f2543Smrg  fd_set		fdsRead;
93706f2543Smrg  int			iMaxDescriptor;
94706f2543Smrg  Display		*pDisplay = NULL;
95706f2543Smrg  Window		iWindow = None;
96706f2543Smrg  int			iRetries;
97706f2543Smrg  Bool			fUseUnicode;
98706f2543Smrg  char			szDisplay[512];
99706f2543Smrg  int			iSelectError;
100706f2543Smrg
101706f2543Smrg  ErrorF ("winClipboardProc - Hello\n");
102706f2543Smrg
103706f2543Smrg  /* Do we have Unicode support? */
104706f2543Smrg  g_fUnicodeSupport = winClipboardDetectUnicodeSupport ();
105706f2543Smrg
106706f2543Smrg  /* Do we use Unicode clipboard? */
107706f2543Smrg  fUseUnicode = g_fUnicodeClipboard && g_fUnicodeSupport;
108706f2543Smrg
109706f2543Smrg  /* Save the Unicode support flag in a global */
110706f2543Smrg  g_fUseUnicode = fUseUnicode;
111706f2543Smrg
112706f2543Smrg  /* Allow multiple threads to access Xlib */
113706f2543Smrg  if (XInitThreads () == 0)
114706f2543Smrg    {
115706f2543Smrg      ErrorF ("winClipboardProc - XInitThreads failed.\n");
116706f2543Smrg      pthread_exit (NULL);
117706f2543Smrg    }
118706f2543Smrg
119706f2543Smrg  /* See if X supports the current locale */
120706f2543Smrg  if (XSupportsLocale () == False)
121706f2543Smrg    {
122706f2543Smrg      ErrorF ("winClipboardProc - Warning: Locale not supported by X.\n");
123706f2543Smrg    }
124706f2543Smrg
125706f2543Smrg  /* Set jump point for Error exits */
126706f2543Smrg  iReturn = setjmp (g_jmpEntry);
127706f2543Smrg
128706f2543Smrg  /* Check if we should continue operations */
129706f2543Smrg  if (iReturn != WIN_JMP_ERROR_IO
130706f2543Smrg      && iReturn != WIN_JMP_OKAY)
131706f2543Smrg    {
132706f2543Smrg      /* setjmp returned an unknown value, exit */
133706f2543Smrg      ErrorF ("winClipboardProc - setjmp returned: %d exiting\n",
134706f2543Smrg	      iReturn);
135706f2543Smrg      pthread_exit (NULL);
136706f2543Smrg    }
137706f2543Smrg  else if (iReturn == WIN_JMP_ERROR_IO)
138706f2543Smrg    {
139706f2543Smrg      /* TODO: Cleanup the Win32 window and free any allocated memory */
140706f2543Smrg      ErrorF ("winClipboardProc - setjmp returned for IO Error Handler.\n");
141706f2543Smrg      pthread_exit (NULL);
142706f2543Smrg    }
143706f2543Smrg
144706f2543Smrg  /* Use our generated cookie for authentication */
145706f2543Smrg  winSetAuthorization();
146706f2543Smrg
147706f2543Smrg  /* Set error handler */
148706f2543Smrg  XSetErrorHandler (winClipboardErrorHandler);
149706f2543Smrg  XSetIOErrorHandler (winClipboardIOErrorHandler);
150706f2543Smrg
151706f2543Smrg  /* Initialize retry count */
152706f2543Smrg  iRetries = 0;
153706f2543Smrg
154706f2543Smrg  /* Setup the display connection string x */
155706f2543Smrg  /*
156706f2543Smrg   * NOTE: Always connect to screen 0 since we require that screen
157706f2543Smrg   * numbers start at 0 and increase without gaps.  We only need
158706f2543Smrg   * to connect to one screen on the display to get events
159706f2543Smrg   * for all screens on the display.  That is why there is only
160706f2543Smrg   * one clipboard client thread.
161706f2543Smrg   */
162706f2543Smrg  snprintf (szDisplay,
163706f2543Smrg	    512,
164706f2543Smrg	    "127.0.0.1:%s.0",
165706f2543Smrg	    display);
166706f2543Smrg
167706f2543Smrg  /* Print the display connection string */
168706f2543Smrg  ErrorF ("winClipboardProc - DISPLAY=%s\n", szDisplay);
169706f2543Smrg
170706f2543Smrg  /* Open the X display */
171706f2543Smrg  do
172706f2543Smrg    {
173706f2543Smrg      pDisplay = XOpenDisplay (szDisplay);
174706f2543Smrg      if (pDisplay == NULL)
175706f2543Smrg	{
176706f2543Smrg	  ErrorF ("winClipboardProc - Could not open display, "
177706f2543Smrg		  "try: %d, sleeping: %d\n",
178706f2543Smrg		  iRetries + 1, WIN_CONNECT_DELAY);
179706f2543Smrg	  ++iRetries;
180706f2543Smrg	  sleep (WIN_CONNECT_DELAY);
181706f2543Smrg	  continue;
182706f2543Smrg	}
183706f2543Smrg      else
184706f2543Smrg	break;
185706f2543Smrg    }
186706f2543Smrg  while (pDisplay == NULL && iRetries < WIN_CONNECT_RETRIES);
187706f2543Smrg
188706f2543Smrg  /* Make sure that the display opened */
189706f2543Smrg  if (pDisplay == NULL)
190706f2543Smrg    {
191706f2543Smrg      ErrorF ("winClipboardProc - Failed opening the display, giving up\n");
192706f2543Smrg      pthread_exit (NULL);
193706f2543Smrg    }
194706f2543Smrg
195706f2543Smrg  /* Save the display in the screen privates */
196706f2543Smrg  g_pClipboardDisplay = pDisplay;
197706f2543Smrg
198706f2543Smrg  ErrorF ("winClipboardProc - XOpenDisplay () returned and "
199706f2543Smrg	  "successfully opened the display.\n");
200706f2543Smrg
201706f2543Smrg  /* Get our connection number */
202706f2543Smrg  iConnectionNumber = ConnectionNumber (pDisplay);
203706f2543Smrg
204706f2543Smrg#ifdef HAS_DEVWINDOWS
205706f2543Smrg  /* Open a file descriptor for the windows message queue */
206706f2543Smrg  fdMessageQueue = open (WIN_MSG_QUEUE_FNAME, O_RDONLY);
207706f2543Smrg  if (fdMessageQueue == -1)
208706f2543Smrg    {
209706f2543Smrg      ErrorF ("winClipboardProc - Failed opening %s\n", WIN_MSG_QUEUE_FNAME);
210706f2543Smrg      pthread_exit (NULL);
211706f2543Smrg    }
212706f2543Smrg
213706f2543Smrg  /* Find max of our file descriptors */
214706f2543Smrg  iMaxDescriptor = max (fdMessageQueue, iConnectionNumber) + 1;
215706f2543Smrg#else
216706f2543Smrg  iMaxDescriptor = iConnectionNumber + 1;
217706f2543Smrg#endif
218706f2543Smrg
219706f2543Smrg  /* Create atoms */
220706f2543Smrg  atomClipboard = XInternAtom (pDisplay, "CLIPBOARD", False);
221706f2543Smrg  atomClipboardManager = XInternAtom (pDisplay, "CLIPBOARD_MANAGER", False);
222706f2543Smrg
223706f2543Smrg  /* Create a messaging window */
224706f2543Smrg  iWindow = XCreateSimpleWindow (pDisplay,
225706f2543Smrg				 DefaultRootWindow (pDisplay),
226706f2543Smrg				 1, 1,
227706f2543Smrg				 500, 500,
228706f2543Smrg				 0,
229706f2543Smrg				 BlackPixel (pDisplay, 0),
230706f2543Smrg				 BlackPixel (pDisplay, 0));
231706f2543Smrg  if (iWindow == 0)
232706f2543Smrg    {
233706f2543Smrg      ErrorF ("winClipboardProc - Could not create an X window.\n");
234706f2543Smrg      pthread_exit (NULL);
235706f2543Smrg    }
236706f2543Smrg
237706f2543Smrg  /* Select event types to watch */
238706f2543Smrg  if (XSelectInput (pDisplay,
239706f2543Smrg		    iWindow,
240706f2543Smrg		    PropertyChangeMask) == BadWindow)
241706f2543Smrg    ErrorF ("winClipboardProc - XSelectInput generated BadWindow "
242706f2543Smrg	    "on messaging window\n");
243706f2543Smrg
244706f2543Smrg  /* Save the window in the screen privates */
245706f2543Smrg  g_iClipboardWindow = iWindow;
246706f2543Smrg
247706f2543Smrg  /* Create Windows messaging window */
248706f2543Smrg  hwnd = winClipboardCreateMessagingWindow ();
249706f2543Smrg
250706f2543Smrg  /* Save copy of HWND in screen privates */
251706f2543Smrg  g_hwndClipboard = hwnd;
252706f2543Smrg
253706f2543Smrg  /* Assert ownership of selections if Win32 clipboard is owned */
254706f2543Smrg  if (NULL != GetClipboardOwner ())
255706f2543Smrg    {
256706f2543Smrg      /* PRIMARY */
257706f2543Smrg      iReturn = XSetSelectionOwner (pDisplay, XA_PRIMARY,
258706f2543Smrg				    iWindow, CurrentTime);
259706f2543Smrg      if (iReturn == BadAtom || iReturn == BadWindow ||
260706f2543Smrg	  XGetSelectionOwner (pDisplay, XA_PRIMARY) != iWindow)
261706f2543Smrg	{
262706f2543Smrg	  ErrorF ("winClipboardProc - Could not set PRIMARY owner\n");
263706f2543Smrg	  pthread_exit (NULL);
264706f2543Smrg	}
265706f2543Smrg
266706f2543Smrg      /* CLIPBOARD */
267706f2543Smrg      iReturn = XSetSelectionOwner (pDisplay, atomClipboard,
268706f2543Smrg				    iWindow, CurrentTime);
269706f2543Smrg      if (iReturn == BadAtom || iReturn == BadWindow ||
270706f2543Smrg	  XGetSelectionOwner (pDisplay, atomClipboard) != iWindow)
271706f2543Smrg	{
272706f2543Smrg	  ErrorF ("winClipboardProc - Could not set CLIPBOARD owner\n");
273706f2543Smrg	  pthread_exit (NULL);
274706f2543Smrg	}
275706f2543Smrg    }
276706f2543Smrg
277706f2543Smrg  /* Pre-flush X events */
278706f2543Smrg  /*
279706f2543Smrg   * NOTE: Apparently you'll freeze if you don't do this,
280706f2543Smrg   *	   because there may be events in local data structures
281706f2543Smrg   *	   already.
282706f2543Smrg   */
283706f2543Smrg  winClipboardFlushXEvents (hwnd,
284706f2543Smrg			    iWindow,
285706f2543Smrg			    pDisplay,
286706f2543Smrg			    fUseUnicode);
287706f2543Smrg
288706f2543Smrg  /* Pre-flush Windows messages */
289706f2543Smrg  if (!winClipboardFlushWindowsMessageQueue (hwnd))
290706f2543Smrg    return 0;
291706f2543Smrg
292706f2543Smrg  /* Signal that the clipboard client has started */
293706f2543Smrg  g_fClipboardStarted = TRUE;
294706f2543Smrg
295706f2543Smrg  /* Loop for X events */
296706f2543Smrg  while (1)
297706f2543Smrg    {
298706f2543Smrg      /* Setup the file descriptor set */
299706f2543Smrg      /*
300706f2543Smrg       * NOTE: You have to do this before every call to select
301706f2543Smrg       *       because select modifies the mask to indicate
302706f2543Smrg       *       which descriptors are ready.
303706f2543Smrg       */
304706f2543Smrg      FD_ZERO (&fdsRead);
305706f2543Smrg      FD_SET (iConnectionNumber, &fdsRead);
306706f2543Smrg#ifdef HAS_DEVWINDOWS
307706f2543Smrg      FD_SET (fdMessageQueue, &fdsRead);
308706f2543Smrg#else
309706f2543Smrg      tvTimeout.tv_sec = 0;
310706f2543Smrg      tvTimeout.tv_usec = 100;
311706f2543Smrg#endif
312706f2543Smrg
313706f2543Smrg      /* Wait for a Windows event or an X event */
314706f2543Smrg      iReturn = select (iMaxDescriptor,	/* Highest fds number */
315706f2543Smrg			&fdsRead,	/* Read mask */
316706f2543Smrg			NULL,		/* No write mask */
317706f2543Smrg			NULL,		/* No exception mask */
318706f2543Smrg#ifdef HAS_DEVWINDOWS
319706f2543Smrg			NULL		/* No timeout */
320706f2543Smrg#else
321706f2543Smrg			&tvTimeout      /* Set timeout */
322706f2543Smrg#endif
323706f2543Smrg          );
324706f2543Smrg
325706f2543Smrg#ifndef HAS_WINSOCK
326706f2543Smrg      iSelectError = errno;
327706f2543Smrg#else
328706f2543Smrg      iSelectError = WSAGetLastError();
329706f2543Smrg#endif
330706f2543Smrg
331706f2543Smrg      if (iReturn < 0)
332706f2543Smrg	{
333706f2543Smrg#ifndef HAS_WINSOCK
334706f2543Smrg          if (iSelectError == EINTR)
335706f2543Smrg#else
336706f2543Smrg          if (iSelectError == WSAEINTR)
337706f2543Smrg#endif
338706f2543Smrg            continue;
339706f2543Smrg
340706f2543Smrg	  ErrorF ("winClipboardProc - Call to select () failed: %d.  "
341706f2543Smrg		  "Bailing.\n", iReturn);
342706f2543Smrg	  break;
343706f2543Smrg	}
344706f2543Smrg
345706f2543Smrg      /* Branch on which descriptor became active */
346706f2543Smrg      if (FD_ISSET (iConnectionNumber, &fdsRead))
347706f2543Smrg	{
348706f2543Smrg	  /* Process X events */
349706f2543Smrg	  /* Exit when we see that server is shutting down */
350706f2543Smrg	  iReturn = winClipboardFlushXEvents (hwnd,
351706f2543Smrg					      iWindow,
352706f2543Smrg					      pDisplay,
353706f2543Smrg					      fUseUnicode);
354706f2543Smrg	  if (WIN_XEVENTS_SHUTDOWN == iReturn)
355706f2543Smrg	    {
356706f2543Smrg	      ErrorF ("winClipboardProc - winClipboardFlushXEvents "
357706f2543Smrg		      "trapped shutdown event, exiting main loop.\n");
358706f2543Smrg	      break;
359706f2543Smrg	    }
360706f2543Smrg	}
361706f2543Smrg
362706f2543Smrg#ifdef HAS_DEVWINDOWS
363706f2543Smrg      /* Check for Windows event ready */
364706f2543Smrg      if (FD_ISSET (fdMessageQueue, &fdsRead))
365706f2543Smrg#else
366706f2543Smrg      if (1)
367706f2543Smrg#endif
368706f2543Smrg	{
369706f2543Smrg	  /* Process Windows messages */
370706f2543Smrg	  if (!winClipboardFlushWindowsMessageQueue (hwnd))
371706f2543Smrg	    {
372706f2543Smrg	      ErrorF ("winClipboardProc - "
373706f2543Smrg		      "winClipboardFlushWindowsMessageQueue trapped "
374706f2543Smrg		      "WM_QUIT message, exiting main loop.\n");
375706f2543Smrg	      break;
376706f2543Smrg	    }
377706f2543Smrg	}
378706f2543Smrg    }
379706f2543Smrg
380706f2543Smrg  /* Close our X window */
381706f2543Smrg  if (pDisplay && iWindow)
382706f2543Smrg    {
383706f2543Smrg      iReturn = XDestroyWindow (pDisplay, iWindow);
384706f2543Smrg      if (iReturn == BadWindow)
385706f2543Smrg	ErrorF ("winClipboardProc - XDestroyWindow returned BadWindow.\n");
386706f2543Smrg      else
387706f2543Smrg	ErrorF ("winClipboardProc - XDestroyWindow succeeded.\n");
388706f2543Smrg    }
389706f2543Smrg
390706f2543Smrg
391706f2543Smrg#ifdef HAS_DEVWINDOWS
392706f2543Smrg  /* Close our Win32 message handle */
393706f2543Smrg  if (fdMessageQueue)
394706f2543Smrg    close (fdMessageQueue);
395706f2543Smrg#endif
396706f2543Smrg
397706f2543Smrg#if 0
398706f2543Smrg  /*
399706f2543Smrg   * FIXME: XCloseDisplay hangs if we call it, as of 2004/03/26.  The
400706f2543Smrg   * XSync and XSelectInput calls did not help.
401706f2543Smrg   */
402706f2543Smrg
403706f2543Smrg  /* Discard any remaining events */
404706f2543Smrg  XSync (pDisplay, TRUE);
405706f2543Smrg
406706f2543Smrg  /* Select event types to watch */
407706f2543Smrg  XSelectInput (pDisplay,
408706f2543Smrg		DefaultRootWindow (pDisplay),
409706f2543Smrg		None);
410706f2543Smrg
411706f2543Smrg  /* Close our X display */
412706f2543Smrg  if (pDisplay)
413706f2543Smrg    {
414706f2543Smrg      XCloseDisplay (pDisplay);
415706f2543Smrg    }
416706f2543Smrg#endif
417706f2543Smrg
418706f2543Smrg  g_iClipboardWindow = None;
419706f2543Smrg  g_pClipboardDisplay = NULL;
420706f2543Smrg  g_hwndClipboard = NULL;
421706f2543Smrg
422706f2543Smrg  return NULL;
423706f2543Smrg}
424706f2543Smrg
425706f2543Smrg
426706f2543Smrg/*
427706f2543Smrg * winClipboardErrorHandler - Our application specific error handler
428706f2543Smrg */
429706f2543Smrg
430706f2543Smrgstatic int
431706f2543SmrgwinClipboardErrorHandler (Display *pDisplay, XErrorEvent *pErr)
432706f2543Smrg{
433706f2543Smrg  char pszErrorMsg[100];
434706f2543Smrg
435706f2543Smrg  XGetErrorText (pDisplay,
436706f2543Smrg		 pErr->error_code,
437706f2543Smrg		 pszErrorMsg,
438706f2543Smrg		 sizeof (pszErrorMsg));
439706f2543Smrg  ErrorF ("winClipboardErrorHandler - ERROR: \n\t%s\n"
440706f2543Smrg	  "\tSerial: %lu, Request Code: %d, Minor Code: %d\n",
441706f2543Smrg	  pszErrorMsg,
442706f2543Smrg	  pErr->serial,
443706f2543Smrg	  pErr->request_code,
444706f2543Smrg	  pErr->minor_code);
445706f2543Smrg  return 0;
446706f2543Smrg}
447706f2543Smrg
448706f2543Smrg
449706f2543Smrg/*
450706f2543Smrg * winClipboardIOErrorHandler - Our application specific IO error handler
451706f2543Smrg */
452706f2543Smrg
453706f2543Smrgstatic int
454706f2543SmrgwinClipboardIOErrorHandler (Display *pDisplay)
455706f2543Smrg{
456706f2543Smrg  ErrorF ("winClipboardIOErrorHandler!\n\n");
457706f2543Smrg
458706f2543Smrg  /* Restart at the main entry point */
459706f2543Smrg  longjmp (g_jmpEntry, WIN_JMP_ERROR_IO);
460706f2543Smrg
461706f2543Smrg  return 0;
462706f2543Smrg}
463