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