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

MobileInterface.C

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

Generated on Fri Apr 19 02:44:40 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002