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 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 */ 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 */ 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 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 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 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 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 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