From 9f44b0dfeeca747a15481246b919902cf920b343 Mon Sep 17 00:00:00 2001 From: Tim Jansen Date: Mon, 24 Jun 2002 22:23:45 +0000 Subject: [PATCH] Added support for Softcursor protocol extension svn path=/trunk/kdenetwork/krfb/; revision=162994 --- TODO | 2 - libvncserver/cursor.c | 195 +++++++++++++++++++++++++++++++++++++++ libvncserver/main.c | 1 + libvncserver/rfb.h | 10 ++ libvncserver/rfbproto.h | 48 ++++++++++ libvncserver/rfbserver.c | 34 ++++++- 6 files changed, 285 insertions(+), 5 deletions(-) diff --git a/TODO b/TODO index 0711cd86..d873fa3f 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,6 @@ Todo (unscheduled features): -- attach .vnc file to invitations? - SLP support (or UPnP, or whatever KDE will use) - NAT traversal support (MIDCOM stun/turn, UPnP) -- better cursor encodings - SSL/TLS support? - better authentication mechanisms? - look into adding an extension to xfree to improve speed (get noticed of diff --git a/libvncserver/cursor.c b/libvncserver/cursor.c index b83c8bec..6fa083c4 100644 --- a/libvncserver/cursor.c +++ b/libvncserver/cursor.c @@ -172,6 +172,134 @@ rfbSendCursorShape(cl) return TRUE; } +/* + * Send soft cursor state and possibly image + */ + +Bool +rfbSendSoftCursor(cl) + rfbClientPtr cl; +{ + rfbCursorPtr pCursor; + rfbSoftCursorSetImage setImage; + rfbSoftCursorMove moveMsg; + rfbFramebufferUpdateRectHeader rect; + int saved_ublen, i, scOindex, scNindex, nlen, imgLen; + + pCursor = cl->screen->getCursorPtr(cl); + MakeSoftCursor(cl->screen,pCursor); + + /* If there is no cursor, send update with empty cursor data. */ + + if ( pCursor && pCursor->width <= 1 && + pCursor->height <= 1 && + pCursor->mask[0] == 0 ) { + pCursor = NULL; + } + + + if (pCursor == NULL) + imgLen = 0; + else + imgLen = pCursor->softCursorLength; + setImage.imageLength = Swap16IfLE(imgLen); + + scOindex = -1; + scNindex = -1; + for (i = 0; i < rfbSoftCursorMaxImages; i++) { + rfbSoftCursorSetImage *scsi = cl->softCursorImages[i]; + if (!scsi) { + scNindex = i; + break; + } + + setImage.imageIndex = scsi->imageIndex; + if (!memcmp((char*)scsi, (char*)&setImage, sizeof(setImage))) { + if (imgLen && !memcmp(((char*)scsi)+sizeof(rfbSoftCursorSetImage), + (char*)pCursor->softSource, + imgLen)) { + scOindex = i; + break; + } + } + } + + nlen = 0; + if (scOindex < 0) { + if (scNindex < 0) { + scNindex = cl->nextUnusedSoftCursorImage; + cl->nextUnusedSoftCursorImage = (cl->nextUnusedSoftCursorImage+1) + % rfbSoftCursorMaxImages; + free(cl->softCursorImages[scNindex]); + } + + scOindex = scNindex; + setImage.imageIndex = scNindex + rfbSoftCursorSetIconOffset; + nlen = sizeof(setImage) + imgLen; + cl->softCursorImages[scNindex] = calloc(1, nlen); + memcpy((char*)cl->softCursorImages[scNindex], (char*)&setImage, + sizeof(setImage)); + if (imgLen) + memcpy(((char*)cl->softCursorImages[scNindex])+sizeof(setImage), + (char*)pCursor->softSource, imgLen); + } + + /* Send buffer contents if needed. */ + + if ( cl->ublen + sizeof(rfbSoftCursorMove) + + ((scNindex >= 0) ? + (sizeof(rfbSoftCursorSetImage) + imgLen) : 0)) { + + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + saved_ublen = cl->ublen; + + if (scNindex >= 0) { + rect.encoding = Swap32IfLE(rfbEncodingSoftCursor); + if (pCursor) { + rect.r.x = Swap16IfLE(pCursor->xhot); + rect.r.y = Swap16IfLE(pCursor->yhot); + rect.r.w = Swap16IfLE(pCursor->width); + rect.r.h = Swap16IfLE(pCursor->height); + } + else { + rect.r.x = 0; + rect.r.y = 0; + rect.r.w = 0; + rect.r.h = 0; + } + + memcpy(&cl->updateBuf[cl->ublen], + (char *)&rect,sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + memcpy(&cl->updateBuf[cl->ublen], (char*)cl->softCursorImages[scNindex], nlen); + cl->ublen += nlen; + cl->rfbCursorUpdatesSent++; + } + + rect.encoding = Swap32IfLE(rfbEncodingSoftCursor); + rect.r.x = 0; + rect.r.y = 0; + rect.r.w = Swap16IfLE(cl->screen->cursorX); + rect.r.h = Swap16IfLE(cl->screen->cursorY); + moveMsg.imageIndex = scOindex; + moveMsg.buttonMask = 0; /* todo */ + memcpy(&cl->updateBuf[cl->ublen], + (char *)&rect,sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + memcpy(&cl->updateBuf[cl->ublen], (char*)&moveMsg, sizeof(moveMsg)); + cl->ublen += sizeof(moveMsg); + + /* Send everything we have prepared in the cl->updateBuf[]. */ + + cl->rfbCursorBytesSent += (cl->ublen - saved_ublen); + cl->rfbCursorUpdatesSent++; + + return rfbSendUpdateBuf(cl); +} + /* conversion routine for predefined cursors in LSB order */ unsigned char rfbReverseByte[0x100] = { /* copied from Xvnc/lib/font/util/utilbitmap.c */ @@ -332,6 +460,73 @@ void MakeRichCursorFromXCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor) else memcpy(cp,back,bpp); } +void MakeSoftCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor) +{ + int w = (cursor->width+7)/8; + int bpp = rfbScreen->rfbServerFormat.bitsPerPixel/8; + unsigned char *cp, *sp; + int state; /* 0 = transparent, 1 otherwise */ + CARD8 *counter; + unsigned char bit; + int i,j; + + if ((!cursor) || cursor->softSource) + return; + + if (!cursor->richSource) + MakeRichCursorFromXCursor(rfbScreen, cursor); + + cp=cursor->softSource=(unsigned char*)calloc(cursor->width*(bpp+2),cursor->height); + sp=cursor->richSource; + + state = 0; + counter = cp++; + *counter = 0; + + for(j=0;jheight;j++) + for(i=0,bit=0x80;iwidth;i++,bit=(bit&1)?0x80:bit>>1) + if(cursor->mask[j*w+i/8]&bit) { + if (state) { + memcpy(cp,sp,bpp); + cp += bpp; + sp += bpp; + (*counter)++; + if (*counter == 255) { + state = 0; + counter = cp++; + *counter = 0; + } + } + else { + state = 1; + counter = cp++; + *counter = 1; + memcpy(cp,sp,bpp); + cp += bpp; + sp += bpp; + } + } + else { + if (!state) { + (*counter)++; + if (*counter == 255) { + state = 1; + counter = cp++; + *counter = 0; + } + } + else { + state = 0; + counter = cp++; + *counter = 1; + } + sp += bpp; + } + + cursor->softCursorLength = cp - cursor->softSource; +} + + /* functions to draw/hide cursor directly in the frame buffer */ void rfbUndrawCursor(rfbScreenInfoPtr s) diff --git a/libvncserver/main.c b/libvncserver/main.c index 7b414370..1ec3c02e 100644 --- a/libvncserver/main.c +++ b/libvncserver/main.c @@ -370,6 +370,7 @@ void defaultPtrAddEvent(int buttonMask, int x, int y, rfbClientPtr cl) { if(x!=cl->screen->cursorX || y!=cl->screen->cursorY) { + cl->cursorWasMoved = TRUE; if(cl->screen->cursorIsDrawn) rfbUndrawCursor(cl->screen); LOCK(cl->screen->cursorMutex); diff --git a/libvncserver/rfb.h b/libvncserver/rfb.h index 8d59d311..601671d9 100644 --- a/libvncserver/rfb.h +++ b/libvncserver/rfb.h @@ -503,10 +503,16 @@ typedef struct _rfbClientRec { int tightCompressLevel; int tightQualityLevel; + /* soft cursor images */ + rfbSoftCursorSetImage *softCursorImages[rfbSoftCursorMaxImages]; + int nextUnusedSoftCursorImage; + Bool enableLastRectEncoding; /* client supports LastRect encoding */ + Bool enableSoftCursorUpdates; /* client supports softcursor updates */ Bool enableCursorShapeUpdates; /* client supports cursor shape updates */ Bool useRichCursorEncoding; /* rfbEncodingRichCursor is preferred */ Bool cursorWasChanged; /* cursor shape update should be sent */ + Bool cursorWasMoved; /* cursor move shape update should be sent */ #ifdef BACKCHANNEL Bool enableBackChannel; #endif @@ -697,16 +703,20 @@ typedef struct rfbCursor { unsigned short width, height, xhot, yhot; /* metrics */ unsigned short foreRed, foreGreen, foreBlue; /* device-independent colour */ unsigned short backRed, backGreen, backBlue; /* device-independent colour */ + unsigned short softCursorLength; unsigned char *richSource; /* source bytes for a rich cursor */ + unsigned char *softSource; /* image for a soft cursor */ } rfbCursor, *rfbCursorPtr; extern Bool rfbSendCursorShape(rfbClientPtr cl/*, rfbScreenInfoPtr pScreen*/); +extern Bool rfbSendSoftCursor(rfbClientPtr cl); extern unsigned char rfbReverseByte[0x100]; 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); extern void MakeXCursorFromRichCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor); extern void MakeRichCursorFromXCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor); +extern void MakeSoftCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor); extern void rfbFreeCursor(rfbCursorPtr cursor); extern void rfbDrawCursor(rfbScreenInfoPtr rfbScreen); extern void rfbUndrawCursor(rfbScreenInfoPtr rfbScreen); diff --git a/libvncserver/rfbproto.h b/libvncserver/rfbproto.h index 6ea6a62e..51880005 100644 --- a/libvncserver/rfbproto.h +++ b/libvncserver/rfbproto.h @@ -326,6 +326,7 @@ typedef struct { #define rfbEncodingXCursor 0xFFFFFF10 #define rfbEncodingRichCursor 0xFFFFFF11 +#define rfbEncodingSoftCursor 0xFFFFFF12 #define rfbEncodingLastRect 0xFFFFFF20 @@ -557,6 +558,53 @@ typedef struct { * default local cursor should be set by the client). */ +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * SoftCursor encoding. This encoding is used to transmit image and position + * of the remote cursor. It has two sub-messages: SetImage is used to upload + * one of 16 images, and Move selects the image and sets the position of the + * cursor. + * Each SoftCursor message starts with a CARD8. If it is in the 0-63 range + * it specifies the image of the cursor and is followed by the + * rfbSoftCursorMove message. If the given cursor has not been set yet the + * message will be ignored. If the first CARD8 is in the 128-191 range it + * specifies the cursor that will be set in the following + * rfbSoftCursorSetImage message. To hide the cursor send a SetCursor + * message with width and height 0 and imageLength 0. + * SetImage transports the hotspot coordinates in the x/y fields of the + * rfbFramebufferUpdateRectHeader, width and height of the image in the + * header's width and height fields. + * Move transports the pointer coordinates in the w/h fields of the + * header, x/y are always 0. + */ + +typedef struct { + CARD8 imageIndex; + CARD8 buttonMask; /* bits 0-7 are buttons 1-8, 0=up, 1=down */ +} rfbSoftCursorMove; + +typedef struct { + CARD8 imageIndex; + CARD8 padding; + CARD16 imageLength; + /* + * Followed by an image of the cursor in the client's image format + * with the following RLE mask compression. It begins with CARD8 that + * specifies the number of mask'ed pixels that will be NOT transmitted. + * Then follows a CARD8 that specified by the number of unmask'd pixels + * that will be transmitted next. Then a CARD8 with the number of mask'd + * pixels and so on. + */ +} rfbSoftCursorSetImage; + +typedef union { + CARD8 type; + rfbSoftCursorMove move; + rfbSoftCursorSetImage setImage; +} rfbSoftCursorMsg; + +#define rfbSoftCursorMaxImages 16 +#define rfbSoftCursorSetIconOffset 128 + /*----------------------------------------------------------------------------- * SetColourMapEntries - these messages are only sent if the pixel diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 0b2897e2..642e8d27 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -732,6 +732,13 @@ rfbProcessClientNormalMessage(cl) cl->useRichCursorEncoding = TRUE; cl->cursorWasChanged = TRUE; break; + case rfbEncodingSoftCursor: + rfbLog("Enabling soft cursor updates for client " + "%s\n", cl->host); + cl->enableSoftCursorUpdates = TRUE; + cl->cursorWasChanged = TRUE; + cl->cursorWasMoved = TRUE; + break; case rfbEncodingLastRect: if (!cl->enableLastRectEncoding) { rfbLog("Enabling LastRect protocol extension for client " @@ -918,6 +925,7 @@ rfbSendFramebufferUpdate(cl, givenUpdateRegion) sraRegionPtr updateRegion,updateCopyRegion,tmpRegion; int dx, dy; Bool sendCursorShape = FALSE; + int sendSoftCursorRects = 0; if(cl->screen->displayHook) cl->screen->displayHook(cl); @@ -934,6 +942,15 @@ rfbSendFramebufferUpdate(cl, givenUpdateRegion) if (!cl->screen->cursorIsDrawn && cl->cursorWasChanged && cl->readyForSetColourMapEntries) sendCursorShape = TRUE; + } + else if (cl->enableSoftCursorUpdates) { + if (cl->screen->cursorIsDrawn) { + rfbUndrawCursor(cl->screen); + } + if (cl->cursorWasChanged) + sendSoftCursorRects=2; + else if (cl->cursorWasMoved) + sendSoftCursorRects=1; } else { if (!cl->screen->cursorIsDrawn) { rfbDrawCursor(cl->screen); @@ -959,7 +976,8 @@ rfbSendFramebufferUpdate(cl, givenUpdateRegion) updateRegion = sraRgnCreateRgn(givenUpdateRegion); sraRgnOr(updateRegion,cl->copyRegion); - if(!sraRgnAnd(updateRegion,cl->requestedRegion) && !sendCursorShape) { + if(!sraRgnAnd(updateRegion,cl->requestedRegion) && + !(sendCursorShape || sendSoftCursorRects)) { sraRgnDestroy(updateRegion); UNLOCK(cl->updateMutex); return TRUE; @@ -1059,7 +1077,8 @@ rfbSendFramebufferUpdate(cl, givenUpdateRegion) fu->type = rfbFramebufferUpdate; if (nUpdateRegionRects != 0xFFFF) { fu->nRects = Swap16IfLE((CARD16)(sraRgnCountRects(updateCopyRegion) - + nUpdateRegionRects + !!sendCursorShape)); + + nUpdateRegionRects + + !!sendCursorShape + sendSoftCursorRects)); } else { fu->nRects = 0xFFFF; } @@ -1072,7 +1091,16 @@ rfbSendFramebufferUpdate(cl, givenUpdateRegion) return FALSE; } } - + + if (sendSoftCursorRects) { + cl->cursorWasChanged = FALSE; + cl->cursorWasMoved = FALSE; + if (!rfbSendSoftCursor(cl)) { + sraRgnDestroy(updateRegion); + return FALSE; + } + } + if (!sraRgnEmpty(updateCopyRegion)) { if (!rfbSendCopyRegion(cl,updateCopyRegion,dx,dy)) { sraRgnDestroy(updateRegion);