Home | History | Annotate | Download | only in client_examples
      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