Commit 19c7fc02 authored by dscho's avatar dscho

patch from Const for CursorPosUpdate encoding

parent 347ab48b
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
......
......@@ -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.
......
......@@ -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) {
......
......@@ -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)
......
......@@ -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);
......
......@@ -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
......
......@@ -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);
......
......@@ -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));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment