You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libtdevnc/VisualNaCro/nacro.c

802 lines
18 KiB

#include <assert.h>
#include <string.h>
#include <rfb/rfb.h>
#include <rfb/rfbclient.h>
#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_count<MAX_RESOURCE_COUNT) {
memset(resource_pool+resource_count,0,sizeof(private_resource_t));
resource_count++;
return resource_pool+resource_count-1;
} else {
int i;
for(i=0;i<MAX_RESOURCE_COUNT && resource_pool[i].client;i++);
if(i<MAX_RESOURCE_COUNT)
return resource_pool+i;
}
return NULL;
}
static void free_resource(int resource)
{
private_resource_t* res=get_resource(resource);
if(res)
res->client=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);
if(!res->server)
return FALSE;
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;y<y_end;y++)
for(x=x_start;x<x_end;x++) {
bool_t matching=1;
int i,j;
for(j=0;matching && j<image->height;j++)
for(i=0;matching && i<image->width;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;j<h;j++)
for(i=0;i<w;i++)
if(fread(image->buffer+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;j<line_count;j++) {
const char* eol;
int x_cur,y_cur=*y-y1+j*(y2-y1),width;
for(width=0,eol=pointer;*eol && *eol!='\n';eol++)
width+=rfbWidthOfChar(font,*eol);
if(width>screen->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;
}