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 +#include +#include +#include +#include + +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;