From 19c7fc0217e2087af2173974689b9b0f892a18d3 Mon Sep 17 00:00:00 2001
From: dscho <dscho>
Date: Tue, 29 Oct 2002 14:07:15 +0000
Subject: patch from Const for CursorPosUpdate encoding

---
 CHANGES     |   2 +
 README      |   2 -
 cursor.c    |  47 ++++++++++++++++++++---
 main.c      |  34 +++++++++++-----
 rfb.h       |  11 ++++--
 rfbproto.h  | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 rfbserver.c |  44 ++++++++++++++++++---
 stats.c     |  24 ++++++++----
 8 files changed, 256 insertions(+), 34 deletions(-)

diff --git a/CHANGES b/CHANGES
index 48f7998..2ce6420 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,5 @@
+   got patch from Const Kaplisnky with CursorPosUpdate encoding and some Docs
+   sync'ed with newest RealVNC (ZRLE encoding)
    a HTTP request for tunnelling was added (to fool strict web proxies)
    sync'ed with TightVNC 1.2.5
 0.4
diff --git a/README b/README
index 3ed194b..f6a1586 100644
--- a/README
+++ b/README
@@ -94,8 +94,6 @@ all the details. Just set the cursor and don't bother any more.
 
 To set the mouse coordinates (or emulate mouse clicks), call
   defaultPtrAddEvent(buttonMask,x,y,cl);
-However, this works only if your client doesn't do local cursor drawing. There
-is no way (to my knowledge) to set the pointer of a client via RFB protocol.
 IMPORTANT: do this at the end of your function, because this actually draws
 the cursor if no cursor encoding is active.
 
diff --git a/cursor.c b/cursor.c
index fa28a82..7408a8a 100644
--- a/cursor.c
+++ b/cursor.c
@@ -24,6 +24,8 @@
 
 #include "rfb.h"
 
+static unsigned char rfbReverseByte[0x100];
+
 /*
  * Send cursor shape either in X-style format or in client pixel format.
  */
@@ -73,8 +75,8 @@ rfbSendCursorShape(cl)
 	       sz_rfbFramebufferUpdateRectHeader);
 	cl->ublen += sz_rfbFramebufferUpdateRectHeader;
 
-	cl->rfbCursorBytesSent += sz_rfbFramebufferUpdateRectHeader;
-	cl->rfbCursorUpdatesSent++;
+	cl->rfbCursorShapeBytesSent += sz_rfbFramebufferUpdateRectHeader;
+	cl->rfbCursorShapeUpdatesSent++;
 
 	if (!rfbSendUpdateBuf(cl))
 	    return FALSE;
@@ -163,8 +165,8 @@ rfbSendCursorShape(cl)
 
     /* Send everything we have prepared in the cl->updateBuf[]. */
 
-    cl->rfbCursorBytesSent += (cl->ublen - saved_ublen);
-    cl->rfbCursorUpdatesSent++;
+    cl->rfbCursorShapeBytesSent += (cl->ublen - saved_ublen);
+    cl->rfbCursorShapeUpdatesSent++;
 
     if (!rfbSendUpdateBuf(cl))
 	return FALSE;
@@ -172,8 +174,41 @@ rfbSendCursorShape(cl)
     return TRUE;
 }
 
+/*
+ * Send cursor position (PointerPos pseudo-encoding).
+ */
+
+Bool
+rfbSendCursorPos(rfbClientPtr cl)
+{
+  rfbFramebufferUpdateRectHeader rect;
+
+  if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
+    if (!rfbSendUpdateBuf(cl))
+      return FALSE;
+  }
+
+  rect.encoding = Swap32IfLE(rfbEncodingPointerPos);
+  rect.r.x = Swap16IfLE(cl->screen->cursorX);
+  rect.r.y = Swap16IfLE(cl->screen->cursorY);
+  rect.r.w = 0;
+  rect.r.h = 0;
+
+  memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
+	 sz_rfbFramebufferUpdateRectHeader);
+  cl->ublen += sz_rfbFramebufferUpdateRectHeader;
+
+  cl->rfbCursorPosBytesSent += sz_rfbFramebufferUpdateRectHeader;
+  cl->rfbCursorPosUpdatesSent++;
+
+  if (!rfbSendUpdateBuf(cl))
+    return FALSE;
+
+  return TRUE;
+}
+
 /* conversion routine for predefined cursors in LSB order */
