blob: 9c492b0d9198466fcc2dfa5b5765e74b1374b75c [file] [log] [blame]
San Mehata430b2b2014-09-23 08:30:51 -07001#include <assert.h>
2#include <string.h>
3#include <rfb/rfb.h>
4#include <rfb/rfbclient.h>
5
6#include "nacro.h"
7
8/* for visual grepping */
9typedef struct image_t {
10 int width,height;
11 char* buffer;
12} image_t;
13
14/* this is a VNC connection */
15typedef struct private_resource_t {
16 int listen_port;
17 rfbScreenInfo* server;
18 rfbClient* client;
19
20 uint32_t keysym;
21 rfbBool keydown;
22
23 int x,y;
24 int buttons;
25
26 char* text_client;
27 char* text_server;
28
29 image_t* grep_image;
30 int x_origin,y_origin;
31
32 enum { SLEEP,VISUALGREP,WAITFORUPDATE } state;
33 result_t result;
34} private_resource_t;
35
36/* resource management */
37
38#define MAX_RESOURCE_COUNT 20
39
40static private_resource_t resource_pool[MAX_RESOURCE_COUNT];
41static int resource_count=0;
42
43static private_resource_t* get_resource(int resource)
44{
45 if(resource>=MAX_RESOURCE_COUNT || resource<0 || resource_pool[resource].client==0)
46 return NULL;
47 return resource_pool+resource;
48}
49
50static private_resource_t* get_next_resource(void)
51{
52 if(resource_count<MAX_RESOURCE_COUNT) {
53 memset(resource_pool+resource_count,0,sizeof(private_resource_t));
54 resource_count++;
55 return resource_pool+resource_count-1;
56 } else {
57 int i;
58
59 for(i=0;i<MAX_RESOURCE_COUNT && resource_pool[i].client;i++);
60 if(i<MAX_RESOURCE_COUNT)
61 return resource_pool+i;
62 }
63 return NULL;
64}
65
66static void free_resource(int resource)
67{
68 private_resource_t* res=get_resource(resource);
69 if(res)
70 res->client=NULL;
71}
72
73/* hooks */
74
75static void got_key(rfbBool down,rfbKeySym keysym,rfbClientRec* cl)
76{
77 private_resource_t* res=(private_resource_t*)cl->screen->screenData;
78
79 res->keydown=down;
80 res->keysym=keysym;
81 res->result|=RESULT_KEY;
82}
83
84static void got_mouse(int buttons,int x,int y,rfbClientRec* cl)
85{
86 private_resource_t* res=(private_resource_t*)cl->screen->screenData;
87
88 res->buttons=buttons;
89 res->x=x;
90 res->y=y;
91 res->result|=RESULT_MOUSE;
92}
93
94static void got_text(char* str,int len,rfbClientRec* cl)
95{
96 private_resource_t* res=(private_resource_t*)cl->screen->screenData;
97
98 if (res->text_client)
99 free(res->text_client);
100 res->text_client=strdup(str);
101 res->result|=RESULT_TEXT_CLIENT;
102}
103
104static void got_text_from_server(rfbClient* cl, const char *str, int textlen)
105{
106 private_resource_t* res=(private_resource_t*)cl->clientData;
107
108 if (res->text_server)
109 free(res->text_server);
110 res->text_server=strdup(str);
111 res->result|=RESULT_TEXT_SERVER;
112}
113
114static rfbBool malloc_frame_buffer(rfbClient* cl)
115{
116 private_resource_t* res=(private_resource_t*)cl->clientData;
117
118 if(!res->server) {
119 int w=cl->width,h=cl->height;
120
121 res->client->frameBuffer=malloc(w*4*h);
122
123 res->server=rfbGetScreen(NULL,NULL,w,h,8,3,4);
124 if(!res->server)
125 return FALSE;
126 res->server->screenData=res;
127 res->server->port=res->listen_port;
128 res->server->frameBuffer=res->client->frameBuffer;
129 res->server->kbdAddEvent=got_key;
130 res->server->ptrAddEvent=got_mouse;
131 res->server->setXCutText=got_text;
132 rfbInitServer(res->server);
133 } else {
134 /* TODO: realloc if necessary */
135 /* TODO: resolution change: send NewFBSize */
136 /* TODO: if the origin is out of bounds, reset to 0 */
137 }
138}
139
140static bool_t do_visual_grep(private_resource_t* res,int x,int y,int w,int h)
141{
142 rfbClient* cl;
143 image_t* image;
144 int x_start,y_start,x_end=x+w-1,y_end=y+h-1;
145 bool_t found=0;
146
147 if(res==0 || (cl=res->client)==0 || (image=res->grep_image)==0)
148 return 0;
149
150 x_start=x-image->width;
151 y_start=y-image->height;
152 if(x_start<0) x_start=0;
153 if(y_start<0) y_start=0;
154 if(x_end+image->width>cl->width) x_end=cl->width-image->width;
155 if(y_end+image->height>cl->height) y_end=cl->height-image->height;
156
157 /* find image and set x_origin,y_origin if found */
158 for(y=y_start;y<y_end;y++)
159 for(x=x_start;x<x_end;x++) {
160 bool_t matching=1;
161 int i,j;
162 for(j=0;matching && j<image->height;j++)
163 for(i=0;matching && i<image->width;i++)
164 if(memcmp(cl->frameBuffer+4*(x+i+cl->width*(y+j)),image->buffer+4*(i+image->width*j),3))
165 matching=0;
166 if(matching) {
167 private_resource_t* res=(private_resource_t*)cl->clientData;
168 res->x_origin=x;
169 res->y_origin=y;
170 return -1;
171 }
172 }
173 return 0;
174}
175
176static void got_frame_buffer(rfbClient* cl,int x,int y,int w,int h)
177{
178 private_resource_t* res=(private_resource_t*)cl->clientData;
179
180 assert(res->server);
181
182 if(res->grep_image && do_visual_grep(res,x,y,w,h)) {
183 res->result|=RESULT_FOUNDIMAGE;
184 }
185 if(res->server) {
186 rfbMarkRectAsModified(res->server,x,y,x+w,y+h);
187 }
188
189 res->result|=RESULT_SCREEN;
190}
191
192/* init/shutdown functions */
193
194resource_t initvnc(const char* server,int server_port,int listen_port)
195{
196 private_resource_t* res=get_next_resource();
197 int dummy=0;
198
199 if(res==0)
200 return -1;
201
202 /* remember for later */
203 res->listen_port=listen_port;
204
205 res->text_client = NULL;
206 res->text_server = NULL;
207
208 res->client=rfbGetClient(8,3,4);
209 res->client->clientData=(void*)res;
210 res->client->GotFrameBufferUpdate=got_frame_buffer;
211 res->client->MallocFrameBuffer=malloc_frame_buffer;
212 res->client->GotXCutText=got_text_from_server;
213 res->client->serverHost=strdup(server);
214 res->client->serverPort=server_port;
215 res->client->appData.encodingsString="raw";
216 if(!rfbInitClient(res->client,&dummy,NULL)) {
217 res->client=NULL;
218 return -1;
219 }
220 return res-resource_pool;
221}
222
223void closevnc(resource_t resource)
224{
225 private_resource_t* res=get_resource(resource);
226 if(res==0)
227 return;
228
229 if(res->server)
230 rfbScreenCleanup(res->server);
231
232 assert(res->client);
233
234 rfbClientCleanup(res->client);
235
236 res->client=NULL;
237}
238
239/* PNM (image) helpers */
240
241bool_t savepnm(resource_t resource,const char* filename,int x1,int y1,int x2,int y2)
242{
243 private_resource_t* res=get_resource(resource);
244 int i,j,w,h;
245 uint32_t* buffer;
246 FILE* f;
247
248 if(res==0 || res->client==0)
249 return 0;
250 assert(res->client->format.depth==24);
251
252 w=res->client->width;
253 h=res->client->height;
254 buffer=(uint32_t*)res->client->frameBuffer;
255
256 if(res==0 || x1>x2 || y1>y2 || x1<0 || x2>=w || y1<0 || y2>=h)
257 return FALSE;
258
259 f=fopen(filename,"wb");
260
261 if(f==0)
262 return FALSE;
263
264 fprintf(f,"P6\n%d %d\n255\n",1+x2-x1,1+y2-y1);
265 for(j=y1;j<=y2;j++)
266 for(i=x1;i<=x2;i++) {
267 fwrite(buffer+i+j*w,3,1,f);
268 }
269 if(fclose(f))
270 return FALSE;
271 return TRUE;
272}
273
274static image_t* loadpnm(const char* filename)
275{
276 FILE* f=fopen(filename,"rb");
277 char buffer[1024];
278 int i,j,w,h;
279 image_t* image;
280
281 if(f==0)
282 return NULL;
283
284 if(!fgets(buffer,1024,f) || strcmp("P6\n",buffer)) {
285 fclose(f);
286 return NULL;
287 }
288
289 do {
290 fgets(buffer,1024,f);
291 if(feof(f)) {
292 fclose(f);
293 return NULL;
294 }
295 } while(buffer[0]=='#');
296
297 if( sscanf(buffer,"%d %d",&w,&h)!=2
298 || !fgets(buffer,1024,f) || strcmp("255\n",buffer)) {
299 fclose(f);
300 return NULL;
301 }
302
303 image=(image_t*)malloc(sizeof(image_t));
304 image->width=w;
305 image->height=h;
306 image->buffer=malloc(w*4*h);
307 if(!image->buffer) {
308 fclose(f);
309 free(image);
310 return NULL;
311 }
312
313 for(j=0;j<h;j++)
314 for(i=0;i<w;i++)
315 if(fread(image->buffer+4*(i+w*j),3,1,f)!=1) {
316 fprintf(stderr,"Could not read 3 bytes at %d,%d\n",i,j);
317 fclose(f);
318 free(image->buffer);
319 free(image);
320 return NULL;
321 }
322
323 fclose(f);
324
325 return image;
326}
327
328static void free_image(image_t* image)
329{
330 if(image->buffer)
331 free(image->buffer);
332 free(image);
333}
334
335static void copy_line(rfbScreenInfo *dest, char *backup,
336 int x0, int y0, int x1, int y1, int color_offset)
337{
338 uint8_t *d = (uint8_t *)dest->frameBuffer, *s = (uint8_t *)backup;
339 int i;
340 int steps0 = x1 > x0 ? x1 - x0 : x0 - x1;
341 int steps1 = y1 > y0 ? y1 - y0 : y0 - y1;
342
343 if (steps1 > steps0)
344 steps0 = steps1;
345 else if (steps0 == 0)
346 steps0 = 1;
347
348 for (i = 0; i <= steps0; i++) {
349 int j, index = 4 * (x0 + i * (x1 - x0) / steps0
350 + dest->width * (y0 + i * (y1 - y0) / steps0));
351 for (j = 0; j < 4; j++)
352 d[index + j] = s[index + j] + color_offset;
353 }
354
355 rfbMarkRectAsModified(dest, x0 - 5, y0 - 5, x1 + 1, y1 + 2);
356}
357
358result_t displaypnm(resource_t resource, const char *filename,
359 coordinate_t x, coordinate_t y, bool_t border,
360 timeout_t timeout_in_seconds)
361{
362 private_resource_t* res = get_resource(resource);
363 image_t *image;
364 char* fake_frame_buffer;
365 char* backup;
366 int w, h, i, j, w2, h2;
367 result_t result;
368
369 if (res == NULL || res->server == NULL ||
370 (image = loadpnm(filename)) == NULL)
371 return 0;
372
373 w = res->server->width;
374 h = res->server->height;
375 fake_frame_buffer = malloc(w * 4 * h);
376 if(!fake_frame_buffer)
377 return 0;
378 memcpy(fake_frame_buffer, res->server->frameBuffer, w * 4 * h);
379
380 backup = res->server->frameBuffer;
381 res->server->frameBuffer = fake_frame_buffer;
382
383 w2 = image->width;
384 if (x + w2 > w)
385 w2 = w - x;
386 h2 = image->height;
387 if (y + h2 > h)
388 h2 = h - y;
389 for (j = 0; j < h2; j++)
390 memcpy(fake_frame_buffer + 4 * (x + (y + j) * w),
391 image->buffer + j * 4 * image->width, 4 * w2);
392 free(image);
393 if (border) {
394 copy_line(res->server, backup, x, y, x + w2, y, 0x80);
395 copy_line(res->server, backup, x, y, x, y + h2, 0x80);
396 copy_line(res->server, backup, x + w2, y, x + w2, y + h2, 0x80);
397 copy_line(res->server, backup, x, y + h2, x + w2, y + h2, 0x80);
398 }
399 rfbMarkRectAsModified(res->server,
400 x - 1, y - 1, x + w2 + 1, y + h2 + 1);
401
402 result = waitforinput(resource, timeout_in_seconds);
403
404 res->server->frameBuffer=backup;
405 free(fake_frame_buffer);
406 rfbMarkRectAsModified(res->server,
407 x - 1, y - 1, x + w2 + 1, y + h2 + 1);
408
409 return result;
410}
411
412/* process() and friends */
413
414/* this function returns only if res->result in return_mask */
415static result_t private_process(resource_t resource,timeout_t timeout_in_seconds,result_t return_mask)
416{
417 private_resource_t* res=get_resource(resource);
418 fd_set fds;
419 struct timeval tv,tv_start,tv_end;
420 unsigned long timeout=(unsigned long)(timeout_in_seconds*1000000UL);
421 int count,max_fd;
422
423 if(res==0)
424 return 0;
425
426 assert(res->client);
427
428 gettimeofday(&tv_start,NULL);
429 res->result=0;
430
431 do {
432 unsigned long timeout_done;
433
434 if(res->server) {
435 rfbBool loop;
436 do {
437 loop=rfbProcessEvents(res->server,res->server->deferUpdateTime);
438 } while(loop && (res->result&return_mask)==0
439 && rfbIsActive(res->server));
440
441 if(!rfbIsActive(res->server))
442 return RESULT_SHUTDOWN;
443
444 if((res->result&return_mask)!=0)
445 return res->result;
446
447 memcpy((char*)&fds,(const char*)&(res->server->allFds),sizeof(fd_set));
448 max_fd=res->server->maxFd;
449 } else {
450 FD_ZERO(&fds);
451 max_fd=0;
452 }
453 FD_SET(res->client->sock,&fds);
454 if(res->client->sock>max_fd)
455 max_fd=res->client->sock;
456
457 gettimeofday(&tv_end,NULL);
458 timeout_done=tv_end.tv_usec-tv_start.tv_usec+
459 1000000L*(tv_end.tv_sec-tv_start.tv_sec);
460 if(timeout_done>=timeout)
461 return RESULT_TIMEOUT;
462
463 tv.tv_usec=((timeout-timeout_done)%1000000);
464 tv.tv_sec=(timeout-timeout_done)/1000000;
465
466 count=select(max_fd+1,&fds,NULL,NULL,&tv);
467 if(count<0)
468 return 0;
469
470 if(count>0) {
471 if(FD_ISSET(res->client->sock,&fds)) {
472 if(!HandleRFBServerMessage(res->client)) {
473 closevnc(resource);
474 return 0;
475 }
476 if((res->result&return_mask)!=0)
477 return res->result;
478 }
479 } else {
480 res->result|=RESULT_TIMEOUT;
481 return res->result;
482 }
483 } while(1);
484
485 return RESULT_TIMEOUT;
486}
487
488result_t process(resource_t res,timeout_t timeout)
489{
490 return private_process(res,timeout,RESULT_TIMEOUT);
491}
492
493result_t waitforanything(resource_t res,timeout_t timeout)
494{
495 return private_process(res,timeout,-1);
496}
497
498result_t waitforinput(resource_t res,timeout_t timeout)
499{
500 return private_process(res,timeout,RESULT_KEY|RESULT_MOUSE|RESULT_TIMEOUT);
501}
502
503result_t waitforupdate(resource_t res,timeout_t timeout)
504{
505 return private_process(res,timeout,RESULT_SCREEN|RESULT_TIMEOUT);
506}
507
508result_t visualgrep(resource_t resource,const char* filename,timeout_t timeout)
509{
510 private_resource_t* res=get_resource(resource);
511 image_t* image;
512 result_t result;
513
514 if(res==0 || res->client==0)
515 return 0;
516
517 /* load filename and set res->grep_image to this image */
518 image=loadpnm(filename);
519 if(image==0)
520 return 0;
521 if(res->grep_image)
522 free_image(res->grep_image);
523 res->grep_image=image;
524
525 if(do_visual_grep(res,0,0,res->client->width,res->client->height))
526 return RESULT_FOUNDIMAGE;
527
528 result=private_process(resource,timeout,RESULT_FOUNDIMAGE|RESULT_TIMEOUT);
529
530 /* free image */
531 if(res->grep_image) {
532 free_image(res->grep_image);
533 res->grep_image=NULL;
534 }
535
536 return result;
537}
538
539/* auxiliary function for alert */
540
541#include "default8x16.h"
542
543static void center_text(rfbScreenInfo* screen,const char* message,int* x,int* y,int* w,int* h)
544{
545 rfbFontData* font=&default8x16Font;
546 const char* pointer;
547 int j,x1,y1,x2,y2,line_count=0;
548 if(message==0 || screen==0)
549 return;
550 rfbWholeFontBBox(font,&x1,&y1,&x2,&y2);
551 for(line_count=1,pointer=message;*pointer;pointer++)
552 if(*pointer=='\n')
553 line_count++;
554
555 *h=(y2-y1)*line_count;
556 assert(*h>0);
557
558 if(*h>screen->height)
559 *h=screen->height;
560
561 *x=0; *w=screen->width; *y=(screen->height-*h)/2;
562
563 rfbFillRect(screen,*x,*y,*x+*w,*y+*h,0xff0000);
564
565 for(pointer=message,j=0;j<line_count;j++) {
566 const char* eol;
567 int x_cur,y_cur=*y-y1+j*(y2-y1),width;
568
569 for(width=0,eol=pointer;*eol && *eol!='\n';eol++)
570 width+=rfbWidthOfChar(font,*eol);
571 if(width>screen->width)
572 width=screen->width;
573
574 x_cur=(screen->width-width)/2;
575 for(;pointer!=eol;pointer++)
576 x_cur+=rfbDrawCharWithClip(screen,font,
577 x_cur,y_cur,*pointer,
578 0,0,screen->width,screen->height,
579 0xffffffff,0xffffffff);
580 pointer++;
581 }
582 rfbMarkRectAsModified(screen,*x,*y,*x+*w,*y+*h);
583}
584
585/* this is an overlay which is shown for a certain time */
586
587result_t alert(resource_t resource,const char* message,timeout_t timeout)
588{
589 private_resource_t* res=get_resource(resource);
590 char* fake_frame_buffer;
591 char* backup;
592 int x,y,w,h;
593 result_t result;
594
595 if(res == NULL || res->server==NULL)
596 return -1;
597
598 w=res->server->width;
599 h=res->server->height;
600
601 fake_frame_buffer=malloc(w*4*h);
602 if(!fake_frame_buffer)
603 return -1;
604 memcpy(fake_frame_buffer,res->server->frameBuffer,w*4*h);
605
606 backup=res->server->frameBuffer;
607 res->server->frameBuffer=fake_frame_buffer;
608 center_text(res->server,message,&x,&y,&w,&h);
609 fprintf(stderr,"%s\n",message);
610
611 result=waitforinput(resource,timeout);
612
613 res->server->frameBuffer=backup;
614 free(fake_frame_buffer);
615 rfbMarkRectAsModified(res->server,x,y,x+w,y+h);
616
617 return result;
618}
619/* inspect last events */
620
621keysym_t getkeysym(resource_t res)
622{
623 private_resource_t* r=get_resource(res);
624 return r->keysym;
625}
626
627bool_t getkeydown(resource_t res)
628{
629 private_resource_t* r=get_resource(res);
630 return r->keydown;
631}
632
633coordinate_t getx(resource_t res)
634{
635 private_resource_t* r=get_resource(res);
636 return r->x;
637}
638
639coordinate_t gety(resource_t res)
640{
641 private_resource_t* r=get_resource(res);
642 return r->y;
643}
644
645buttons_t getbuttons(resource_t res)
646{
647 private_resource_t* r=get_resource(res);
648 return r->buttons;
649}
650
651const char *gettext_client(resource_t res)
652{
653 private_resource_t* r=get_resource(res);
654 return r->text_client;
655}
656
657result_t rubberband(resource_t resource, coordinate_t x0, coordinate_t y0)
658{
659 private_resource_t* res=get_resource(resource);
660 char* fake_frame_buffer;
661 char* backup;
662 int w, h, x, y;
663
664 if(res == NULL || res->server==NULL)
665 return -1;
666
667 x = res->x;
668 y = res->y;
669 w = res->server->width;
670 h = res->server->height;
671 fake_frame_buffer = malloc(w * 4 * h);
672 if(!fake_frame_buffer)
673 return 0;
674 memcpy(fake_frame_buffer, res->server->frameBuffer, w * 4 * h);
675
676 backup = res->server->frameBuffer;
677 res->server->frameBuffer = fake_frame_buffer;
678
679 while (res->buttons) {
680 result_t r = waitforinput(resource, 1000000L);
681 if (x == res->x && y == res->y)
682 continue;
683 copy_line(res->server, backup, x0, y0, x, y0, 0);
684 copy_line(res->server, backup, x0, y0, x0, y, 0);
685 copy_line(res->server, backup, x, y0, x, y, 0);
686 copy_line(res->server, backup, x0, y, x, y, 0);
687 x = res->x;
688 y = res->y;
689 copy_line(res->server, backup, x0, y0, x, y0, 0x80);
690 copy_line(res->server, backup, x0, y0, x0, y, 0x80);
691 copy_line(res->server, backup, x, y0, x, y, 0x80);
692 copy_line(res->server, backup, x0, y, x, y, 0x80);
693 }
694
695 copy_line(res->server, backup, x0, y0, x, y0, 0);
696 copy_line(res->server, backup, x0, y0, x0, y, 0);
697 copy_line(res->server, backup, x, y0, x, y, 0);
698 copy_line(res->server, backup, x0, y, x, y, 0);
699
700 res->server->frameBuffer=backup;
701 free(fake_frame_buffer);
702
703 return RESULT_MOUSE;
704}
705
706const char *gettext_server(resource_t res)
707{
708 private_resource_t* r=get_resource(res);
709 return r->text_server;
710}
711
712/* send events to the server */
713
714bool_t sendkey(resource_t res,keysym_t keysym,bool_t keydown)
715{
716 private_resource_t* r=get_resource(res);
717 if(r==NULL)
718 return 0;
719 return SendKeyEvent(r->client,keysym,keydown);
720}
721
722bool_t sendascii(resource_t res,const char *string)
723{
724 timeout_t delay = 0.1;
725 private_resource_t* r=get_resource(res);
726 int i;
727 if(r==NULL)
728 return 0;
729 while (*string) {
730 int keysym = *string;
731 int need_shift = 0;
732
733 if (keysym >= 8 && keysym < ' ')
734 keysym += 0xff00;
735 else if (keysym >= 'A' && keysym <= 'Z')
736 need_shift = 1;
737 else if (keysym > '~') {
738 fprintf(stderr, "String contains non-ASCII "
739 "character 0x%02x\n", *string);
740 return FALSE;
741 }
742
743 if (need_shift) {
744 if (!SendKeyEvent(r->client,0xffe1,1))
745 return FALSE;
746 waitforinput(r,delay);
747 }
748 for (i = 1; i >= 0; i--) {
749 if (!SendKeyEvent(r->client,keysym,i))
750 return FALSE;
751 waitforinput(r,delay);
752 }
753 if (need_shift) {
754 if (!SendKeyEvent(r->client,0xffe1,0))
755 return FALSE;
756 waitforinput(r,delay);
757 }
758 string++;
759 }
760 return TRUE;
761}
762
763bool_t sendmouse(resource_t res,coordinate_t x,coordinate_t y,buttons_t buttons)
764{
765 private_resource_t* r=get_resource(res);
766 if(r==NULL)
767 return 0;
768 return SendPointerEvent(r->client,x,y,buttons);
769}
770
771bool_t sendtext(resource_t res, const char *string)
772{
773 private_resource_t* r=get_resource(res);
774 if(r==NULL)
775 return 0;
776 return SendClientCutText(r->client, (char *)string, (int)strlen(string));
777}
778
779bool_t sendtext_to_server(resource_t res, const char *string)
780{
781 private_resource_t* r=get_resource(res);
782 if(r==NULL)
783 return 0;
784 rfbSendServerCutText(r->server, (char *)string, (int)strlen(string));
785 return 1;
786}
787
788/* for visual grepping */
789
790coordinate_t getxorigin(resource_t res)
791{
792 private_resource_t* r=get_resource(res);
793 return r->x_origin;
794}
795
796coordinate_t getyorigin(resource_t res)
797{
798 private_resource_t* r=get_resource(res);
799 return r->y_origin;
800}
801