summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client_examples/Makefile.am2
-rw-r--r--client_examples/SDLvncviewer.c6
-rw-r--r--client_examples/backchannel.c99
-rw-r--r--libvncclient/rfbproto.c101
-rw-r--r--rfb/rfbclient.h31
5 files changed, 219 insertions, 20 deletions
diff --git a/client_examples/Makefile.am b/client_examples/Makefile.am
index 6ffcf51..77998f0 100644
--- a/client_examples/Makefile.am
+++ b/client_examples/Makefile.am
@@ -23,7 +23,7 @@ SDLvncviewer_CFLAGS=$(SDL_CFLAGS)
SDLvncviewer_LDADD=$(LDADD) $(SDL_LIBS)
endif
-noinst_PROGRAMS=ppmtest $(SDLVIEWER) $(FFMPEG_CLIENT)
+noinst_PROGRAMS=ppmtest $(SDLVIEWER) $(FFMPEG_CLIENT) backchannel
diff --git a/client_examples/SDLvncviewer.c b/client_examples/SDLvncviewer.c
index 07d3869..8375ea6 100644
--- a/client_examples/SDLvncviewer.c
+++ b/client_examples/SDLvncviewer.c
@@ -16,7 +16,7 @@ static rfbBool resize(rfbClient* client) {
okay=SDL_VideoModeOK(width,height,depth,flags);
if(okay) {
SDL_Surface* sdl=SDL_SetVideoMode(width,height,depth,flags);
- client->clientData=sdl;
+ rfbClientSetClientData(client, SDL_Init, sdl);
client->frameBuffer=sdl->pixels;
if(first || depth!=client->format.bitsPerPixel) {
first=FALSE;
@@ -30,7 +30,7 @@ static rfbBool resize(rfbClient* client) {
SetFormatAndEncodings(client);
}
} else {
- SDL_Surface* sdl=client->clientData;
+ SDL_Surface* sdl=rfbClientGetClientData(client, SDL_Init);
rfbClientLog("Could not set resolution %dx%d!\n",
client->width,client->height);
if(sdl) {
@@ -188,7 +188,7 @@ static rfbKeySym SDL_keysym2rfbKeySym(int keysym) {
}
static void update(rfbClient* cl,int x,int y,int w,int h) {
- SDL_UpdateRect(cl->clientData, x, y, w, h);
+ SDL_UpdateRect(rfbClientGetClientData(cl, SDL_Init), x, y, w, h);
}
#ifdef __MINGW32__
diff --git a/client_examples/backchannel.c b/client_examples/backchannel.c
new file mode 100644
index 0000000..643754e
--- /dev/null
+++ b/client_examples/backchannel.c
@@ -0,0 +1,99 @@
+/* A simple example of an RFB client */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+#include <rfb/rfbclient.h>
+
+static void HandleRect(rfbClient* client, int x, int y, int w, int h) {
+}
+
+/*
+ * The client part of the back channel extension example.
+ *
+ */
+
+#define rfbBackChannel 155
+
+typedef struct backChannelMsg {
+ uint8_t type;
+ uint8_t pad1;
+ uint16_t pad2;
+ uint32_t size;
+} backChannelMsg;
+
+static void sendMessage(rfbClient* client, char* text)
+{
+ backChannelMsg msg;
+ uint32_t length = strlen(text)+1;
+
+ msg.type = rfbBackChannel;
+ msg.size = rfbClientSwap32IfLE(length);
+ if(!WriteToRFBServer(client, (char*)&msg, sizeof(msg)) ||
+ !WriteToRFBServer(client, text, length)) {
+ rfbClientLog("enableBackChannel: write error (%d: %s)", errno, strerror(errno));
+ }
+}
+
+static rfbBool handleBackChannelMessage(rfbClient* client,
+ rfbServerToClientMsg* message)
+{
+ backChannelMsg msg;
+ char* text;
+
+ if(message->type != rfbBackChannel)
+ return FALSE;
+
+ rfbClientSetClientData(client, sendMessage, sendMessage);
+
+ if(!ReadFromRFBServer(client, ((char*)&msg)+1, sizeof(msg)-1))
+ return TRUE;
+ msg.size = rfbClientSwap32IfLE(msg.size);
+ text = malloc(msg.size);
+ if(!ReadFromRFBServer(client, text, msg.size)) {
+ free(text);
+ return TRUE;
+ }
+
+ rfbClientLog("got back channel message: %s\n", text);
+ free(text);
+
+ return TRUE;
+}
+
+static int backChannelEncodings[] = { rfbBackChannel, 0 };
+
+static rfbClientProtocolExtension backChannel = {
+ backChannelEncodings, /* encodings */
+ NULL, /* handleEncoding */
+ handleBackChannelMessage, /* handleMessage */
+ NULL /* next extension */
+};
+
+int
+main(int argc, char **argv)
+{
+ rfbClient* client = rfbGetClient(8,3,4);
+
+ client->GotFrameBufferUpdate = HandleRect;
+ rfbClientRegisterExtension(&backChannel);
+
+ if (!rfbInitClient(client,&argc,argv))
+ return 1;
+
+ while (1) {
+ /* After each idle second, send a message */
+ if(WaitForMessage(client,1000000)>0)
+ HandleRFBServerMessage(client);
+ else if(rfbClientGetClientData(client, sendMessage))
+ sendMessage(client, "Dear Server,\n"
+ "thank you for understanding "
+ "back channel messages!");
+ }
+
+ rfbClientCleanup(client);
+
+ return 0;
+}
+
diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c
index 0e8ccfe..1a491a9 100644
--- a/libvncclient/rfbproto.c
+++ b/libvncclient/rfbproto.c
@@ -77,6 +77,49 @@ rfbDefaultClientLog(const char *format, ...)
rfbClientLogProc rfbClientLog=rfbDefaultClientLog;
rfbClientLogProc rfbClientErr=rfbDefaultClientLog;
+/* extensions */
+
+rfbClientProtocolExtension* rfbClientExtensions = NULL;
+
+void rfbClientRegisterExtension(rfbClientProtocolExtension* e)
+{
+ e->next = rfbClientExtensions;
+ rfbClientExtensions = e;
+}
+
+/* client data */
+
+void rfbClientSetClientData(rfbClient* client, void* tag, void* data)
+{
+ rfbClientData* clientData = client->clientData;
+
+ while(clientData && clientData->tag != tag)
+ clientData = clientData->next;
+ if(clientData == NULL) {
+ clientData = calloc(sizeof(rfbClientData), 1);
+ clientData->next = client->clientData;
+ client->clientData = clientData;
+ clientData->tag = tag;
+ }
+
+ clientData->data = data;
+}
+
+void* rfbClientGetClientData(rfbClient* client, void* tag)
+{
+ rfbClientData* clientData = client->clientData;
+
+ while(clientData) {
+ if(clientData->tag == tag)
+ return clientData->data;
+ clientData = clientData->next;
+ }
+
+ return NULL;
+}
+
+/* messages */
+
static void FillRectangle(rfbClient* client, int x, int y, int w, int h, uint32_t colour) {
int i,j;
@@ -259,6 +302,12 @@ InitialiseRFBConnection(rfbClient* client)
return FALSE;
}
+#if rfbProtocolMinorVersion == 7
+ /* work around LibVNCClient not yet speaking RFB 3.7 */
+#undef rfbProtocolMinorVersion
+#define rfbProtocolMinorVersion 3
+#endif
+
rfbClientLog("VNC server supports protocol version %d.%d (viewer %d.%d)\n",
major, minor, rfbProtocolMajorVersion, rfbProtocolMinorVersion);
@@ -394,6 +443,7 @@ SetFormatAndEncodings(rfbClient* client)
rfbBool requestCompressLevel = FALSE;
rfbBool requestQualityLevel = FALSE;
rfbBool requestLastRectEncoding = FALSE;
+ rfbClientProtocolExtension* e;
spf.type = rfbSetPixelFormat;
spf.format = client->format;
@@ -535,6 +585,13 @@ SetFormatAndEncodings(rfbClient* client)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingLastRect);
}
+ for(e = rfbClientExtensions; e; e = e->next)
+ if(e->encodings) {
+ int* enc;
+ for(enc = e->encodings; *enc; enc++)
+ encs[se->nEncodings++] = rfbClientSwap32IfLE(*enc);
+ }
+
len = sz_rfbSetEncodingsMsg + se->nEncodings * 4;
se->nEncodings = rfbClientSwap16IfLE(se->nEncodings);
@@ -923,9 +980,20 @@ HandleRFBServerMessage(rfbClient* client)
#endif
default:
- rfbClientLog("Unknown rect encoding %d\n",
- (int)rect.encoding);
- return FALSE;
+ {
+ rfbBool handled = FALSE;
+ rfbClientProtocolExtension* e;
+
+ for(e = rfbClientExtensions; !handled && e; e = e->next)
+ if(e->handleEncoding && e->handleEncoding(client, &rect))
+ handled = TRUE;
+
+ if(!handled) {
+ rfbClientLog("Unknown rect encoding %d\n",
+ (int)rect.encoding);
+ return FALSE;
+ }
+ }
}
/* Now we may discard "soft cursor locks". */
@@ -934,16 +1002,6 @@ HandleRFBServerMessage(rfbClient* client)
client->GotFrameBufferUpdate(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h);
}
-#ifdef MITSHM
- /* if using shared memory PutImage, make sure that the X server has
- updated its framebuffer before we reuse the shared memory. This is
- mainly to avoid copyrect using invalid screen contents - not sure
- if we'd need it otherwise. */
-
- if (client->appData.useShm)
- XSync(dpy, FALSE);
-#endif
-
if (!SendIncrementalFramebufferUpdateRequest(client))
return FALSE;
@@ -981,8 +1039,21 @@ HandleRFBServerMessage(rfbClient* client)
}
default:
- rfbClientLog("Unknown message type %d from VNC server\n",msg.type);
- return FALSE;
+ {
+ rfbBool handled = FALSE;
+ rfbClientProtocolExtension* e;
+
+ for(e = rfbClientExtensions; !handled && e; e = e->next)
+ if(e->handleMessage && e->handleMessage(client, &msg))
+ handled = TRUE;
+
+ if(!handled) {
+ char buffer[256];
+ ReadFromRFBServer(client, buffer, 256);
+ rfbClientLog("Unknown message type %d from VNC server\n",msg.type);
+ return FALSE;
+ }
+ }
}
return TRUE;
diff --git a/rfb/rfbclient.h b/rfb/rfbclient.h
index 5d34526..c99f4f9 100644
--- a/rfb/rfbclient.h
+++ b/rfb/rfbclient.h
@@ -63,6 +63,15 @@ typedef struct {
rfbBool doNotSleep;
} rfbVNCRec;
+/* client data */
+
+typedef struct rfbClientData {
+ void* tag;
+ void* data;
+ struct rfbClientData* next;
+} rfbClientData;
+
+/* app data (belongs into rfbClient?) */
typedef struct {
rfbBool shareDesktop;
@@ -181,7 +190,7 @@ typedef struct _rfbClient {
uint8_t *rcSource, *rcMask;
/* private data pointer */
- void* clientData;
+ rfbClientData* clientData;
rfbVNCRec* vncRec;
@@ -223,6 +232,26 @@ extern rfbBool HandleRFBServerMessage(rfbClient* client);
extern void PrintPixelFormat(rfbPixelFormat *format);
+/* client data */
+
+void rfbClientSetClientData(rfbClient* client, void* tag, void* data);
+void* rfbClientGetClientData(rfbClient* client, void* tag);
+
+/* protocol extensions */
+
+typedef struct _rfbClientProtocolExtension {
+ int* encodings;
+ /* returns TRUE if the encoding was handled */
+ rfbBool (*handleEncoding)(rfbClient* cl,
+ rfbFramebufferUpdateRectHeader* rect);
+ /* returns TRUE if it handled the message */
+ rfbBool (*handleMessage)(rfbClient* cl,
+ rfbServerToClientMsg* message);
+ struct _rfbClientProtocolExtension* next;
+} rfbClientProtocolExtension;
+
+void rfbClientRegisterExtension(rfbClientProtocolExtension* e);
+
/* sockets.c */
extern rfbBool errorMessageOnReadFailure;