#include #include #include #include #include "nacro.h" /* for visual grepping */ typedef struct image_t { int width,height; char* buffer; } image_t; /* this is a VNC connection */ typedef struct private_resource_t { int listen_port; rfbScreenInfo* server; rfbClient* client; uint32_t keysym; rfbBool keydown; int x,y; int buttons; char* text_client; char* text_server; image_t* grep_image; int x_origin,y_origin; enum { SLEEP,VISUALGREP,WAITFORUPDATE } state; result_t result; } private_resource_t; /* resource management */ #define MAX_RESOURCE_COUNT 20 static private_resource_t resource_pool[MAX_RESOURCE_COUNT]; static int resource_count=0; static private_resource_t* get_resource(int resource) { if(resource>=MAX_RESOURCE_COUNT || resource<0 || resource_pool[resource].client==0) return NULL; return resource_pool+resource; } static private_resource_t* get_next_resource(void) { if(resource_countclient=NULL; } /* hooks */ static void got_key(rfbBool down,rfbKeySym keysym,rfbClientRec* cl) { private_resource_t* res=(private_resource_t*)cl->screen->screenData; res->keydown=down; res->keysym=keysym; res->result|=RESULT_KEY; } static void got_mouse(int buttons,int x,int y,rfbClientRec* cl) { private_resource_t* res=(private_resource_t*)cl->screen->screenData; res->buttons=buttons; res->x=x; res->y=y; res->result|=RESULT_MOUSE; } static void got_text(char* str,int len,rfbClientRec* cl) { private_resource_t* res=(private_resource_t*)cl->screen->screenData; if (res->text_client) free(res->text_client); res->text_client=strdup(str); res->result|=RESULT_TEXT_CLIENT; } static void got_text_from_server(rfbClient* cl, const char *str, int textlen) { private_resource_t* res=(private_resource_t*)cl->clientData; if (res->text_server) free(res->text_server); res->text_server=strdup(str); res->result|=RESULT_TEXT_SERVER; } static rfbBool malloc_frame_buffer(rfbClient* cl) { private_resource_t* res=(private_resource_t*)cl->clientData; if(!res->server) { int w=cl->width,h=cl->height; res->client->frameBuffer=malloc(w*4*h); res->server=rfbGetScreen(NULL,NULL,w,h,8,3,4); res->server->screenData=res; res->server->port=res->listen_port; res->server->frameBuffer=res->client->frameBuffer; res->server->kbdAddEvent=got_key; res->server->ptrAddEvent=got_mouse; res->server->setXCutText=got_text; rfbInitServer(res->server); } else { /* TODO: realloc if necessary */ /* TODO: resolution change: send NewFBSize */ /* TODO: if the origin is out of bounds, reset to 0 */ } } static bool_t do_visual_grep(private_resource_t* res,int x,int y,int w,int h) { rfbClient* cl; image_t* image; int x_start,y_start,x_end=x+w-1,y_end=y+h-1; bool_t found=0; if(res==0 || (cl=res->client)==0 || (image=res->grep_image)==0) return 0; x_start=x-image->width; y_start=y-image->height; if(x_start<0) x_start=0; if(y_start<0) y_start=0; if(x_end+image->width>cl->width) x_end=cl->width-image->width; if(y_end+image->height>cl->height) y_end=cl->height-image->height; /* find image and set x_origin,y_origin if found */ for(y=y_start;yheight;j++) for(i=0;matching && iwidth;i++) if(memcmp(cl->frameBuffer+4*(x+i+cl->width*(y+j)),image->buffer+4*(i+image->width*j),3)) matching=0; if(matching) { private_resource_t* res=(private_resource_t*)cl->clientData; res->x_origin=x; res->y_origin=y; return -1; } } return 0; } static void got_frame_buffer(rfbClient* cl,int x,int y,int w,int h) { private_resource_t* res=(private_resource_t*)cl->clientData; assert(res->server); if(res->grep_image && do_visual_grep(res,x,y,w,h)) { res->result|=RESULT_FOUNDIMAGE; } if(res->server) { rfbMarkRectAsModified(res->server,x,y,x+w,y+h); } res->result|=RESULT_SCREEN; } /* init/shutdown functions */ resource_t initvnc(const char* server,int server_port,int listen_port) { private_resource_t* res=get_next_resource(); int dummy=0; if(res==0) return -1; /* remember for later */ res->listen_port=listen_port; res->text_client = NULL; res->text_server = NULL; res->client=rfbGetClient(8,3,4); res->client->clientData=(void*)res; res->client->GotFrameBufferUpdate=got_frame_buffer; res->client->MallocFrameBuffer=malloc_frame_buffer; res->client->GotXCutText=got_text_from_server; res->client->serverHost=strdup(server); res->client->serverPort=server_port; res->client->appData.encodingsString="raw"; if(!rfbInitClient(res->client,&dummy,NULL)) { res->client=NULL; return -1; } return res-resource_pool; } void closevnc(resource_t resource) { private_resource_t* res=get_resource(resource); if(res==0) return; if(res->server) rfbScreenCleanup(res->server); assert(res->client); rfbClientCleanup(res->client); res->client=NULL; } /* PNM (image) helpers */ bool_t savepnm(resource_t resource,const char* filename,int x1,int y1,int x2,int y2) { private_resource_t* res=get_resource(resource); int i,j,w,h; uint32_t* buffer; FILE* f; if(res==0 || res->client==0) return 0; assert(res->client->format.depth==24); w=res->client->width; h=res->client->height; buffer=(uint32_t*)res->client->frameBuffer; if(res==0 || x1>x2 || y1>y2 || x1<0 || x2>=w || y1<0 || y2>=h) return FALSE; f=fopen(filename,"wb"); if(f==0) return FALSE; fprintf(f,"P6\n%d %d\n255\n",1+x2-x1,1+y2-y1); for(j=y1;j<=y2;j++) for(i=x1;i<=x2;i++) { fwrite(buffer+i+j*w,3,1,f); } if(fclose(f)) return FALSE; return TRUE; } static image_t* loadpnm(const char* filename) { FILE* f=fopen(filename,"rb"); char buffer[1024]; int i,j,w,h; image_t* image; if(f==0) return NULL; if(!fgets(buffer,1024,f) || strcmp("P6\n",buffer)) { fclose(f); return NULL; } do { fgets(buffer,1024,f); if(feof(f)) { fclose(f); return NULL; } } while(buffer[0]=='#'); if( sscanf(buffer,"%d %d",&w,&h)!=2 || !fgets(buffer,1024,f) || strcmp("255\n",buffer)) { fclose(f); return NULL; } image=(image_t*)malloc(sizeof(image_t)); image->width=w; image->height=h; image->buffer=malloc(w*4*h); if(!image->buffer) { fclose(f); free(image); return NULL; } for(j=0;jbuffer+4*(i+w*j),3,1,f)!=1) { fprintf(stderr,"Could not read 3 bytes at %d,%d\n",i,j); fclose(f); free(image->buffer); free(image); return NULL; } fclose(f); return image; } static void free_image(image_t* image) { if(image->buffer) free(image->buffer); free(image); } static void copy_line(rfbScreenInfo *dest, char *backup, int x0, int y0, int x1, int y1, int color_offset) { uint8_t *d = (uint8_t *)dest->frameBuffer, *s = (uint8_t *)backup; int i; int steps0 = x1 > x0 ? x1 - x0 : x0 - x1; int steps1 = y1 > y0 ? y1 - y0 : y0 - y1; if (steps1 > steps0) steps0 = steps1; else if (steps0 == 0) steps0 = 1; for (i = 0; i <= steps0; i++) { int j, index = 4 * (x0 + i * (x1 - x0) / steps0 + dest->width * (y0 + i * (y1 - y0) / steps0)); for (j = 0; j < 4; j++) d[index + j] = s[index + j] + color_offset; } rfbMarkRectAsModified(dest, x0 - 5, y0 - 5, x1 + 1, y1 + 2); } result_t displaypnm(resource_t resource, const char *filename, coordinate_t x, coordinate_t y, bool_t border, timeout_t timeout_in_seconds) { private_resource_t* res = get_resource(resource); image_t *image; char* fake_frame_buffer; char* backup; int w, h, i, j, w2, h2; result_t result; if (res == NULL || res->server == NULL || (image = loadpnm(filename)) == NULL) return 0; w = res->server->width; h = res->server->height; fake_frame_buffer = malloc(w * 4 * h); if(!fake_frame_buffer) return 0; memcpy(fake_frame_buffer, res->server->frameBuffer, w * 4 * h); backup = res->server->frameBuffer; res->server->frameBuffer = fake_frame_buffer; w2 = image->width; if (x + w2 > w) w2 = w - x; h2 = image->height; if (y + h2 > h) h2 = h - y; for (j = 0; j < h2; j++) memcpy(fake_frame_buffer + 4 * (x + (y + j) * w), image->buffer + j * 4 * image->width, 4 * w2); free(image); if (border) { copy_line(res->server, backup, x, y, x + w2, y, 0x80); copy_line(res->server, backup, x, y, x, y + h2, 0x80); copy_line(res->server, backup, x + w2, y, x + w2, y + h2, 0x80); copy_line(res->server, backup, x, y + h2, x + w2, y + h2, 0x80); } rfbMarkRectAsModified(res->server, x - 1, y - 1, x + w2 + 1, y + h2 + 1); result = waitforinput(resource, timeout_in_seconds); res->server->frameBuffer=backup; free(fake_frame_buffer); rfbMarkRectAsModified(res->server, x - 1, y - 1, x + w2 + 1, y + h2 + 1); return result; } /* process() and friends */ /* this function returns only if res->result in return_mask */ static result_t private_process(resource_t resource,timeout_t timeout_in_seconds,result_t return_mask) { private_resource_t* res=get_resource(resource); fd_set fds; struct timeval tv,tv_start,tv_end; unsigned long timeout=(unsigned long)(timeout_in_seconds*1000000UL); int count,max_fd; if(res==0) return 0; assert(res->client); gettimeofday(&tv_start,NULL); res->result=0; do { unsigned long timeout_done; if(res->server) { rfbBool loop; do { loop=rfbProcessEvents(res->server,res->server->deferUpdateTime); } while(loop && (res->result&return_mask)==0 && rfbIsActive(res->server)); if(!rfbIsActive(res->server)) return RESULT_SHUTDOWN; if((res->result&return_mask)!=0) return res->result; memcpy((char*)&fds,(const char*)&(res->server->allFds),sizeof(fd_set)); max_fd=res->server->maxFd; } else { FD_ZERO(&fds); max_fd=0; } FD_SET(res->client->sock,&fds); if(res->client->sock>max_fd) max_fd=res->client->sock; gettimeofday(&tv_end,NULL); timeout_done=tv_end.tv_usec-tv_start.tv_usec+ 1000000L*(tv_end.tv_sec-tv_start.tv_sec); if(timeout_done>=timeout) return RESULT_TIMEOUT; tv.tv_usec=((timeout-timeout_done)%1000000); tv.tv_sec=(timeout-timeout_done)/1000000; count=select(max_fd+1,&fds,NULL,NULL,&tv); if(count<0) return 0; if(count>0) { if(FD_ISSET(res->client->sock,&fds)) { if(!HandleRFBServerMessage(res->client)) { closevnc(resource); return 0; } if((res->result&return_mask)!=0) return res->result; } } else { res->result|=RESULT_TIMEOUT; return res->result; } } while(1); return RESULT_TIMEOUT; } result_t process(resource_t res,timeout_t timeout) { return private_process(res,timeout,RESULT_TIMEOUT); } result_t waitforanything(resource_t res,timeout_t timeout) { return private_process(res,timeout,-1); } result_t waitforinput(resource_t res,timeout_t timeout) { return private_process(res,timeout,RESULT_KEY|RESULT_MOUSE|RESULT_TIMEOUT); } result_t waitforupdate(resource_t res,timeout_t timeout) { return private_process(res,timeout,RESULT_SCREEN|RESULT_TIMEOUT); } result_t visualgrep(resource_t resource,const char* filename,timeout_t timeout) { private_resource_t* res=get_resource(resource); image_t* image; result_t result; if(res==0 || res->client==0) return 0; /* load filename and set res->grep_image to this image */ image=loadpnm(filename); if(image==0) return 0; if(res->grep_image) free_image(res->grep_image); res->grep_image=image; if(do_visual_grep(res,0,0,res->client->width,res->client->height)) return RESULT_FOUNDIMAGE; result=private_process(resource,timeout,RESULT_FOUNDIMAGE|RESULT_TIMEOUT); /* free image */ if(res->grep_image) { free_image(res->grep_image); res->grep_image=NULL; } return result; } /* auxiliary function for alert */ #include "default8x16.h" static void center_text(rfbScreenInfo* screen,const char* message,int* x,int* y,int* w,int* h) { rfbFontData* font=&default8x16Font; const char* pointer; int j,x1,y1,x2,y2,line_count=0; if(message==0 || screen==0) return; rfbWholeFontBBox(font,&x1,&y1,&x2,&y2); for(line_count=1,pointer=message;*pointer;pointer++) if(*pointer=='\n') line_count++; *h=(y2-y1)*line_count; assert(*h>0); if(*h>screen->height) *h=screen->height; *x=0; *w=screen->width; *y=(screen->height-*h)/2; rfbFillRect(screen,*x,*y,*x+*w,*y+*h,0xff0000); for(pointer=message,j=0;jscreen->width) width=screen->width; x_cur=(screen->width-width)/2; for(;pointer!=eol;pointer++) x_cur+=rfbDrawCharWithClip(screen,font, x_cur,y_cur,*pointer, 0,0,screen->width,screen->height, 0xffffffff,0xffffffff); pointer++; } rfbMarkRectAsModified(screen,*x,*y,*x+*w,*y+*h); } /* this is an overlay which is shown for a certain time */ result_t alert(resource_t resource,const char* message,timeout_t timeout) { private_resource_t* res=get_resource(resource); char* fake_frame_buffer; char* backup; int x,y,w,h; result_t result; if(res == NULL || res->server==NULL) return -1; w=res->server->width; h=res->server->height; fake_frame_buffer=malloc(w*4*h); if(!fake_frame_buffer) return -1; memcpy(fake_frame_buffer,res->server->frameBuffer,w*4*h); backup=res->server->frameBuffer; res->server->frameBuffer=fake_frame_buffer; center_text(res->server,message,&x,&y,&w,&h); fprintf(stderr,"%s\n",message); result=waitforinput(resource,timeout); res->server->frameBuffer=backup; free(fake_frame_buffer); rfbMarkRectAsModified(res->server,x,y,x+w,y+h); return result; } /* inspect last events */ keysym_t getkeysym(resource_t res) { private_resource_t* r=get_resource(res); return r->keysym; } bool_t getkeydown(resource_t res) { private_resource_t* r=get_resource(res); return r->keydown; } coordinate_t getx(resource_t res) { private_resource_t* r=get_resource(res); return r->x; } coordinate_t gety(resource_t res) { private_resource_t* r=get_resource(res); return r->y; } buttons_t getbuttons(resource_t res) { private_resource_t* r=get_resource(res); return r->buttons; } const char *gettext_client(resource_t res) { private_resource_t* r=get_resource(res); return r->text_client; } result_t rubberband(resource_t resource, coordinate_t x0, coordinate_t y0) { private_resource_t* res=get_resource(resource); char* fake_frame_buffer; char* backup; int w, h, x, y; if(res == NULL || res->server==NULL) return -1; x = res->x; y = res->y; w = res->server->width; h = res->server->height; fake_frame_buffer = malloc(w * 4 * h); if(!fake_frame_buffer) return 0; memcpy(fake_frame_buffer, res->server->frameBuffer, w * 4 * h); backup = res->server->frameBuffer; res->server->frameBuffer = fake_frame_buffer; while (res->buttons) { result_t r = waitforinput(resource, 1000000L); if (x == res->x && y == res->y) continue; copy_line(res->server, backup, x0, y0, x, y0, 0); copy_line(res->server, backup, x0, y0, x0, y, 0); copy_line(res->server, backup, x, y0, x, y, 0); copy_line(res->server, backup, x0, y, x, y, 0); x = res->x; y = res->y; copy_line(res->server, backup, x0, y0, x, y0, 0x80); copy_line(res->server, backup, x0, y0, x0, y, 0x80); copy_line(res->server, backup, x, y0, x, y, 0x80); copy_line(res->server, backup, x0, y, x, y, 0x80); } copy_line(res->server, backup, x0, y0, x, y0, 0); copy_line(res->server, backup, x0, y0, x0, y, 0); copy_line(res->server, backup, x, y0, x, y, 0); copy_line(res->server, backup, x0, y, x, y, 0); res->server->frameBuffer=backup; free(fake_frame_buffer); return RESULT_MOUSE; } const char *gettext_server(resource_t res) { private_resource_t* r=get_resource(res); return r->text_server; } /* send events to the server */ bool_t sendkey(resource_t res,keysym_t keysym,bool_t keydown) { private_resource_t* r=get_resource(res); if(r==NULL) return 0; return SendKeyEvent(r->client,keysym,keydown); } bool_t sendascii(resource_t res,const char *string) { timeout_t delay = 0.1; private_resource_t* r=get_resource(res); int i; if(r==NULL) return 0; while (*string) { int keysym = *string; int need_shift = 0; if (keysym >= 8 && keysym < ' ') keysym += 0xff00; else if (keysym >= 'A' && keysym <= 'Z') need_shift = 1; else if (keysym > '~') { fprintf(stderr, "String contains non-ASCII " "character 0x%02x\n", *string); return FALSE; } if (need_shift) { if (!SendKeyEvent(r->client,0xffe1,1)) return FALSE; waitforinput(r,delay); } for (i = 1; i >= 0; i--) { if (!SendKeyEvent(r->client,keysym,i)) return FALSE; waitforinput(r,delay); } if (need_shift) { if (!SendKeyEvent(r->client,0xffe1,0)) return FALSE; waitforinput(r,delay); } string++; } return TRUE; } bool_t sendmouse(resource_t res,coordinate_t x,coordinate_t y,buttons_t buttons) { private_resource_t* r=get_resource(res); if(r==NULL) return 0; return SendPointerEvent(r->client,x,y,buttons); } bool_t sendtext(resource_t res, const char *string) { private_resource_t* r=get_resource(res); if(r==NULL) return 0; return SendClientCutText(r->client, (char *)string, (int)strlen(string)); } bool_t sendtext_to_server(resource_t res, const char *string) { private_resource_t* r=get_resource(res); if(r==NULL) return 0; rfbSendServerCutText(r->server, (char *)string, (int)strlen(string)); return 1; } /* for visual grepping */ coordinate_t getxorigin(resource_t res) { private_resource_t* r=get_resource(res); return r->x_origin; } coordinate_t getyorigin(resource_t res) { private_resource_t* r=get_resource(res); return r->y_origin; }