summaryrefslogtreecommitdiffstats
path: root/client_examples
diff options
context:
space:
mode:
authordscho <dscho>2009-03-08 18:33:08 +0000
committerdscho <dscho>2009-03-08 18:33:08 +0000
commitebe79c28c30ad328dcff88b95e7863e885af36f9 (patch)
tree2766085f80235a27b070a8e5eba9cc4dcdbed393 /client_examples
parente7152a7f68063a86887a9059728ca26b5404ce51 (diff)
downloadlibtdevnc-ebe79c28c30ad328dcff88b95e7863e885af36f9.tar.gz
libtdevnc-ebe79c28c30ad328dcff88b95e7863e885af36f9.zip
Clipboard support for SDLvncviewer
The clipboard support has only been tested on Linux so far. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Diffstat (limited to 'client_examples')
-rw-r--r--client_examples/Makefile.am8
-rw-r--r--client_examples/SDLvncviewer.c20
-rw-r--r--client_examples/scrap.c558
-rw-r--r--client_examples/scrap.h18
4 files changed, 603 insertions, 1 deletions
diff --git a/client_examples/Makefile.am b/client_examples/Makefile.am
index 9eed084..fa71a50 100644
--- a/client_examples/Makefile.am
+++ b/client_examples/Makefile.am
@@ -19,8 +19,14 @@ SDLVIEWER=SDLvncviewer
SDLvncviewer_CFLAGS=$(SDL_CFLAGS)
+SDLvncviewer_SOURCES=SDLvncviewer.c scrap.c
+
+if HAVE_X
+X11_LIB=-lX11
+endif
+
# thanks to autoconf, this looks ugly
-SDLvncviewer_LDADD=$(LDADD) $(SDL_LIBS)
+SDLvncviewer_LDADD=$(LDADD) $(SDL_LIBS) $(X11_LIB)
endif
noinst_PROGRAMS=ppmtest $(SDLVIEWER) $(FFMPEG_CLIENT) backchannel
diff --git a/client_examples/SDLvncviewer.c b/client_examples/SDLvncviewer.c
index 6fe1a17..0042284 100644
--- a/client_examples/SDLvncviewer.c
+++ b/client_examples/SDLvncviewer.c
@@ -1,5 +1,6 @@
#include <SDL.h>
#include <rfb/rfbclient.h>
+#include "scrap.h"
struct { int sdl; int rfb; } buttonMapping[]={
{1, rfbButton1Mask},
@@ -397,6 +398,17 @@ static void handleSDLEvent(rfbClient *cl, SDL_Event *e)
leftAltKeyDown = FALSE;
rfbClientLog("released left Alt key\n");
}
+
+ if (e->active.gain && lost_scrap()) {
+ static char *data = NULL;
+ static int len = 0;
+ get_scrap(T('T', 'E', 'X', 'T'), &len, &data);
+ if (len)
+ SendClientCutText(cl, data, len);
+ }
+ break;
+ case SDL_SYSWMEVENT:
+ clipboard_filter(&e);
break;
case SDL_VIDEORESIZE:
setRealDimension(cl, e->resize.w, e->resize.h);
@@ -406,6 +418,11 @@ static void handleSDLEvent(rfbClient *cl, SDL_Event *e)
}
}
+static void got_selection(rfbClient *cl, const char *text, int len)
+{
+ put_scrap(T('T', 'E', 'X', 'T'), len, text);
+}
+
#ifdef mac
#define main SDLmain
#endif
@@ -443,9 +460,12 @@ int main(int argc,char** argv) {
cl->GotFrameBufferUpdate=update;
cl->HandleKeyboardLedState=kbd_leds;
cl->HandleTextChat=text_chat;
+ cl->GotXCutText = got_selection;
if(!rfbInitClient(cl,&argc,argv))
return 1;
+ init_scrap();
+
while(1) {
if(SDL_PollEvent(&e))
handleSDLEvent(cl, &e);
diff --git a/client_examples/scrap.c b/client_examples/scrap.c
new file mode 100644
index 0000000..c28800c
--- /dev/null
+++ b/client_examples/scrap.c
@@ -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);
+}
diff --git a/client_examples/scrap.h b/client_examples/scrap.h
new file mode 100644
index 0000000..647bd74
--- /dev/null
+++ b/client_examples/scrap.h
@@ -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 */