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