fbvncserver.c

Go to the documentation of this file.
00001 
00007 #include "fbvncserver.h"
00008 
00009 #include <sys/ioctl.h>
00010 #include <sys/types.h>
00011 #include <sys/stat.h>
00012 #include <sys/time.h>
00013 
00014 #include <fcntl.h>
00015 #include <linux/fb.h>
00016 #include <signal.h>
00017 
00018 /* Project */
00019 #include "keysym2scancode.h"  /* keysym to scancode conversion */
00020 
00021 /* Types */
00022 struct ClientData {
00023   rfbBool oldButton;
00024   int oldx,oldy;
00025 };
00026 
00027 typedef struct {
00028   long x;
00029   long y;
00030   long pressure;
00031 } ARTIFICIAL_TS_EVENT;
00032 
00033 // Variables
00034 static int last_pen_down = 0;
00035 
00036 struct kbd_layout_t *kbd_layout = NULL;
00037 struct fb_var_screeninfo vscrinfo;
00038 
00039 unsigned short int
00040     *fbbuf    = NULL,
00041     *cmpfbbuf = NULL,
00042     *rfbbuf   = NULL;
00043 
00047 void emit_scancode (unsigned int s, rfbBool down) {
00049   unsigned short keycode = s;
00050 
00051   if (uinput_fd_kbd && uinput) {
00052     TRACE3("emitting keycode ... %d (%#x) [down: %d] \n", keycode, keycode, down);
00053     dev_uinput_key(uinput_fd_kbd,keycode,down);
00054   } else {
00055     TRACE2(" [W]: unable to emit keycode - uinput not ready ... (uinput: %d, fd: %d)", uinput, uinput_fd_kbd);
00056   } // end of [if]
00057 } // end of [emit_scancode]
00058 
00059 
00071 int emit_touchscreen_sequence(ARTIFICIAL_TS_EVENT *s) {
00072   int fd;
00073   int ret;
00074   ret = 0;
00075 
00076   if((fd=open("/dev/tssim",O_WRONLY))>=0) {
00077     while(s->x!=-1) {
00078       write(fd,s,sizeof(ARTIFICIAL_TS_EVENT));
00079       s++;
00080     }
00081     close(fd);
00082   } else {
00083     fprintf(stderr, " unable to open /dev/tssim for write-only access \n");
00084     ret=-1;
00085   }
00086   return(ret);
00087 }
00088 
00092 void ptrevent( int buttonMask, int x, int y, rfbClientPtr cl)
00093 {
00094 
00095   ARTIFICIAL_TS_EVENT touchscreen_sequence[5];
00096   int i, buffer = 0;
00097   if ((x < 0 ) ||
00098       (y < 0 ) ||
00099       (x > cl -> screen -> width) ||
00100       (y > cl -> screen -> height)) {
00101     TRACE4(" [W] ptrevent() - invalid ts event (x: %d, y: %d [%3d,%3d]) \n",
00102             cl -> screen -> width,
00103             cl -> screen -> height,
00104             x,y);
00105     return;
00106   };
00107 
00108   switch (rotate) {
00109     case 0: break;
00110     case 1: break;
00111     case 2: break;
00112     case 3:
00113           buffer = x;
00114           x = -y + (cl -> screen -> height);
00115           y = buffer;
00116        break;
00117     default: ;
00118   }
00119 
00120   if(buttonMask!=0) {
00121     for(i=0;i<(last_pen_down?1:5);i++) {
00122       touchscreen_sequence[i].x=x;
00123       touchscreen_sequence[i].y=y;
00124       touchscreen_sequence[i].pressure=1;
00125     }
00126     touchscreen_sequence[i].x=-1;
00127     rfbLog ( " pointer event @ x: %d, y:%d (down) \n", x, y);
00128     if (uinput_fd_mouse && uinput) {
00129       ptr_abs(uinput_fd_mouse,x,y);
00130       button_click(uinput_fd_mouse,1);
00131       dev_uinput_sync(uinput_fd_mouse);
00132       rfbLog (" => sent to uinput. \n");
00133 
00134     } else {
00135       emit_touchscreen_sequence(touchscreen_sequence);
00136     };
00137   } else {
00138     if(last_pen_down) {
00139       touchscreen_sequence[0].x=x;
00140       touchscreen_sequence[0].y=y;
00141       touchscreen_sequence[0].pressure=0;
00142       touchscreen_sequence[1].x=-1;
00143 
00144       rfbLog (" pointer event @ x: %d, y:%d (up) \n", x, y);
00145       if (uinput_fd_mouse && uinput) {
00146         ptr_abs(uinput_fd_mouse,x,y);
00147         button_click(uinput_fd_mouse,0);
00148         dev_uinput_sync(uinput_fd_mouse);
00149         rfbLog (" => sent to uinput. \n");
00150 
00151       } else {
00152         emit_touchscreen_sequence(touchscreen_sequence);
00153       };
00154     };
00155   };
00156   last_pen_down=(buttonMask!=0);
00157   return;
00158 }
00159 
00160 
00164 void keyevent(rfbBool down,rfbKeySym key,rfbClientPtr cl)
00165 {
00166   unsigned int scancode;
00167 
00168   scancode = keysym2scancode(kbd_layout, key);
00169   TRACE2 (" [I]: KeyEvent: Key %#x - %d \n",key,down);
00170   TRACE1 ("       => Scancode: %#x \n",scancode);
00171   emit_scancode(scancode, down);
00172   return;
00173 }
00174 
00178 void clientleft(rfbClientPtr cl) { free(cl->clientData); }
00179 
00183 static enum rfbNewClientAction newclient(rfbClientPtr cl) {
00184   cl->clientData = (void*)calloc(sizeof(struct ClientData),1);
00185   cl->clientGoneHook = clientleft;
00186 
00187   return RFB_CLIENT_ACCEPT;
00188 }
00189  static int getFileTransferPermission (rfbClientPtr cl) { return TRUE; }
00193 
00194 void print_usage(char *arg0) {
00195   printf("Usage: %s [options] \n", arg0);
00196 #ifdef DEBUG
00197   printf(" -debug\n");
00198 #endif
00199   printf("\n");
00200   printf(" -cw                    rotate  90 \n");
00201   printf(" -cw -cw                rotate 180 \n");
00202   printf(" -cw -cw -cw | -ccw     rotate 270 \n");
00203   printf("\n");
00204   printf(" -keymap <map>          name of the keymap to be used for event \n");
00205   printf("                        translation (default: %s) \n", DEFAULT_KBD_LAYOUT);
00206   printf("\n");
00207   printf("# ***  \n");
00208   printf("# * libVNCServer options (excerpt) \n");
00209   printf("# **** \n");
00210   printf(" -rfbport port          TCP port for RFB protocol\n");
00211   printf(" -rfbwait time          max time in ms to wait for RFB client\n");
00212   printf(" -rfbauth passwd-file   use authentication on RFB protocol\n");
00213   printf("                        (use 'storepasswd' to create a password file)\n");
00214 //  printf(" -rfbversion 3.x        Set the version of the RFB we choose to advertise\n");
00215 #ifndef HAVE_STRUCT_RFBSCREEN_PERMITFILETRANSFER
00216 //  printf(" -permitfiletransfer    permit file transfer support\n");
00218 #endif
00219   printf(" -passwd plain-password use authentication\n");
00220   printf("                        (use plain-password as password, USE AT YOUR RISK)\n");
00221   printf(" -deferupdate time      time in ms to defer updates (default 40)\n");
00222   printf(" -deferptrupdate time   time in ms to defer pointer updates (default none)\n");
00223 //  printf(" -desktop name          VNC desktop name\n");
00224 //  printf(" -alwaysshared          always treat new clients as shared\n");
00225 //  printf(" -nevershared           never treat new clients as shared\n");
00226 //  printf(" -dontdisconnect        don't disconnect existing clients when a new non-shared\n");
00227 //  printf("                        connection comes in (refuse new connection instead)\n");
00228 //  printf(" -httpdir dir-path      enable http server using dir-path home\n");
00229 //  printf(" -httpport portnum      use portnum for http connection\n");
00230 //  printf(" -enablehttpproxy       enable http proxy support\n");
00231   printf(" -progressive height    enable progressive updating for slow links\n");
00232   printf("\n");
00233 #ifndef HAVE_STRUCT_RFBSCREEN_PERMITFILETRANSFER
00234   printf(" -disablefiletransfer   disable file transfer (tight)\n");
00235   printf(" -ftproot string        set ftp root (tight)\n");
00236   printf("\n");
00237 #endif
00238   printf("# ***  \n");
00239   printf("# * additional info (build option(s) used) \n");
00240   printf("# **** \n");
00241 #ifndef DEBUG
00242   printf(" [I]: release build (-debug not supported)\n");
00243 #endif
00244 #ifdef HTTP
00245   printf(" [I]: http connections to port 5800 supported\n");
00246 #else
00247   printf(" [I]: http connections NOT supported \n");
00248 #endif
00249 
00250 #ifndef HAVE_STRUCT_RFBSCREEN_PERMITFILETRANSFER
00251   printf(" [I]: ultra-file transfer NOT supported \n");
00252 #else
00253   printf(" [I]: ultra-file transfer supported \n");
00254 #endif
00255 
00256 #ifndef HAVE_RFBREGISTERTIGHTVNCFILETRANSFEREXTENSION
00257   printf(" [I]: tight-file transfer NOT supported \n ");
00258 #else
00259   printf(" [I]: tight-file transfer supported \n ");
00260 #endif
00261   printf("\n");
00262 } // end of [print_usage]
00263 
00264 /*****************************************************************************/
00265 /*                            Main program                                   */
00266 /*****************************************************************************/
00267 int main(int argc,char **argv) {
00268   int
00269     i,fbfd;
00270 
00271   rfbScreenInfoPtr
00272     rfbScreen;
00273 
00274   unsigned long int
00275     compareBufferSize = 0,
00276     remoteBufferSize  = 0,
00277     bytesPerPixel     = 0;
00278 
00279   char
00280     keyboard_layout[10] = DEFAULT_KBD_LAYOUT;
00281 
00282   div_t
00283     divresult;
00284 
00285   printf("%s (%s %s) \n",PACKAGE_STRING,__DATE__, __TIME__ );
00288  // ... process commandline arguments.
00289   for(i=1;i<argc;i++) {
00290     if (   !strcmp(argv[i],"-h")
00291         || !strcmp(argv[i],"-help")
00292         || !strcmp(argv[i],"--help")
00293         || !strcmp(argv[i],"-?") ) {
00294       print_usage(argv[0]);
00295       exit(1);
00296     } else if(!strcmp(argv[i],"-debug"))  debug  = 1;
00297       else if(!strcmp(argv[i],"-keymap")) {
00298         if (argc > i++) {
00299           if ( strlen(argv[i]) < sizeof(keyboard_layout) ) {
00300             strcpy(&keyboard_layout[0], argv[i]);
00301           } else {
00302             printf(" [W] invalid argument for -keymap: %s => I will use [%s] \n",argv[i], keyboard_layout[0]);
00303           } // end of [if]
00304         } // end of [if]
00305       } // end of [if]
00306       else if(!strcmp(argv[i],"-ccw"))    rotate = 3;
00307       else if(!strcmp(argv[i],"-cw")) {
00308         if (++rotate > 3) {
00309           fprintf(stderr," [F]: only 0,90,180 & 270 rotations are supported\n");
00310           fprintf(stderr,"      => 0 will be used. \n ");
00311           rotate = 0;
00312         }
00313     }
00314 } // end of [for]
00315 
00316 #ifdef DEBUG
00317   if (debug) rfbLogEnable(1);
00318     else rfbLogEnable(0);
00319 #else
00320   rfbLogEnable(0);
00321 #endif
00322 
00323 // open framebuffer
00324   TRACE(" [I]: open framebuffer (/dev/fb0) ... \n");
00325 
00326   if((fbfd=open("/dev/fb0",O_RDONLY))<0) {
00327     perror("Opening /dev/fb0");
00328     return(-1);
00329   }
00330 
00331    TRACE(" [I]: requesting fb info via ioctl ...\n");
00332 
00333 // Request the framebuffer's screen info
00334   if (ioctl(fbfd,FBIOGET_VSCREENINFO,&vscrinfo) <0 ) {
00335     close(fbfd);
00336     perror("ioctl(FBIOGET_VSCREENINFO)");
00337     return(-1);
00338   }
00339 
00340   TRACE1(" visible resolution [x]: %d px \n", vscrinfo.xres );
00341   TRACE1("                    [y]: %d px \n", vscrinfo.yres );
00342   TRACE1(" virtual resolution [x]: %d px \n", vscrinfo.xres_virtual);
00343   TRACE1("                    [y]: %d px \n", vscrinfo.yres_virtual);
00344   TRACE (" offset from virtual to visible resolution \n");
00345   TRACE1("                    [x]: %d px \n", vscrinfo.xoffset);
00346   TRACE1("                    [y]: %d px \n", vscrinfo.yoffset);
00347   TRACE1(" grayscale (0 = false) : %d \n", vscrinfo.grayscale);
00348   TRACE1(" bits per pixel        : %d \n", vscrinfo.bits_per_pixel);
00349   if (!vscrinfo.grayscale) {
00350     TRACE2 (" color offsets     red : %2d length : %d \n", vscrinfo.red.offset, vscrinfo.red.length);
00351     TRACE2 ("                 green : %2d length : %d \n", vscrinfo.green.offset, vscrinfo.green.length);
00352     TRACE2 ("                  blue : %2d length : %d \n", vscrinfo.blue.offset, vscrinfo.blue.length);
00353   }
00354   if (!vscrinfo.nonstd) {
00355     TRACE (" standard pixel format \n");
00356   } else {
00357     TRACE (" [W]: using non standard pixel format \n");
00358   };
00359 
00360   if (vscrinfo.height > 0) TRACE1 (" height of picture     : %d mm\n", vscrinfo.height);
00361   if (vscrinfo.width  > 0) TRACE1 (" width                 : %d mm\n", vscrinfo.width);
00362 
00363 // adjust fb-dimensions accordnig to our needs
00364   if ((rotate == 1) ||  // for 90 & 270 degree we have to swap
00365       (rotate == 3)) {
00366       vscrinfo.xres = vscrinfo.xres + vscrinfo.yres;  // two reason for doing it "in place"
00367       vscrinfo.yres = vscrinfo.xres - vscrinfo.yres;  //  1. just for the fun of it
00368       vscrinfo.xres = vscrinfo.xres - vscrinfo.yres;  //  2. I do not care about the type/size of the vars
00369       TRACE (" [I]: swap'ed width vs. height to adjust dimensions \n");
00370   };
00371 
00372 // ... allocate memory for compare-buffer
00373   divresult = div(vscrinfo.bits_per_pixel,8); // note: bytesPerPixel = ceil(vscrinfo.bits_per_pixel/8); <= soemtimes this doesn't work (don't know why)
00374 
00375   if (divresult.rem != 0) bytesPerPixel = divresult.quot + 1;
00376     else bytesPerPixel = divresult.quot;
00377 
00378   compareBufferSize = vscrinfo.xres * vscrinfo.yres * bytesPerPixel;
00379   TRACE2(" [I]: %d bytes per pixel => I will allocate %d bytes \n", bytesPerPixel, compareBufferSize);
00380   TRACE (" [I]: allocate memory for compare-buffer ... \n");
00381 
00382   if((cmpfbbuf=(unsigned short int *)malloc(compareBufferSize)) == NULL) {
00383     close(fbfd);
00384     perror("malloc");
00385     return(-1);
00386   }
00387   memset(cmpfbbuf,0x00,compareBufferSize);
00388 
00389 // ... allocate and initialize the remote framebuffer
00390   TRACE (" [I]: allocate and initialize the remote framebuffer ... \n");
00391 
00392   remoteBufferSize = vscrinfo.xres * vscrinfo.yres * bytesPerPixel;
00393   if((rfbbuf=(unsigned short int *)malloc(remoteBufferSize))==NULL) {
00394     close(fbfd);
00395     free(cmpfbbuf);
00396     perror("malloc");
00397     return(-1);
00398   }
00399 // ... mmap the fb
00400   TRACE (" [I]: mapping fb to memory ... \n");
00401   if((fbbuf=(unsigned short int *)mmap(0,                 /* */
00402                                        compareBufferSize, /* */
00403                                        PROT_READ,         /* Data can be read          */
00404                                        MAP_SHARED,        /* changes are shared        */
00405                                        fbfd,              /* file handle (framebuffer) */
00406                                        0)                 /* offset                    */
00407 /*
00408  * WARNING: ... it might be that we need to specify/calculate an offset in case
00409  *              virtual resolution != visible resolution.
00410  */
00411       ) <0
00412     ) {
00413     close(fbfd);
00414     free(cmpfbbuf);
00415     free(rfbbuf);
00416     perror("mmap");
00417     return(-1);
00418   }
00419 
00420 /*
00421  * check if we can use uinput for Keyboard-Events
00422  */
00423   if (uinput) {
00424     TRACE (" [I]: check if we have uinput device nodes ... \n");
00425     uinput_fd_kbd   = dev_uinput_init_kbd(UINPUT_KBD_NAME);
00426     uinput_fd_mouse = dev_uinput_init_mouse(UINPUT_TS_NAME);
00427 
00428     if ((uinput_fd_kbd > 0) &
00429         (uinput_fd_mouse > 0)) {
00430       TRACE1("    => Keyboard OK (fd=%d). \n",uinput_fd_kbd);
00431       TRACE1("    => Mouse OK    (fd=%d). \n",uinput_fd_mouse);
00432 
00433       kbd_layout = init_keyboard_layout(keyboard_layout);
00434       if (kbd_layout) {
00435         TRACE1(" [I]: keyboard [%s] loaded \n", keyboard_layout);
00436       } else {
00437         TRACE1(" [W]: keyboard layout [%s] NOT loaded \n", keyboard_layout);
00438         dev_uinput_close(uinput_fd_kbd);
00439         uinput_fd_kbd = 0;
00440         TRACE (" [W]: ... => keyboard event source released. \n")
00441       } // end of [if]
00442     } else {
00443       uinput = 0;
00444       TRACE(" [W]: => unable to initialize uinput \n ");
00445     }
00446   }
00447 
00448 /*
00449  * initialize remote fb screen structure
00450  */
00451   rfbScreen = rfbGetScreen(&argc,    // &argc
00452               argv,                  // argv
00453               vscrinfo.xres,         // width
00454               vscrinfo.yres,         // height
00455               5,                     // bits per sample
00456               2,                     // samles per pixel
00457               bytesPerPixel);        // bytes per pixel
00458 
00459 #ifdef HAVE_RFBSETPROTOCOLVERSION
00460 #ifdef HAVE_STRUCT_RFBSCREEN_PERMITFILETRANSFER
00461 /*
00462  * UltraVNC 1.0.2 win32
00463  * uses the rfb version to identify that it is talking
00464  * to an UltraVNC compatible server.
00465  * This is technically wrong to assume this as the rfb
00466  * version identifies the rfb handshake and not the
00467  * supported messages ...
00468  */
00469 rfbSetProtocolVersion(rfbScreen, 3, 6);
00470 #endif
00471 #endif
00472 
00473   rfbScreen -> desktopName     = DESKTOP_NAME;
00474   rfbScreen -> frameBuffer     = (char *)rfbbuf;
00475   rfbScreen -> alwaysShared    = TRUE;
00476   rfbScreen -> serverFormat.bitsPerPixel  = vscrinfo.bits_per_pixel;
00477 
00478 // set callback functions
00479   rfbScreen -> ptrAddEvent     = ptrevent;  // pointer
00480   rfbScreen -> kbdAddEvent     = keyevent;  // keyboard
00481   rfbScreen -> newClientHook   = newclient; // new client
00482 // file transfer permission
00483 #ifdef HAVE_STRUCT_RFBSCREEN_GETFILETRANSFERPERMISSION
00484   rfbScreen -> getFileTransferPermission = getFileTransferPermission;
00485 #endif
00486 #ifdef HAVE_STRUCT_RFBSCREEN_PERMITFILETRANSFER
00487   rfbScreen -> permitFileTransfer = TRUE;
00488 #endif
00489 #ifdef HAVE_RFBREGISTERTIGHTVNCFILETRANSFEREXTENSION
00490   rfbRegisterTightVNCFileTransferExtension();   // register TightVNC file transfer protocol extension(s)
00491 #endif
00492 /* if TRUE, an ignoring signal handler is installed for SIGPIPE  */
00493   rfbScreen -> ignoreSIGPIPE   = TRUE;
00494 /* if not zero, only a slice of this height is processed every time
00495  * an update should be sent. This should make working on a slow
00496  * link more interactive. */
00497   rfbScreen -> progressiveSliceHeight = 0;
00498 
00499   if (!vscrinfo.grayscale) {
00500     rfbScreen -> serverFormat.redMax      = pow(2,vscrinfo.red.length)  -1;
00501     rfbScreen -> serverFormat.greenMax    = pow(2,vscrinfo.green.length)-1;
00502     rfbScreen -> serverFormat.blueMax     = pow(2,vscrinfo.blue.length) -1;
00503     rfbScreen -> serverFormat.redShift    = vscrinfo.red.offset;
00504     rfbScreen -> serverFormat.greenShift  = vscrinfo.green.offset;
00505     rfbScreen -> serverFormat.blueShift   = vscrinfo.blue.offset;
00506 
00507   } // end of [if]
00508 #ifdef HTTP
00509   rfbScreen->httpDir         = HTTP;
00510 #endif
00511 
00512   rfbInitServer(rfbScreen);           // initialize the remote framebuffer & start VNC-Server
00513   rfbRunEventLoop(rfbScreen,-1,TRUE); // non-blocking event loop; a background thread is started
00514 
00515   while (1) {
00516     if (rfbScreen -> clientHead != NULL) { // only update the fb if there are connected clients
00517       if (notEqual(fbbuf,cmpfbbuf,compareBufferSize) == 1) { // check if the screen has changed
00518         if (rotate == 0) { rotate0  (rfbScreen,fbbuf,cmpfbbuf); }; // end of [if]
00519         if (rotate == 1) { rotate90 (rfbScreen,fbbuf,cmpfbbuf); }; // end of [if]
00520         if (rotate == 2) { rotate180(rfbScreen,fbbuf,cmpfbbuf); }; // end of [if]
00521         if (rotate == 3) { rotate270(rfbScreen,fbbuf,cmpfbbuf); }; // end of [if]
00522         usleep(5000);
00523       } else { // no screen changes => no update needed
00524         usleep(50000);
00525       }; // end of [if]
00526     } else { // no clients connected @ the moment
00527       sleep(1);
00528     };
00529   }; // end of [while]
00530 
00531   rfbScreenCleanup(rfbScreen);
00532 
00533   free(fbbuf);
00534   free(cmpfbbuf);
00535 
00536   if (uinput_fd_kbd) dev_uinput_close(uinput_fd_kbd);
00537   if (uinput_fd_mouse) dev_uinput_close(uinput_fd_mouse);
00538 
00539   exit (0);
00540 }