#include #include #include #include #ifndef LIBVNCSERVER_HAVE_LIBPTHREAD #error This test need pthread support (otherwise the client blocks the client) #endif #define ALL_AT_ONCE //#define VERY_VERBOSE MUTEX(frameBufferMutex); typedef struct { int id; char* str; } encoding_t; encoding_t testEncodings[]={ { rfbEncodingRaw, "raw" }, { rfbEncodingRRE, "rre" }, /* TODO: fix corre */ /* { rfbEncodingCoRRE, "corre" }, */ { rfbEncodingHextile, "hextile" }, #ifdef LIBVNCSERVER_HAVE_LIBZ { rfbEncodingZlib, "zlib" }, { rfbEncodingZlibHex, "zlibhex" }, /* TODO: implement ZRLE decoding */ /* { rfbEncodingZRLE, "zrle" }, */ #ifdef LIBVNCSERVER_HAVE_LIBJPEG { rfbEncodingTight, "tight" }, #endif #endif { 0, 0 } }; #define NUMBER_OF_ENCODINGS_TO_TEST (sizeof(testEncodings)/sizeof(encoding_t)-1) //#define NUMBER_OF_ENCODINGS_TO_TEST 1 /* Here come the variables/functions to handle the test output */ const int width=400,height=300; struct { int x1,y1,x2,y2; } lastUpdateRect; unsigned int statistics[2][NUMBER_OF_ENCODINGS_TO_TEST]; unsigned int totalFailed,totalCount; unsigned int countGotUpdate; MUTEX(statisticsMutex); void initStatistics() { memset(statistics[0],0,sizeof(int)*NUMBER_OF_ENCODINGS_TO_TEST); memset(statistics[1],0,sizeof(int)*NUMBER_OF_ENCODINGS_TO_TEST); totalFailed=totalCount=0; lastUpdateRect.x1=0; lastUpdateRect.y1=0; lastUpdateRect.x2=width; lastUpdateRect.y2=height; INIT_MUTEX(statisticsMutex); } void updateServerStatistics(int x1,int y1,int x2,int y2) { LOCK(statisticsMutex); countGotUpdate=0; lastUpdateRect.x1=x1; lastUpdateRect.y1=y1; lastUpdateRect.x2=x2; lastUpdateRect.y2=y2; UNLOCK(statisticsMutex); } void updateStatistics(int encodingIndex,rfbBool failed) { LOCK(statisticsMutex); if(failed) { statistics[1][encodingIndex]++; totalFailed++; } statistics[0][encodingIndex]++; totalCount++; countGotUpdate++; UNLOCK(statisticsMutex); } /* Here begin the functions for the client. They will be called in a * pthread. */ /* maxDelta=0 means they are expected to match exactly; * maxDelta>0 means that the average difference must be lower than maxDelta */ rfbBool doFramebuffersMatch(rfbScreenInfo* server,rfbClient* client, int maxDelta) { int i,j,k; unsigned int total=0,diff=0; if(server->width!=client->width || server->height!=client->height) return FALSE; LOCK(frameBufferMutex); /* TODO: write unit test for colour transformation, use here, too */ for(i=0;iwidth;i++) for(j=0;jheight;j++) for(k=0;k<3/*server->serverFormat.bitsPerPixel/8*/;k++) { unsigned char s=server->frameBuffer[k+i*4+j*server->paddedWidthInBytes]; unsigned char cl=client->frameBuffer[k+i*4+j*client->width*4]; if(maxDelta==0 && s!=cl) { UNLOCK(frameBufferMutex); return FALSE; } else { total++; diff+=(s>cl?s-cl:cl-s); } } UNLOCK(frameBufferMutex); if(maxDelta>0 && diff/total>=maxDelta) return FALSE; return TRUE; } static rfbBool resize(rfbClient* cl) { if(cl->frameBuffer) free(cl->frameBuffer); cl->frameBuffer=(char*)malloc(cl->width*cl->height*cl->format.bitsPerPixel/8); SendFramebufferUpdateRequest(cl,0,0,cl->width,cl->height,FALSE); } typedef struct clientData { int encodingIndex; rfbScreenInfo* server; char* display; } clientData; static void update(rfbClient* client,int x,int y,int w,int h) { clientData* cd=(clientData*)client->clientData; int maxDelta=0; #ifndef VERY_VERBOSE static const char* progress="|/-\\"; static int counter=0; if(++counter>sizeof(progress)) counter=0; fprintf(stderr,"%c\r",progress[counter]); #else rfbClientLog("Got update (encoding=%s): (%d,%d)-(%d,%d)\n", testEncodings[cd->encodingIndex].str, x,y,x+w,y+h); #endif /* only check if this was the last update */ if(x+w!=lastUpdateRect.x2 || y+h!=lastUpdateRect.y2) { #ifdef VERY_VERBOSE rfbClientLog("Waiting (%d!=%d or %d!=%d)\n", x+w,lastUpdateRect.x2,y+h,lastUpdateRect.y2); #endif return; } if(testEncodings[cd->encodingIndex].id==rfbEncodingTight) maxDelta=5; updateStatistics(cd->encodingIndex, !doFramebuffersMatch(cd->server,client,maxDelta)); } static void* clientLoop(void* data) { rfbClient* client=(rfbClient*)data; clientData* cd=(clientData*)client->clientData; int argc=4; char* argv[4]={"client", "-encodings", testEncodings[cd->encodingIndex].str, cd->display}; sleep(1); rfbClientLog("Starting client (encoding %s, display %s)\n", testEncodings[cd->encodingIndex].str, cd->display); if(!rfbInitClient(client,&argc,argv)) { rfbClientErr("Had problems starting client (encoding %s)\n", testEncodings[cd->encodingIndex].str); updateStatistics(cd->encodingIndex,TRUE); return 0; } while(1) { if(WaitForMessage(client,50)>=0) if(!HandleRFBServerMessage(client)) break; } free(((clientData*)client->clientData)->display); free(client->clientData); if(client->frameBuffer) free(client->frameBuffer); rfbClientCleanup(client); return 0; } static void startClient(int encodingIndex,rfbScreenInfo* server) { rfbClient* client=rfbGetClient(8,3,4); clientData* cd; pthread_t clientThread; client->clientData=malloc(sizeof(clientData)); client->MallocFrameBuffer=resize; client->GotFrameBufferUpdate=update; cd=(clientData*)client->clientData; cd->encodingIndex=encodingIndex; cd->server=server; cd->display=(char*)malloc(6); sprintf(cd->display,":%d",server->port-5900); lastUpdateRect.x1=lastUpdateRect.y1=0; lastUpdateRect.x2=server->width; lastUpdateRect.y2=server->height; pthread_create(&clientThread,NULL,clientLoop,(void*)client); } /* Here begin the server functions */ static void idle(rfbScreenInfo* server) { int c; rfbBool goForward; LOCK(statisticsMutex); #ifdef ALL_AT_ONCE goForward=(countGotUpdate==NUMBER_OF_ENCODINGS_TO_TEST); #else goForward=(countGotUpdate==1); #endif /* if(lastUpdateRect.x2==354) rfbLog("server checked: countGotUpdate=%d\n",countGotUpdate); */ UNLOCK(statisticsMutex); if(!goForward) return; countGotUpdate=0; LOCK(frameBufferMutex); { int i,j; int x1=(rand()%(server->width-1)),x2=(rand()%(server->width-1)), y1=(rand()%(server->height-1)),y2=(rand()%(server->height-1)); if(x1>x2) { i=x1; x1=x2; x2=i; } if(y1>y2) { i=y1; y1=y2; y2=i; } x2++; y2++; for(c=0;c<3;c++) { for(i=x1;iframeBuffer[i*4+c+j*server->paddedWidthInBytes]=255*(i-x1+j-y1)/(x2-x1+y2-y1); } rfbMarkRectAsModified(server,x1,y1,x2,y2); lastUpdateRect.x1=x1; lastUpdateRect.y1=y1; lastUpdateRect.x2=x2; lastUpdateRect.y2=y2; #ifdef VERY_VERBOSE rfbLog("Sent update (%d,%d)-(%d,%d)\n",x1,y1,x2,y2); #endif } UNLOCK(frameBufferMutex); } /* log function (to show what messages are from the client) */ void rfbTestLog(const char *format, ...) { va_list args; char buf[256]; time_t log_clock; if(!rfbEnableClientLogging) return; va_start(args, format); time(&log_clock); strftime(buf, 255, "%d/%m/%Y %X (client) ", localtime(&log_clock)); fprintf(stderr,buf); vfprintf(stderr, format, args); fflush(stderr); va_end(args); } /* the main function */ int main(int argc,char** argv) { int i,j; time_t t; rfbScreenInfoPtr server; rfbClientLog=rfbTestLog; rfbClientErr=rfbTestLog; /* Initialize server */ server=rfbGetScreen(&argc,argv,width,height,8,3,4); server->frameBuffer=malloc(400*300*4); for(j=0;j<400*300*4;j++) server->frameBuffer[j]=j; rfbInitServer(server); rfbProcessEvents(server,0); initStatistics(); #ifndef ALL_AT_ONCE for(i=0;iframeBuffer); rfbScreenCleanup(server); rfbLog("Statistics:\n"); for(i=0;i