/* This file (x11vnc.c) is part of LibVNCServer. It is a small clone of x0rfbserver by HexoNet, demonstrating the capabilities of LibVNCServer. */ #include #include #include #ifndef NO_SHM #include #include #endif #define KEYSYM_H #include "rfb.h" Display *dpy = 0; int window; int c=0,blockLength = 32; int tileX=0,tileY=0,tileWidth=32,tileHeight=32*2,dontTile=TRUE; Bool gotInput = FALSE; Bool viewOnly = FALSE; Bool sharedMode = FALSE; Bool disconnectAfterFirstClient = TRUE; /* keyboard handling */ char modifiers[0x100]; KeyCode keycodes[0x100],leftShiftCode,rightShiftCode,altGrCode; void init_keycodes() { KeySym key,*keymap; int i,j,minkey,maxkey,syms_per_keycode; memset(modifiers,-1,sizeof(modifiers)); XDisplayKeycodes(dpy,&minkey,&maxkey); keymap=XGetKeyboardMapping(dpy,minkey,(maxkey - minkey + 1),&syms_per_keycode); for (i = minkey; i <= maxkey; i++) for(j=0;j=' ' && key<0x100 && i==XKeysymToKeycode(dpy,key)) { keycodes[key]=i; modifiers[key]=j; } } leftShiftCode=XKeysymToKeycode(dpy,XK_Shift_L); rightShiftCode=XKeysymToKeycode(dpy,XK_Shift_R); altGrCode=XKeysymToKeycode(dpy,XK_Mode_switch); XFree ((char *) keymap); } /* the hooks */ void clientGone(rfbClientPtr cl) { exit(0); } void newClient(rfbClientPtr cl) { if(disconnectAfterFirstClient) cl->clientGoneHook = clientGone; if(viewOnly) cl->clientData = (void*)-1; else cl->clientData = (void*)0; } #define LEFTSHIFT 1 #define RIGHTSHIFT 2 #define ALTGR 4 char ModifierState = 0; /* this function adjusts the modifiers according to mod (as from modifiers) and ModifierState */ void tweakModifiers(char mod,Bool down) { Bool isShift=ModifierState&(LEFTSHIFT|RIGHTSHIFT); if(mod<0) return; if(isShift && mod!=1) { if(ModifierState&LEFTSHIFT) XTestFakeKeyEvent(dpy,leftShiftCode,!down,CurrentTime); if(ModifierState&RIGHTSHIFT) XTestFakeKeyEvent(dpy,rightShiftCode,!down,CurrentTime); } if(!isShift && mod==1) XTestFakeKeyEvent(dpy,leftShiftCode,down,CurrentTime); if(ModifierState&ALTGR && mod!=2) XTestFakeKeyEvent(dpy,altGrCode,!down,CurrentTime); if(!(ModifierState&ALTGR) && mod==2) XTestFakeKeyEvent(dpy,altGrCode,down,CurrentTime); } void keyboard(Bool down,KeySym keySym,rfbClientPtr cl) { if(((int)cl->clientData)==-1) return; /* viewOnly */ #define ADJUSTMOD(sym,state) \ if(keySym==sym) { if(down) ModifierState|=state; else ModifierState&=~state; } ADJUSTMOD(XK_Shift_L,LEFTSHIFT) ADJUSTMOD(XK_Shift_R,RIGHTSHIFT) ADJUSTMOD(XK_Mode_switch,ALTGR) if(keySym>=' ' && keySym<0x100) { KeyCode k; if(down) tweakModifiers(modifiers[keySym],True); //tweakModifiers(modifiers[keySym],down); //k = XKeysymToKeycode( dpy,keySym ); k = keycodes[keySym]; if(k!=NoSymbol) { XTestFakeKeyEvent(dpy,k,down,CurrentTime); gotInput = TRUE; } /*XTestFakeKeyEvent(dpy,keycodes[keySym],down,CurrentTime);*/ if(down) tweakModifiers(modifiers[keySym],False); gotInput = TRUE; } else { KeyCode k = XKeysymToKeycode( dpy,keySym ); if(k!=NoSymbol) { XTestFakeKeyEvent(dpy,k,down,CurrentTime); gotInput = TRUE; } } } int oldButtonMask = 0; void mouse(int buttonMask,int x,int y,rfbClientPtr cl) { int i=0; if(((int)cl->clientData)==-1) return; /* viewOnly */ XTestFakeMotionEvent(dpy,0,x,y,CurrentTime ); while(i<5) { if ((oldButtonMask&(1<0) { static Bool firstTime = TRUE; if(firstTime) { firstTime = FALSE; *i = XShmCreateImage(dpy, DefaultVisual( dpy, xscreen ), bpp, ZPixmap, NULL, &shminfo, width,height); if(*i == 0) { useSHM = FALSE; getImage(bpp,dpy,xscreen,i,x,y,width,height); return; } shminfo.shmid = shmget( IPC_PRIVATE, (*i)->bytes_per_line * (*i)->height, IPC_CREAT | 0777 ); shminfo.shmaddr = (*i)->data = (char *) shmat( shminfo.shmid, 0, 0 ); shminfo.readOnly = False; XShmAttach( dpy, &shminfo ); } if(x==0 && y==0 && width==DisplayWidth(dpy,xscreen) && height==DisplayHeight(dpy,xscreen)) XShmGetImage(dpy,window,*i,0,0,AllPlanes); else XGetSubImage(dpy,window,x,y,width,height,AllPlanes,ZPixmap,*i,0,0); } else { *i = XGetImage(dpy,window,x,y,width,height,AllPlanes,ZPixmap ); } } void checkForImageUpdates(rfbScreenInfoPtr s,char *b,int rowstride,int x,int y,int width,int height) { Bool changed; int i,j,k,l1,l2,x1,y1; int bpp=s->bitsPerPixel/8; for(j=0;jheight) y1=height; x1=i+blockLength; if(x1>width) x1=width; y1*=rowstride; x1*=bpp; changed=FALSE; for(l1=j*rowstride,l2=(j+y)*s->paddedWidthInBytes+x*bpp;l1paddedWidthInBytes) for(k=i*bpp;kframeBuffer[l2+k]!=b[l1+k]) { // fprintf(stderr,"changed: %d, %d\n",k,l); changed=TRUE; goto changed_p; } if(changed) { changed_p: for(l1+=i*bpp,l2+=i*bpp;l1paddedWidthInBytes) memcpy(/*b+l,*/s->frameBuffer+l2,b+l1,x1-i*bpp); rfbMarkRectAsModified(s,x+i,y+j,x+i+blockLength,y+j+blockLength); } } } int probeX=0,probeY=0; void probeScreen(rfbScreenInfoPtr s,int xscreen) { int i,j,/*pixel,i1,*/j1, bpp=s->rfbServerFormat.bitsPerPixel/8,/*mask=(1<paddedWidthInBytes; XImage* im; //fprintf(stderr,"/%d,%d",probeX,probeY); #if 0 probeX++; if(probeX>=tileWidth) { probeX=0; probeY++; if(probeY>=tileHeight) probeY=0; } #else probeX=(rand()%tileWidth); probeY=(rand()%tileHeight); #endif for(j=probeY;jheight;j+=tileHeight) for(i=0/*probeX*/;iwidth;i+=tileWidth) { im=XGetImage(dpy,window,i,j,tileWidth/*1*/,1,AllPlanes,ZPixmap); /* for(i1=0;i1data[i1]==(s->frameBuffer+i*bpp+j*rstride)[i1];i1++); if(i1data,s->frameBuffer+i*bpp+j*rstride,tileWidth*bpp)) { /* do update */ int x=i/*-probeX*/,w=(x+tileWidth>s->width)?s->width-x:tileWidth, y=j-probeY,h=(y+tileHeight>s->height)?s->height-y:tileHeight; XDestroyImage(im); //getImage(bpp,dpy,xscreen,&im,x,y,w,h); //fprintf(stderr,"GetImage(%d,%d,%d,%d)",x,y,w,h); im = XGetImage(dpy,window,x,y,w,h,AllPlanes,ZPixmap ); for(j1=0;j1frameBuffer+x*bpp+(y+j1)*rstride, im->data+j1*im->bytes_per_line,bpp*w); //checkForImageUpdates(s,im->data,rstride,x,y,w,h); //if(0 && !useSHM) XDestroyImage(im); //memcpy(s->frameBuffer+i*bpp+j*rstride,&pixel,bpp); rfbMarkRectAsModified(s,x,y,x+w,y+h); //fprintf(stderr,"%d:%d:%x\n",i,j,pixel); //fprintf(stderr,"*"); } else XDestroyImage(im); } } #define LOCAL_CONTROL #ifdef LOCAL_CONTROL #include "1instance.c" #endif /* the main program */ int main(int argc,char** argv) { //Screen *sc; //Colormap cm; XImage *framebufferImage; char *backupImage; int xscreen,i; rfbScreenInfoPtr screen; int maxMsecsToConnect = 5000; /* a maximum of 5 seconds to connect */ int updateCounter; /* about every 50 ms a screen update should be made. */ #ifdef LOCAL_CONTROL char message[1024]; single_instance_struct single_instance = { "/tmp/x11vnc_control" }; open_control_file(&single_instance); #endif for(i=argc-1;i>0;i--) #ifdef LOCAL_CONTROL if(iwidth, framebufferImage->height, framebufferImage->bits_per_pixel, 8, framebufferImage->bits_per_pixel/8); screen->paddedWidthInBytes = framebufferImage->bytes_per_line; screen->rfbServerFormat.bitsPerPixel = framebufferImage->bits_per_pixel; screen->rfbServerFormat.depth = framebufferImage->depth; //rfbEndianTest = framebufferImage->bitmap_bit_order != MSBFirst; screen->rfbServerFormat.trueColour = TRUE; if ( screen->rfbServerFormat.bitsPerPixel == 8 ) { if(CellsOfScreen(ScreenOfDisplay(dpy,xscreen))) { XColor color[256]; int i; screen->colourMap.count = 256; screen->rfbServerFormat.trueColour = FALSE; screen->colourMap.is16 = TRUE; for(i=0;i<256;i++) color[i].pixel=i; XQueryColors(dpy,DefaultColormap(dpy,xscreen),color,256); screen->colourMap.data.shorts = (short*)malloc(3*sizeof(short)*screen->colourMap.count); for(i=0;icolourMap.count;i++) { screen->colourMap.data.shorts[i*3+0] = color[i].red; screen->colourMap.data.shorts[i*3+1] = color[i].green; screen->colourMap.data.shorts[i*3+2] = color[i].blue; } } else { screen->rfbServerFormat.redShift = 0; screen->rfbServerFormat.greenShift = 2; screen->rfbServerFormat.blueShift = 5; screen->rfbServerFormat.redMax = 3; screen->rfbServerFormat.greenMax = 7; screen->rfbServerFormat.blueMax = 3; } } else { screen->rfbServerFormat.redShift = 0; if ( framebufferImage->red_mask ) while ( ! ( framebufferImage->red_mask & (1 << screen->rfbServerFormat.redShift) ) ) screen->rfbServerFormat.redShift++; screen->rfbServerFormat.greenShift = 0; if ( framebufferImage->green_mask ) while ( ! ( framebufferImage->green_mask & (1 << screen->rfbServerFormat.greenShift) ) ) screen->rfbServerFormat.greenShift++; screen->rfbServerFormat.blueShift = 0; if ( framebufferImage->blue_mask ) while ( ! ( framebufferImage->blue_mask & (1 << screen->rfbServerFormat.blueShift) ) ) screen->rfbServerFormat.blueShift++; screen->rfbServerFormat.redMax = framebufferImage->red_mask >> screen->rfbServerFormat.redShift; screen->rfbServerFormat.greenMax = framebufferImage->green_mask >> screen->rfbServerFormat.greenShift; screen->rfbServerFormat.blueMax = framebufferImage->blue_mask >> screen->rfbServerFormat.blueShift; } backupImage = malloc(screen->height*screen->paddedWidthInBytes); memcpy(backupImage,framebufferImage->data,screen->height*screen->paddedWidthInBytes); screen->frameBuffer = backupImage; screen->cursor = 0; screen->newClientHook = newClient; screen->kbdAddEvent = keyboard; screen->ptrAddEvent = mouse; if(sharedMode) { screen->rfbAlwaysShared = TRUE; } screen->rfbDeferUpdateTime = 1; updateCounter /= screen->rfbDeferUpdateTime; rfbInitServer(screen); c=0; while(1) { if(screen->rfbClientHead) maxMsecsToConnect = 1<<16; else { maxMsecsToConnect -= screen->rfbDeferUpdateTime; if(maxMsecsToConnect<0) { fprintf(stderr,"Maximum time to connect reached. Exiting.\n"); XTestDiscard(dpy); exit(2); } } #ifdef LOCAL_CONTROL if(get_next_message(message,1024,&single_instance,50)) { if(message[0]=='l' && message[1]==0) { rfbClientPtr cl; int i; for(i=0,cl=screen->rfbClientHead;cl;cl=cl->next,i++) fprintf(stderr,"%02d: %s\n",i,cl->host); } else if(message[0]=='t') { rfbClientPtr cl; for(cl=screen->rfbClientHead;cl;cl=cl->next) if(!strcmp(message+1,cl->host)) { cl->clientData=(cl->clientData==0)?-1:0; break; } } #ifdef BACKCHANNEL else if(message[0]=='b') rfbSendBackChannel(screen,message+1,strlen(message+1)); #endif } #endif rfbProcessEvents(screen,-1); if(dontTile) { if(gotInput) { gotInput = FALSE; c=updateCounter; } else if(screen->rfbClientHead && c++>updateCounter) { c=0; //fprintf(stderr,"*"); if(!useSHM) framebufferImage->f.destroy_image(framebufferImage); if(dontTile) { getImage(screen->rfbServerFormat.bitsPerPixel,dpy,xscreen,&framebufferImage,0,0,screen->width,screen->height); checkForImageUpdates(screen,framebufferImage->data,framebufferImage->bytes_per_line, 0,0,screen->width,screen->height); } else { /* old tile code. Eventually to be removed (TODO) */ char isRightEdge = tileX+tileWidth>=screen->width; char isLowerEdge = tileY+tileHeight>=screen->height; getImage(screen->rfbServerFormat.bitsPerPixel,dpy,xscreen,&framebufferImage,tileX,tileY, isRightEdge?screen->width-tileX:tileWidth, isLowerEdge?screen->height-tileY:tileHeight); checkForImageUpdates(screen,framebufferImage->data,framebufferImage->bytes_per_line, tileX,tileY, isRightEdge?screen->width-tileX:tileWidth, isLowerEdge?screen->height-tileY:tileHeight); if(isRightEdge) { tileX=0; if(isLowerEdge) tileY=0; else tileY+=tileHeight; } else tileX+=tileWidth; } } } else if(c++>updateCounter) { c=0; probeScreen(screen,xscreen); } #ifdef WRITE_SNAPS { int i,j,r,g,b; FILE* f=fopen("test.pnm","wb"); fprintf(f,"P6\n%d %d\n255\n",screen->width,screen->height); for(j=0;jheight;j++) for(i=0;iwidth;i++) { //r=screen->frameBuffer[j*screen->paddedWidthInBytes+i*2]; r=framebufferImage->data[j*screen->paddedWidthInBytes+i*2]; fputc(((r>>screen->rfbServerFormat.redShift)&screen->rfbServerFormat.redMax)*255/screen->rfbServerFormat.redMax,f); fputc(((r>>screen->rfbServerFormat.greenShift)&screen->rfbServerFormat.greenMax)*255/screen->rfbServerFormat.greenMax,f); fputc(((r>>screen->rfbServerFormat.blueShift)&screen->rfbServerFormat.blueMax)*255/screen->rfbServerFormat.blueMax,f); } fclose(f); } #endif } #ifndef NO_SHM //XShmDetach(dpy,framebufferImage); #endif return(0); }