1 /* Handle clipboard text and data in arbitrary formats */
2 
3 #include <stdio.h>
4 #include <limits.h>
5 
6 #ifdef WIN32
7 #include <SDL.h>
8 #include <SDL_syswm.h>
9 #else
10 #include <SDL/SDL.h>
11 #include <SDL/SDL_syswm.h>
12 #endif
13 #include "scrap.h"
14 #include "rfb/rfbconfig.h"
15 
16 /* Determine what type of clipboard we are using */
17 #if defined(__unix__) && !defined(__QNXNTO__) && defined(LIBVNCSERVER_HAVE_X11)
18 #define X11_SCRAP
19 #elif defined(__WIN32__)
20 #define WIN_SCRAP
21 #elif defined(__QNXNTO__)
22 #define QNX_SCRAP
23 #else
24 #warning Unknown window manager for clipboard handling
25 #endif /* scrap type */
26 
27 /* System dependent data types */
28 #if defined(X11_SCRAP)
29 typedef Atom scrap_type;
30 static Atom XA_TARGETS, XA_TEXT, XA_COMPOUND_TEXT, XA_UTF8_STRING;
31 #elif defined(WIN_SCRAP)
32 typedef UINT scrap_type;
33 #elif defined(QNX_SCRAP)
34 typedef uint32_t scrap_type;
35 #define Ph_CL_TEXT T('T', 'E', 'X', 'T')
36 #else
37 typedef int scrap_type;
38 #endif /* scrap type */
39 
40 /* System dependent variables */
41 #if defined(X11_SCRAP)
42 static Display *SDL_Display;
43 static Window SDL_Window;
44 static void (*Lock_Display)(void);
45 static void (*Unlock_Display)(void);
46 static Atom XA_UTF8_STRING;
47 #elif defined(WIN_SCRAP)
48 static HWND SDL_Window;
49 #elif defined(QNX_SCRAP)
50 static unsigned short InputGroup;
51 #endif /* scrap type */
52 
53 #define FORMAT_PREFIX	"SDL_scrap_0x"
54 
convert_format(int type)55 static scrap_type convert_format(int type)
56 {
57 	switch (type) {
58 	case T('T', 'E', 'X', 'T'):
59 #if defined(X11_SCRAP)
60 		return XA_UTF8_STRING ? XA_UTF8_STRING : XA_STRING;
61 #elif defined(WIN_SCRAP)
62 		return CF_TEXT;
63 #elif defined(QNX_SCRAP)
64 		return Ph_CL_TEXT;
65 #endif /* scrap type */
66 		default:
67 		{
68 			char format[sizeof(FORMAT_PREFIX)+8+1];
69 
70 			sprintf(format, "%s%08lx", FORMAT_PREFIX,
71 					(unsigned long)type);
72 #if defined(X11_SCRAP)
73 			return XInternAtom(SDL_Display, format, False);
74 #elif defined(WIN_SCRAP)
75 			return RegisterClipboardFormat(format);
76 #endif /* scrap type */
77 		}
78 	}
79 }
80 
81 /* Convert internal data to scrap format */
convert_data(int type,char * dst,const char * src,int srclen)82 static int convert_data(int type, char *dst, const char *src, int srclen)
83 {
84 	int dstlen;
85 
86 	dstlen = 0;
87 	switch (type) {
88 	case T('T', 'E', 'X', 'T'):
89 		if (dst) {
90 			while (--srclen >= 0) {
91 #if defined(__unix__)
92 				if (*src == '\r') {
93 					*dst++ = '\n';
94 					++dstlen;
95 				}
96 				else
97 #elif defined(__WIN32__)
98 				if (*src == '\r') {
99 					*dst++ = '\r';
100 					++dstlen;
101 					*dst++ = '\n';
102 					++dstlen;
103 				}
104 				else
105 #endif
106 				{
107 					*dst++ = *src;
108 					++dstlen;
109 				}
110 				++src;
111 			}
112 			*dst = '\0';
113 			++dstlen;
114 		}
115 		else {
116 			while (--srclen >= 0) {
117 #if defined(__unix__)
118 				if (*src == '\r')
119 					++dstlen;
120 				else
121 #elif defined(__WIN32__)
122 				if (*src == '\r') {
123 					++dstlen;
124 					++dstlen;
125 				}
126 				else
127 #endif
128 				{
129 					++dstlen;
130 				}
131 				++src;
132 			}
133 			++dstlen;
134 		}
135 		break;
136 	default:
137 		if (dst) {
138 			*(int *)dst = srclen;
139 			dst += sizeof(int);
140 			memcpy(dst, src, srclen);
141 		}
142 		dstlen = sizeof(int)+srclen;
143 		break;
144 	}
145 	return(dstlen);
146 }
147 
148 /* Convert scrap data to internal format */
convert_scrap(int type,char * dst,char * src,int srclen)149 static int convert_scrap(int type, char *dst, char *src, int srclen)
150 {
151 	int dstlen;
152 
153 	dstlen = 0;
154 	switch (type) {
155 	case T('T', 'E', 'X', 'T'):
156 	{
157 		if (srclen == 0)
158 			srclen = strlen(src);
159 		if (dst) {
160 			while (--srclen >= 0) {
161 #if defined(__WIN32__)
162 				if (*src == '\r')
163 					/* drop extraneous '\r' */;
164 				else
165 #endif
166 				if (*src == '\n') {
167 					*dst++ = '\r';
168 					++dstlen;
169 				}
170 				else {
171 					*dst++ = *src;
172 					++dstlen;
173 				}
174 				++src;
175 			}
176 			*dst = '\0';
177 			++dstlen;
178 		}
179 		else {
180 			while (--srclen >= 0) {
181 #if defined(__WIN32__)
182 				/* drop extraneous '\r' */;
183 				if (*src != '\r')
184 #endif
185 					++dstlen;
186 				++src;
187 			}
188 			++dstlen;
189 		}
190 		break;
191 	}
192 	default:
193 		dstlen = *(int *)src;
194 		if (dst)
195 			memcpy(dst, src + sizeof(int),
196 				srclen ? srclen - sizeof(int) : dstlen);
197 		break;
198 	}
199 	return dstlen;
200 }
201 
init_scrap(void)202 int init_scrap(void)
203 {
204 	SDL_SysWMinfo info;
205 	int retval;
206 
207 	/* Grab the window manager specific information */
208 	retval = -1;
209 	SDL_SetError("SDL is not running on known window manager");
210 
211 	SDL_VERSION(&info.version);
212 	if (SDL_GetWMInfo(&info)) {
213 		/* Save the information for later use */
214 #if defined(X11_SCRAP)
215 		if (info.subsystem == SDL_SYSWM_X11) {
216 			SDL_Display = info.info.x11.display;
217 			SDL_Window = info.info.x11.window;
218 			Lock_Display = info.info.x11.lock_func;
219 			Unlock_Display = info.info.x11.unlock_func;
220 
221 			/* Enable the special window hook events */
222 			SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
223 			SDL_SetEventFilter(clipboard_filter);
224 
225 			XA_TARGETS = XInternAtom(SDL_Display, "TARGETS", False);
226 			XA_TEXT = XInternAtom(SDL_Display, "TEXT", False);
227 			XA_COMPOUND_TEXT = XInternAtom(SDL_Display,
228 					"COMPOUND_TEXT", False);
229 			XA_UTF8_STRING = XInternAtom(SDL_Display,
230 					"UTF8_STRING", False);
231 
232 			retval = 0;
233 		}
234 		else
235 			SDL_SetError("SDL is not running on X11");
236 #elif defined(WIN_SCRAP)
237 		SDL_Window = info.window;
238 		retval = 0;
239 #elif defined(QNX_SCRAP)
240 		InputGroup = PhInputGroup(NULL);
241 		retval = 0;
242 #endif /* scrap type */
243 	}
244 	return(retval);
245 }
246 
lost_scrap(void)247 int lost_scrap(void)
248 {
249 	int retval;
250 
251 #if defined(X11_SCRAP)
252 	if (Lock_Display)
253 		Lock_Display();
254 	retval = (XGetSelectionOwner(SDL_Display, XA_PRIMARY) != SDL_Window);
255 	if (Unlock_Display)
256 		Unlock_Display();
257 #elif defined(WIN_SCRAP)
258 	retval = (GetClipboardOwner() != SDL_Window);
259 #elif defined(QNX_SCRAP)
260 	retval = (PhInputGroup(NULL) != InputGroup);
261 #endif /* scrap type */
262 
263 	return(retval);
264 }
265 
put_scrap(int type,int srclen,const char * src)266 void put_scrap(int type, int srclen, const char *src)
267 {
268 	scrap_type format;
269 	int dstlen;
270 	char *dst;
271 
272 	format = convert_format(type);
273 	dstlen = convert_data(type, NULL, src, srclen);
274 
275 #if defined(X11_SCRAP)
276 	dst = (char *)malloc(dstlen);
277 	if (dst != NULL) {
278 		if (Lock_Display)
279 			Lock_Display();
280 		convert_data(type, dst, src, srclen);
281 		XChangeProperty(SDL_Display, DefaultRootWindow(SDL_Display),
282 				XA_CUT_BUFFER0, format, 8, PropModeReplace,
283 				(unsigned char *)dst, dstlen);
284 		free(dst);
285 		if (lost_scrap())
286 			XSetSelectionOwner(SDL_Display, XA_PRIMARY,
287 					SDL_Window, CurrentTime);
288 		if (Unlock_Display)
289 			Unlock_Display();
290 	}
291 #elif defined(WIN_SCRAP)
292 	if (OpenClipboard(SDL_Window)) {
293 		HANDLE hMem;
294 
295 		hMem = GlobalAlloc((GMEM_MOVEABLE|GMEM_DDESHARE), dstlen);
296 		if (hMem != NULL) {
297 			dst = (char *)GlobalLock(hMem);
298 			convert_data(type, dst, src, srclen);
299 			GlobalUnlock(hMem);
300 			EmptyClipboard();
301 			SetClipboardData(format, hMem);
302 		}
303 		CloseClipboard();
304 	}
305 #elif defined(QNX_SCRAP)
306 #if (_NTO_VERSION < 620) /* before 6.2.0 releases */
307 #define PhClipboardHdr PhClipHeader
308 #endif
309 	{
310 		PhClipboardHdr clheader = { Ph_CLIPBOARD_TYPE_TEXT, 0, NULL };
311 		int* cldata;
312 		int status;
313 
314 		dst = (char *)malloc(dstlen+4);
315 		if (dst != NULL) {
316 			cldata = (int*)dst;
317 			*cldata = type;
318 			convert_data(type, dst+4, src, srclen);
319 			clheader.data = dst;
320 #if (_NTO_VERSION < 620) /* before 6.2.0 releases */
321 			if (dstlen > 65535)
322 				/* maximum photon clipboard size :(*/
323 				clheader.length = 65535;
324 			else
325 #endif
326 				clheader.length = dstlen+4;
327 			status = PhClipboardCopy(InputGroup, 1, &clheader);
328 			if (status == -1)
329 				fprintf(stderr,
330 					"Photon: copy to clipboard failed!\n");
331 			free(dst);
332 		}
333 	}
334 #endif /* scrap type */
335 }
336 
get_scrap(int type,int * dstlen,char ** dst)337 void get_scrap(int type, int *dstlen, char **dst)
338 {
339 	scrap_type format;
340 
341 	*dstlen = 0;
342 	format = convert_format(type);
343 
344 #if defined(X11_SCRAP)
345 	{
346 		Window owner;
347 		Atom selection;
348 		Atom seln_type;
349 		int seln_format;
350 		unsigned long nbytes;
351 		unsigned long overflow;
352 		char *src;
353 
354 		if (Lock_Display)
355 			Lock_Display();
356 		owner = XGetSelectionOwner(SDL_Display, XA_PRIMARY);
357 		if (Unlock_Display)
358 			Unlock_Display();
359 		if ((owner == None) || (owner == SDL_Window)) {
360 			owner = DefaultRootWindow(SDL_Display);
361 			selection = XA_CUT_BUFFER0;
362 		}
363 		else {
364 			int selection_response = 0;
365 			SDL_Event event;
366 
367 			owner = SDL_Window;
368 			if (Lock_Display)
369 				Lock_Display();
370 			selection = XInternAtom(SDL_Display, "SDL_SELECTION",
371 					False);
372 			XConvertSelection(SDL_Display, XA_PRIMARY, format,
373 					selection, owner, CurrentTime);
374 			if (Unlock_Display)
375 				Unlock_Display();
376 			while (!selection_response) {
377 				SDL_WaitEvent(&event);
378 				if (event.type == SDL_SYSWMEVENT) {
379 					XEvent xevent =
380 						event.syswm.msg->event.xevent;
381 
382 					if ((xevent.type == SelectionNotify) &&
383 					    (xevent.xselection.requestor
384 							== owner))
385 						selection_response = 1;
386 				}
387 			}
388 		}
389 		if (Lock_Display)
390 			Lock_Display();
391 		if (XGetWindowProperty(SDL_Display, owner, selection,
392 				0, INT_MAX/4, False, format, &seln_type,
393 				&seln_format, &nbytes, &overflow,
394 				(unsigned char **)&src) == Success) {
395 			if (seln_type == format) {
396 				*dstlen = convert_scrap(type, NULL,
397 						src, nbytes);
398 				*dst = (char *)realloc(*dst, *dstlen);
399 				if (*dst == NULL)
400 					*dstlen = 0;
401 				else
402 					convert_scrap(type, *dst, src, nbytes);
403 			}
404 			XFree(src);
405 		}
406 	}
407 	if (Unlock_Display)
408 		Unlock_Display();
409 #elif defined(WIN_SCRAP)
410 	if (IsClipboardFormatAvailable(format) && OpenClipboard(SDL_Window)) {
411 		HANDLE hMem;
412 		char *src;
413 
414 		hMem = GetClipboardData(format);
415 		if (hMem != NULL) {
416 			src = (char *)GlobalLock(hMem);
417 			*dstlen = convert_scrap(type, NULL, src, 0);
418 			*dst = (char *)realloc(*dst, *dstlen);
419 			if (*dst == NULL)
420 				*dstlen = 0;
421 			else
422 				convert_scrap(type, *dst, src, 0);
423 			GlobalUnlock(hMem);
424 		}
425 		CloseClipboard();
426 	}
427 #elif defined(QNX_SCRAP)
428 #if (_NTO_VERSION < 620) /* before 6.2.0 releases */
429 	{
430 		void* clhandle;
431 		PhClipHeader* clheader;
432 		int* cldata;
433 
434 		clhandle = PhClipboardPasteStart(InputGroup);
435 		if (clhandle != NULL) {
436 			clheader = PhClipboardPasteType(clhandle,
437 				Ph_CLIPBOARD_TYPE_TEXT);
438 			if (clheader != NULL) {
439 				cldata = clheader->data;
440 				if ((clheader->length>4) && (*cldata == type)) {
441 					*dstlen = convert_scrap(type, NULL,
442 						(char*)clheader->data+4,
443 						clheader->length-4);
444 					*dst = (char *)realloc(*dst, *dstlen);
445 					if (*dst == NULL)
446 						*dstlen = 0;
447 					else
448 						convert_scrap(type, *dst,
449 							(char*)clheader->data+4,
450 							clheader->length-4);
451 				}
452 			}
453 			PhClipboardPasteFinish(clhandle);
454 		}
455 	}
456 #else /* 6.2.0 and 6.2.1 and future releases */
457 	{
458 		void* clhandle;
459 		PhClipboardHdr* clheader;
460 		int* cldata;
461 
462 		clheader=PhClipboardRead(InputGroup, Ph_CLIPBOARD_TYPE_TEXT);
463 		if (clheader!=NULL) {
464 			cldata=clheader->data;
465 			if ((clheader->length>4) && (*cldata==type)) {
466 				*dstlen = convert_scrap(type, NULL,
467 					(char*)clheader->data+4,
468 					clheader->length-4);
469 				*dst = (char *)realloc(*dst, *dstlen);
470 				if (*dst == NULL)
471 					*dstlen = 0;
472 				else
473 					convert_scrap(type, *dst,
474 						(char*)clheader->data+4,
475 						clheader->length-4);
476 			}
477 		}
478 	}
479 #endif
480 #endif /* scrap type */
481 }
482 
clipboard_filter(const SDL_Event * event)483 int clipboard_filter(const SDL_Event *event)
484 {
485 #if defined(X11_SCRAP)
486 	/* Post all non-window manager specific events */
487 	if (event->type != SDL_SYSWMEVENT)
488 		return(1);
489 
490 	/* Handle window-manager specific clipboard events */
491 	switch (event->syswm.msg->event.xevent.type) {
492 	/* Copy the selection from XA_CUT_BUFFER0 to the requested property */
493 	case SelectionRequest: {
494 		XSelectionRequestEvent *req;
495 		XEvent sevent;
496 		int seln_format;
497 		unsigned long nbytes;
498 		unsigned long overflow;
499 		unsigned char *seln_data;
500 
501 		req = &event->syswm.msg->event.xevent.xselectionrequest;
502 		if (req->target == XA_TARGETS) {
503 			Atom supported[] = {
504 				XA_TEXT, XA_COMPOUND_TEXT, XA_UTF8_STRING,
505 				XA_TARGETS, XA_STRING
506 			};
507 			XEvent response;
508 
509 			XChangeProperty(SDL_Display, req->requestor,
510 				req->property, req->target, 32, PropModeReplace,
511 				(unsigned char*)supported,
512 				sizeof(supported) / sizeof(supported[0]));
513 			response.xselection.property=None;
514 			response.xselection.type= SelectionNotify;
515 			response.xselection.display= req->display;
516 			response.xselection.requestor= req->requestor;
517 			response.xselection.selection=req->selection;
518 			response.xselection.target= req->target;
519 			response.xselection.time = req->time;
520 			XSendEvent (SDL_Display, req->requestor,0,0,&response);
521 			XFlush (SDL_Display);
522 			return 1;
523 		}
524 
525 		sevent.xselection.type = SelectionNotify;
526 		sevent.xselection.display = req->display;
527 		sevent.xselection.selection = req->selection;
528 		sevent.xselection.target = None;
529 		sevent.xselection.property = req->property;
530 		sevent.xselection.requestor = req->requestor;
531 		sevent.xselection.time = req->time;
532 		if (XGetWindowProperty(SDL_Display,
533 				DefaultRootWindow(SDL_Display), XA_CUT_BUFFER0,
534 				0, INT_MAX/4, False, req->target,
535 				&sevent.xselection.target, &seln_format,
536 				&nbytes, &overflow, &seln_data) == Success) {
537 			if (sevent.xselection.target == req->target) {
538 				if (sevent.xselection.target == XA_STRING &&
539 						nbytes > 0 &&
540 						seln_data[nbytes-1] == '\0')
541 					--nbytes;
542 				XChangeProperty(SDL_Display, req->requestor,
543 					req->property, sevent.xselection.target,
544 					seln_format, PropModeReplace,
545 					seln_data, nbytes);
546 				sevent.xselection.property = req->property;
547 			}
548 			XFree(seln_data);
549 		}
550 		XSendEvent(SDL_Display,req->requestor,False,0,&sevent);
551 		XSync(SDL_Display, False);
552 		break;
553 	}
554 	}
555 	/* Post the event for X11 clipboard reading above */
556 #endif /* X11_SCRAP */
557 	return(1);
558 }
559