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
init_fb(void)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
cleanup_fb(void)118 static void cleanup_fb(void)
119 {
120 if(fbfd != -1)
121 {
122 close(fbfd);
123 }
124 }
125
init_kbd()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
cleanup_kbd()135 static void cleanup_kbd()
136 {
137 if(kbdfd != -1)
138 {
139 close(kbdfd);
140 }
141 }
142
init_touch()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
cleanup_touch()167 static void cleanup_touch()
168 {
169 if(touchfd != -1)
170 {
171 close(touchfd);
172 }
173 }
174
175 /*****************************************************************************/
176
init_fb_server(int argc,char ** argv)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 /*****************************************************************************/
injectKeyEvent(uint16_t code,uint16_t value)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
keysym2scancode(rfbBool down,rfbKeySym key,rfbClientPtr cl)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
keyevent(rfbBool down,rfbKeySym key,rfbClientPtr cl)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
injectTouchEvent(int down,int x,int y)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
ptrevent(int buttonMask,int x,int y,rfbClientPtr cl)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
update_screen(void)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
print_usage(char ** argv)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
main(int argc,char ** argv)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