text: /* Handle clipboard text and data in arbitrary formats */
  
  #include <stdio.h>
  #include <limits.h>
  
  #ifdef WIN32
  #include <SDL.h>
  #include <SDL_syswm.h>
  #else
  #include <SDL/SDL.h>
  #include <SDL/SDL_syswm.h>
  #endif
  #include "scrap.h"
  #include "rfb/rfbconfig.h"
  
  /* Determine what type of clipboard we are using */
  #if defined(__unix__) && !defined(__QNXNTO__) && defined(LIBVNCSERVER_HAVE_X11)
  #define X11_SCRAP
  #elif defined(__WIN32__)
  #define WIN_SCRAP
  #elif defined(__QNXNTO__)
  #define QNX_SCRAP
  #else
  #warning Unknown window manager for clipboard handling
  #endif /* scrap type */
  
  /* System dependent data types */
  #if defined(X11_SCRAP)
  typedef Atom scrap_type;
  static Atom XA_TARGETS, XA_TEXT, XA_COMPOUND_TEXT, XA_UTF8_STRING;
  #elif defined(WIN_SCRAP)
  typedef UINT scrap_type;
  #elif defined(QNX_SCRAP)
  typedef uint32_t scrap_type;
  #define Ph_CL_TEXT T('T', 'E', 'X', 'T')
  #else
  typedef int scrap_type;
  #endif /* scrap type */
  
  /* System dependent variables */
  #if defined(X11_SCRAP)
  static Display *SDL_Display;
  static Window SDL_Window;
  static void (*Lock_Display)(void);
  static void (*Unlock_Display)(void);
  static Atom XA_UTF8_STRING;
  #elif defined(WIN_SCRAP)
  static HWND SDL_Window;
  #elif defined(QNX_SCRAP)
  static unsigned short InputGroup;
  #endif /* scrap type */
  
  #define FORMAT_PREFIX	"SDL_scrap_0x"
  
  static scrap_type convert_format(int type)
  {
  	switch (type) {
  	case T('T', 'E', 'X', 'T'):
  #if defined(X11_SCRAP)
  		return XA_UTF8_STRING ? XA_UTF8_STRING : XA_STRING;
  #elif defined(WIN_SCRAP)
  		return CF_TEXT;
  #elif defined(QNX_SCRAP)
  		return Ph_CL_TEXT;
  #endif /* scrap type */
  		default:
  		{
  			char format[sizeof(FORMAT_PREFIX)+8+1];
  
  			sprintf(format, "%s%08lx", FORMAT_PREFIX,
  					(unsigned long)type);
  #if defined(X11_SCRAP)
  			return XInternAtom(SDL_Display, format, False);
  #elif defined(WIN_SCRAP)
  			return RegisterClipboardFormat(format);
  #endif /* scrap type */
  		}
  	}
  }
  
  /* Convert internal data to scrap format */
  static int convert_data(int type, char *dst, const char *src, int srclen)
  {
  	int dstlen;
  
  	dstlen = 0;
  	switch (type) {
  	case T('T', 'E', 'X', 'T'):
  		if (dst) {
  			while (--srclen >= 0) {
  #if defined(__unix__)
  				if (*src == '\r') {
  					*dst++ = '\n';
  					++dstlen;
  				}
  				else
  #elif defined(__WIN32__)
  				if (*src == '\r') {
  					*dst++ = '\r';
  					++dstlen;
  					*dst++ = '\n';
  					++dstlen;
  				}
  				else
  #endif
  				{
  					*dst++ = *src;
  					++dstlen;
  				}
  				++src;
  			}
  			*dst = '\0';
  			++dstlen;
  		}
  		else {
  			while (--srclen >= 0) {
  #if defined(__unix__)
  				if (*src == '\r')
  					++dstlen;
  				else
  #elif defined(__WIN32__)
  				if (*src == '\r') {
  					++dstlen;
  					++dstlen;
  				}
  				else
  #endif
  				{
  					++dstlen;
  				}
  				++src;
  			}
  			++dstlen;
  		}
  		break;
  	default:
  		if (dst) {
  			*(int *)dst = srclen;
  			dst += sizeof(int);
  			memcpy(dst, src, srclen);
  		}
  		dstlen = sizeof(int)+srclen;
  		break;
  	}
  	return(dstlen);
  }
  
  /* Convert scrap data to internal format */
  static int convert_scrap(int type, char *dst, char *src, int srclen)
  {
  	int dstlen;
  
  	dstlen = 0;
  	switch (type) {
  	case T('T', 'E', 'X', 'T'):
  	{
  		if (srclen == 0)
  			srclen = strlen(src);
  		if (dst) {
  			while (--srclen >= 0) {
  #if defined(__WIN32__)
  				if (*src == '\r')
  					/* drop extraneous '\r' */;
  				else
  #endif
  				if (*src == '\n') {
  					*dst++ = '\r';
  					++dstlen;
  				}
  				else {
  					*dst++ = *src;
  					++dstlen;
  				}
  				++src;
  			}
  			*dst = '\0';
  			++dstlen;
  		}
  		else {
  			while (--srclen >= 0) {
  #if defined(__WIN32__)
  				/* drop extraneous '\r' */;
  				if (*src != '\r')
  #endif
  					++dstlen;
  				++src;
  			}
  			++dstlen;
  		}
  		break;
  	}
  	default:
  		dstlen = *(int *)src;
  		if (dst)
  			memcpy(dst, src + sizeof(int),
  				srclen ? srclen - sizeof(int) : dstlen);
  		break;
  	}
  	return dstlen;
  }
  
  int init_scrap(void)
  {
  	SDL_SysWMinfo info;
  	int retval;
  
  	/* Grab the window manager specific information */
  	retval = -1;
  	SDL_SetError("SDL is not running on known window manager");
  
  	SDL_VERSION(&info.version);
  	if (SDL_GetWMInfo(&info)) {
  		/* Save the information for later use */
  #if defined(X11_SCRAP)
  		if (info.subsystem == SDL_SYSWM_X11) {
  			SDL_Display = info.info.x11.display;
  			SDL_Window = info.info.x11.window;
  			Lock_Display = info.info.x11.lock_func;
  			Unlock_Display = info.info.x11.unlock_func;
  
  			/* Enable the special window hook events */
  			SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
  			SDL_SetEventFilter(clipboard_filter);
  
  			XA_TARGETS = XInternAtom(SDL_Display, "TARGETS", False);
  			XA_TEXT = XInternAtom(SDL_Display, "TEXT", False);
  			XA_COMPOUND_TEXT = XInternAtom(SDL_Display,
  					"COMPOUND_TEXT", False);
  			XA_UTF8_STRING = XInternAtom(SDL_Display,
  					"UTF8_STRING", False);
  
  			retval = 0;
  		}
  		else
  			SDL_SetError("SDL is not running on X11");
  #elif defined(WIN_SCRAP)
  		SDL_Window = info.window;
  		retval = 0;
  #elif defined(QNX_SCRAP)
  		InputGroup = PhInputGroup(NULL);
  		retval = 0;
  #endif /* scrap type */
  	}
  	return(retval);
  }
  
  int lost_scrap(void)
  {
  	int retval;
  
  #if defined(X11_SCRAP)
  	if (Lock_Display)
  		Lock_Display();
  	retval = (XGetSelectionOwner(SDL_Display, XA_PRIMARY) != SDL_Window);
  	if (Unlock_Display)
  		Unlock_Display();
  #elif defined(WIN_SCRAP)
  	retval = (GetClipboardOwner() != SDL_Window);
  #elif defined(QNX_SCRAP)
  	retval = (PhInputGroup(NULL) != InputGroup);
  #endif /* scrap type */
  
  	return(retval);
  }
  
  void put_scrap(int type, int srclen, const char *src)
  {
  	scrap_type format;
  	int dstlen;
  	char *dst;
  
  	format = convert_format(type);
  	dstlen = convert_data(type, NULL, src, srclen);
  
  #if defined(X11_SCRAP)
  	dst = (char *)malloc(dstlen);
  	if (dst != NULL) {
  		if (Lock_Display)
  			Lock_Display();
  		convert_data(type, dst, src, srclen);
  		XChangeProperty(SDL_Display, DefaultRootWindow(SDL_Display),
  				XA_CUT_BUFFER0, format, 8, PropModeReplace,
  				(unsigned char *)dst, dstlen);
  		free(dst);
  		if (lost_scrap())
  			XSetSelectionOwner(SDL_Display, XA_PRIMARY,
  					SDL_Window, CurrentTime);
  		if (Unlock_Display)
  			Unlock_Display();
  	}
  #elif defined(WIN_SCRAP)
  	if (OpenClipboard(SDL_Window)) {
  		HANDLE hMem;
  
  		hMem = GlobalAlloc((GMEM_MOVEABLE|GMEM_DDESHARE), dstlen);
  		if (hMem != NULL) {
  			dst = (char *)GlobalLock(hMem);
  			convert_data(type, dst, src, srclen);
  			GlobalUnlock(hMem);
  			EmptyClipboard();
  			SetClipboardData(format, hMem);
  		}
  		CloseClipboard();
  	}
  #elif defined(QNX_SCRAP)
  #if (_NTO_VERSION < 620) /* before 6.2.0 releases */
  #define PhClipboardHdr PhClipHeader
  #endif
  	{
  		PhClipboardHdr clheader = { Ph_CLIPBOARD_TYPE_TEXT, 0, NULL };
  		int* cldata;
  		int status;
  
  		dst = (char *)malloc(dstlen+4);
  		if (dst != NULL) {
  			cldata = (int*)dst;
  			*cldata = type;
  			convert_data(type, dst+4, src, srclen);
  			clheader.data = dst;
  #if (_NTO_VERSION < 620) /* before 6.2.0 releases */
  			if (dstlen > 65535)
  				/* maximum photon clipboard size :(*/
  				clheader.length = 65535;
  			else
  #endif
  				clheader.length = dstlen+4;
  			status = PhClipboardCopy(InputGroup, 1, &clheader);
  			if (status == -1)
  				fprintf(stderr,
  					"Photon: copy to clipboard failed!\n");
  			free(dst);
  		}
  	}
  #endif /* scrap type */
  }
  
  void get_scrap(int type, int *dstlen, char **dst)
  {
  	scrap_type format;
  
  	*dstlen = 0;
  	format = convert_format(type);
  
  #if defined(X11_SCRAP)
  	{
  		Window owner;
  		Atom selection;
  		Atom seln_type;
  		int seln_format;
  		unsigned long nbytes;
  		unsigned long overflow;
  		char *src;
  
  		if (Lock_Display)
  			Lock_Display();
  		owner = XGetSelectionOwner(SDL_Display, XA_PRIMARY);
  		if (Unlock_Display)
  			Unlock_Display();
  		if ((owner == None) || (owner == SDL_Window)) {
  			owner = DefaultRootWindow(SDL_Display);
  			selection = XA_CUT_BUFFER0;
  		}
  		else {
  			int selection_response = 0;
  			SDL_Event event;
  
  			owner = SDL_Window;
  			if (Lock_Display)
  				Lock_Display();
  			selection = XInternAtom(SDL_Display, "SDL_SELECTION",
  					False);
  			XConvertSelection(SDL_Display, XA_PRIMARY, format,
  					selection, owner, CurrentTime);
  			if (Unlock_Display)
  				Unlock_Display();
  			while (!selection_response) {
  				SDL_WaitEvent(&event);
  				if (event.type == SDL_SYSWMEVENT) {
  					XEvent xevent =
  						event.syswm.msg->event.xevent;
  
  					if ((xevent.type == SelectionNotify) &&
  					    (xevent.xselection.requestor
  							== owner))
  						selection_response = 1;
  				}
  			}
  		}
  		if (Lock_Display)
  			Lock_Display();
  		if (XGetWindowProperty(SDL_Display, owner, selection,
  				0, INT_MAX/4, False, format, &seln_type,
  				&seln_format, &nbytes, &overflow,
  				(unsigned char **)&src) == Success) {
  			if (seln_type == format) {
  				*dstlen = convert_scrap(type, NULL,
  						src, nbytes);
  				*dst = (char *)realloc(*dst, *dstlen);
  				if (*dst == NULL)
  					*dstlen = 0;
  				else
  					convert_scrap(type, *dst, src, nbytes);
  			}
  			XFree(src);
  		}
  	}
  	if (Unlock_Display)
  		Unlock_Display();
  #elif defined(WIN_SCRAP)
  	if (IsClipboardFormatAvailable(format) && OpenClipboard(SDL_Window)) {
  		HANDLE hMem;
  		char *src;
  
  		hMem = GetClipboardData(format);
  		if (hMem != NULL) {
  			src = (char *)GlobalLock(hMem);
  			*dstlen = convert_scrap(type, NULL, src, 0);
  			*dst = (char *)realloc(*dst, *dstlen);
  			if (*dst == NULL)
  				*dstlen = 0;
  			else
  				convert_scrap(type, *dst, src, 0);
  			GlobalUnlock(hMem);
  		}
  		CloseClipboard();
  	}
  #elif defined(QNX_SCRAP)
  #if (_NTO_VERSION < 620) /* before 6.2.0 releases */
  	{
  		void* clhandle;
  		PhClipHeader* clheader;
  		int* cldata;
  
  		clhandle = PhClipboardPasteStart(InputGroup);
  		if (clhandle != NULL) {
  			clheader = PhClipboardPasteType(clhandle,
  				Ph_CLIPBOARD_TYPE_TEXT);
  			if (clheader != NULL) {
  				cldata = clheader->data;
  				if ((clheader->length>4) && (*cldata == type)) {
  					*dstlen = convert_scrap(type, NULL,
  						(char*)clheader->data+4,
  						clheader->length-4);
  					*dst = (char *)realloc(*dst, *dstlen);
  					if (*dst == NULL)
  						*dstlen = 0;
  					else
  						convert_scrap(type, *dst,
  							(char*)clheader->data+4,
  							clheader->length-4);
  				}
  			}
  			PhClipboardPasteFinish(clhandle);
  		}
  	}
  #else /* 6.2.0 and 6.2.1 and future releases */
  	{
  		void* clhandle;
  		PhClipboardHdr* clheader;
  		int* cldata;
  
  		clheader=PhClipboardRead(InputGroup, Ph_CLIPBOARD_TYPE_TEXT);
  		if (clheader!=NULL) {
  			cldata=clheader->data;
  			if ((clheader->length>4) && (*cldata==type)) {
  				*dstlen = convert_scrap(type, NULL,
  					(char*)clheader->data+4,
  					clheader->length-4);
  				*dst = (char *)realloc(*dst, *dstlen);
  				if (*dst == NULL)
  					*dstlen = 0;
  				else
  					convert_scrap(type, *dst,
  						(char*)clheader->data+4,
  						clheader->length-4);
  			}
  		}
  	}
  #endif
  #endif /* scrap type */
  }
  
  int clipboard_filter(const SDL_Event *event)
  {
  #if defined(X11_SCRAP)
  	/* Post all non-window manager specific events */
  	if (event->type != SDL_SYSWMEVENT)
  		return(1);
  
  	/* Handle window-manager specific clipboard events */
  	switch (event->syswm.msg->event.xevent.type) {
  	/* Copy the selection from XA_CUT_BUFFER0 to the requested property */
  	case SelectionRequest: {
  		XSelectionRequestEvent *req;
  		XEvent sevent;
  		int seln_format;
  		unsigned long nbytes;
  		unsigned long overflow;
  		unsigned char *seln_data;
  
  		req = &event->syswm.msg->event.xevent.xselectionrequest;
  		if (req->target == XA_TARGETS) {
  			Atom supported[] = {
  				XA_TEXT, XA_COMPOUND_TEXT, XA_UTF8_STRING,
  				XA_TARGETS, XA_STRING
  			};
  			XEvent response;
  
  			XChangeProperty(SDL_Display, req->requestor,
  				req->property, req->target, 32, PropModeReplace,
  				(unsigned char*)supported,
  				sizeof(supported) / sizeof(supported[0]));
  			response.xselection.property=None;
  			response.xselection.type= SelectionNotify;
  			response.xselection.display= req->display;
  			response.xselection.requestor= req->requestor;
  			response.xselection.selection=req->selection;
  			response.xselection.target= req->target;
  			response.xselection.time = req->time;
  			XSendEvent (SDL_Display, req->requestor,0,0,&response);
  			XFlush (SDL_Display);
  			return 1;
  		}
  
  		sevent.xselection.type = SelectionNotify;
  		sevent.xselection.display = req->display;
  		sevent.xselection.selection = req->selection;
  		sevent.xselection.target = None;
  		sevent.xselection.property = req->property;
  		sevent.xselection.requestor = req->requestor;
  		sevent.xselection.time = req->time;
  		if (XGetWindowProperty(SDL_Display,
  				DefaultRootWindow(SDL_Display), XA_CUT_BUFFER0,
  				0, INT_MAX/4, False, req->target,
  				&sevent.xselection.target, &seln_format,
  				&nbytes, &overflow, &seln_data) == Success) {
  			if (sevent.xselection.target == req->target) {
  				if (sevent.xselection.target == XA_STRING &&
  						nbytes > 0 &&
  						seln_data[nbytes-1] == '\0')
  					--nbytes;
  				XChangeProperty(SDL_Display, req->requestor,
  					req->property, sevent.xselection.target,
  					seln_format, PropModeReplace,
  					seln_data, nbytes);
  				sevent.xselection.property = req->property;
  			}
  			XFree(seln_data);
  		}
  		XSendEvent(SDL_Display,req->requestor,False,0,&sevent);
  		XSync(SDL_Display, False);
  		break;
  	}
  	}
  	/* Post the event for X11 clipboard reading above */
  #endif /* X11_SCRAP */
  	return(1);
  }
  