| /* 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); |
| } |