From 7e6d5c65eb90a567b665965643f3c874cb4a1658 Mon Sep 17 00:00:00 2001
From: dscho <dscho>
Date: Sun, 28 Jul 2002 16:45:16 +0000
Subject: NewFB encoding added

---
 CHANGES               |   1 +
 classes/VncViewer.jar | Bin 36406 -> 37317 bytes
 classes/index.vnc     |   3 +-
 example.c             |  42 +++++++++++--
 httpd.c               |   2 +
 main.c                | 161 +++++++++++++++++++++++++++++++++++++++-----------
 rfb.h                 |  18 ++++--
 rfbproto.h            |   1 +
 rfbserver.c           |  62 +++++++++++++++++++
 stats.c               |   2 +-
 10 files changed, 246 insertions(+), 46 deletions(-)

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 @@
 <TITLE>
 $USER's $DESKTOP desktop ($DISPLAY)
 </TITLE>
-<APPLET CODE=vncviewer.class ARCHIVE=VncViewer.jar
+<APPLET CODE=VncViewer.class ARCHIVE=VncViewer.jar
         WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT>
 <param name=PORT value=$PORT>
+<param name="Open New Window" value=yes>
 </APPLET>
 <BR>
 <A href="http://www.tightvnc.com/">www.TightVNC.com</A>
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)
-- 
cgit v1.2.3

