Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members   Related Pages  

MobileInterface.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr                                                                       
00003  *cr            (C) Copyright 1995-2011 The Board of Trustees of the           
00004  *cr                        University of Illinois                       
00005  *cr                         All Rights Reserved                        
00006  *cr                                                                   
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  * RCS INFORMATION:
00011  *
00012  *      $RCSfile: MobileInterface.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.36 $       $Date: 2012/03/20 15:44:37 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  *
00019  * The Mobile UI object, which maintains the current state of connected
00020  * smartmobile/tablet clients.
00021  *
00022  ***************************************************************************
00023  * TODO list:
00024  *      Pretty much everything          
00025  *
00026  ***************************************************************************/
00027 
00028 /* socket stuff */
00029 #if defined(_MSC_VER)
00030 #include <winsock2.h>
00031 #else
00032 #include <stdio.h>
00033 #include <arpa/inet.h>
00034 #include <fcntl.h>
00035 #include <sys/types.h>
00036 #include <unistd.h>
00037 #include <sys/socket.h>
00038 #include <time.h>
00039 #include <netinet/in.h>
00040 #endif
00041 
00042 #include "MobileInterface.h"
00043 #include "DisplayDevice.h"
00044 #include "TextEvent.h"
00045 #include "CommandQueue.h"
00046 #include "Inform.h"
00047 #include "PickList.h"
00048 #include "Animation.h"
00049 #include "VMDApp.h"
00050 #include "math.h"
00051 #include "stdlib.h" // for getenv(), abs() etc.
00052 
00053 // ------------------------------------------------------------------------
00054 // maximum API version that we understand
00055 #define CURRENTAPIVERSION       9 
00056 
00057 // packet contains:
00058 #define PACKET_ORIENT       1    //   device orientation (gyro, accel, etc)
00059 #define PACKET_TOUCH        2    //   touchpad events (up, down, move, etc)
00060 #define PACKET_HEARTBEAT    3    //   heartbeat.. likely every second
00061 #define PACKET_CONNECT      4    //   information about new connection
00062 #define PACKET_DISCONNECT   5    //   notice that a device has disconnected
00063 #define PACKET_BUTTON       6    //   button state has changed.
00064 
00065 // what type of event are we getting from client
00066 #define EVENT_NON_TOUCH     -1
00067 #define EVENT_TOUCH_DOWN     0
00068 #define EVENT_TOUCH_UP       1
00069 #define EVENT_TOUCH_MOVE     2
00070 #define EVENT_TOUCH_SOMEUP   5
00071 #define EVENT_TOUCH_SOMEDOWN 6
00072 #define EVENT_COMMAND        7
00073 
00074 // what type of event are we sending to mobile client
00075 #define SEND_HEARTBEAT          0
00076 #define SEND_ADDCLIENT          1
00077 #define SEND_REMOVECLIENT       2
00078 #define SEND_SETACTIVECLIENT    3
00079 #define SEND_SETMODE            4
00080 #define SEND_MESSAGE            5
00081 
00082 // ------------------------------------------------------------------------
00083 /* Only works with aligned 4-byte quantities, will cause a bus error */
00084 /* on some platforms if used on unaligned data.                      */
00085 static void swap4_aligned(void *v, long ndata) {
00086   int *data = (int *) v;
00087   long i;
00088   int *N;
00089   for (i=0; i<ndata; i++) {
00090     N = data + i;
00091     *N=(((*N>>24)&0xff) | ((*N&0xff)<<24) |
00092         ((*N>>8)&0xff00) | ((*N&0xff00)<<8));
00093   }
00094 }
00095 
00096 // ------------------------------------------------------------------------
00097 void Mobile::prepareSendBuffer(const int eventType)
00098 {
00099   memset(statusSendBuffer, 0, sizeof(statusSendBuffer));
00100   int caretLoc = 0;
00101   *(statusSendBuffer + caretLoc) = 1;    // endianism 
00102   caretLoc += sizeof(int);
00103   *(statusSendBuffer + caretLoc) = CURRENTAPIVERSION;    // version
00104   caretLoc += sizeof(int);
00105 
00106   // current mode in VMD (off, move, animate, tracker, etc)
00107   *(statusSendBuffer + caretLoc) = moveMode;    // off/move/animate/etc
00108   caretLoc += sizeof(int);
00109 
00110   // event code for what we are sending
00111   *(statusSendBuffer + caretLoc) = eventType;    
00112   caretLoc += sizeof(int);
00113 
00114   // we are putting a placeholder in.. at position 16, where we will insert
00115   // whether or not this specific user is active.
00116   caretLoc += 4;
00117 
00118   *(statusSendBuffer + caretLoc) = clientNick.num();    // how many connections?
00119   caretLoc += sizeof(int);
00120 //  fprintf(stderr, "num connections: %d\n", clientNick.num());
00121 
00122   // names of who is connected, in control
00123   for (int i=0; i<clientNick.num();i++)
00124   {
00125      int strLength = strlen((const char *)(*(clientNick)[i]));
00126      *(statusSendBuffer + caretLoc) = strLength;
00127      caretLoc += sizeof(int);
00128 //     fprintf(stderr, "nick is '%s'\n", (const char *)(*(clientNick)[i]));
00129      memcpy((statusSendBuffer + caretLoc), (const char *)(*(clientNick)[i]), strLength);
00130      caretLoc += strLength;  
00131 
00132 //     fprintf(stderr, "clientactive is '%d'\n", (clientActive[i] ? 1 : 0));
00133      *(statusSendBuffer + caretLoc) = (clientActive[i] ? 1 : 0);    // Is this nick active?
00134      caretLoc += sizeof(int);
00135   }
00136    
00137 // xxx: could send different configurations to different clients (client
00138 // in control might get different setup)
00139 // we need to send:
00140   // desired button states
00141 
00142 
00143    // caretLoc is also the length of the useful data in the packet
00144    statusSendBufferLength = caretLoc;
00145 }
00146 
00147 
00148 // ------------------------------------------------------------------------
00149 
00150 typedef struct {
00151   /* socket management data */
00152   char buffer[1024];
00153   struct sockaddr_in sockaddr;
00154 #if defined(_MSC_VER)
00155   SOCKET sockfd;
00156 #else
00157   int sockfd;
00158 #endif
00159   int fromlen;
00160 
00161   /* mobile state vector */
00162   int seqnum;
00163   int buttons;
00164   float rx;
00165   float ry;
00166   float rz;
00167   float tx;
00168   float ty;
00169   float tz;
00170   int padaction;
00171   int touchcnt;
00172   int upid;
00173   int touchid[16];
00174   float padx[16];
00175   float pady[16];
00176   float rotmatrix[9];
00177 } mobilehandle;
00178 
00179 
00180 // ------------------------------------------------------------------------
00181 static void * mobile_listener_create(int port) {
00182   mobilehandle *ph = (mobilehandle *) calloc(1, sizeof(mobilehandle));
00183   if (ph == NULL)
00184     return NULL;
00185 
00186 #if defined(_MSC_VER)
00187   // ensure that winsock is initialized
00188   WSADATA wsaData;
00189   if (WSAStartup(MAKEWORD(2,2), &wsaData) != NO_ERROR)
00190     return NULL;
00191 #endif
00192 
00193   if ((ph->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
00194     perror("socket: ");
00195     free(ph);
00196     return NULL;
00197   }
00198 
00199   /* make socket non-blocking */
00200 #if defined(_MSC_VER)
00201   u_long nonblock = 1;
00202   ioctlsocket(ph->sockfd, FIONBIO, &nonblock);
00203 #else
00204   int sockflags;
00205   sockflags = fcntl(ph->sockfd, F_GETFL, 0);
00206   fcntl(ph->sockfd, F_SETFL, sockflags | O_NONBLOCK);
00207 #endif
00208 
00209   memset(&ph->sockaddr, 0, sizeof(ph->sockaddr));
00210   ph->sockaddr.sin_family      = AF_INET;
00211   ph->sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
00212   ph->sockaddr.sin_port        = htons(port);
00213 
00214   if (bind(ph->sockfd, (struct sockaddr *)&ph->sockaddr, sizeof(sockaddr)) < 0) {
00215     perror("bind: ");
00216     free(ph);
00217     return NULL;
00218   }
00219 
00220   return ph;
00221 }
00222 
00223 // ------------------------------------------------------------------------
00224 static int mobile_listener_poll(void *voidhandle,
00225                         float &tx, float &ty, float &tz,
00226                         float &rx, float &ry, float &rz,
00227                         int &padaction, int &upid,
00228                         int &touchcnt, int *touchid,
00229                         float *padx, float *pady,
00230                         int &buttons, int &packtype, JString &incomingIP,
00231                         JString &currentNick, int &listenerPort, 
00232                         float &tranScal, float &rotScal, float &zoomScal,
00233                         JString &commandToSend) {
00234   mobilehandle *ph = (mobilehandle *) voidhandle;
00235 
00236   int offset = 0;
00237 
00238   memset(ph->buffer, 0, sizeof(ph->buffer));
00239 #if defined(_MSC_VER)
00240   int fromlen=sizeof(ph->sockaddr);
00241 #else
00242   socklen_t fromlen=sizeof(ph->sockaddr);
00243 #endif
00244   int packlen=0;
00245 
00246   packlen=recvfrom(ph->sockfd, ph->buffer, sizeof(ph->buffer), 0, (struct sockaddr *)&ph->sockaddr, &fromlen);
00247   /* no packets read */
00248   if (packlen < 1) {
00249     return 0;
00250   }
00251 
00252   /* we now have info.  Decode it */
00253   if (((int*)ph->buffer)[0] != 1) {
00254     swap4_aligned(ph->buffer, sizeof(ph->buffer) / 4);
00255   }
00256   if (((int*)ph->buffer)[0] != 1) {
00257     printf("Received unrecognized mobile packet...\n");
00258     return 0;
00259   }
00260 
00261   int endianism  = ((int*)ph->buffer)[offset++];     /* endianism.  Should be 1  */
00262   int apiversion = ((int*)ph->buffer)[offset++];     /* API version */
00263 
00264   /* drop old format packets, or packets with incorrect protocol,   */
00265   /* corruption, or that aren't formatted correctly for some reason */
00266   if (endianism != 1 || (apiversion < 7 || apiversion > CURRENTAPIVERSION)) {
00267     msgWarn << "Dropped incoming mobile input packet from "
00268             << inet_ntoa((ph->sockaddr).sin_addr)
00269             << ", version: " << apiversion << sendmsg;
00270     return 0;
00271   }
00272 
00273   // there are now 16 bytes of data for the nickname
00274   char nickName[17];
00275   memcpy(nickName, ph->buffer+(offset*sizeof(int)), 16);  // this might not be null terminated
00276   nickName[16] = 0;   // so we'll put a null in the last element of the char*
00277   currentNick = nickName;
00278 //fprintf(stderr, "currentNick is %s\n", (const char *)currentNick);
00279   offset += 4;
00280 
00281   if (apiversion >= 9) {
00282     listenerPort = ((int*)ph->buffer)[offset++];     /* listener port on the client*/
00283     rotScal = ((float*)ph->buffer)[offset++];     /* scale factor for rotate */
00284     zoomScal = ((float*)ph->buffer)[offset++];     /* scale factor for zoom */
00285     tranScal = ((float*)ph->buffer)[offset++];     /* scale factor for translate*/
00286   } else {
00287     listenerPort = 4141;  /* default */
00288   }
00289 
00290   packtype   = ((int*)ph->buffer)[offset++];     /* payload description */
00291 //  if (packtype == PACKET_HEARTBEAT) { fprintf(stderr,"{HB}"); }
00292   ph->buttons    = ((int*)ph->buffer)[offset++];     /* button state */
00293   ph->seqnum     = ((int*)ph->buffer)[offset++];     /* sequence number */
00294 
00295 
00296   buttons = ph->buttons;
00297   incomingIP = inet_ntoa((ph->sockaddr).sin_addr);
00298 
00299   // at this point, lets check to see if we have a command that needs
00300   // to be send to the script side.
00301   if (packtype == EVENT_COMMAND) {
00302      // xxx: extend to allow data parameters to be retrieved from client.
00303      // 'buttons' and 'ph->seqnum' have been set.
00304      // 'buttons' stores the type of message that it is
00305      // and seqnum just stores the sequence number.  No big deal
00306      // now, let's read in any command parameters that have been sent
00307      int msgSize = ((int*)ph->buffer)[offset++];     /* msg length */
00308 
00309 //fprintf(stderr, "packtype: %d, buttons: %d, seq: %d, msg size is %d\n", 
00310 //                      packtype, buttons, ph->seqnum, msgSize);
00311      if (msgSize > 0) {
00312         char *tmpmsg = new char[msgSize+1];
00313         memcpy(tmpmsg, ph->buffer+(offset*sizeof(int)), msgSize);  
00314         tmpmsg[msgSize] = 0;           // can't assume it was null terminated
00315 
00316         commandToSend = tmpmsg; 
00317         delete tmpmsg;
00318      } else {
00319         commandToSend = ""; 
00320      }
00321 
00322      return 1;
00323   }
00324 
00325 
00326   padaction = EVENT_NON_TOUCH;
00327 
00328   // check to see if we need to go farther
00329   //if (packtype != PACKET_ORIENT && packtype != PACKET_TOUCH)
00330 //  {
00331 //    return 1;
00332 //  }
00333 
00334   // clear previous state from handle before decoding incoming packet
00335   ph->rx = 0;
00336   ph->ry = 0;
00337   ph->rz = 0;
00338   ph->tx = 0;
00339   ph->ty = 0;
00340   ph->tz = 0;
00341   ph->padaction = EVENT_NON_TOUCH;
00342   ph->upid = 0;
00343   memset(ph->touchid, 0, sizeof(ph->touchid));
00344   memset(ph->padx, 0, sizeof(ph->padx));
00345   memset(ph->pady, 0, sizeof(ph->pady));
00346   memset(ph->rotmatrix, 0, sizeof(9*sizeof(float)));
00347 
00348   // decode incoming packet based on packet type
00349   int i;
00350 
00351   switch (packtype) {
00352     case PACKET_ORIENT:
00353       // Android sensor/orientation packet
00354       // r[0]: Azimuth, rotation around the Z axis (0<=azimuth<360).
00355       //       0 = North, 90 = East, 180 = South, 270 = West
00356       // r[1]: Pitch, rotation around X axis (-180<=pitch<=180),
00357       //       with positive values when the z-axis moves toward the y-axis.
00358       // r[2]: Roll, rotation around Y axis (-90<=roll<=90),
00359       //       with positive values when the z-axis moves toward the x-axis.
00360       ph->rz         = ((float*)ph->buffer)[offset  ]; /* orientation 0 */
00361       ph->rx         = ((float*)ph->buffer)[offset+1]; /* orientation 1 */
00362       ph->ry         = ((float*)ph->buffer)[offset+2]; /* orientation 2 */
00363       ph->tx         = ((float*)ph->buffer)[offset+3]; /* accel 0 */
00364       ph->ty         = ((float*)ph->buffer)[offset+4]; /* accel 1 */
00365       ph->tz         = ((float*)ph->buffer)[offset+5]; /* accel 2 */
00366 
00367       /* 3x3 rotation matrix stored as 9 floats */
00368       for (i=0; i<9; i++)
00369         ph->rotmatrix[i] = ((float*)ph->buffer)[offset+6+i];
00370       break;
00371 
00372     case PACKET_TOUCH:  case PACKET_HEARTBEAT:
00373       float xdpi = ((float*)ph->buffer)[offset];      /* X dots-per-inch */
00374       float ydpi = ((float*)ph->buffer)[offset+1];      /* Y dots-per-inch */
00375 //      int xsz    = ((int*)ph->buffer)[11];        /* screen size in pixels */
00376 //      int ysz    = ((int*)ph->buffer)[12];        /* screen size in pixels */
00377       float xinvdpi = 1.0f / xdpi;
00378       float yinvdpi = 1.0f / ydpi;
00379 
00380       /* For single touch, Actions are basically:  0:down, 2: move, 1: up.  */
00381       /* for multi touch, the actions can indicate, by masking, which pointer
00382          is being manipulated */
00383       ph->padaction = ((int*) ph->buffer)[offset+4];   /* action */
00384       ph->upid      = ((int*) ph->buffer)[offset+5];   /* UP, pointer id */
00385 
00386       if (ph->padaction == 1) {
00387          ph->touchcnt  = touchcnt = 0;
00388       } else {
00389          ph->touchcnt  = ((int*) ph->buffer)[offset+6];   /* number of touches */
00390          touchcnt = ph->touchcnt;
00391 
00392          for (int i=0; i<ph->touchcnt; i++) {
00393            float px, py;
00394            int ptrid;
00395            ptrid = ((int*) ph->buffer)[offset+7+3*i];     /* pointer id */
00396            px  = ((float*) ph->buffer)[offset+8+3*i];     /* X pixel */
00397            py  = ((float*) ph->buffer)[offset+9+3*i];     /* Y pixel */
00398 
00399 //        printf("PID:%2d, X:%4.3f, Y:%4.3f, ", ptrid, px, py);
00400 
00401            /* scale coords to be in inches rather than pixels */
00402            ph->padx[i] = px * xinvdpi;
00403            ph->pady[i] = py * yinvdpi;
00404          }
00405       }
00406 //      printf("\n");
00407 
00408       break;
00409   } // end switch (packtype)
00410 
00411 
00412   if (packtype == PACKET_ORIENT) {
00413     rx = -ph->rx;
00414     ry = -(ph->rz-180); // Renormalize Android X angle from 0:360deg to -180:180
00415     rz =  ph->ry;
00416     tx = 0.0;
00417     ty = 0.0;
00418     tz = 0.0;
00419   }
00420 
00421   if (packtype == PACKET_TOUCH) {
00422     padaction = ph->padaction;
00423     upid = ph->upid;
00424 
00425     for (int i=0; i<ph->touchcnt; i++) {
00426       padx[i] = ph->padx[i];
00427       pady[i] = ph->pady[i];
00428     }
00429   }
00430 
00431 #if 1
00432   // get absolute values of axis forces for use in
00433   // null region processing and min/max comparison tests
00434   float t_null_region = 0.01f;
00435   float r_null_region = 10.0f;
00436   float atx = fabsf(tx);
00437   float aty = fabsf(ty);
00438   float atz = fabsf(tz);
00439   float arx = fabsf(rx);
00440   float ary = fabsf(ry);
00441   float arz = fabsf(rz);
00442 
00443   // perform null region processing
00444   if (atx > t_null_region) {
00445     tx = ((tx > 0) ? (tx - t_null_region) : (tx + t_null_region));
00446   } else {
00447     tx = 0;
00448   }
00449   if (aty > t_null_region) {
00450     ty = ((ty > 0) ? (ty - t_null_region) : (ty + t_null_region));
00451   } else {
00452     ty = 0;
00453   }
00454   if (atz > t_null_region) {
00455     tz = ((tz > 0) ? (tz - t_null_region) : (tz + t_null_region));
00456   } else {
00457     tz = 0;
00458   }
00459   if (arx > r_null_region) {
00460     rx = ((rx > 0) ? (rx - r_null_region) : (rx + r_null_region));
00461   } else {
00462     rx = 0;
00463   }
00464   if (ary > r_null_region) {
00465     ry = ((ry > 0) ? (ry - r_null_region) : (ry + r_null_region));
00466   } else {
00467     ry = 0;
00468   }
00469   if (arz > r_null_region) {
00470     rz = ((rz > 0) ? (rz - r_null_region) : (rz + r_null_region));
00471   } else {
00472     rz = 0;
00473   }
00474 #endif
00475 
00476   return 1;
00477 } // end of mobile_listener_poll
00478 
00479 // ------------------------------------------------------------------------
00480 
00481 static int mobile_listener_destroy(void *voidhandle) {
00482   mobilehandle *ph = (mobilehandle *) voidhandle;
00483 
00484 #if defined(_MSC_VER)
00485   closesocket(ph->sockfd); /* close the socket */
00486 #else
00487   close(ph->sockfd); /* close the socket */
00488 #endif
00489   free(ph);
00490 
00491   // all of our clients are, by definition, gone too
00492 
00493 
00494 
00495   return 0;
00496 }
00497 
00498 // ------------------------------------------------------------------------
00499 
00500 // constructor
00501 Mobile::Mobile(VMDApp *vmdapp)
00502         : UIObject(vmdapp) {
00503   mobile = NULL;
00504   port = 3141; // default UDP port to use
00505 
00506   packtimer = wkf_timer_create();
00507   wkf_timer_start(packtimer);
00508 
00509   statustimer = wkf_timer_create();
00510   wkf_timer_start(statustimer);
00511 
00512   // how often should we send the status to the clients
00513   statusSendSeconds = 5.0f;
00514 
00515   touchinprogress = 0;
00516   touchcount = 0;
00517   touchmode = ROTATE;
00518   touchscale = 1.0;
00519   touchscalestartdist = 0;
00520   touchrotstartangle = 0;
00521   touchdeltaX = 0;
00522   touchdeltaY = 0;
00523   buttonDown = 0;
00524 
00525   tranScaling = 1.0;
00526   rotScaling = 1.0;
00527   zoomScaling = 1.0;
00528 
00529   reset();
00530 }
00531 
00532 // ------------------------------------------------------------------------
00533 
00534 // destructor
00535 Mobile::~Mobile(void) {
00536   wkf_timer_destroy(packtimer);
00537   wkf_timer_destroy(statustimer);
00538 
00539   for (int i=0; i<clientNick.num();i++)
00540   {
00541     delete clientNick[i];
00542     delete clientIP[i];
00543   }
00544 
00545   // clean up the client list
00546 //  while (clientList.num() > 0)
00547 //  {
00548 //     MobileClientList *ptr = clientList.pop();
00549 //     delete ptr;
00550 //  }
00551 
00552   if (mobile != NULL)
00553     mobile_listener_destroy(mobile);
00554 }
00555 
00556 // ------------------------------------------------------------------------
00557 int send_dgram(const char *host_addr, int port, const char *buf, int buflen) {
00558   struct sockaddr_in addr;
00559   int sockfd;
00560 
00561   if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
00562     return -1;
00563   } 
00564 
00565   memset(&addr, 0, sizeof(addr));
00566   addr.sin_family = AF_INET;
00567   addr.sin_port = htons(port);
00568   addr.sin_addr.s_addr = inet_addr(host_addr);
00569 
00570   sendto(sockfd, buf, buflen, 0, (struct sockaddr *)&addr, sizeof(addr));
00571 
00572 #if defined(_MSC_VER)
00573   closesocket(sockfd);
00574 #else
00575   close(sockfd);
00576 #endif 
00577 
00578   return 0;
00579 }                     
00580 
00581 // ------------------------------------------------------------------------
00582 void Mobile::sendStatus(const int eventType)
00583 {
00584     prepareSendBuffer(eventType);
00585 //    int port = 4141;
00586     // loop over connected clients specifically, and send them the status
00587     for (int i=0; i< clientIP.num(); i++)
00588     {
00589 
00590        // we need to insert whether or not this specific user is active
00591        *(statusSendBuffer + 16) = (clientActive[i] ? 1 : 0);    // Is this nick active?
00592 //       fprintf(stderr, "Sending '%s': %d a message.\n", (const char*)*(clientIP[i]), clientListenerPort[i]);
00593        send_dgram((const char *)*(clientIP[i]), clientListenerPort[i], 
00594                                      statusSendBuffer, statusSendBufferLength);
00595     }
00596 
00597     // broadcast the same to everyone that might be listening
00598     // need to determine how widely we want to reasonably broadcast. For 
00599     // our local case, we are on the ks subnet, but wireless devices aren't.
00600     // not likely we should spam every device in illinois.edu, though.
00601     // so, we have to be more intelligent about it.
00602 
00603     // now that we've done everything, reset the timer.
00604     wkf_timer_start(statustimer);
00605 }
00606 
00607 
00608 // ------------------------------------------------------------------------
00609 void Mobile::checkAndSendStatus()
00610 {
00611   if (wkf_timer_timenow(statustimer) > statusSendSeconds) {
00612      sendStatus(SEND_HEARTBEAT);
00613   }
00614 }
00615 
00616 
00617 
00618 // ------------------------------------------------------------------------
00619 bool Mobile::isInControl(JString* nick, JString* ip, const int port, const int packtype)
00620 {
00621 //   fprintf(stderr, "isInControl.start: %s, %s\n", (const char*)*nick, (const char *)*ip);
00622   int i;
00623   for (i=0; i < clientNick.num(); i++) {
00624     if (*nick == *(clientNick[i]) && *ip == *(clientIP[i])) {
00625       // xxx: update the timer here?
00626       break;
00627     }
00628   }
00629 
00630   if (i < clientNick.num()) // we found them!
00631   {
00632     // was this a disconnect?
00633     if (packtype == PACKET_DISCONNECT) {
00634       removeClient(i);
00635       return false;
00636     }
00637     return clientActive[i];
00638   }  else {
00639     JString *tmpNick, *tmpIp;
00640     tmpNick = new JString(*nick);
00641     tmpIp = new JString(*ip);
00642 //   fprintf(stderr, "isInControl: Adding %s, %s\n", (const char *)*tmpNick, (const char *)*tmpIp);
00643     // we didn't find this particular IP/nick combination.  Let's add it.
00644     if (clientNick.num() == 0) // there weren't any before
00645     {
00646       addNewClient(tmpNick, tmpIp, port, true);  // make this client in control
00647       return true;
00648     } else {
00649       addNewClient(tmpNick, tmpIp, port, false);  // just a new client.. not in control
00650       return false;
00651     }
00652   }
00653   return false; // won't ever get here
00654 }
00655 // ------------------------------------------------------------------------
00657    
00658 // reset the Mobile to original settings
00659 void Mobile::reset(void) {
00660   // set the default motion mode and initialize button state
00661   move_mode(OFF);
00662 
00663   // set the maximum animate stride allowed to 20 by default
00664   set_max_stride(20);
00665 
00666   // set the default translation and rotation increments
00667   // these really need to be made user modifiable at runtime
00668   transInc = 1.0f / 25000.0f;
00669     rotInc = 1.0f /   200.0f;
00670   scaleInc = 1.0f / 25000.0f;
00671    animInc = 1.0f /     1.0f;
00672 }
00673 
00674 // ------------------------------------------------------------------------
00675 // update the display due to a command being executed.  Return whether
00676 // any action was taken on this command.
00677 // Arguments are the command type, command object, and the 
00678 // success of the command (T or F).
00679 int Mobile::act_on_command(int type, Command *cmd) {
00680   return FALSE; // we don't take any commands presently
00681 }
00682 
00683 // ------------------------------------------------------------------------
00684 // check for an event, and queue it if found.  Return TRUE if an event
00685 // was generated.
00686 int Mobile::check_event(void) {
00687   float tx, ty, tz, rx, ry, rz;
00688   int touchid[16];
00689   float padx[16], pady[16];
00690   
00691   int padaction, upid, buttons, touchcnt;
00692   int buttonchanged;
00693   int win_event=FALSE;
00694   int packtype;
00695   JString incIP, nick, commandToSend;
00696   bool inControl = false;
00697 
00698   int clientPort;
00699 
00700   // for use in UserKeyEvent() calls
00701 //  DisplayDevice::EventCodes keydev=DisplayDevice::WIN_KBD;
00702 
00703   // if enough time has passed, let's send a heartbeat to all connected
00704   // clients
00705   checkAndSendStatus();
00706 
00707   // explicitly initialize event state variables
00708   rx=ry=rz=tx=ty=tz=0.0f;
00709   buttons=padaction=upid=0;
00710   memset(touchid, 0, sizeof(touchid));
00711   memset(padx, 0, sizeof(padx));
00712   memset(pady, 0, sizeof(pady));
00713   touchcnt=0;
00714 
00715   // process as many events as we can to prevent a packet backlog 
00716   while (moveMode != OFF && 
00717          mobile_listener_poll(mobile, rx, ry, rz, tx, ty, tz, padaction, upid,
00718                               touchcnt, touchid, padx, pady, buttons, packtype,
00719                               incIP, nick, clientPort, tranScaling, rotScaling,
00720                               zoomScaling, commandToSend)) 
00721   {
00722 //fprintf(stderr, "inside while. %s, %s\n", (const char *)nick, (const char *)incIP);
00723     win_event = TRUE;
00724 
00725     // is this a command?  If so, we need to send it on to the script
00726     // side and let them deal with it.
00727     if (packtype == EVENT_COMMAND) {
00728        // the 'buttons' variable has the specific command stored in it
00729        //   incIP and nick are important, too
00730        char strTmp[11];
00731        sprintf(strTmp, "%d",buttons);
00732        JString jstr = "{" + nick + "} {" + incIP + "} {" + strTmp + 
00733                       "} {" + commandToSend + "}";
00734 //       nick + " " + incIP + " " + strTmp;
00735 //fprintf(stderr, "running %s\n", (const char *)jstr);
00736        runcommand(new MobileDeviceCommandEvent(jstr));
00737        break;
00738     }
00739 
00740     // let's figure out who this is, and whether or not they are in
00741     // control
00742     if (isInControl(&nick, &incIP, clientPort, packtype))
00743     {
00744       DisplayDevice::EventCodes keydev=DisplayDevice::WIN_KBD;
00745       inControl = true;
00746 
00747       // find which buttons changed state
00748       buttonchanged = buttons ^ buttonDown; 
00749 
00750         // XXX change hardcoded numbers and support >3 buttons
00751       if (buttonchanged) {
00752          // for normal buttons, we want the down event
00753         if (buttonchanged == (1<<0) && (buttonchanged & buttons)) {
00754            runcommand(new UserKeyEvent(keydev, '0', (int) DisplayDevice::AUX));
00755         }
00756         if (buttonchanged == (1<<1) && (buttonchanged & buttons)) {
00757            runcommand(new UserKeyEvent(keydev, '1', (int) DisplayDevice::AUX));
00758         }
00759         if (buttonchanged == (1<<2) && (buttonchanged & buttons)) {
00760            runcommand(new UserKeyEvent(keydev, '2', (int) DisplayDevice::AUX));
00761         }
00762         if (buttonchanged == (1<<3) && (buttonchanged & buttons)) {
00763            runcommand(new UserKeyEvent(keydev, '3', (int) DisplayDevice::AUX));
00764         }
00765 
00766 
00767         if (buttonchanged == (1<<31)) {         // 'reset view' 
00768           if (buttonchanged & buttons) {       // get it on the 'down' event.
00769             app->scene_resetview();
00770           }
00771         }
00772       } // end if on buttonchanged
00773 
00774 #if 0
00775        printf("Touchpad action: %d upid %d", padaction, upid);
00776        for (int i=0; i<touchcnt; i++) {
00777          printf("ID[%d] x: %.2f y: %.2f ",
00778                 i, padx[i], pady[i]);
00779        }
00780        printf("\n");
00781 #endif
00782 
00783       if (padaction != EVENT_NON_TOUCH) {
00784 
00785       // detect end of a touch event
00786       if (touchcnt < touchcount || 
00787            padaction == EVENT_TOUCH_UP || padaction == EVENT_TOUCH_SOMEUP) {
00788 //         fprintf(stderr,"<(a:%d,b:%d)", touchcnt, touchcount);
00789         touchinprogress = 0;
00790         touchmode = ROTATE;
00791         touchcount = 0;
00792         touchstartX = 0;
00793         touchstartY = 0;
00794         touchdeltaX = 0;
00795         touchdeltaY = 0;
00796         touchscale = 1.0;
00797         touchscalestartdist = 0;
00798         touchrotstartangle = 0;
00799       }
00800     
00801       // detect a touch starting event 
00802       if (touchcnt > touchcount ||
00803            padaction == EVENT_TOUCH_DOWN ||
00804            padaction == EVENT_TOUCH_SOMEDOWN) {
00805 //         fprintf(stderr,">(a:%d,b:%d)", touchcnt, touchcount);
00806         touchcount = touchcnt;
00807         touchstartX = 0;
00808         touchstartY = 0;
00809         touchdeltaX = 0;
00810         touchdeltaY = 0;
00811         touchscale = 1.0;
00812         touchscalestartdist = 0;
00813         touchrotstartangle = 0;
00814 
00815    // printf("Touchcount: %d\n", touchcount);
00816         if (touchcount == 1) {
00817    // printf("Start rotate..\n");
00818           touchinprogress=1;
00819           touchmode = ROTATE;
00820           touchstartX = padx[0];
00821           touchstartY = pady[0];
00822         } else if (touchcount == 2) {
00823           touchinprogress=1;
00824           touchstartX = (padx[0] + padx[1]) * 0.5f;
00825           touchstartY = (pady[0] + pady[1]) * 0.5f;
00826 
00827           float dx = padx[1] - padx[0];
00828           float dy = pady[1] - pady[0];
00829           touchscalestartdist = sqrtf(dx*dx + dy*dy) + 0.00001f;
00830           if (touchscalestartdist > 0.65f) { 
00831             touchrotstartangle = atan2(dx, -dy) + VMD_PI;
00832    // printf("Start scale.. dist: %.2f  angle: %.2f\n", touchscalestartdist, touchrotstartangle);
00833             touchmode = SCALEROTZ;
00834           } else {
00835    // printf("Start translate.. dist(%.2f)\n", touchscalestartdist);
00836             touchmode = TRANSLATE;
00837           }
00838         }
00839       }
00840 
00841       if (touchinprogress && padaction == EVENT_TOUCH_MOVE) {
00842         if (touchmode == ROTATE) {
00843           touchdeltaX = padx[0] - touchstartX;
00844           touchdeltaY = pady[0] - touchstartY;
00845         } else if (touchmode == SCALEROTZ) {
00846           // only move the structure if we're in move mode,
00847           // in animate mode we do nothing...
00848           if (moveMode == MOVE) {
00849             float dx = padx[1] - padx[0];
00850             float dy = pady[1] - pady[0];
00851             float dist = sqrtf(dx*dx + dy*dy);
00852      
00853             // Only scale if the scale changes by at least 1%
00854             float newscale = (dist / touchscalestartdist) / touchscale;
00855             if (fabsf(newscale - 1.0f) > 0.01f) {
00856               touchscale *= newscale;
00857               app->scene_scale_by((newscale - 1.0f) * zoomScaling + 1.0f);
00858             }
00859 
00860             // Only rotate if the angle update is large enough to make
00861             // it worthwhile, otherwise we get visible "jitter" from noise
00862             // in the touchpad coordinates.  Currently, we only rotate if
00863             // the rotation magnitude is greater than a quarter-degree
00864             float newrotangle = atan2(dx, -dy) + VMD_PI;
00865             float rotby = (newrotangle-touchrotstartangle)*180.0f/VMD_PI;
00866             if (fabsf(rotby) > 0.25f) {
00867               app->scene_rotate_by(-rotScaling*rotby, 'z');
00868               touchrotstartangle=newrotangle;
00869             }
00870           }
00871         } else if (touchmode == TRANSLATE) {
00872           touchdeltaX = ((padx[0]+padx[1])*0.5f) - touchstartX;
00873           touchdeltaY = ((pady[0]+pady[1])*0.5f) - touchstartY;
00874         }
00875       }
00876 
00877       }
00878 
00879       // update button status for next time through
00880       buttonDown = buttons;
00881     }  // end of isInControl
00882 
00883     // restart last-packet timer
00884     wkf_timer_start(packtimer);
00885   }           // end while (moveMode != OFF && mobile_listener_poll())
00886 
00887   // xxx: this next check really needs to be done on a per-client basis and 
00888   // non responsive clients should be kicked out
00889   // check for dropped packets or mobile shutdown and 
00890   // halt any ongoing events if we haven't heard from the
00891   // client in over 1 second.  
00892   if (!win_event && wkf_timer_timenow(packtimer) > 3.0) {
00893     touchinprogress = 0;
00894     touchmode = ROTATE;
00895     touchcount = 0;
00896     touchstartX = 0;
00897     touchstartY = 0;
00898     touchdeltaX = 0;
00899     touchdeltaY = 0;
00900     touchscalestartdist = 0;
00901     touchrotstartangle = 0;
00902   }
00903 
00904   if (touchinprogress) {
00905     if (moveMode == MOVE) {
00906       if (touchmode == ROTATE) {
00907 //         fprintf(stderr,"+");
00908         // Motion in Android "X" rotates around VMD Y axis...
00909         app->scene_rotate_by(touchdeltaY*rotScaling*0.5f, 'x');
00910         app->scene_rotate_by(touchdeltaX*rotScaling*0.5f, 'y');
00911       } else if (touchmode == TRANSLATE) {
00912         app->scene_translate_by(touchdeltaX*tranScaling*0.005f, -touchdeltaY*tranScaling*0.005f, 0.0f);
00913       }
00914     } else if (moveMode == ANIMATE) {
00915       if (fabsf(touchdeltaX) > 0.25f) {
00916 #if 0
00917         // exponential input scaling
00918         float speed = fabsf(expf(fabsf((fabsf(touchdeltaX) * animInc) / 1.7f))) - 1.0f;
00919 #else
00920         // linear input scaling
00921         float speed = fabsf(touchdeltaX) * animInc;
00922 #endif
00923 
00924         if (speed > 0) {
00925           if (speed < 1.0)
00926             app->animation_set_speed(speed);
00927           else
00928             app->animation_set_speed(1.0f);
00929 
00930           int stride = 1;
00931           if (fabs(speed - 1.0) > (double) maxstride)
00932             stride = maxstride;
00933           else
00934             stride = 1 + (int) fabs(speed-1.0);
00935           if (stride < 1)
00936             stride = 1;
00937           app->animation_set_stride(stride);
00938 
00939           if (touchdeltaX > 0) {
00940             app->animation_set_dir(Animation::ANIM_FORWARD1);
00941           } else {
00942             app->animation_set_dir(Animation::ANIM_REVERSE1);
00943           }
00944         } else {
00945           app->animation_set_dir(Animation::ANIM_PAUSE);
00946           app->animation_set_speed(1.0f);
00947         }
00948       } else {
00949         app->animation_set_dir(Animation::ANIM_PAUSE);
00950         app->animation_set_speed(1.0f);
00951       }
00952     }
00953   } else { 
00954 //     if (!inControl) fprintf(stderr,"-");
00955 //     if (!touchinprogress) fprintf(stderr,"|");
00956      } 
00957 
00958   if (win_event) {
00959     return TRUE;
00960   } else {
00961     return FALSE; // no events to report
00962   }
00963 } // end of Mobile::check_event(void) {
00964 
00965 // ------------------------------------------------------------------------
00967 
00968 const char* Mobile::get_mode_str(MoveMode mm) {
00969   const char* modestr;
00970 
00971   switch (mm) {
00972     default:
00973     case OFF:         modestr = "off";        break;
00974     case MOVE:        modestr = "move";       break;
00975     case ANIMATE:     modestr = "animate";    break;
00976     case TRACKER:     modestr = "tracker";    break;
00977     case USER:        modestr = "user";       break;
00978   }
00979 
00980   return modestr;
00981 }
00982 
00983 // ------------------------------------------------------------------------
00984 int Mobile::get_port () {
00985   return port;
00986 }
00987 
00988 // ------------------------------------------------------------------------
00989 int Mobile::get_APIsupported () {
00990   return CURRENTAPIVERSION;
00991 }
00992 
00993 // ------------------------------------------------------------------------
00994 int Mobile::get_move_mode () {
00995   return moveMode;
00996 }
00997 
00998 // ------------------------------------------------------------------------
00999 void  Mobile::get_client_list (ResizeArray <JString*>* &nick, 
01000                          ResizeArray <JString*>* &ip, ResizeArray <bool>* &active)
01001 {
01002   nick = &clientNick;
01003   ip = &clientIP;
01004   active = &clientActive;
01005 }
01006 
01007 // ------------------------------------------------------------------------
01008 int Mobile::sendMsgToClient(const char *nick, const char *ip, 
01009                             const char *msgType, const char *msg)
01010 {
01011 //   fprintf(stderr, "Sending %s (%s) msgtype %s, msg '%s'\n",nick,ip,msgType,msg);
01012    // find the user with the given nick and ip
01013   bool found = false;
01014   int i;
01015   for (i=0; i<clientNick.num();i++)
01016   {
01017     if (*(clientNick[i]) == nick) {
01018       // we've found the right nick.  Now let's check the IP
01019       if (*(clientIP[i]) == ip) {
01020         found = true;
01021         break;
01022       }
01023     }
01024   }
01025    
01026   if (found) {
01027      // let's send them the msg
01028     prepareSendBuffer(SEND_MESSAGE);
01029     // we need to insert whether or not this specific user is active
01030     *(statusSendBuffer + 16) = (clientActive[i] ? 1 : 0);    // Is this nick active?
01031 
01032     // pack the message. Right now it is length long.  We need to add to it.
01033     int length = statusSendBufferLength;
01034 
01035     // msgType must be an integer.  So, let's get the int.
01036     int tmpInt;
01037     if (EOF == sscanf(msgType, "%d", &tmpInt))
01038     {
01039        return false;
01040     }
01041     *(statusSendBuffer + length) = tmpInt;
01042     length += sizeof(int);
01043 
01044     int msgLength = strlen(msg);
01045     *(statusSendBuffer + length) = msgLength;
01046     length += sizeof(int);
01047 
01048     memcpy((statusSendBuffer + length), msg, msgLength);
01049     length += msgLength;
01050 
01051     send_dgram((const char *)*(clientIP[i]), clientListenerPort[i], 
01052                                      statusSendBuffer, length);
01053     return true;
01054   } else {
01055     return false;
01056   }
01057 }  // end of Mobile::sendMsgToClient
01058 
01059 // ------------------------------------------------------------------------
01060 int Mobile::set_activeClient(const char *nick, const char *ip)
01061 {
01062 //   fprintf(stderr, "in set_activeClient.  nick: %s, ip: %s\n", nick, ip);
01063    // find the user with the given nick and ip
01064   bool found = false;
01065   int i;
01066   for (i=0; i<clientNick.num();i++)
01067   {
01068     if (*(clientNick[i]) == nick) {
01069       // we've found the right nick.  Now let's check the IP
01070       if (*(clientIP[i]) == ip) {
01071         found = true;
01072         break;
01073       }
01074     }
01075   }
01076    
01077   if (found) { 
01078     // First, run through clientActive and turn everyone off.
01079     for (int j=0; j<clientActive.num();j++)
01080     {
01081        clientActive[j] = false;
01082     }
01083 
01084     // turn off any movements that might have been going on
01085     touchinprogress = 0;
01086     touchmode = ROTATE;
01087     touchcount = 0;
01088     touchstartX = 0;
01089     touchstartY = 0;
01090     touchdeltaX = 0;
01091     touchdeltaY = 0;
01092     touchscalestartdist = 0;
01093     touchrotstartangle = 0;
01094 
01095     // set this one client to active.
01096     clientActive[i] = true;
01097 
01098     sendStatus(SEND_SETACTIVECLIENT);
01099     return true;
01100   } else {
01101     return false;
01102   }
01103 }  // end set active client
01104 
01105 // ------------------------------------------------------------------------
01106 
01107 void Mobile::get_tracker_status(float &tx, float &ty, float &tz,
01108                                 float &rx, float &ry, float &rz, 
01109                                 int &buttons) {
01110   tx =  trtx * transInc;
01111   ty =  trty * transInc;
01112   tz = -trtz * transInc;
01113   rx =  trrx * rotInc;
01114   ry =  trry * rotInc;
01115   rz = -trrz * rotInc;
01116   buttons = trbuttons;
01117 }
01118 
01119 // ------------------------------------------------------------------------
01120 
01121 // set the Mobile move mode to the given state; return success
01122 int Mobile::move_mode(MoveMode mm) {
01123   // change the mode now
01124   moveMode = mm;
01125 
01126   if (moveMode != OFF && !mobile) {
01127     mobile = mobile_listener_create(port);
01128     if (mobile == NULL) {
01129       msgErr << "Failed to open mobile port " << port 
01130              << ", move mode disabled" << sendmsg;
01131       moveMode = OFF;
01132     } else {
01133       msgInfo << "Opened mobile port " << port << sendmsg;
01134     }
01135   }
01136 
01137   // let's destroy the port binding since they've turned moving off
01138   if (moveMode == OFF && mobile) {
01139     mobile_listener_destroy(mobile);
01140     mobile = 0;
01141     removeAllClients();
01142   }
01143 
01145   if (moveMode != TRACKER) {
01146     trtx=trty=trtz=trrx=trry=trrz=0.0f; 
01147     trbuttons=0;
01148   }
01149 //fprintf(stderr,"Triggering command due to mode change\n");
01150   runcommand(new MobileStateChangedEvent());
01151   sendStatus(SEND_SETMODE);
01152 
01153   return TRUE; // report success
01154 }   // end of Mobile::move_mode
01155 
01156 // ------------------------------------------------------------------------
01157 // set the incoming UDP port (closing the old one if needed)
01158 int Mobile::network_port(int newport) {
01159 
01160   if (mobile != NULL) {
01161     mobile_listener_destroy(mobile);
01162     removeAllClients();
01163   }
01164 
01165   if (moveMode != OFF) {
01166     mobile = mobile_listener_create(newport);
01167     if (mobile == NULL) {
01168       msgErr << "Failed to open mobile port " << newport 
01169                << ", move mode disabled" << sendmsg;
01170       moveMode = OFF;
01171     } else {
01172       port = newport;
01173 //fprintf(stderr,"Triggering command due to port change\n");
01174       msgInfo << "Opened mobile port " << port << sendmsg;
01175     }
01176     runcommand(new MobileStateChangedEvent());
01177   } else {
01178     port = newport;
01179   }
01180 
01181   return TRUE; // report success
01182 }     // end of Mobile::network_port
01183 
01184 
01185 // ------------------------------------------------------------------------
01186 int  Mobile::addNewClient(JString* nick,  JString* ip, const int port, const bool active)
01187 {
01188 //  fprintf(stderr, "Adding %s, %s, %d\n", (const char*)*nick, (const char*)*ip, active);
01189   clientNick.append(nick);
01190   clientIP.append(ip);
01191   clientListenerPort.append(port);
01192   clientActive.append(active);
01193 
01194 //fprintf(stderr,"Triggering command due to addNewClient\n");
01195   runcommand(new MobileStateChangedEvent());
01196   sendStatus(SEND_ADDCLIENT);
01197   return 0;
01198 }     // end of Mobile::addNewClient
01199 
01200 // ------------------------------------------------------------------------
01201 void Mobile::removeAllClients()
01202 {
01203   while (clientNick.num() > 0) {
01204     removeClient(0);
01205   }
01206 }
01207 
01208 // ------------------------------------------------------------------------
01209 int  Mobile::removeClient(const int num)
01210 {
01211   delete clientNick[num];
01212   clientNick.remove(num);
01213 
01214   delete clientIP[num];
01215   clientIP.remove(num);
01216 
01217   clientActive.remove(num);
01218 
01219   clientListenerPort.remove(num);
01220 
01221   // let's see how many active clients are left?
01222   int iCount;
01223   for (iCount=0; iCount<clientActive.num();iCount++)
01224   {
01225      if (clientActive[iCount]) {
01226         break;
01227      }
01228   }
01229 
01230   // did we make it all the way through the client list without
01231   // finding anyone that is active?  If so (and there are actually
01232   // still clients) set the first one to active
01233   if (iCount == clientActive.num() && clientActive.num() > 0)
01234   {
01235     clientActive[0] = true;
01236   }
01237 
01238 //fprintf(stderr,"Triggering command due to removeClient\n");
01239   runcommand(new MobileStateChangedEvent());
01240   sendStatus(SEND_REMOVECLIENT);
01241   return 0;
01242 }
01243 
01244 
01245 

Generated on Mon May 21 01:51:02 2012 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002