diff --git a/CHANGES b/CHANGES index 8767a12..bba61b9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,4 @@ + support for NewFB from Const Kaplinsky memory leaks squashed (localtime pseudo leak is still there :-) small improvements for OSXvnc (still not working correctly) synced with TightVNC 1.2.3 diff --git a/classes/VncViewer.jar b/classes/VncViewer.jar index ac35865..cf4c124 100644 Binary files a/classes/VncViewer.jar and b/classes/VncViewer.jar differ diff --git a/classes/index.vnc b/classes/index.vnc index 5eae27e..6cab43e 100644 --- a/classes/index.vnc +++ b/classes/index.vnc @@ -8,9 +8,10 @@ $USER's $DESKTOP desktop ($DISPLAY) - +
www.TightVNC.com diff --git a/example.c b/example.c index 01ec504..2100b41 100644 --- a/example.c +++ b/example.c @@ -34,7 +34,8 @@ #include "rfb.h" #include "keysym.h" -const int maxx=640, maxy=480, bpp=4; +const int bpp=4; +int maxx=800, maxy=600; /* TODO: odd maxx doesn't work (vncviewer bug) */ /* This initializes a nice (?) background */ @@ -74,6 +75,23 @@ enum rfbNewClientAction newclient(rfbClientPtr cl) return RFB_CLIENT_ACCEPT; } +/* switch to new framebuffer contents */ + +void newframebuffer(rfbScreenInfoPtr screen, int width, int height) +{ + char *oldfb, *newfb; + + maxx = width; + maxy = height; + oldfb = screen->frameBuffer; + newfb = (char*)malloc(maxx * maxy * bpp); + initBuffer(newfb); + rfbNewFramebuffer(screen, newfb, maxx, maxy, 8, 3, bpp); + free(oldfb); + + /*** FIXME: Re-install cursor. ***/ +} + /* aux function to draw a line */ void drawline(unsigned char* buffer,int rowstride,int bpp,int x1,int y1,int x2,int y2) @@ -158,6 +176,22 @@ void dokey(Bool down,KeySym key,rfbClientPtr cl) rfbUndrawCursor(cl->screen); initBuffer(cl->screen->frameBuffer); rfbMarkRectAsModified(cl->screen,0,0,maxx,maxy); + } else if (key == XK_Up) { + if (maxx < 1024) { + if (maxx < 800) { + newframebuffer(cl->screen, 800, 600); + } else { + newframebuffer(cl->screen, 1024, 768); + } + } + } else if(key==XK_Down) { + if (maxx > 640) { + if (maxx > 800) { + newframebuffer(cl->screen, 800, 600); + } else { + newframebuffer(cl->screen, 640, 480); + } + } } else if(key>=' ' && key<0x100) { ClientData* cd=cl->clientData; int x1=cd->oldx,y1=cd->oldy,x2,y2; @@ -266,7 +300,7 @@ int main(int argc,char** argv) #ifdef USE_OWN_LOOP { int i; - for(i=0;i<200;i++) { + for(i=0;;i++) { fprintf(stderr,"%d\r",i); rfbProcessEvents(rfbScreen,100000); } @@ -275,7 +309,7 @@ int main(int argc,char** argv) #ifndef BACKGROUND_LOOP_TEST /* this is the blocking event loop, i.e. it never returns */ - /* 40000 are the microseconds, i.e. 0.04 seconds */ + /* 40000 are the microseconds to wait on select(), i.e. 0.04 seconds */ rfbRunEventLoop(rfbScreen,40000,FALSE); #elif !defined(HAVE_PTHREADS) #error "I need pthreads for that." @@ -283,7 +317,7 @@ int main(int argc,char** argv) /* this is the non-blocking event loop; a background thread is started */ rfbRunEventLoop(rfbScreen,-1,TRUE); - /* now we could do some cool things like rendering */ + /* now we could do some cool things like rendering in idle time */ while(1) sleep(5); /* render(); */ #endif diff --git a/httpd.c b/httpd.c index 8f6aa01..813b239 100644 --- a/httpd.c +++ b/httpd.c @@ -218,6 +218,8 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen) fname = &fullFname[strlen(fullFname)]; maxFnameLen = 255 - strlen(fullFname); + buf_filled=0; + /* Read data from the HTTP client until we get a complete request. */ while (1) { ssize_t got = read (rfbScreen->httpSock, buf + buf_filled, diff --git a/main.c b/main.c index f1f800a..ea2eeb9 100644 --- a/main.c +++ b/main.c @@ -468,12 +468,57 @@ enum rfbNewClientAction defaultNewClientHook(rfbClientPtr cl) return RFB_CLIENT_ACCEPT; } +/* + * Update server's pixel format in rfbScreenInfo structure. This + * function is called from rfbGetScreen() and rfbNewFramebuffer(). + */ + +static void rfbInitServerFormat(rfbScreenInfoPtr rfbScreen, int bitsPerSample) +{ + rfbPixelFormat* format=&rfbScreen->rfbServerFormat; + + format->bitsPerPixel = rfbScreen->bitsPerPixel; + format->depth = rfbScreen->depth; + format->bigEndian = rfbEndianTest?FALSE:TRUE; + format->trueColour = TRUE; + rfbScreen->colourMap.count = 0; + rfbScreen->colourMap.is16 = 0; + rfbScreen->colourMap.data.bytes = NULL; + + if (format->bitsPerPixel == 8) { + format->redMax = 7; + format->greenMax = 7; + format->blueMax = 3; + format->redShift = 0; + format->greenShift = 3; + format->blueShift = 6; + } else { + format->redMax = (1 << bitsPerSample) - 1; + format->greenMax = (1 << bitsPerSample) - 1; + format->blueMax = (1 << bitsPerSample) - 1; + if(rfbEndianTest) { + format->redShift = 0; + format->greenShift = bitsPerSample; + format->blueShift = bitsPerSample * 2; + } else { + if(format->bitsPerPixel==8*3) { + format->redShift = bitsPerSample*2; + format->greenShift = bitsPerSample*1; + format->blueShift = 0; + } else { + format->redShift = bitsPerSample*3; + format->greenShift = bitsPerSample*2; + format->blueShift = bitsPerSample; + } + } + } +} + rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, int width,int height,int bitsPerSample,int samplesPerPixel, int bytesPerPixel) { rfbScreenInfoPtr rfbScreen=malloc(sizeof(rfbScreenInfo)); - rfbPixelFormat* format=&rfbScreen->rfbServerFormat; INIT_MUTEX(logMutex); @@ -530,41 +575,7 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, /* format */ - format->bitsPerPixel = rfbScreen->bitsPerPixel; - format->depth = rfbScreen->depth; - format->bigEndian = rfbEndianTest?FALSE:TRUE; - format->trueColour = TRUE; - rfbScreen->colourMap.count = 0; - rfbScreen->colourMap.is16 = 0; - rfbScreen->colourMap.data.bytes = NULL; - - if(bytesPerPixel == 1) { - format->redMax = 7; - format->greenMax = 7; - format->blueMax = 3; - format->redShift = 0; - format->greenShift = 3; - format->blueShift = 6; - } else { - format->redMax = (1 << bitsPerSample) - 1; - format->greenMax = (1 << bitsPerSample) - 1; - format->blueMax = (1 << bitsPerSample) - 1; - if(rfbEndianTest) { - format->redShift = 0; - format->greenShift = bitsPerSample; - format->blueShift = bitsPerSample * 2; - } else { - if(bytesPerPixel==3) { - format->redShift = bitsPerSample*2; - format->greenShift = bitsPerSample*1; - format->blueShift = 0; - } else { - format->redShift = bitsPerSample*3; - format->greenShift = bitsPerSample*2; - format->blueShift = bitsPerSample; - } - } - } + rfbInitServerFormat(rfbScreen, bitsPerSample); /* cursor */ @@ -597,6 +608,84 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, return(rfbScreen); } +/* + * Switch to another framebuffer (maybe of different size and color + * format). Clients supporting NewFBSize pseudo-encoding will change + * their local framebuffer dimensions if necessary. + * NOTE: Rich cursor data should be converted to new pixel format by + * the caller. + */ + +void rfbNewFramebuffer(rfbScreenInfoPtr rfbScreen, char *framebuffer, + int width, int height, + int bitsPerSample, int samplesPerPixel, + int bytesPerPixel) +{ + rfbPixelFormat old_format; + Bool format_changed = FALSE; + rfbClientIteratorPtr iterator; + rfbClientPtr cl; + + /* Remove the pointer */ + + rfbUndrawCursor(rfbScreen); + + /* Update information in the rfbScreenInfo structure */ + + old_format = rfbScreen->rfbServerFormat; + + if (width & 3) + rfbLog("WARNING: New width (%d) is not a multiple of 4.\n", width); + + rfbScreen->width = width; + rfbScreen->height = height; + rfbScreen->bitsPerPixel = rfbScreen->depth = 8*bytesPerPixel; + rfbScreen->paddedWidthInBytes = width*bytesPerPixel; + + rfbInitServerFormat(rfbScreen, bitsPerSample); + + if (memcmp(&rfbScreen->rfbServerFormat, &old_format, + sizeof(rfbPixelFormat)) != 0) { + format_changed = TRUE; + } + + rfbScreen->frameBuffer = framebuffer; + + /* Adjust pointer position if necessary */ + + if (rfbScreen->cursorX >= width) + rfbScreen->cursorX = width - 1; + if (rfbScreen->cursorY >= height) + rfbScreen->cursorY = height - 1; + + /* For each client: */ + iterator = rfbGetClientIterator(rfbScreen); + while ((cl = rfbClientIteratorNext(iterator)) != NULL) { + + /* Re-install color translation tables if necessary */ + + if (format_changed) + rfbScreen->setTranslateFunction(cl); + + /* Mark the screen contents as changed, and schedule sending + NewFBSize message if supported by this client. */ + + LOCK(cl->updateMutex); + sraRgnDestroy(cl->modifiedRegion); + cl->modifiedRegion = sraRgnCreateRect(0, 0, width, height); + sraRgnMakeEmpty(cl->copyRegion); + cl->copyDX = 0; + cl->copyDY = 0; + + if (cl->useNewFBSize) + cl->newFBSizePending = TRUE; + + TSIGNAL(cl->updateCond); + UNLOCK(cl->updateMutex); + } + rfbReleaseClientIterator(iterator); +} + void rfbScreenCleanup(rfbScreenInfoPtr rfbScreen) { rfbClientIteratorPtr i=rfbGetClientIterator(rfbScreen); diff --git a/rfb.h b/rfb.h index 7c7121c..98dc004 100644 --- a/rfb.h +++ b/rfb.h @@ -503,8 +503,12 @@ typedef struct _rfbClientRec { Bool enableCursorShapeUpdates; /* client supports cursor shape updates */ Bool useRichCursorEncoding; /* rfbEncodingRichCursor is preferred */ Bool cursorWasChanged; /* cursor shape update should be sent */ + + Bool useNewFBSize; /* client supports NewFBSize encoding */ + Bool newFBSizePending; /* framebuffer size was changed */ + #ifdef BACKCHANNEL - Bool enableBackChannel; + Bool enableBackChannel; /* custom channel for special clients */ #endif struct _rfbClientRec *prev; @@ -532,9 +536,10 @@ typedef struct _rfbClientRec { * be sent to the client. */ -#define FB_UPDATE_PENDING(cl) \ - ((!(cl)->enableCursorShapeUpdates && !(cl)->screen->cursorIsDrawn) || \ - ((cl)->enableCursorShapeUpdates && (cl)->cursorWasChanged) || \ +#define FB_UPDATE_PENDING(cl) \ + ((!(cl)->enableCursorShapeUpdates && !(cl)->screen->cursorIsDrawn) || \ + ((cl)->enableCursorShapeUpdates && (cl)->cursorWasChanged) || \ + ((cl)->useNewFBSize && (cl)->newFBSizePending) || \ !sraRgnEmpty((cl)->copyRegion) || !sraRgnEmpty((cl)->modifiedRegion)) /* @@ -603,6 +608,7 @@ extern Bool rfbSendUpdateBuf(rfbClientPtr cl); extern void rfbSendServerCutText(rfbScreenInfoPtr rfbScreen,char *str, int len); extern Bool rfbSendCopyRegion(rfbClientPtr cl,sraRegionPtr reg,int dx,int dy); extern Bool rfbSendLastRectMarker(rfbClientPtr cl); +extern Bool rfbSendNewFBSize(rfbClientPtr cl, int w, int h); extern Bool rfbSendSetColourMapEntries(rfbClientPtr cl, int firstColour, int nColours); extern void rfbSendBell(rfbScreenInfoPtr rfbScreen); @@ -796,6 +802,10 @@ extern rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, int width,int height,int bitsPerSample,int samplesPerPixel, int bytesPerPixel); extern void rfbInitServer(rfbScreenInfoPtr rfbScreen); +extern void rfbNewFramebuffer(rfbScreenInfoPtr rfbScreen,char *framebuffer, + int width,int height, int bitsPerSample,int samplesPerPixel, + int bytesPerPixel); + extern void rfbScreenCleanup(rfbScreenInfoPtr screenInfo); /* functions to accept/refuse a client that has been put on hold diff --git a/rfbproto.h b/rfbproto.h index 6ea6a62..f839019 100644 --- a/rfbproto.h +++ b/rfbproto.h @@ -328,6 +328,7 @@ typedef struct { #define rfbEncodingRichCursor 0xFFFFFF11 #define rfbEncodingLastRect 0xFFFFFF20 +#define rfbEncodingNewFBSize 0xFFFFFF21 #define rfbEncodingQualityLevel0 0xFFFFFFE0 #define rfbEncodingQualityLevel1 0xFFFFFFE1 diff --git a/rfbserver.c b/rfbserver.c index 11aeff6..f2dfada 100644 --- a/rfbserver.c +++ b/rfbserver.c @@ -266,6 +266,7 @@ rfbNewTCPOrUDPClient(rfbScreen,sock,isUDP) cl->enableCursorShapeUpdates = FALSE; cl->useRichCursorEncoding = FALSE; cl->enableLastRectEncoding = FALSE; + cl->useNewFBSize = FALSE; cl->compStreamInited = FALSE; cl->compStream.total_in = 0; @@ -663,6 +664,7 @@ rfbProcessClientNormalMessage(cl) cl->useCopyRect = FALSE; cl->enableCursorShapeUpdates = FALSE; cl->enableLastRectEncoding = FALSE; + cl->useNewFBSize = FALSE; for (i = 0; i < msg.se.nEncodings; i++) { if ((n = ReadExact(cl, (char *)&enc, 4)) <= 0) { @@ -742,6 +744,13 @@ rfbProcessClientNormalMessage(cl) cl->enableLastRectEncoding = TRUE; } break; + case rfbEncodingNewFBSize: + if (!cl->useNewFBSize) { + rfbLog("Enabling NewFBSize protocol extension for client " + "%s\n", cl->host); + cl->useNewFBSize = TRUE; + } + break; #ifdef BACKCHANNEL case rfbEncodingBackChannel: if (!cl->enableBackChannel) { @@ -924,6 +933,25 @@ rfbSendFramebufferUpdate(cl, givenUpdateRegion) if(cl->screen->displayHook) cl->screen->displayHook(cl); + + /* + * If framebuffer size was changed and the client supports NewFBSize + * encoding, just send NewFBSize marker and return. + */ + + if (cl->useNewFBSize && cl->newFBSizePending) { + LOCK(cl->updateMutex); + cl->newFBSizePending = FALSE; + UNLOCK(cl->updateMutex); + cl->rfbFramebufferUpdateMessagesSent++; + fu->type = rfbFramebufferUpdate; + fu->nRects = Swap16IfLE(1); + cl->ublen = sz_rfbFramebufferUpdateMsg; + if (!rfbSendNewFBSize(cl, cl->screen->width, cl->screen->height)) { + return FALSE; + } + return rfbSendUpdateBuf(cl); + } /* * If this client understands cursor shape updates, cursor should be @@ -1309,6 +1337,40 @@ rfbSendLastRectMarker(cl) } +/* + * Send NewFBSize pseudo-rectangle. This tells the client to change + * its framebuffer size. + */ + +Bool +rfbSendNewFBSize(cl, w, h) + rfbClientPtr cl; + int w, h; +{ + rfbFramebufferUpdateRectHeader rect; + + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.encoding = Swap32IfLE(rfbEncodingNewFBSize); + rect.r.x = 0; + rect.r.y = 0; + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbLastRectMarkersSent++; + cl->rfbLastRectBytesSent += sz_rfbFramebufferUpdateRectHeader; + + return TRUE; +} + + /* * Send the contents of cl->updateBuf. Returns 1 if successful, -1 if * not (errno should be set). diff --git a/stats.c b/stats.c index 7774d2f..a0b5ccc 100644 --- a/stats.c +++ b/stats.c @@ -78,7 +78,7 @@ rfbPrintStats(rfbClientPtr cl) totalBytesSent); if (cl->rfbLastRectMarkersSent != 0) - rfbLog(" LastRect markers %d, bytes %d\n", + rfbLog(" LastRect and NewFBSize markers %d, bytes %d\n", cl->rfbLastRectMarkersSent, cl->rfbLastRectBytesSent); if (cl->rfbCursorUpdatesSent != 0)