blob: c28800cd396c396054ab428641191542d2db8e28 [file] [log] [blame]
San Mehata430b2b2014-09-23 08:30:51 -07001/* 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)
29typedef Atom scrap_type;
30static Atom XA_TARGETS, XA_TEXT, XA_COMPOUND_TEXT, XA_UTF8_STRING;
31#elif defined(WIN_SCRAP)
32typedef UINT scrap_type;
33#elif defined(QNX_SCRAP)
34typedef uint32_t scrap_type;
35#define Ph_CL_TEXT T('T', 'E', 'X', 'T')
36#else
37typedef int scrap_type;
38#endif /* scrap type */
39
40/* System dependent variables */
41#if defined(X11_SCRAP)
42static Display *SDL_Display;
43static Window SDL_Window;
44static void (*Lock_Display)(void);
45static void (*Unlock_Display)(void);
46static Atom XA_UTF8_STRING;
47#elif defined(WIN_SCRAP)
48static HWND SDL_Window;
49#elif defined(QNX_SCRAP)
50static unsigned short InputGroup;
51#endif /* scrap type */
52
53#define FORMAT_PREFIX "SDL_scrap_0x"
54
55static 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 */
82static 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 */
149static 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
202int 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
247int 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
266void 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
337void 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
483int 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}