From 2670641432683c15529d93f3ec2e09fed220b53c Mon Sep 17 00:00:00 2001 From: steven_carr Date: Wed, 3 May 2006 19:29:18 +0000 Subject: [PATCH] Client Independent Server Side Scaling is now supported Both PalmVNC and UltraVNC SetScale messages are supported --- ChangeLog | 4 + libvncserver/Makefile.am | 4 +- libvncserver/corre.c | 8 +- libvncserver/cursor.c | 10 + libvncserver/hextile.c | 8 +- libvncserver/main.c | 21 +- libvncserver/rfbserver.c | 116 ++++++++--- libvncserver/rre.c | 8 +- libvncserver/scale.c | 411 +++++++++++++++++++++++++++++++++++++++ libvncserver/scale.h | 10 + libvncserver/stats.c | 2 +- libvncserver/tight.c | 22 +-- libvncserver/ultra.c | 6 +- libvncserver/zlib.c | 10 +- libvncserver/zrle.c | 8 +- rfb/rfb.h | 10 + 16 files changed, 594 insertions(+), 64 deletions(-) create mode 100644 libvncserver/scale.c create mode 100644 libvncserver/scale.h diff --git a/ChangeLog b/ChangeLog index 81b6ec9..da59b24 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2006-05-03 Steven Carr + * Server Side Scaling is now supported in libvncserver + Both PalmVNC and UltraVNC SetScale messages are supported + 2006-05-02 Steven Carr * Ultra Encoding added. Tested against UltraVNC V1.01 * libvncclient/rfbproto.c CopyRectangle() BPP!=8 bug fixed. diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index 4ec7c39..8f66f64 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -19,7 +19,7 @@ include_HEADERS=../rfb/rfb.h ../rfb/rfbconfig.h ../rfb/rfbint.h \ ../rfb/rfbproto.h ../rfb/keysym.h ../rfb/rfbregion.h ../rfb/rfbclient.h noinst_HEADERS=d3des.h ../rfb/default8x16.h zrleoutstream.h \ - zrlepalettehelper.h zrletypes.h private.h minilzo.h lzoconf.h \ + zrlepalettehelper.h zrletypes.h private.h minilzo.h lzoconf.h scale.h \ $(TIGHTVNCFILETRANSFERHDRS) EXTRA_DIST=tableinit24.c tableinittctemplate.c tabletranstemplate.c \ @@ -36,7 +36,7 @@ endif LIB_SRCS = main.c rfbserver.c rfbregion.c auth.c sockets.c \ stats.c corre.c hextile.c rre.c translate.c cutpaste.c \ httpd.c cursor.c font.c \ - draw.c selbox.c d3des.c vncauth.c cargs.c minilzo.c ultra.c \ + draw.c selbox.c d3des.c vncauth.c cargs.c minilzo.c ultra.c scale.c \ $(ZLIBSRCS) $(JPEGSRCS) $(TIGHTVNCFILETRANSFERSRCS) libvncserver_a_SOURCES=$(LIB_SRCS) diff --git a/libvncserver/corre.c b/libvncserver/corre.c index 058a77c..c1164c0 100755 --- a/libvncserver/corre.c +++ b/libvncserver/corre.c @@ -97,10 +97,10 @@ rfbSendSmallRectEncodingCoRRE(rfbClientPtr cl, rfbRREHeader hdr; int nSubrects; int i; - char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) - + (x * (cl->screen->bitsPerPixel / 8))); + char *fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y) + + (x * (cl->scaledScreen->bitsPerPixel / 8))); - int maxRawSize = (cl->screen->width * cl->screen->height + int maxRawSize = (cl->scaledScreen->width * cl->scaledScreen->height * (cl->format.bitsPerPixel / 8)); if (rreBeforeBufSize < maxRawSize) { @@ -121,7 +121,7 @@ rfbSendSmallRectEncodingCoRRE(rfbClientPtr cl, (*cl->translateFn)(cl->translateLookupTable,&(cl->screen->serverFormat), &cl->format, fbptr, rreBeforeBuf, - cl->screen->paddedWidthInBytes, w, h); + cl->scaledScreen->paddedWidthInBytes, w, h); switch (cl->format.bitsPerPixel) { case 8: diff --git a/libvncserver/cursor.c b/libvncserver/cursor.c index d40fb76..2accc25 100644 --- a/libvncserver/cursor.c +++ b/libvncserver/cursor.c @@ -26,6 +26,8 @@ #include #include "private.h" +void rfbScaledScreenUpdate(rfbScreenInfoPtr screen, int x1, int y1, int x2, int y2); + /* * Send cursor shape either in X-style format or in client pixel format. */ @@ -42,6 +44,8 @@ rfbSendCursorShape(rfbClientPtr cl) uint8_t *bitmapData; uint8_t bitmapByte; + /* TODO: scale the cursor data to the correct size */ + pCursor = cl->screen->getCursorPtr(cl); /*if(!pCursor) return TRUE;*/ @@ -461,6 +465,9 @@ void rfbHideCursor(rfbClientPtr cl) memcpy(s->frameBuffer+(y1+j)*rowstride+x1*bpp, s->underCursorBuffer+j*x2*bpp, x2*bpp); + + /* Copy to all scaled versions */ + rfbScaledScreenUpdate(s, x1, y1, x1+x2, y1+y2); UNLOCK(s->cursorMutex); } @@ -619,6 +626,9 @@ void rfbShowCursor(rfbClientPtr cl) c->richSource+(j+j1)*c->width*bpp+(i+i1)*bpp,bpp); } + /* Copy to all scaled versions */ + rfbScaledScreenUpdate(s, x1, y1, x1+x2, y1+y2); + UNLOCK(s->cursorMutex); } diff --git a/libvncserver/hextile.c b/libvncserver/hextile.c index 70ee67f..cd598ac 100755 --- a/libvncserver/hextile.c +++ b/libvncserver/hextile.c @@ -126,12 +126,12 @@ sendHextiles##bpp(rfbClientPtr cl, int rx, int ry, int rw, int rh) { return FALSE; \ } \ \ - fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) \ - + (x * (cl->screen->bitsPerPixel / 8))); \ + fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y) \ + + (x * (cl->scaledScreen->bitsPerPixel / 8))); \ \ (*cl->translateFn)(cl->translateLookupTable, &(cl->screen->serverFormat), \ &cl->format, fbptr, (char *)clientPixelData, \ - cl->screen->paddedWidthInBytes, w, h); \ + cl->scaledScreen->paddedWidthInBytes, w, h); \ \ startUblen = cl->ublen; \ cl->updateBuf[startUblen] = 0; \ @@ -175,7 +175,7 @@ sendHextiles##bpp(rfbClientPtr cl, int rx, int ry, int rw, int rh) { (*cl->translateFn)(cl->translateLookupTable, \ &(cl->screen->serverFormat), &cl->format, fbptr, \ (char *)clientPixelData, \ - cl->screen->paddedWidthInBytes, w, h); \ + cl->scaledScreen->paddedWidthInBytes, w, h); \ \ memcpy(&cl->updateBuf[cl->ublen], (char *)clientPixelData, \ w * h * (bpp/8)); \ diff --git a/libvncserver/main.c b/libvncserver/main.c index 3aa9673..6a34980 100644 --- a/libvncserver/main.c +++ b/libvncserver/main.c @@ -48,9 +48,9 @@ static MUTEX(extMutex); static int rfbEnableLogging=1; #ifdef LIBVNCSERVER_WORDS_BIGENDIAN -char rfbEndianTest = 0; +char rfbEndianTest = (1==0); #else -char rfbEndianTest = -1; +char rfbEndianTest = (1==1); #endif /* @@ -407,6 +407,7 @@ void rfbMarkRegionAsModified(rfbScreenInfoPtr screen,sraRegionPtr modRegion) rfbReleaseClientIterator(iterator); } +void rfbScaledScreenUpdate(rfbScreenInfoPtr screen, int x1, int y1, int x2, int y2); void rfbMarkRectAsModified(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2) { sraRegionPtr region; @@ -421,7 +422,10 @@ void rfbMarkRectAsModified(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2) if(y1<0) y1=0; if(y2>screen->height) y2=screen->height; if(y1==y2) return; - + + /* update scaled copies for this rectangle */ + rfbScaledScreenUpdate(screen,x1,y1,x2,y2); + region = sraRgnCreateRect(x1,y1,x2,y2); rfbMarkRegionAsModified(screen,region); sraRgnDestroy(region); @@ -959,6 +963,17 @@ void rfbScreenCleanup(rfbScreenInfoPtr screen) #ifdef LIBVNCSERVER_HAVE_LIBJPEG rfbTightCleanup(screen); #endif + + /* free all 'scaled' versions of this screen */ + while (screen->scaledScreenNext!=NULL) + { + rfbScreenInfoPtr ptr; + ptr = screen->scaledScreenNext; + screen->scaledScreenNext = ptr->scaledScreenNext; + free(ptr->frameBuffer); + free(ptr); + } + #endif free(screen); } diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 9c52382..d3e1fbb 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -65,6 +65,7 @@ #define DEBUGPROTO(x) #endif +#include static void rfbProcessClientProtocolVersion(rfbClientPtr cl); static void rfbProcessClientNormalMessage(rfbClientPtr cl); @@ -250,6 +251,9 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, cl->screen = rfbScreen; cl->sock = sock; cl->viewOnly = FALSE; + /* setup pseudo scaling */ + cl->scaledScreen = rfbScreen; + cl->scaledScreen->scaledScreenRefCount++; rfbResetStats(cl); @@ -446,6 +450,9 @@ rfbClientConnectionGone(rfbClientPtr cl) if(cl->sock>0) close(cl->sock); + if (cl->scaledScreen!=NULL) + cl->scaledScreen->scaledScreenRefCount--; + #ifdef LIBVNCSERVER_HAVE_LIBZ rfbFreeZrleData(cl); #endif @@ -711,21 +718,31 @@ rfbProcessClientInitMessage(rfbClientPtr cl) } } +/* The values come in based on the scaled screen, we need to convert them to + * values based on the man screen's coordinate system + */ static rfbBool rectSwapIfLEAndClip(uint16_t* x,uint16_t* y,uint16_t* w,uint16_t* h, - rfbScreenInfoPtr screen) + rfbClientPtr cl) { - *x=Swap16IfLE(*x); - *y=Swap16IfLE(*y); - *w=Swap16IfLE(*w); - *h=Swap16IfLE(*h); - if(*w>screen->width-*x) - *w=screen->width-*x; + int x1=Swap16IfLE(*x); + int y1=Swap16IfLE(*y); + int w1=Swap16IfLE(*w); + int h1=Swap16IfLE(*h); + + rfbScaledCorrection(cl->scaledScreen, cl->screen, &x1, &y1, &w1, &h1, "rectSwapIfLEAndClip"); + *x = x1; + *y = y1; + *w = w1; + *h = h1; + + if(*w>cl->screen->width-*x) + *w=cl->screen->width-*x; /* possible underflow */ - if(*w>screen->width-*x) + if(*w>cl->screen->width-*x) return FALSE; - if(*h>screen->height-*y) - *h=screen->height-*y; - if(*h>screen->height-*y) + if(*h>cl->screen->height-*y) + *h=cl->screen->height-*y; + if(*h>cl->screen->height-*y) return FALSE; return TRUE; @@ -1062,10 +1079,17 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) return; } - if(!rectSwapIfLEAndClip(&msg.fur.x,&msg.fur.y,&msg.fur.w,&msg.fur.h, - cl->screen)) - return; + /* The values come in based on the scaled screen, we need to convert them to + * values based on the main screen's coordinate system + */ + if(!rectSwapIfLEAndClip(&msg.fur.x,&msg.fur.y,&msg.fur.w,&msg.fur.h,cl)) + { + rfbLog("Warning, ignoring rfbFramebufferUpdateRequest: %dXx%dY-%dWx%dH\n",msg.fur.x, msg.fur.y, msg.fur.w, msg.fur.h); + return; + } + + tmpRegion = sraRgnCreateRect(msg.fur.x, msg.fur.y, @@ -1142,18 +1166,19 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) if (msg.pe.buttonMask != cl->lastPtrButtons || cl->screen->deferPtrUpdateTime == 0) { cl->screen->ptrAddEvent(msg.pe.buttonMask, - Swap16IfLE(msg.pe.x), Swap16IfLE(msg.pe.y), cl); + ScaleX(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.x)), + ScaleY(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.y)), + cl); cl->lastPtrButtons = msg.pe.buttonMask; } else { - cl->lastPtrX = Swap16IfLE(msg.pe.x); - cl->lastPtrY = Swap16IfLE(msg.pe.y); + cl->lastPtrX = ScaleX(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.x)); + cl->lastPtrY = ScaleY(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.y)); cl->lastPtrButtons = msg.pe.buttonMask; } } return; - case rfbClientCutText: if ((n = rfbReadExact(cl, ((char *)&msg) + 1, @@ -1183,6 +1208,23 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) return; + case rfbPalmVNCSetScaleFactor: + cl->PalmVNC = TRUE; + case rfbSetScale: + + if ((n = rfbReadExact(cl, ((char *)&msg) + 1, + sz_rfbSetScaleMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + rfbLog("rfbSetScale(%d)\n", msg.ssc.scale); + rfbScalingSetup(cl,cl->screen->width/msg.ssc.scale, cl->screen->height/msg.ssc.scale); + + rfbSendNewScaleSize(cl); + + return; default: { @@ -1245,7 +1287,7 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, fu->type = rfbFramebufferUpdate; fu->nRects = Swap16IfLE(1); cl->ublen = sz_rfbFramebufferUpdateMsg; - if (!rfbSendNewFBSize(cl, cl->screen->width, cl->screen->height)) { + if (!rfbSendNewFBSize(cl, cl->scaledScreen->width, cl->scaledScreen->height)) { return FALSE; } return rfbSendUpdateBuf(cl); @@ -1401,6 +1443,9 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, int y = rect.y1; int w = rect.x2 - x; int h = rect.y2 - y; + /* We need to count the number of rects in the scaled screen */ + if (cl->screen!=cl->scaledScreen) + rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); int rectsPerRow = (w-1)/cl->correMaxWidth+1; int rows = (h-1)/cl->correMaxHeight+1; nUpdateRegionRects += rectsPerRow*rows; @@ -1414,6 +1459,9 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, int y = rect.y1; int w = rect.x2 - x; int h = rect.y2 - y; + /* We need to count the number of rects in the scaled screen */ + if (cl->screen!=cl->scaledScreen) + rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); nUpdateRegionRects += (((h-1) / (ULTRA_MAX_SIZE( w ) / w)) + 1); } sraRgnReleaseIterator(i); @@ -1426,6 +1474,9 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, int y = rect.y1; int w = rect.x2 - x; int h = rect.y2 - y; + /* We need to count the number of rects in the scaled screen */ + if (cl->screen!=cl->scaledScreen) + rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); nUpdateRegionRects += (((h-1) / (ZLIB_MAX_SIZE( w ) / w)) + 1); } sraRgnReleaseIterator(i); @@ -1438,6 +1489,9 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, int y = rect.y1; int w = rect.x2 - x; int h = rect.y2 - y; + /* We need to count the number of rects in the scaled screen */ + if (cl->screen!=cl->scaledScreen) + rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); int n = rfbNumCodedRectsTight(cl, x, y, w, h); if (n == 0) { nUpdateRegionRects = 0xFFFF; @@ -1509,6 +1563,10 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, int w = rect.x2 - x; int h = rect.y2 - y; + /* We need to count the number of rects in the scaled screen */ + if (cl->screen!=cl->scaledScreen) + rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); + cl->rawBytesEquivalent += (sz_rfbFramebufferUpdateRectHeader + w * (cl->format.bitsPerPixel / 8) * h); @@ -1598,12 +1656,19 @@ rfbSendCopyRegion(rfbClientPtr cl, /* printf("copyrect: "); sraRgnPrint(reg); putchar('\n');fflush(stdout); */ i = sraRgnGetReverseIterator(reg,dx>0,dy>0); + /* correct for the scale of the screen */ + dx = ScaleX(cl->screen, cl->scaledScreen, dx); + dy = ScaleX(cl->screen, cl->scaledScreen, dy); + while(sraRgnIteratorNext(i,&rect1)) { x = rect1.x1; y = rect1.y1; w = rect1.x2 - x; h = rect1.y2 - y; + /* correct for scaling (if necessary) */ + rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "copyrect"); + rect.r.x = Swap16IfLE(x); rect.r.y = Swap16IfLE(y); rect.r.w = Swap16IfLE(w); @@ -1644,8 +1709,8 @@ rfbSendRectEncodingRaw(rfbClientPtr cl, rfbFramebufferUpdateRectHeader rect; int nlines; int bytesPerLine = w * (cl->format.bitsPerPixel / 8); - char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) - + (x * (cl->screen->bitsPerPixel / 8))); + char *fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y) + + (x * (cl->scaledScreen->bitsPerPixel / 8))); /* Flush the buffer to guarantee correct alignment for translateFn(). */ if (cl->ublen > 0) { @@ -1675,7 +1740,7 @@ rfbSendRectEncodingRaw(rfbClientPtr cl, (*cl->translateFn)(cl->translateLookupTable, &(cl->screen->serverFormat), &cl->format, fbptr, &cl->updateBuf[cl->ublen], - cl->screen->paddedWidthInBytes, w, nlines); + cl->scaledScreen->paddedWidthInBytes, w, nlines); cl->ublen += nlines * bytesPerLine; h -= nlines; @@ -1688,7 +1753,7 @@ rfbSendRectEncodingRaw(rfbClientPtr cl, if (!rfbSendUpdateBuf(cl)) return FALSE; - fbptr += (cl->screen->paddedWidthInBytes * nlines); + fbptr += (cl->scaledScreen->paddedWidthInBytes * nlines); nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine; if (nlines == 0) { @@ -1752,6 +1817,11 @@ rfbSendNewFBSize(rfbClientPtr cl, return FALSE; } + if (cl->PalmVNC==TRUE) + rfbLog("Sending a rfbEncodingNewFBSize in response to a PalmVNC style frameuffer resize request (%dx%d)\n", w, h); + else + rfbLog("Sending a rfbEncodingNewFBSize in response to a UltraVNC style frameuffer resize request (%dx%d)\n", w, h); + rect.encoding = Swap32IfLE(rfbEncodingNewFBSize); rect.r.x = 0; rect.r.y = 0; diff --git a/libvncserver/rre.c b/libvncserver/rre.c index ca11bb3..8ef91fd 100755 --- a/libvncserver/rre.c +++ b/libvncserver/rre.c @@ -63,10 +63,10 @@ rfbSendRectEncodingRRE(rfbClientPtr cl, rfbRREHeader hdr; int nSubrects; int i; - char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) - + (x * (cl->screen->bitsPerPixel / 8))); + char *fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y) + + (x * (cl->scaledScreen->bitsPerPixel / 8))); - int maxRawSize = (cl->screen->width * cl->screen->height + int maxRawSize = (cl->scaledScreen->width * cl->scaledScreen->height * (cl->format.bitsPerPixel / 8)); if (rreBeforeBufSize < maxRawSize) { @@ -88,7 +88,7 @@ rfbSendRectEncodingRRE(rfbClientPtr cl, (*cl->translateFn)(cl->translateLookupTable, &(cl->screen->serverFormat), &cl->format, fbptr, rreBeforeBuf, - cl->screen->paddedWidthInBytes, w, h); + cl->scaledScreen->paddedWidthInBytes, w, h); switch (cl->format.bitsPerPixel) { case 8: diff --git a/libvncserver/scale.c b/libvncserver/scale.c new file mode 100644 index 0000000..44d1d11 --- /dev/null +++ b/libvncserver/scale.c @@ -0,0 +1,411 @@ +/* + * scale.c - deal with server-side scaling. + */ + +/* + * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin + * Copyright (C) 2002 RealVNC Ltd. + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifdef __STRICT_ANSI__ +#define _BSD_SOURCE +#endif +#include +#include +#include +#include "private.h" + +#ifdef LIBVNCSERVER_HAVE_FCNTL_H +#include +#endif + +#ifdef WIN32 +#define write(sock,buf,len) send(sock,buf,len,0) +#else +#ifdef LIBVNCSERVER_HAVE_UNISTD_H +#include +#endif +#include +#ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H +#include +#endif +#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H +#include +#include +#include +#endif +#endif + +#ifdef CORBA +#include +#endif + +#ifdef DEBUGPROTO +#undef DEBUGPROTO +#define DEBUGPROTO(x) x +#else +#define DEBUGPROTO(x) +#endif + +/****************************/ +#include + +int ScaleX(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int x) +{ + if ((from==to) || (from==NULL) || (to==NULL)) return x; + return ((int)(((double) x / (double)from->width) * (double)to->width )); +} + +int ScaleY(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int y) +{ + if ((from==to) || (from==NULL) || (to==NULL)) return y; + return ((int)(((double) y / (double)from->height) * (double)to->height )); +} + +/* So, all of the encodings point to the ->screen->frameBuffer, + * We need to change this! + */ +void rfbScaledCorrection(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int *x, int *y, int *w, int *h, char *function) +{ + double x1,y1,w1,h1, x2, y2, w2, h2; + double scaleW = ((double) to->width) / ((double) from->width); + double scaleH = ((double) to->height) / ((double) from->height); + + + /* + * rfbLog("rfbScaledCorrection(%p -> %p, %dx%d->%dx%d (%dXx%dY-%dWx%dH)\n", + * from, to, from->width, from->height, to->width, to->height, *x, *y, *w, *h); + */ + + /* If it's the original framebuffer... */ + if (from==to) return; + + x1 = ((double) *x) * scaleW; + y1 = ((double) *y) * scaleH; + w1 = ((double) *w) * scaleW; + h1 = ((double) *h) * scaleH; + + + /*cast from double to int is same as "*x = floor(x1);" */ + x2 = floor(x1); + y2 = floor(y1); + + /* include into W and H the jitter of scaling X and Y */ + w2 = ceil(w1 + ( x1 - x2 )); + h2 = ceil(h1 + ( y1 - y2 )); + + /* + * rfbLog("%s (%dXx%dY-%dWx%dH -> %fXx%fY-%fWx%fH) {%dWx%dH -> %dWx%dH}\n", + * function, *x, *y, *w, *h, x2, y2, w2, h2, + * from->width, from->height, to->width, to->height); + */ + + /* simulate ceil() without math library */ + *x = (int)x2; + *y = (int)y2; + *w = (int)w2; + *h = (int)h2; + + /* Small changes for a thumbnail may be scaled to zero */ + if (*w==0) *w++; + if (*h==0) *h++; + /* scaling from small to big may overstep the size a bit */ + if (*x+*w > to->width) *w=to->width - *x; + if (*y+*h > to->height) *h=to->height - *y; +} + +void rfbScaledScreenUpdateRect(rfbScreenInfoPtr screen, rfbScreenInfoPtr ptr, int x0, int y0, int w0, int h0) +{ + int x,y,w,v,z; + int x1, y1, w1, h1; + int bitsPerPixel, bytesPerPixel, bytesPerLine, areaX, areaY, area2; + unsigned char *srcptr, *dstptr; + + /* Nothing to do!!! */ + if (screen==ptr) return; + + x1 = x0; + y1 = y0; + w1 = w0; + h1 = h0; + + rfbScaledCorrection(screen, ptr, &x1, &y1, &w1, &h1, "rfbScaledScreenUpdateRect"); + + bitsPerPixel = screen->bitsPerPixel; + bytesPerPixel = bitsPerPixel / 8; + bytesPerLine = w1 * bytesPerPixel; + srcptr = (unsigned char *)(screen->frameBuffer + + (y0 * screen->paddedWidthInBytes + x0 * bytesPerPixel)); + dstptr = (unsigned char *)(ptr->frameBuffer + + ( y1 * ptr->paddedWidthInBytes + x1 * bytesPerPixel)); + /* The area of the source framebuffer for each destination pixel */ + areaX = ScaleX(ptr,screen,1); + areaY = ScaleY(ptr,screen,1); + area2 = areaX*areaY; + + + /* Ensure that we do not go out of bounds */ + if ((x1+w1) > (ptr->width)) + { + if (x1==0) w1=ptr->width; else x1 = ptr->width - w1; + } + if ((y1+h1) > (ptr->height)) + { + if (y1==0) h1=ptr->height; else y1 = ptr->height - h1; + } + /* + * rfbLog("rfbScaledScreenUpdateRect(%dXx%dY-%dWx%dH -> %dXx%dY-%dWx%dH <%dx%d>) {%dWx%dH -> %dWx%dH} 0x%p\n", + * x0, y0, w0, h0, x1, y1, w1, h1, areaX, areaY, + * screen->width, screen->height, ptr->width, ptr->height, ptr->frameBuffer); + */ + + if (screen->serverFormat.trueColour) { /* Blend neighbouring pixels together */ + unsigned char *srcptr2; + unsigned long pixel_value, red, green, blue; + unsigned int redShift = screen->serverFormat.redShift; + unsigned int greenShift = screen->serverFormat.greenShift; + unsigned int blueShift = screen->serverFormat.blueShift; + unsigned long redMax = screen->serverFormat.redMax; + unsigned long greenMax = screen->serverFormat.greenMax; + unsigned long blueMax = screen->serverFormat.blueMax; + + /* for each *destination* pixel... */ + for (y = 0; y < h1; y++) { + for (x = 0; x < w1; x++) { + red = green = blue = 0; + /* Get the totals for rgb from the source grid... */ + for (w = 0; w < areaX; w++) { + for (v = 0; v < areaY; v++) { + srcptr2 = &srcptr[(((x * areaX) + v) * bytesPerPixel) + + (w * screen->paddedWidthInBytes)]; + pixel_value = 0; + + + switch (bytesPerPixel) { + case 4: pixel_value = *((unsigned int *)srcptr2); break; + case 2: pixel_value = *((unsigned short *)srcptr2); break; + case 1: pixel_value = *((unsigned char *)srcptr2); break; + default: + /* fixme: endianess problem? */ + for (z = 0; z < bytesPerPixel; z++) + pixel_value += (srcptr2[z] << (8 * z)); + break; + } + //srcptr2 += bytesPerPixel; + + red += ((pixel_value >> redShift) & redMax); + green += ((pixel_value >> greenShift) & greenMax); + blue += ((pixel_value >> blueShift) & blueMax); + + } + } + /* We now have a total for all of the colors, find the average! */ + red /= area2; + green /= area2; + blue /= area2; + /* Stuff the new value back into memory */ + pixel_value = ((red & redMax) << redShift) | ((green & greenMax) << greenShift) | ((blue & blueMax) << blueShift); + + switch (bytesPerPixel) { + case 4: *((unsigned int *)dstptr) = (unsigned int) pixel_value; break; + case 2: *((unsigned short *)dstptr) = (unsigned short) pixel_value; break; + case 1: *((unsigned char *)dstptr) = (unsigned char) pixel_value; break; + default: + /* fixme: endianess problem? */ + for (z = 0; z < bytesPerPixel; z++) + dstptr[z]=(pixel_value >> (8 * z)) & 0xff; + break; + } + dstptr += bytesPerPixel; + } + srcptr += (screen->paddedWidthInBytes * areaY); + dstptr += (ptr->paddedWidthInBytes - bytesPerLine); + } + } else + { /* Not truecolour, so we can't blend. Just use the top-left pixel instead */ + for (y = y1; y < (y1+h1); y++) { + for (x = x1; x < (x1+w1); x++) + memcpy (&ptr->frameBuffer[(y *ptr->paddedWidthInBytes) + (x * bytesPerPixel)], + &screen->frameBuffer[(y * areaY * screen->paddedWidthInBytes) + (x *areaX * bytesPerPixel)], bytesPerPixel); + } + } +} + +void rfbScaledScreenUpdate(rfbScreenInfoPtr screen, int x1, int y1, int x2, int y2) +{ + /* ok, now the task is to update each and every scaled version of the framebuffer + * and we only have to do this for this specific changed rectangle! + */ + rfbScreenInfoPtr ptr; + int count=0; + + /* We don't point to cl->screen as it is the original */ + for (ptr=screen->scaledScreenNext;ptr!=NULL;ptr=ptr->scaledScreenNext) + { + /* Only update if it has active clients... */ + if (ptr->scaledScreenRefCount>0) + { + rfbScaledScreenUpdateRect(screen, ptr, x1, y1, x2-x1, y2-y1); + count++; + } + } +} + +/* Create a new scaled version of the framebuffer */ +rfbScreenInfoPtr rfbScaledScreenAllocate(rfbClientPtr cl, int width, int height) +{ + rfbScreenInfoPtr ptr; + ptr = malloc(sizeof(rfbScreenInfo)); + if (ptr!=NULL) + { + /* copy *everything* (we don't use most of it, but just in case) */ + memcpy(ptr, cl->screen, sizeof(rfbScreenInfo)); + ptr->width = width; + ptr->height = height; + ptr->paddedWidthInBytes = (ptr->bitsPerPixel/8)*ptr->width; + + /* Need to by multiples of 4 for Sparc systems */ + ptr->paddedWidthInBytes += (ptr->paddedWidthInBytes % 4); + + /* Reset the reference count to 0! */ + ptr->scaledScreenRefCount = 0; + + ptr->sizeInBytes = ptr->paddedWidthInBytes * ptr->height; + ptr->serverFormat = cl->screen->serverFormat; + + ptr->frameBuffer = malloc(ptr->sizeInBytes); + if (ptr->frameBuffer!=NULL) + { + /* Reset to a known condition: scale the entire framebuffer */ + rfbScaledScreenUpdateRect(cl->screen, ptr, 0, 0, cl->screen->width, cl->screen->height); + /* Now, insert into the chain */ + LOCK(cl->updateMutex); + ptr->scaledScreenNext = cl->screen->scaledScreenNext; + cl->screen->scaledScreenNext = ptr; + UNLOCK(cl->updateMutex); + } + else + { + /* Failed to malloc the new frameBuffer, cleanup */ + free(ptr); + ptr=NULL; + } + } + return ptr; +} + +/* Find an active scaled version of the framebuffer + * TODO: implement a refcount per scaled screen to prevent + * unreferenced scaled screens from hanging around + */ +rfbScreenInfoPtr rfbScalingFind(rfbClientPtr cl, int width, int height) +{ + rfbScreenInfoPtr ptr; + /* include the original in the search (ie: fine 1:1 scaled version of the frameBuffer) */ + for (ptr=cl->screen; ptr!=NULL; ptr=ptr->scaledScreenNext) + { + if ((ptr->width==width) && (ptr->height==height)) + return ptr; + } + return NULL; +} + +/* Future needs "scale to 320x240, as that's the client's screen size */ +void rfbScalingSetup(rfbClientPtr cl, int width, int height) +{ + rfbScreenInfoPtr ptr; + + ptr = rfbScalingFind(cl,width,height); + if (ptr==NULL) + ptr = rfbScaledScreenAllocate(cl,width,height); + /* Now, there is a new screen available (if ptr is not NULL) */ + if (ptr!=NULL) + { + /* Update it! */ + if (ptr->scaledScreenRefCount<1) + rfbScaledScreenUpdateRect(cl->screen, ptr, 0, 0, cl->screen->width, cl->screen->height); + /* + * rfbLog("Taking one from %dx%d-%d and adding it to %dx%d-%d\n", + * cl->scaledScreen->width, cl->scaledScreen->height, + * cl->scaledScreen->scaledScreenRefCount, + * ptr->width, ptr->height, ptr->scaledScreenRefCount); + */ + + LOCK(cl->updateMutex); + cl->scaledScreen->scaledScreenRefCount--; + ptr->scaledScreenRefCount++; + cl->scaledScreen=ptr; + cl->newFBSizePending = TRUE; + UNLOCK(cl->updateMutex); + + rfbLog("Scaling to %dx%d (refcount=%d)\n",width,height,ptr->scaledScreenRefCount); + } + else + rfbLog("Scaling to %dx%d failed, leaving things alone\n",width,height); +} + +int rfbSendNewScaleSize(rfbClientPtr cl) +{ + /* if the client supports newFBsize Encoding, use it */ + if (cl->useNewFBSize && cl->newFBSizePending) + return FALSE; + + LOCK(cl->updateMutex); + cl->newFBSizePending = FALSE; + UNLOCK(cl->updateMutex); + + if (cl->PalmVNC==TRUE) + { + rfbPalmVNCReSizeFrameBufferMsg pmsg; + pmsg.type = rfbPalmVNCReSizeFrameBuffer; + pmsg.pad1 = 0; + pmsg.desktop_w = Swap16IfLE(cl->screen->width); + pmsg.desktop_h = Swap16IfLE(cl->screen->height); + pmsg.buffer_w = Swap16IfLE(cl->scaledScreen->width); + pmsg.buffer_h = Swap16IfLE(cl->scaledScreen->height); + pmsg.pad2 = 0; + + rfbLog("Sending a response to a PalmVNC style frameuffer resize event (%dx%d)\n", cl->scaledScreen->width, cl->scaledScreen->height); + if (rfbWriteExact(cl, (char *)&pmsg, sz_rfbPalmVNCReSizeFrameBufferMsg) < 0) { + rfbLogPerror("rfbNewClient: write"); + rfbCloseClient(cl); + rfbClientConnectionGone(cl); + return FALSE; + } + } + else + { + rfbResizeFrameBufferMsg rmsg; + rmsg.type = rfbResizeFrameBuffer; + rmsg.pad1=0; + rmsg.framebufferWidth = Swap16IfLE(cl->scaledScreen->width); + rmsg.framebufferHeigth = Swap16IfLE(cl->scaledScreen->height); + rfbLog("Sending a response to a UltraVNC style frameuffer resize event (%dx%d)\n", cl->scaledScreen->width, cl->scaledScreen->height); + if (rfbWriteExact(cl, (char *)&rmsg, sz_rfbResizeFrameBufferMsg) < 0) { + rfbLogPerror("rfbNewClient: write"); + rfbCloseClient(cl); + rfbClientConnectionGone(cl); + return FALSE; + } + } + return TRUE; +} +/****************************/ diff --git a/libvncserver/scale.h b/libvncserver/scale.h new file mode 100644 index 0000000..13dd942 --- /dev/null +++ b/libvncserver/scale.h @@ -0,0 +1,10 @@ + +int ScaleX(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int x); +int ScaleY(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int y); +void rfbScaledCorrection(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int *x, int *y, int *w, int *h, char *function); +void rfbScaledScreenUpdateRect(rfbScreenInfoPtr screen, rfbScreenInfoPtr ptr, int x0, int y0, int w0, int h0); +void rfbScaledScreenUpdate(rfbScreenInfoPtr screen, int x1, int y1, int x2, int y2); +rfbScreenInfoPtr rfbScaledScreenAllocate(rfbClientPtr cl, int width, int height); +rfbScreenInfoPtr rfbScalingFind(rfbClientPtr cl, int width, int height); +void rfbScalingSetup(rfbClientPtr cl, int width, int height); +int rfbSendNewScaleSize(rfbClientPtr cl); diff --git a/libvncserver/stats.c b/libvncserver/stats.c index 969eeed..ed9a3d0 100755 --- a/libvncserver/stats.c +++ b/libvncserver/stats.c @@ -28,7 +28,7 @@ static const char* encNames[] = { "raw", "copyRect", "RRE", "[encoding 3]", "CoRRE", "hextile", - "zlib", "tight", "[encoding 8]", "[encoding 9]", "[encoding 10]", + "zlib", "tight", "[encoding 8]", "ultra", "[encoding 10]", "[encoding 11]", "[encoding 12]", "[encoding 13]", "[encoding 14]", "[encoding 15]", "ZRLE", "[encoding 17]", "[encoding 18]", "[encoding 19]", "[encoding 20]" diff --git a/libvncserver/tight.c b/libvncserver/tight.c index 4d76e8f..71ce0d9 100644 --- a/libvncserver/tight.c +++ b/libvncserver/tight.c @@ -332,13 +332,13 @@ rfbSendRectEncodingTight(rfbClientPtr cl, if (!SendTightHeader(cl, x_best, y_best, w_best, h_best)) return FALSE; - fbptr = (cl->screen->frameBuffer + - (cl->screen->paddedWidthInBytes * y_best) + - (x_best * (cl->screen->bitsPerPixel / 8))); + fbptr = (cl->scaledScreen->frameBuffer + + (cl->scaledScreen->paddedWidthInBytes * y_best) + + (x_best * (cl->scaledScreen->bitsPerPixel / 8))); (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat, &cl->format, fbptr, tightBeforeBuf, - cl->screen->paddedWidthInBytes, 1, 1); + cl->scaledScreen->paddedWidthInBytes, 1, 1); if (!SendSolidRect(cl)) return FALSE; @@ -486,7 +486,7 @@ CheckSolidTile##bpp(rfbClientPtr cl, int x, int y, int w, int h, \ int dx, dy; \ \ fbptr = (uint##bpp##_t *) \ - &cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + x * (bpp/8)]; \ + &cl->scaledScreen->frameBuffer[y * cl->scaledScreen->paddedWidthInBytes + x * (bpp/8)]; \ \ colorValue = *fbptr; \ if (needSameColor && (uint32_t)colorValue != *colorPtr) \ @@ -497,7 +497,7 @@ CheckSolidTile##bpp(rfbClientPtr cl, int x, int y, int w, int h, \ if (colorValue != fbptr[dx]) \ return FALSE; \ } \ - fbptr = (uint##bpp##_t *)((uint8_t *)fbptr + cl->screen->paddedWidthInBytes); \ + fbptr = (uint##bpp##_t *)((uint8_t *)fbptr + cl->scaledScreen->paddedWidthInBytes); \ } \ \ *colorPtr = (uint32_t)colorValue; \ @@ -580,12 +580,12 @@ SendSubrect(rfbClientPtr cl, if (!SendTightHeader(cl, x, y, w, h)) return FALSE; - fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) - + (x * (cl->screen->bitsPerPixel / 8))); + fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y) + + (x * (cl->scaledScreen->bitsPerPixel / 8))); (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat, &cl->format, fbptr, tightBeforeBuf, - cl->screen->paddedWidthInBytes, w, h); + cl->scaledScreen->paddedWidthInBytes, w, h); paletteMaxColors = w * h / tightConf[compressLevel].idxMaxColorsDivisor; if ( paletteMaxColors < 2 && @@ -1723,7 +1723,7 @@ PrepareRowForJpeg24(rfbClientPtr cl, uint32_t pix; fbptr = (uint32_t *) - &cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + x * 4]; + &cl->scaledScreen->frameBuffer[y * cl->scaledScreen->paddedWidthInBytes + x * 4]; while (count--) { pix = *fbptr++; @@ -1742,7 +1742,7 @@ PrepareRowForJpeg##bpp(rfbClientPtr cl, uint8_t *dst, int x, int y, int count) { int inRed, inGreen, inBlue; \ \ fbptr = (uint##bpp##_t *) \ - &cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + \ + &cl->scaledScreen->frameBuffer[y * cl->scaledScreen->paddedWidthInBytes + \ x * (bpp / 8)]; \ \ while (count--) { \ diff --git a/libvncserver/ultra.c b/libvncserver/ultra.c index 5825d5b..e1821bb 100644 --- a/libvncserver/ultra.c +++ b/libvncserver/ultra.c @@ -43,8 +43,8 @@ rfbSendOneRectEncodingUltra(rfbClientPtr cl, rfbZlibHeader hdr; int deflateResult; int i; - char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) - + (x * (cl->screen->bitsPerPixel / 8))); + char *fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y) + + (x * (cl->scaledScreen->bitsPerPixel / 8))); int maxRawSize; int maxCompSize; @@ -78,7 +78,7 @@ rfbSendOneRectEncodingUltra(rfbClientPtr cl, */ (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat, &cl->format, fbptr, lzoBeforeBuf, - cl->screen->paddedWidthInBytes, w, h); + cl->scaledScreen->paddedWidthInBytes, w, h); if ( cl->compStreamInitedLZO == FALSE ) { cl->compStreamInitedLZO = TRUE; diff --git a/libvncserver/zlib.c b/libvncserver/zlib.c index 8503f59..321d86f 100644 --- a/libvncserver/zlib.c +++ b/libvncserver/zlib.c @@ -64,13 +64,13 @@ rfbSendOneRectEncodingZlib(rfbClientPtr cl, int deflateResult; int previousOut; int i; - char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) - + (x * (cl->screen->bitsPerPixel / 8))); + char *fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y) + + (x * (cl->scaledScreen->bitsPerPixel / 8))); int maxRawSize; int maxCompSize; - maxRawSize = (cl->screen->width * cl->screen->height + maxRawSize = (cl->scaledScreen->width * cl->scaledScreen->height * (cl->format.bitsPerPixel / 8)); if (zlibBeforeBufSize < maxRawSize) { @@ -84,7 +84,7 @@ rfbSendOneRectEncodingZlib(rfbClientPtr cl, /* zlib compression is not useful for very small data sets. * So, we just send these raw without any compression. */ - if (( w * h * (cl->screen->bitsPerPixel / 8)) < + if (( w * h * (cl->scaledScreen->bitsPerPixel / 8)) < VNC_ENCODE_ZLIB_MIN_COMP_SIZE ) { int result; @@ -126,7 +126,7 @@ rfbSendOneRectEncodingZlib(rfbClientPtr cl, */ (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat, &cl->format, fbptr, zlibBeforeBuf, - cl->screen->paddedWidthInBytes, w, h); + cl->scaledScreen->paddedWidthInBytes, w, h); cl->compStream.next_in = ( Bytef * )zlibBeforeBuf; cl->compStream.avail_in = w * h * (cl->format.bitsPerPixel / 8); diff --git a/libvncserver/zrle.c b/libvncserver/zrle.c index da9e67b..76123a0 100644 --- a/libvncserver/zrle.c +++ b/libvncserver/zrle.c @@ -30,13 +30,13 @@ #define GET_IMAGE_INTO_BUF(tx,ty,tw,th,buf) \ -{ char *fbptr = (cl->screen->frameBuffer \ - + (cl->screen->paddedWidthInBytes * ty) \ - + (tx * (cl->screen->bitsPerPixel / 8))); \ +{ char *fbptr = (cl->scaledScreen->frameBuffer \ + + (cl->scaledScreen->paddedWidthInBytes * ty) \ + + (tx * (cl->scaledScreen->bitsPerPixel / 8))); \ \ (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat,\ &cl->format, fbptr, (char*)buf, \ - cl->screen->paddedWidthInBytes, tw, th); } + cl->scaledScreen->paddedWidthInBytes, tw, th); } #define EXTRA_ARGS , rfbClientPtr cl diff --git a/rfb/rfb.h b/rfb/rfb.h index 06ffa8f..23d64d4 100644 --- a/rfb/rfb.h +++ b/rfb/rfb.h @@ -201,6 +201,10 @@ typedef struct _rfbExtensionData { typedef struct _rfbScreenInfo { + /* this structure has children that are scaled versions of this screen */ + struct _rfbScreenInfo *scaledScreenNext; + int scaledScreenRefCount; + int width; int paddedWidthInBytes; int height; @@ -348,6 +352,12 @@ typedef struct _rfbClientRec { /* back pointer to the screen */ rfbScreenInfoPtr screen; + + /* points to a scaled version of the screen buffer in cl->scaledScreenList */ + rfbScreenInfoPtr scaledScreen; + /* how did the client tell us it wanted the screen changed? Ultra style or palm style? */ + rfbBool PalmVNC; + /* private data. You should put any application client specific data * into a struct and let clientData point to it. Don't forget to