-unsigned char rfbReverseByte[0x100] = {
+static unsigned char rfbReverseByte[0x100] = {
   /* copied from Xvnc/lib/font/util/utilbitmap.c */
 	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
 	0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
@@ -452,7 +487,7 @@ void rfbPrintXCursor(rfbCursorPtr cursor)
    }
 }
 
-extern void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c,Bool freeOld)
+void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c,Bool freeOld)
 {
   LOCK(rfbScreen->cursorMutex);
   while(rfbScreen->cursorIsDrawn) {
diff --git a/main.c b/main.c
index 044e3c6..0f4bf7d 100644
--- a/main.c
+++ b/main.c
@@ -364,16 +364,32 @@ defaultKbdAddEvent(Bool down, KeySym keySym, rfbClientPtr cl)
 void
 defaultPtrAddEvent(int buttonMask, int x, int y, rfbClientPtr cl)
 {
-   if(x!=cl->screen->cursorX || y!=cl->screen->cursorY) {
-      if(cl->screen->cursorIsDrawn)
-	rfbUndrawCursor(cl->screen);
-      LOCK(cl->screen->cursorMutex);
-      if(!cl->screen->cursorIsDrawn) {
-	  cl->screen->cursorX = x;
-	  cl->screen->cursorY = y;
+  rfbClientIteratorPtr iterator;
+  rfbClientPtr other_client;
+
+  if (x != cl->screen->cursorX || y != cl->screen->cursorY) {
+    if (cl->screen->cursorIsDrawn)
+      rfbUndrawCursor(cl->screen);
+    LOCK(cl->screen->cursorMutex);
+    if (!cl->screen->cursorIsDrawn) {
+      cl->screen->cursorX = x;
+      cl->screen->cursorY = y;
+    }
+    UNLOCK(cl->screen->cursorMutex);
+
+    /* The cursor was moved by this client, so don't send CursorPos. */
+    if (cl->enableCursorPosUpdates)
+      cl->cursorWasMoved = FALSE;
+
+    /* But inform all remaining clients about this cursor movement. */
+    iterator = rfbGetClientIterator(cl->screen);
+    while ((other_client = rfbClientIteratorNext(iterator)) != NULL) {
+      if (other_client != cl && other_client->enableCursorPosUpdates) {
+	other_client->cursorWasMoved = TRUE;
       }
-      UNLOCK(cl->screen->cursorMutex);
-   }
+    }
+    rfbReleaseClientIterator(iterator);
+  }
 }
 
 void defaultSetXCutText(char* text, int len, rfbClientPtr cl)
diff --git a/rfb.h b/rfb.h
index 7e096f0..856dd9b 100644
--- a/rfb.h
+++ b/rfb.h
@@ -483,8 +483,10 @@ typedef struct _rfbClientRec {
     int rfbRectanglesSent[MAX_ENCODINGS];
     int rfbLastRectMarkersSent;
     int rfbLastRectBytesSent;
-    int rfbCursorBytesSent;
-    int rfbCursorUpdatesSent;
+    int rfbCursorShapeBytesSent;
+    int rfbCursorShapeUpdatesSent;
+    int rfbCursorPosBytesSent;
+    int rfbCursorPosUpdatesSent;
     int rfbFramebufferUpdateMessagesSent;
     int rfbRawBytesEquivalent;
     int rfbKeyEventsRcvd;
@@ -506,8 +508,10 @@ typedef struct _rfbClientRec {
 
     Bool enableLastRectEncoding;   /* client supports LastRect encoding */
     Bool enableCursorShapeUpdates; /* client supports cursor shape updates */
+    Bool enableCursorPosUpdates;   /* client supports cursor position updates */
     Bool useRichCursorEncoding;    /* rfbEncodingRichCursor is preferred */
     Bool cursorWasChanged;         /* cursor shape update should be sent */
+    Bool cursorWasMoved;           /* cursor position update should be sent */
 
     Bool useNewFBSize;             /* client supports NewFBSize encoding */
     Bool newFBSizePending;         /* framebuffer size was changed */
@@ -545,6 +549,7 @@ typedef struct _rfbClientRec {
      ((!(cl)->enableCursorShapeUpdates && !(cl)->screen->cursorIsDrawn) || \
      ((cl)->enableCursorShapeUpdates && (cl)->cursorWasChanged) ||         \
      ((cl)->useNewFBSize && (cl)->newFBSizePending) ||                     \
+     ((cl)->enableCursorPosUpdates && (cl)->cursorWasMoved) ||             \
      !sraRgnEmpty((cl)->copyRegion) || !sraRgnEmpty((cl)->modifiedRegion))
 
 /*
@@ -705,7 +710,7 @@ typedef struct rfbCursor {
 } rfbCursor, *rfbCursorPtr;
 
 extern Bool rfbSendCursorShape(rfbClientPtr cl/*, rfbScreenInfoPtr pScreen*/);
-extern unsigned char rfbReverseByte[0x100];
+extern Bool rfbSendCursorPos(rfbClientPtr cl);
 extern void rfbConvertLSBCursorBitmapOrMask(int width,int height,unsigned char* bitmap);
 extern rfbCursorPtr rfbMakeXCursor(int width,int height,char* cursorString,char* maskString);
 extern char* rfbMakeMaskForXCursor(int width,int height,char* cursorString);
diff --git a/rfbproto.h b/rfbproto.h
index e84f6a0..0fb19b3 100644
--- a/rfbproto.h
+++ b/rfbproto.h
@@ -2,7 +2,7 @@
 #define RFBPROTO_H
 
 /*
- *  Copyright (C) 2000, 2001 Const Kaplinsky.  All Rights Reserved.
+ *  Copyright (C) 2000-2002 Constantin Kaplinsky.  All Rights Reserved.
  *  Copyright (C) 2000 Tridia Corporation.  All Rights Reserved.
  *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
  *
@@ -327,6 +327,7 @@ typedef struct {
 
 #define rfbEncodingXCursor         0xFFFFFF10
 #define rfbEncodingRichCursor      0xFFFFFF11
+#define rfbEncodingPointerPos      0xFFFFFF18
 
 #define rfbEncodingLastRect        0xFFFFFF20
 #define rfbEncodingNewFBSize       0xFFFFFF21
@@ -502,7 +503,128 @@ typedef struct {
 
 
 /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- * Tight Encoding.  FIXME: Add more documentation.
+ * Tight Encoding.
+ *
+ *-- The first byte of each Tight-encoded rectangle is a "compression control
+ *   byte". Its format is as follows (bit 0 is the least significant one):
+ *
+ *   bit 0:    if 1, then compression stream 0 should be reset;
+ *   bit 1:    if 1, then compression stream 1 should be reset;
+ *   bit 2:    if 1, then compression stream 2 should be reset;
+ *   bit 3:    if 1, then compression stream 3 should be reset;
+ *   bits 7-4: if 1000 (0x08), then the compression type is "fill",
+ *             if 1001 (0x09), then the compression type is "jpeg",
+ *             if 0xxx, then the compression type is "basic",
+ *             values greater than 1001 are not valid.
+ *
+ * If the compression type is "basic", then bits 6..4 of the
+ * compression control byte (those xxx in 0xxx) specify the following:
+ *
+ *   bits 5-4:  decimal representation is the index of a particular zlib
+ *              stream which should be used for decompressing the data;
+ *   bit 6:     if 1, then a "filter id" byte is following this byte.
+ *
+ *-- The data that follows after the compression control byte described
+ * above depends on the compression type ("fill", "jpeg" or "basic").
+ *
+ *-- If the compression type is "fill", then the only pixel value follows, in
+ * client pixel format (see NOTE 1). This value applies to all pixels of the
+ * rectangle.
+ *
+ *-- If the compression type is "jpeg", the following data stream looks like
+ * this:
+ *
+ *   1..3 bytes:  data size (N) in compact representation;
+ *   N bytes:     JPEG image.
+ *
+ * Data size is compactly represented in one, two or three bytes, according
+ * to the following scheme:
+ *
+ *  0xxxxxxx                    (for values 0..127)
+ *  1xxxxxxx 0yyyyyyy           (for values 128..16383)
+ *  1xxxxxxx 1yyyyyyy zzzzzzzz  (for values 16384..4194303)
+ *
+ * Here each character denotes one bit, xxxxxxx are the least significant 7
+ * bits of the value (bits 0-6), yyyyyyy are bits 7-13, and zzzzzzzz are the
+ * most significant 8 bits (bits 14-21). For example, decimal value 10000
+ * should be represented as two bytes: binary 10010000 01001110, or
+ * hexadecimal 90 4E.
+ *
+ *-- If the compression type is "basic" and bit 6 of the compression control
+ * byte was set to 1, then the next (second) byte specifies "filter id" which
+ * tells the decoder what filter type was used by the encoder to pre-process
+ * pixel data before the compression. The "filter id" byte can be one of the
+ * following:
+ *
+ *   0:  no filter ("copy" filter);
+ *   1:  "palette" filter;
+ *   2:  "gradient" filter.
+ *
+ *-- If bit 6 of the compression control byte is set to 0 (no "filter id"
+ * byte), or if the filter id is 0, then raw pixel values in the client
+ * format (see NOTE 1) will be compressed. See below details on the
+ * compression.
+ *
+ *-- The "gradient" filter pre-processes pixel data with a simple algorithm
+ * which converts each color component to a difference between a "predicted"
+ * intensity and the actual intensity. Such a technique does not affect
+ * uncompressed data size, but helps to compress photo-like images better. 
+ * Pseudo-code for converting intensities to differences is the following:
+ *
+ *   P[i,j] := V[i-1,j] + V[i,j-1] - V[i-1,j-1];
+ *   if (P[i,j] < 0) then P[i,j] := 0;
+ *   if (P[i,j] > MAX) then P[i,j] := MAX;
+ *   D[i,j] := V[i,j] - P[i,j];
+ *
+ * Here V[i,j] is the intensity of a color component for a pixel at
+ * coordinates (i,j). MAX is the maximum value of intensity for a color
+ * component.
+ *
+ *-- The "palette" filter converts true-color pixel data to indexed colors
+ * and a palette which can consist of 2..256 colors. If the number of colors
+ * is 2, then each pixel is encoded in 1 bit, otherwise 8 bits is used to
+ * encode one pixel. 1-bit encoding is performed such way that the most
+ * significant bits correspond to the leftmost pixels, and each raw of pixels
+ * is aligned to the byte boundary. When "palette" filter is used, the
+ * palette is sent before the pixel data. The palette begins with an unsigned
+ * byte which value is the number of colors in the palette minus 1 (i.e. 1
+ * means 2 colors, 255 means 256 colors in the palette). Then follows the
+ * palette itself which consist of pixel values in client pixel format (see
+ * NOTE 1).
+ *
+ *-- The pixel data is compressed using the zlib library. But if the data
+ * size after applying the filter but before the compression is less then 12,
+ * then the data is sent as is, uncompressed. Four separate zlib streams
+ * (0..3) can be used and the decoder should read the actual stream id from
+ * the compression control byte (see NOTE 2).
+ *
+ * If the compression is not used, then the pixel data is sent as is,
+ * otherwise the data stream looks like this:
+ *
+ *   1..3 bytes:  data size (N) in compact representation;
+ *   N bytes:     zlib-compressed data.
+ *
+ * Data size is compactly represented in one, two or three bytes, just like
+ * in the "jpeg" compression method (see above).
+ *
+ *-- NOTE 1. If the color depth is 24, and all three color components are
+ * 8-bit wide, then one pixel in Tight encoding is always represented by
+ * three bytes, where the first byte is red component, the second byte is
+ * green component, and the third byte is blue component of the pixel color
+ * value. This applies to colors in palettes as well.
+ *
+ *-- NOTE 2. The decoder must reset compression streams' states before
+ * decoding the rectangle, if some of bits 0,1,2,3 in the compression control
+ * byte are set to 1. Note that the decoder must reset zlib streams even if
+ * the compression type is "fill" or "jpeg".
+ *
+ *-- NOTE 3. The "gradient" filter and "jpeg" compression may be used only
+ * when bits-per-pixel value is either 16 or 32, not 8.
+ *
+ *-- NOTE 4. The width of any Tight-encoded rectangle cannot exceed 2048
+ * pixels. If a rectangle is wider, it must be split into several rectangles
+ * and each one should be encoded separately.
+ *
  */
 
 #define rfbTightExplicitFilter         0x04
diff --git a/rfbserver.c b/rfbserver.c
index 51769be..9cdda1e 100644
--- a/rfbserver.c
+++ b/rfbserver.c
@@ -293,6 +293,7 @@ rfbNewTCPOrUDPClient(rfbScreen,sock,isUDP)
         cl->zsActive[i] = FALSE;
 
       cl->enableCursorShapeUpdates = FALSE;
+      cl->enableCursorPosUpdates = FALSE;
       cl->useRichCursorEncoding = FALSE;
       cl->enableLastRectEncoding = FALSE;
       cl->useNewFBSize = FALSE;
@@ -696,6 +697,7 @@ rfbProcessClientNormalMessage(cl)
         cl->preferredEncoding = -1;
 	cl->useCopyRect = FALSE;
 	cl->enableCursorShapeUpdates = FALSE;
+	cl->enableCursorPosUpdates = FALSE;
 	cl->enableLastRectEncoding = FALSE;
         cl->useNewFBSize = FALSE;
 
@@ -764,12 +766,20 @@ rfbProcessClientNormalMessage(cl)
 		}
 		break;
 	    case rfbEncodingRichCursor:
-	        rfbLog("Enabling full-color cursor updates for client "
-		      "%s\n", cl->host);
+	        rfbLog("Enabling full-color cursor updates for client %s\n",
+		       cl->host);
 	        cl->enableCursorShapeUpdates = TRUE;
 	        cl->useRichCursorEncoding = TRUE;
 	        cl->cursorWasChanged = TRUE;
 	        break;
+	    case rfbEncodingPointerPos:
+		if (!cl->enableCursorPosUpdates) {
+		    rfbLog("Enabling cursor position updates for client %s\n",
+			   cl->host);
+		    cl->enableCursorPosUpdates = TRUE;
+		    cl->cursorWasMoved = TRUE;
+		}
+	        break;
 	    case rfbEncodingLastRect:
 		if (!cl->enableLastRectEncoding) {
 		    rfbLog("Enabling LastRect protocol extension for client "
@@ -824,6 +834,12 @@ rfbProcessClientNormalMessage(cl)
             cl->preferredEncoding = rfbEncodingRaw;
         }
 
+	if (cl->enableCursorPosUpdates && !cl->enableCursorShapeUpdates) {
+	  rfbLog("Disabling cursor position updates for client %s\n",
+		 cl->host);
+	  cl->enableCursorPosUpdates = FALSE;
+	}
+
         return;
     }
 
@@ -972,6 +988,7 @@ rfbSendFramebufferUpdate(cl, givenUpdateRegion)
     sraRegionPtr updateRegion,updateCopyRegion,tmpRegion;
     int dx, dy;
     Bool sendCursorShape = FALSE;
+    Bool sendCursorPos = FALSE;
 
     if(cl->screen->displayHook)
       cl->screen->displayHook(cl);
@@ -1013,6 +1030,13 @@ rfbSendFramebufferUpdate(cl, givenUpdateRegion)
       }
     }
 
+    /*
+     * Do we plan to send cursor position update?
+     */
+
+    if (cl->enableCursorPosUpdates && cl->cursorWasMoved)
+      sendCursorPos = TRUE;
+
     LOCK(cl->updateMutex);
 
     /*
@@ -1032,7 +1056,8 @@ rfbSendFramebufferUpdate(cl, givenUpdateRegion)
 
     updateRegion = sraRgnCreateRgn(givenUpdateRegion);
     sraRgnOr(updateRegion,cl->copyRegion);
-    if(!sraRgnAnd(updateRegion,cl->requestedRegion) && !sendCursorShape) {
+    if(!sraRgnAnd(updateRegion,cl->requestedRegion) &&
+       !sendCursorShape && !sendCursorPos) {
       sraRgnDestroy(updateRegion);
       UNLOCK(cl->updateMutex);
       return TRUE;
@@ -1131,8 +1156,9 @@ rfbSendFramebufferUpdate(cl, givenUpdateRegion)
 
     fu->type = rfbFramebufferUpdate;
     if (nUpdateRegionRects != 0xFFFF) {
-	fu->nRects = Swap16IfLE((CARD16)(sraRgnCountRects(updateCopyRegion)
-				+ nUpdateRegionRects + !!sendCursorShape));
+	fu->nRects = Swap16IfLE((CARD16)(sraRgnCountRects(updateCopyRegion) +
+					 nUpdateRegionRects +
+					 !!sendCursorShape + !!sendCursorPos));
     } else {
 	fu->nRects = 0xFFFF;
     }
@@ -1146,6 +1172,14 @@ rfbSendFramebufferUpdate(cl, givenUpdateRegion)
 	}
     }
    
+   if (sendCursorPos) {
+	cl->cursorWasMoved = FALSE;
+	if (!rfbSendCursorPos(cl)) {
+	    sraRgnDestroy(updateRegion);
+	    return FALSE;
+	}
+    }
+   
     if (!sraRgnEmpty(updateCopyRegion)) {
 	if (!rfbSendCopyRegion(cl,updateCopyRegion,dx,dy)) {
 	    sraRgnDestroy(updateRegion);
diff --git a/stats.c b/stats.c
index a941741..7d8b6b6 100644
--- a/stats.c
+++ b/stats.c
@@ -51,8 +51,10 @@ rfbResetStats(rfbClientPtr cl)
     }
     cl->rfbLastRectMarkersSent = 0;
     cl->rfbLastRectBytesSent = 0;
-    cl->rfbCursorBytesSent = 0;
-    cl->rfbCursorUpdatesSent = 0;
+    cl->rfbCursorShapeBytesSent = 0;
+    cl->rfbCursorShapeUpdatesSent = 0;
+    cl->rfbCursorPosBytesSent = 0;
+    cl->rfbCursorPosUpdatesSent = 0;
     cl->rfbFramebufferUpdateMessagesSent = 0;
     cl->rfbRawBytesEquivalent = 0;
     cl->rfbKeyEventsRcvd = 0;
@@ -77,9 +79,12 @@ rfbPrintStats(rfbClientPtr cl)
         totalBytesSent += cl->rfbBytesSent[i];
     }
 
-    totalRectanglesSent += (cl->rfbCursorUpdatesSent +
+    totalRectanglesSent += (cl->rfbCursorShapeUpdatesSent +
+                            cl->rfbCursorPosUpdatesSent +
 			    cl->rfbLastRectMarkersSent);
-    totalBytesSent += (cl->rfbCursorBytesSent + cl->rfbLastRectBytesSent);
+    totalBytesSent += (cl->rfbCursorShapeBytesSent +
+                       cl->rfbCursorPosBytesSent +
+                       cl->rfbLastRectBytesSent);
 
     rfbLog("  framebuffer updates %d, rectangles %d, bytes %d\n",
             cl->rfbFramebufferUpdateMessagesSent, totalRectanglesSent,
@@ -89,9 +94,13 @@ rfbPrintStats(rfbClientPtr cl)
 	rfbLog("    LastRect and NewFBSize markers %d, bytes %d\n",
 		cl->rfbLastRectMarkersSent, cl->rfbLastRectBytesSent);
 
-    if (cl->rfbCursorUpdatesSent != 0)
+    if (cl->rfbCursorShapeUpdatesSent != 0)
 	rfbLog("    cursor shape updates %d, bytes %d\n",
-		cl->rfbCursorUpdatesSent, cl->rfbCursorBytesSent);
+		cl->rfbCursorShapeUpdatesSent, cl->rfbCursorShapeBytesSent);
+
+    if (cl->rfbCursorPosUpdatesSent != 0)
+	rfbLog("    cursor position updates %d, bytes %d\n",
+		cl->rfbCursorPosUpdatesSent, cl->rfbCursorPosBytesSent);
 
     for (i = 0; i < MAX_ENCODINGS; i++) {
         if (cl->rfbRectanglesSent[i] != 0)
@@ -105,7 +114,8 @@ rfbPrintStats(rfbClientPtr cl)
                 (double)cl->rfbRawBytesEquivalent
                 / (double)(totalBytesSent
                            - cl->rfbBytesSent[rfbEncodingCopyRect]-
-			   cl->rfbCursorBytesSent -
+			   cl->rfbCursorShapeBytesSent -
+                           cl->rfbCursorPosBytesSent -
 			   cl->rfbLastRectBytesSent));
     }
 }
-- 
cgit v1.2.3

