summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES2
-rw-r--r--README2
-rw-r--r--cursor.c47
-rw-r--r--main.c34
-rw-r--r--rfb.h11
-rw-r--r--rfbproto.h126
-rw-r--r--rfbserver.c44
-rw-r--r--stats.c24
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));
}
}