Home | History | Annotate | Download | only in jni
      1 /*
      2  * $Id$
      3  *
      4  * This program is free software; you can redistribute it and/or modify it
      5  * under the terms of the GNU General Public License as published by the
      6  * Free Software Foundation; either version 2, or (at your option) any
      7  * later version.
      8  *
      9  * This program is distributed in the hope that it will be useful, but
     10  * WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * General Public License for more details.
     13  *
     14  * This project is an adaptation of the original fbvncserver for the iPAQ
     15  * and Zaurus.
     16  */
     17 
     18 #include <stdio.h>
     19 #include <stdlib.h>
     20 #include <string.h>
     21 
     22 #include <unistd.h>
     23 #include <sys/mman.h>
     24 #include <sys/ioctl.h>
     25 
     26 #include <sys/stat.h>
     27 #include <sys/sysmacros.h>             /* For makedev() */
     28 
     29 #include <fcntl.h>
     30 #include <linux/fb.h>
     31 #include <linux/input.h>
     32 
     33 #include <assert.h>
     34 #include <errno.h>
     35 
     36 /* libvncserver */
     37 #include "rfb/rfb.h"
     38 #include "rfb/keysym.h"
     39 
     40 /*****************************************************************************/
     41 
     42 /* Android does not use /dev/fb0. */
     43 #define FB_DEVICE "/dev/graphics/fb0"
     44 static char KBD_DEVICE[256] = "/dev/input/event3";
     45 static char TOUCH_DEVICE[256] = "/dev/input/event1";
     46 static struct fb_var_screeninfo scrinfo;
     47 static int fbfd = -1;
     48 static int kbdfd = -1;
     49 static int touchfd = -1;
     50 static unsigned short int *fbmmap = MAP_FAILED;
     51 static unsigned short int *vncbuf;
     52 static unsigned short int *fbbuf;
     53 
     54 /* Android already has 5900 bound natively. */
     55 #define VNC_PORT 5901
     56 static rfbScreenInfoPtr vncscr;
     57 
     58 static int xmin, xmax;
     59 static int ymin, ymax;
     60 
     61 /* No idea, just copied from fbvncserver as part of the frame differerencing
     62  * algorithm.  I will probably be later rewriting all of this. */
     63 static struct varblock_t
     64 {
     65 	int min_i;
     66 	int min_j;
     67 	int max_i;
     68 	int max_j;
     69 	int r_offset;
     70 	int g_offset;
     71 	int b_offset;
     72 	int rfb_xres;
     73 	int rfb_maxy;
     74 } varblock;
     75 
     76 /*****************************************************************************/
     77 
     78 static void keyevent(rfbBool down, rfbKeySym key, rfbClientPtr cl);
     79 static void ptrevent(int buttonMask, int x, int y, rfbClientPtr cl);
     80 
     81 /*****************************************************************************/
     82 
     83 static void init_fb(void)
     84 {
     85 	size_t pixels;
     86 	size_t bytespp;
     87 
     88 	if ((fbfd = open(FB_DEVICE, O_RDONLY)) == -1)
     89 	{
     90 		printf("cannot open fb device %s\n", FB_DEVICE);
     91 		exit(EXIT_FAILURE);
     92 	}
     93 
     94 	if (ioctl(fbfd, FBIOGET_VSCREENINFO, &scrinfo) != 0)
     95 	{
     96 		printf("ioctl error\n");
     97 		exit(EXIT_FAILURE);
     98 	}
     99 
    100 	pixels = scrinfo.xres * scrinfo.yres;
    101 	bytespp = scrinfo.bits_per_pixel / 8;
    102 
    103 	fprintf(stderr, "xres=%d, yres=%d, xresv=%d, yresv=%d, xoffs=%d, yoffs=%d, bpp=%d\n",
    104 	  (int)scrinfo.xres, (int)scrinfo.yres,
    105 	  (int)scrinfo.xres_virtual, (int)scrinfo.yres_virtual,
    106 	  (int)scrinfo.xoffset, (int)scrinfo.yoffset,
    107 	  (int)scrinfo.bits_per_pixel);
    108 
    109 	fbmmap = mmap(NULL, pixels * bytespp, PROT_READ, MAP_SHARED, fbfd, 0);
    110 
    111 	if (fbmmap == MAP_FAILED)
    112 	{
    113 		printf("mmap failed\n");
    114 		exit(EXIT_FAILURE);
    115 	}
    116 }
    117 
    118 static void cleanup_fb(void)
    119 {
    120 	if(fbfd != -1)
    121 	{
    122 		close(fbfd);
    123 	}
    124 }
    125 
    126 static void init_kbd()
    127 {
    128 	if((kbdfd = open(KBD_DEVICE, O_RDWR)) == -1)
    129 	{
    130 		printf("cannot open kbd device %s\n", KBD_DEVICE);
    131 		exit(EXIT_FAILURE);
    132 	}
    133 }
    134 
    135 static void cleanup_kbd()
    136 {
    137 	if(kbdfd != -1)
    138 	{
    139 		close(kbdfd);
    140 	}
    141 }
    142 
    143 static void init_touch()
    144 {
    145     struct input_absinfo info;
    146         if((touchfd = open(TOUCH_DEVICE, O_RDWR)) == -1)
    147         {
    148                 printf("cannot open touch device %s\n", TOUCH_DEVICE);
    149                 exit(EXIT_FAILURE);
    150         }
    151     // Get the Range of X and Y
    152     if(ioctl(touchfd, EVIOCGABS(ABS_X), &info)) {
    153         printf("cannot get ABS_X info, %s\n", strerror(errno));
    154         exit(EXIT_FAILURE);
    155     }
    156     xmin = info.minimum;
    157     xmax = info.maximum;
    158     if(ioctl(touchfd, EVIOCGABS(ABS_Y), &info)) {
    159         printf("cannot get ABS_Y, %s\n", strerror(errno));
    160         exit(EXIT_FAILURE);
    161     }
    162     ymin = info.minimum;
    163     ymax = info.maximum;
    164 
    165 }
    166 
    167 static void cleanup_touch()
    168 {
    169 	if(touchfd != -1)
    170 	{
    171 		close(touchfd);
    172 	}
    173 }
    174 
    175 /*****************************************************************************/
    176 
    177 static void init_fb_server(int argc, char **argv)
    178 {
    179 	printf("Initializing server...\n");
    180 
    181 	/* Allocate the VNC server buffer to be managed (not manipulated) by
    182 	 * libvncserver. */
    183 	vncbuf = calloc(scrinfo.xres * scrinfo.yres, scrinfo.bits_per_pixel / 8);
    184 	assert(vncbuf != NULL);
    185 
    186 	/* Allocate the comparison buffer for detecting drawing updates from frame
    187 	 * to frame. */
    188 	fbbuf = calloc(scrinfo.xres * scrinfo.yres, scrinfo.bits_per_pixel / 8);
    189 	assert(fbbuf != NULL);
    190 
    191 	/* TODO: This assumes scrinfo.bits_per_pixel is 16. */
    192 	vncscr = rfbGetScreen(&argc, argv, scrinfo.xres, scrinfo.yres, 5, 2, (scrinfo.bits_per_pixel / 8));
    193 	assert(vncscr != NULL);
    194 
    195 	vncscr->desktopName = "Android";
    196 	vncscr->frameBuffer = (char *)vncbuf;
    197 	vncscr->alwaysShared = TRUE;
    198 	vncscr->httpDir = NULL;
    199 	vncscr->port = VNC_PORT;
    200 
    201 	vncscr->kbdAddEvent = keyevent;
    202 	vncscr->ptrAddEvent = ptrevent;
    203 
    204 	rfbInitServer(vncscr);
    205 
    206 	/* Mark as dirty since we haven't sent any updates at all yet. */
    207 	rfbMarkRectAsModified(vncscr, 0, 0, scrinfo.xres, scrinfo.yres);
    208 
    209 	/* No idea. */
    210 	varblock.r_offset = scrinfo.red.offset + scrinfo.red.length - 5;
    211 	varblock.g_offset = scrinfo.green.offset + scrinfo.green.length - 5;
    212 	varblock.b_offset = scrinfo.blue.offset + scrinfo.blue.length - 5;
    213 	varblock.rfb_xres = scrinfo.yres;
    214 	varblock.rfb_maxy = scrinfo.xres - 1;
    215 }
    216 
    217 /*****************************************************************************/
    218 void injectKeyEvent(uint16_t code, uint16_t value)
    219 {
    220     struct input_event ev;
    221     memset(&ev, 0, sizeof(ev));
    222     gettimeofday(&ev.time,0);
    223     ev.type = EV_KEY;
    224     ev.code = code;
    225     ev.value = value;
    226     if(write(kbdfd, &ev, sizeof(ev)) < 0)
    227     {
    228         printf("write event failed, %s\n", strerror(errno));
    229     }
    230 
    231     printf("injectKey (%d, %d)\n", code , value);
    232 }
    233 
    234 static int keysym2scancode(rfbBool down, rfbKeySym key, rfbClientPtr cl)
    235 {
    236     int scancode = 0;
    237 
    238     int code = (int)key;
    239     if (code>='0' && code<='9') {
    240         scancode = (code & 0xF) - 1;
    241         if (scancode<0) scancode += 10;
    242         scancode += KEY_1;
    243     } else if (code>=0xFF50 && code<=0xFF58) {
    244         static const uint16_t map[] =
    245              {  KEY_HOME, KEY_LEFT, KEY_UP, KEY_RIGHT, KEY_DOWN,
    246                 KEY_SOFT1, KEY_SOFT2, KEY_END, 0 };
    247         scancode = map[code & 0xF];
    248     } else if (code>=0xFFE1 && code<=0xFFEE) {
    249         static const uint16_t map[] =
    250              {  KEY_LEFTSHIFT, KEY_LEFTSHIFT,
    251                 KEY_COMPOSE, KEY_COMPOSE,
    252                 KEY_LEFTSHIFT, KEY_LEFTSHIFT,
    253                 0,0,
    254                 KEY_LEFTALT, KEY_RIGHTALT,
    255                 0, 0, 0, 0 };
    256         scancode = map[code & 0xF];
    257     } else if ((code>='A' && code<='Z') || (code>='a' && code<='z')) {
    258         static const uint16_t map[] = {
    259                 KEY_A, KEY_B, KEY_C, KEY_D, KEY_E,
    260                 KEY_F, KEY_G, KEY_H, KEY_I, KEY_J,
    261                 KEY_K, KEY_L, KEY_M, KEY_N, KEY_O,
    262                 KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T,
    263                 KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z };
    264         scancode = map[(code & 0x5F) - 'A'];
    265     } else {
    266         switch (code) {
    267             case 0x0003:    scancode = KEY_CENTER;      break;
    268             case 0x0020:    scancode = KEY_SPACE;       break;
    269             case 0x0023:    scancode = KEY_SHARP;       break;
    270             case 0x0033:    scancode = KEY_SHARP;       break;
    271             case 0x002C:    scancode = KEY_COMMA;       break;
    272             case 0x003C:    scancode = KEY_COMMA;       break;
    273             case 0x002E:    scancode = KEY_DOT;         break;
    274             case 0x003E:    scancode = KEY_DOT;         break;
    275             case 0x002F:    scancode = KEY_SLASH;       break;
    276             case 0x003F:    scancode = KEY_SLASH;       break;
    277             case 0x0032:    scancode = KEY_EMAIL;       break;
    278             case 0x0040:    scancode = KEY_EMAIL;       break;
    279             case 0xFF08:    scancode = KEY_BACKSPACE;   break;
    280             case 0xFF1B:    scancode = KEY_BACK;        break;
    281             case 0xFF09:    scancode = KEY_TAB;         break;
    282             case 0xFF0D:    scancode = KEY_ENTER;       break;
    283             case 0x002A:    scancode = KEY_STAR;        break;
    284             case 0xFFBE:    scancode = KEY_F1;        break; // F1
    285             case 0xFFBF:    scancode = KEY_F2;         break; // F2
    286             case 0xFFC0:    scancode = KEY_F3;        break; // F3
    287             case 0xFFC5:    scancode = KEY_F4;       break; // F8
    288             case 0xFFC8:    rfbShutdownServer(cl->screen,TRUE);       break; // F11
    289         }
    290     }
    291 
    292     return scancode;
    293 }
    294 
    295 static void keyevent(rfbBool down, rfbKeySym key, rfbClientPtr cl)
    296 {
    297 	int scancode;
    298 
    299 	printf("Got keysym: %04x (down=%d)\n", (unsigned int)key, (int)down);
    300 
    301 	if ((scancode = keysym2scancode(down, key, cl)))
    302 	{
    303 		injectKeyEvent(scancode, down);
    304 	}
    305 }
    306 
    307 void injectTouchEvent(int down, int x, int y)
    308 {
    309     struct input_event ev;
    310 
    311     // Calculate the final x and y
    312     /* Fake touch screen always reports zero */
    313     if (xmin != 0 && xmax != 0 && ymin != 0 && ymax != 0)
    314     {
    315         x = xmin + (x * (xmax - xmin)) / (scrinfo.xres);
    316         y = ymin + (y * (ymax - ymin)) / (scrinfo.yres);
    317     }
    318 
    319     memset(&ev, 0, sizeof(ev));
    320 
    321     // Then send a BTN_TOUCH
    322     gettimeofday(&ev.time,0);
    323     ev.type = EV_KEY;
    324     ev.code = BTN_TOUCH;
    325     ev.value = down;
    326     if(write(touchfd, &ev, sizeof(ev)) < 0)
    327     {
    328         printf("write event failed, %s\n", strerror(errno));
    329     }
    330 
    331     // Then send the X
    332     gettimeofday(&ev.time,0);
    333     ev.type = EV_ABS;
    334     ev.code = ABS_X;
    335     ev.value = x;
    336     if(write(touchfd, &ev, sizeof(ev)) < 0)
    337     {
    338         printf("write event failed, %s\n", strerror(errno));
    339     }
    340 
    341     // Then send the Y
    342     gettimeofday(&ev.time,0);
    343     ev.type = EV_ABS;
    344     ev.code = ABS_Y;
    345     ev.value = y;
    346     if(write(touchfd, &ev, sizeof(ev)) < 0)
    347     {
    348         printf("write event failed, %s\n", strerror(errno));
    349     }
    350 
    351     // Finally send the SYN
    352     gettimeofday(&ev.time,0);
    353     ev.type = EV_SYN;
    354     ev.code = 0;
    355     ev.value = 0;
    356     if(write(touchfd, &ev, sizeof(ev)) < 0)
    357     {
    358         printf("write event failed, %s\n", strerror(errno));
    359     }
    360 
    361     printf("injectTouchEvent (x=%d, y=%d, down=%d)\n", x , y, down);
    362 }
    363 
    364 static void ptrevent(int buttonMask, int x, int y, rfbClientPtr cl)
    365 {
    366 	/* Indicates either pointer movement or a pointer button press or release. The pointer is
    367 now at (x-position, y-position), and the current state of buttons 1 to 8 are represented
    368 by bits 0 to 7 of button-mask respectively, 0 meaning up, 1 meaning down (pressed).
    369 On a conventional mouse, buttons 1, 2 and 3 correspond to the left, middle and right
    370 buttons on the mouse. On a wheel mouse, each step of the wheel upwards is represented
    371 by a press and release of button 4, and each step downwards is represented by
    372 a press and release of button 5.
    373   From: http://www.vislab.usyd.edu.au/blogs/index.php/2009/05/22/an-headerless-indexed-protocol-for-input-1?blog=61 */
    374 
    375 	//printf("Got ptrevent: %04x (x=%d, y=%d)\n", buttonMask, x, y);
    376 	if(buttonMask & 1) {
    377 		// Simulate left mouse event as touch event
    378 		injectTouchEvent(1, x, y);
    379 		injectTouchEvent(0, x, y);
    380 	}
    381 }
    382 
    383 #define PIXEL_FB_TO_RFB(p,r,g,b) ((p>>r)&0x1f001f)|(((p>>g)&0x1f001f)<<5)|(((p>>b)&0x1f001f)<<10)
    384 
    385 static void update_screen(void)
    386 {
    387 	unsigned int *f, *c, *r;
    388 	int x, y;
    389 
    390 	varblock.min_i = varblock.min_j = 9999;
    391 	varblock.max_i = varblock.max_j = -1;
    392 
    393 	f = (unsigned int *)fbmmap;        /* -> framebuffer         */
    394 	c = (unsigned int *)fbbuf;         /* -> compare framebuffer */
    395 	r = (unsigned int *)vncbuf;        /* -> remote framebuffer  */
    396 
    397 	for (y = 0; y < scrinfo.yres; y++)
    398 	{
    399 		/* Compare every 2 pixels at a time, assuming that changes are likely
    400 		 * in pairs. */
    401 		for (x = 0; x < scrinfo.xres; x += 2)
    402 		{
    403 			unsigned int pixel = *f;
    404 
    405 			if (pixel != *c)
    406 			{
    407 				*c = pixel;
    408 
    409 				/* XXX: Undo the checkered pattern to test the efficiency
    410 				 * gain using hextile encoding. */
    411 				if (pixel == 0x18e320e4 || pixel == 0x20e418e3)
    412 					pixel = 0x18e318e3;
    413 
    414 				*r = PIXEL_FB_TO_RFB(pixel,
    415 				  varblock.r_offset, varblock.g_offset, varblock.b_offset);
    416 
    417 				if (x < varblock.min_i)
    418 					varblock.min_i = x;
    419 				else
    420 				{
    421 					if (x > varblock.max_i)
    422 						varblock.max_i = x;
    423 
    424 					if (y > varblock.max_j)
    425 						varblock.max_j = y;
    426 					else if (y < varblock.min_j)
    427 						varblock.min_j = y;
    428 				}
    429 			}
    430 
    431 			f++, c++;
    432 			r++;
    433 		}
    434 	}
    435 
    436 	if (varblock.min_i < 9999)
    437 	{
    438 		if (varblock.max_i < 0)
    439 			varblock.max_i = varblock.min_i;
    440 
    441 		if (varblock.max_j < 0)
    442 			varblock.max_j = varblock.min_j;
    443 
    444 		fprintf(stderr, "Dirty page: %dx%d+%d+%d...\n",
    445 		  (varblock.max_i+2) - varblock.min_i, (varblock.max_j+1) - varblock.min_j,
    446 		  varblock.min_i, varblock.min_j);
    447 
    448 		rfbMarkRectAsModified(vncscr, varblock.min_i, varblock.min_j,
    449 		  varblock.max_i + 2, varblock.max_j + 1);
    450 
    451 		rfbProcessEvents(vncscr, 10000);
    452 	}
    453 }
    454 
    455 /*****************************************************************************/
    456 
    457 void print_usage(char **argv)
    458 {
    459 	printf("%s [-k device] [-t device] [-h]\n"
    460 		"-k device: keyboard device node, default is /dev/input/event3\n"
    461 		"-t device: touch device node, default is /dev/input/event1\n"
    462 		"-h : print this help\n");
    463 }
    464 
    465 int main(int argc, char **argv)
    466 {
    467 	if(argc > 1)
    468 	{
    469 		int i=1;
    470 		while(i < argc)
    471 		{
    472 			if(*argv[i] == '-')
    473 			{
    474 				switch(*(argv[i] + 1))
    475 				{
    476 					case 'h':
    477 						print_usage(argv);
    478 						exit(0);
    479 						break;
    480 					case 'k':
    481 						i++;
    482 						strcpy(KBD_DEVICE, argv[i]);
    483 						break;
    484 					case 't':
    485 						i++;
    486 						strcpy(TOUCH_DEVICE, argv[i]);
    487 						break;
    488 				}
    489 			}
    490 			i++;
    491 		}
    492 	}
    493 
    494 	printf("Initializing framebuffer device " FB_DEVICE "...\n");
    495 	init_fb();
    496 	printf("Initializing keyboard device %s ...\n", KBD_DEVICE);
    497 	init_kbd();
    498 	printf("Initializing touch device %s ...\n", TOUCH_DEVICE);
    499 	init_touch();
    500 
    501 	printf("Initializing VNC server:\n");
    502 	printf("	width:  %d\n", (int)scrinfo.xres);
    503 	printf("	height: %d\n", (int)scrinfo.yres);
    504 	printf("	bpp:    %d\n", (int)scrinfo.bits_per_pixel);
    505 	printf("	port:   %d\n", (int)VNC_PORT);
    506 	init_fb_server(argc, argv);
    507 
    508 	/* Implement our own event loop to detect changes in the framebuffer. */
    509 	while (1)
    510 	{
    511 		while (vncscr->clientHead == NULL)
    512 			rfbProcessEvents(vncscr, 100000);
    513 
    514 		rfbProcessEvents(vncscr, 100000);
    515 		update_screen();
    516 	}
    517 
    518 	printf("Cleaning up...\n");
    519 	cleanup_fb();
    520 	cleanup_kdb();
    521 	cleanup_touch();
    522 }
    523