Browse Source
The clipboard support has only been tested on Linux so far. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>pull/1/head
5 changed files with 606 additions and 1 deletions
@ -0,0 +1,558 @@
|
||||
/* 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); |
||||
} |
@ -0,0 +1,18 @@
|
||||
/* Handle clipboard text and data in arbitrary formats */ |
||||
|
||||
/* Miscellaneous defines */ |
||||
#define T(A, B, C, D) (int)((A<<24)|(B<<16)|(C<<8)|(D<<0)) |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif /* __cplusplus */ |
||||
|
||||
extern int init_scrap(void); |
||||
extern int lost_scrap(void); |
||||
extern void put_scrap(int type, int srclen, const char *src); |
||||
extern void get_scrap(int type, int *dstlen, char **dst); |
||||
extern int clipboard_filter(const SDL_Event *event); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif /* __cplusplus */ |
Loading…
Reference in new issue