Commit | Line | Data |
---|---|---|
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 | ||
50 | extern Bool g_fUnicodeClipboard; | |
51 | extern Bool g_fClipboardStarted; | |
52 | extern Bool g_fClipboardLaunched; | |
53 | extern Bool g_fClipboard; | |
54 | extern HWND g_hwndClipboard; | |
55 | extern void *g_pClipboardDisplay; | |
56 | extern Window g_iClipboardWindow; | |
57 | ||
58 | /* | |
59 | * Global variables | |
60 | */ | |
61 | ||
62 | static jmp_buf g_jmpEntry; | |
63 | static int clipboardRestarts = 0; | |
64 | static XIOErrorHandler g_winClipboardOldIOErrorHandler; | |
65 | static pthread_t g_winClipboardProcThread; | |
66 | ||
67 | Bool g_fUseUnicode = FALSE; | |
68 | ||
69 | /* | |
70 | * Local function prototypes | |
71 | */ | |
72 | ||
73 | static int | |
74 | winClipboardErrorHandler(Display * pDisplay, XErrorEvent * pErr); | |
75 | ||
76 | static int | |
77 | winClipboardIOErrorHandler(Display * pDisplay); | |
78 | ||
79 | /* | |
80 | * Main thread function | |
81 | */ | |
82 | ||
83 | void * | |
84 | winClipboardProc(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 | ||
437 | static int | |
438 | winClipboardErrorHandler(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 | ||
453 | static int | |
454 | winClipboardIOErrorHandler(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 | } |