main.cpp

Go to the documentation of this file.
00001 /*
00002  * main.cpp
00003  *
00004  * Copyright (C) 2009,2010  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  * Sample gamepad configurator.
00008  */
00009 
00010 // include files ---------------------------------------------------------------
00011 #include <iostream>
00012 #include <stdio.h>
00013 
00014 #include "gamepad-config/gamepad-config.h"
00015 #include "i18n/i18n.h"
00016 #include "perf/perf.h"
00017 #include "threadsafe/threadsafe.h"
00018 #include "threadsafe/threadsafe_multimap.h"
00019 
00020 
00021 // if all else fails fall back to US English
00022 static const char * s_defaultLocale             = "en_US.UTF-8";
00023 
00024 
00025 // I use this because it has a nicer interface than std::multimap<>
00026 typedef threadsafe_multimap<std::string, int> string_int_map_t;
00027 
00028 
00029 ////////////////////////////////////////////////////////////////////////////////
00030 //
00031 //      static helper methods
00032 //
00033 ////////////////////////////////////////////////////////////////////////////////
00034 
00035 static int
00036 selectFromMap
00037 (
00038 IN string_int_map_t& map,
00039 IN const char * title
00040 )
00041 {
00042         ASSERT(title, "null");
00043 
00044         int N = map.size();
00045         ASSERT(N > 0, "Empty map?");
00046         fprintf(stdout, "\n%s\n", title);
00047 
00048         // walk through and display options (alphabetic order)
00049         int ctr = 0;
00050         string_int_map_t::iterator_t i;
00051         map.getIterator(i);
00052         std::string name;
00053         int idx;
00054         while (map.getNextElement(i, name, idx)) {
00055                 ++ctr;
00056                 fprintf(stdout, "  %2d...%s\n", ctr, name.c_str());
00057         }
00058 
00059         // have user pick one
00060         int iSelected = 1;
00061         if (N > 1) {
00062                 iSelected = 0;
00063                 while (iSelected < 1 || iSelected > N) {
00064                         fprintf(stdout,
00065                             "Please enter your selection [1-%d]: ", N);
00066                         const int bufsize = 16;
00067                         char buffer[bufsize];
00068 
00069                         fgets(buffer, bufsize, stdin);
00070                         iSelected = atoi(buffer);
00071                 }
00072         }
00073 
00074         // get index of selection
00075         map.getIterator(i);
00076         while (iSelected && map.getNextElement(i, name, idx)) {
00077                 --iSelected;
00078         }
00079         ASSERT(!iSelected, "map changed size?");
00080         DPRINTF("  Selected option %d --> index %d", iSelected, idx);
00081         return idx;
00082 }
00083 
00084 
00085 
00086 static smart_ptr<gamepad::SourceDevice>
00087 selectDevice
00088 (
00089 IN gamepad::Manager * mgr
00090 )
00091 {
00092         ASSERT(mgr, "null");
00093 
00094         string_int_map_t map;
00095 
00096         int nDevs = mgr->getSourceDeviceCount();
00097         ASSERT_THROW(nDevs > 0,
00098             "No source devices found!  Is everything plugged in?");
00099         for (int i = 0; i < nDevs; ++i) {
00100                 smart_ptr<gamepad::SourceDevice> device =
00101                     mgr->getSourceDevice(i);
00102                 ASSERT_THROW(device, "manager returned null device for index: "
00103                     << i);
00104                 std::string name = device->getPublicName();
00105         //      name += " (";
00106         //      name += device->getUniqueId();
00107         //      name += ")";
00108                 map.insert(name, i);
00109         }
00110         int iSelected = selectFromMap(map, "What is the input source device?");
00111         ASSERT_THROW(iSelected >= 0 && iSelected < nDevs,
00112             "Invalid input device selection: " << iSelected);
00113 
00114         smart_ptr<gamepad::SourceDevice> device =
00115             mgr->getSourceDevice(iSelected);
00116         ASSERT_THROW(device, "manager returned null for selected device?");
00117         fprintf(stdout, "Selected input device '%s'\n",
00118             device->getPublicName());
00119 
00120         return device;
00121 }
00122 
00123 
00124 
00125 static smart_ptr<gamepad::Type>
00126 selectType
00127 (
00128 IN gamepad::Manager * mgr
00129 )
00130 {
00131         ASSERT(mgr, "null");
00132 
00133         string_int_map_t map;
00134         int nTypes = mgr->getTypeCount();
00135         ASSERT_THROW(nTypes > 0, "No valid gamepad types found!");
00136         for (int i = 0; i < nTypes; ++i) {
00137                 smart_ptr<gamepad::Type> type = mgr->getType(i);
00138                 ASSERT_THROW(type, "manager returned null type for index: "
00139                     << i);
00140                 const char * name = type->getName();
00141                 map.insert(name, i);
00142         }
00143         int iSelected = selectFromMap(map,
00144             "What type of gamepad is being configured?");
00145         ASSERT_THROW(iSelected >= 0 && iSelected < nTypes,
00146             "Invalid gamepad type selection: " << iSelected);
00147         smart_ptr<gamepad::Type> type = mgr->getType(iSelected);
00148         ASSERT_THROW(type, "manager returned null for selected type?");
00149         fprintf(stdout, "Selected gamepad type '%s'\n", type->getName());
00150 
00151         return type;
00152 }
00153 
00154 
00155 
00156 static void
00157 displayHints
00158 (
00159 IN i18n::Manager * mgr
00160 )
00161 {
00162         ASSERT(mgr, "null");
00163 
00164         const char * friendly = i18n::getString(mgr, "friendlyName");
00165         fprintf(stdout, "\nConfiguring gamepad: %s\n", friendly);
00166 
00167         const int bufsize = 16;
00168         char buffer[bufsize];
00169         for (int i = 1; ; ++i) {
00170                 snprintf(buffer, bufsize, "hint%d", i);
00171                 const char * hint = mgr->getString(buffer);
00172                 if (!hint)
00173                         break;  // no more hints
00174                 fprintf(stdout, "  %s\n", hint);
00175         }
00176 
00177         fprintf(stdout, "Press Enter when ready: ");
00178         fgets(buffer, bufsize, stdin);
00179 }
00180 
00181 
00182 
00183 static int
00184 atExtreme
00185 (
00186 IN const gamepad::pot_value_t& pv
00187 )
00188 throw()
00189 {
00190         if (!pv.isValid())
00191                 return 0;       // not even valid--skip it
00192 
00193         int range = pv.maxSeen - pv.minSeen;
00194         ASSERT(range > 0, "supposed to be valid!");
00195         int threshold = (int) (0.2 * range) + 1;
00196 
00197         int dx = pv.value - pv.minSeen;
00198         if (dx <= threshold) {
00199                 return -1;      // pot is near minimal value
00200         }
00201         dx = pv.maxSeen - pv.value;
00202         if (dx <= threshold) {
00203                 return +1;      // pot is near maximal value
00204         }
00205         return 0;       // somewhere in the middle
00206 }
00207 
00208 
00209 
00210 ////////////////////////////////////////////////////////////////////////////////
00211 //
00212 //      entry point
00213 //
00214 ////////////////////////////////////////////////////////////////////////////////
00215 
00216 int
00217 main
00218 (
00219 IN int argc,
00220 IN const char * argv[]
00221 )
00222 {
00223         int retval = 0;
00224         ASSERT(2 == argc, "Usage: configureGamepad <configDataDirectory>");
00225         const char * dataDir = argv[1];
00226         DPRINTF("Using data directory: %s", dataDir);
00227 
00228         try {
00229                 perf::Timer timer("overall timer");
00230 
00231                 // get data directory
00232                 smart_ptr<nstream::Manager> fsMgr =
00233                     nstream::getFilesystemManager(dataDir);
00234                 ASSERT_THROW(fsMgr,
00235                     "failed to create filesystem stream manager for path: " <<
00236                     dataDir);
00237                 smart_ptr<nstream::Folder> root = fsMgr->getRoot();
00238                 ASSERT(root, "null");
00239 
00240                 // start up manager
00241                 fprintf(stdout, "Initializing gamepad manager...\n");
00242                 smart_ptr<gamepad::Manager> mgr =
00243                     gamepad::Manager::create(root);
00244                 ASSERT_THROW(mgr, "failed to create gamepad manager");
00245                 fprintf(stdout, "Discovering devices...\n");
00246                 mgr->rediscover();
00247 
00248                 // let user select source device
00249                 smart_ptr<gamepad::SourceDevice> device = selectDevice(mgr);
00250                 ASSERT_THROW(device, "no gamepad input device selected");
00251 
00252                 // let user select a gamepad type
00253                 smart_ptr<gamepad::Type> type = selectType(mgr);
00254                 ASSERT_THROW(type, "no gamepad type selected");
00255 
00256                 const char * name = "Test Gamepad Mapping";
00257 
00258                 // get localization manager for instructions
00259                 const char * locale = i18n::getHostLocale();
00260                 smart_ptr<i18n::Manager> configMgr =
00261                     gamepad::getConfigLocaleMgr(locale, s_defaultLocale,
00262                         root);
00263                 ASSERT_THROW(configMgr, "failed to create locale mgr");
00264                 DPRINTF("Using strings for locale: '%s'",
00265                     configMgr->getLocale());
00266 
00267                 const char * endCalibrate =
00268                     getString(configMgr, "endCalibrate");
00269 
00270                 // get localization manager for gamepad type
00271                 smart_ptr<i18n::Manager> gamepadMgr =
00272                     gamepad::getTypeLocaleMgr(type, locale, s_defaultLocale,
00273                         root);
00274                 ASSERT_THROW(gamepadMgr,
00275                     "failed to create locale manager for gamepad type");
00276                 const char * pressMainButton =
00277                     getString(gamepadMgr, "pressMainAction");
00278 
00279                 displayHints(gamepadMgr);
00280 
00281                 // create configurator
00282                 smart_ptr<gamepad::Configurator> config =
00283                     gamepad::Configurator::create(mgr, device, type, name);
00284                 ASSERT_THROW(config, "failed to create gamepad configurator");
00285 
00286                 // loop and call configurator...
00287                 const int s_hz = 100;   // sample s_hz times per second
00288                 fprintf(stdout, "\nConfiguring gamepad (sampling at %d hz)\n",
00289                     s_hz);
00290                 ASSERT_THROW(s_hz > 0, "Bad sample rate: " << s_hz);
00291                 int msSleep = 1000 / s_hz;
00292                 int currStep = -1;
00293                 gamepad::config_status_t status;
00294                 std::string friendlyInstruction;
00295                 int counter = 0;
00296                 const int displayFriendly = s_hz / 5;
00297                 int waitCount = 0;      // number of cycles to wait
00298                 const int calibrateCount = 8 * s_hz;
00299                 const int endCalibrateCount = 3 * s_hz;
00300                 while (true) {
00301                         mgr->update();
00302                         if (!config->update(status))
00303                                 break;
00304 
00305                         sleepMilliseconds(msSleep);
00306 
00307                         ++counter;
00308                         if (counter >= displayFriendly) {
00309                                 counter = 0;
00310                                 fprintf(stdout, "%s",
00311                                     friendlyInstruction.c_str());
00312                                 if (gamepad::eInstruction_ExternalEvent & status.instruct) {
00313                                         waitCount -= displayFriendly;
00314                                         float secRemaining;
00315                                         if (waitCount >= 0) {
00316                                                 secRemaining = waitCount / (1.0 * s_hz);
00317                                         } else {
00318                                                 secRemaining = (endCalibrateCount + waitCount) / (1.0 * s_hz);
00319                                         }
00320                                         fprintf(stdout,
00321                                             "  %3.1f seconds remaining...",
00322                                             secRemaining);
00323                                         if (waitCount < 1) {
00324                                                 friendlyInstruction = endCalibrate;
00325                                         } if (waitCount < -endCalibrateCount) {
00326                                                 config->calibrationComplete();
00327                                         }
00328                                 }
00329                                 fprintf(stdout, "\n");
00330                         }
00331 
00332                         if (currStep != status.currStep) {
00333                         //      fprintf(stdout, "\nNew step: %d of %d\n",
00334                         //          status.currStep + 1, status.maxSteps);
00335                         //      fprintf(stdout, "  instruction: %d\n",
00336                         //          status.instruct);
00337                                 const char * token = 
00338                                     gamepad::getInstructionToken(status.instruct);
00339                         //      fprintf(stdout, "  token: '%s'\n", token);
00340                                 const char * friendly =
00341                                     getString(configMgr, token);
00342                         //      fprintf(stdout, "  friendly: '%s'\n", friendly);
00343                         //      fprintf(stdout, "  current: %s\n",
00344                         //          status.logical);
00345                                 const char * localName =
00346                                     getString(gamepadMgr, status.logical);
00347                         //      fprintf(stdout, "  local: '%s'\n", localName);
00348                         //      fprintf(stdout, "  primary button: %s\n",
00349                         //          status.primary);
00350                                 currStep = status.currStep;
00351 
00352                                 friendlyInstruction = "";
00353                                 if (gamepad::eInstruction_RequestControl & status.instruct) {
00354                                         friendlyInstruction = localName;
00355                                         friendlyInstruction += " : ";
00356                                 }
00357                                 friendlyInstruction += friendly;
00358                                 if (gamepad::eInstruction_PressMainAction & status.instruct) {
00359                                         friendlyInstruction += " ";
00360                                         friendlyInstruction += pressMainButton;
00361                                 }
00362                                 if (gamepad::eInstruction_ExternalEvent & status.instruct) {
00363                                         waitCount = calibrateCount;
00364                                 }
00365                         }
00366                 }
00367 
00368                 smart_ptr<gamepad::Map> map = config->getMapping();
00369                 ASSERT(map, "null");
00370 
00371                 // okay, have a map based on user's configuration!
00372                 // create a gamepad using that mapping
00373                 smart_ptr<gamepad::Gamepad> gamepad;
00374                 gamepad::Manager::eResult res =
00375                     mgr->createGamepad(device, map, gamepad);
00376                 ASSERT_THROW(gamepad::Manager::eResult_Success == res,
00377                     "Failed to create gamepad.  result: " << res);
00378                 ASSERT(gamepad, "null");
00379 
00380                 // let the user know...
00381                 const int bufsize = 16;
00382                 char buffer[bufsize];
00383                 fprintf(stdout, "\nSuccessfully created gamepad mapping!\n");
00384                 fprintf(stdout, "Press <Enter> to test mapping with gamepad: ");
00385                 fgets(buffer, bufsize, stdin);
00386 
00387                 while (true) {
00388                         sleepMilliseconds(msSleep);
00389 
00390                         mgr->update();
00391 
00392                         if (gamepad::eDevice_Detached == gamepad->getState()) {
00393                                 DPRINTF("Gamepad detached: halting!\n");
00394                                 break;
00395                         }
00396 
00397                         const gamepad::Type * type = gamepad->getType();
00398                         ASSERT(type, "null");
00399 
00400                         gamepad::buttons_t buttons = gamepad->getButtons();
00401                         int mask = 1;
00402                         int nButtons = type->getInputCount(gamepad::eInput_Button);
00403                         for (int i = 0; i < nButtons; ++i) {
00404                                 if (mask & buttons) {
00405                                         //DPRINTF("Button %d is pushed!", i);
00406                                         const char * logical =
00407                                             type->getLogicalName(gamepad::eInput_Button, i);
00408                                         const char * localName =
00409                                             getString(gamepadMgr, logical);
00410                                         gamepad::eButton btn = gamepad->getButton(i);
00411                                         std::string out;
00412                                         if (gamepad::eButtonDown & btn) {
00413                                                 out += "isDown ";
00414                                         }
00415                                         if (gamepad::eButtonWasPushed & btn) {
00416                                                 out += "wasPushed ";
00417                                         }
00418                                         if (gamepad::eButtonWasReleased & btn) {
00419                                                 out += "wasReleased ";
00420                                         }
00421                                         printf("Button %d = %s = '%s': %s\n",
00422                                             i, logical, localName, out.c_str());
00423                                 }
00424                                 mask *= 2;
00425                         }
00426 
00427                         int nDpads = type->getInputCount(gamepad::eInput_Dpad);
00428                         for (int i = 0; i < nDpads; ++i) {
00429                                 gamepad::eDpad dpad = gamepad->getDpad(i);
00430                                 if (dpad) {
00431                                         const char * logical =
00432                                             type->getLogicalName(gamepad::eInput_Dpad, i);
00433                                         const char * localName =
00434                                             getString(gamepadMgr, logical);
00435                                         std::string out;
00436                                         if (gamepad::eDpad_Up & dpad) {
00437                                                 out += "up ";
00438                                         }
00439                                         if (gamepad::eDpad_Down & dpad) {
00440                                                 out += "down ";
00441                                         }
00442                                         if (gamepad::eDpad_Left & dpad) {
00443                                                 out += "left ";
00444                                         }
00445                                         if (gamepad::eDpad_Right & dpad) {
00446                                                 out += "right ";
00447                                         }
00448                                         printf("dpad %d = %s = '%s': %s\n", i,
00449                                             logical, localName, out.c_str());
00450                                 }
00451                         }
00452 
00453                         int nJoys = type->getInputCount(gamepad::eInput_Joystick);
00454                         for (int i = 0; i < nJoys; ++i) {
00455                                 gamepad::joystick_t joy;
00456                                 gamepad->getJoystick(i, joy);
00457 
00458                                 int x = atExtreme(joy.x);
00459                                 int y = atExtreme(joy.y);
00460                                 if (x || y) {
00461                                         const int bufsize = 256;
00462                                         char buffer[bufsize];
00463                                         const char * logical =
00464                                             type->getLogicalName(gamepad::eInput_Joystick, i);
00465                                         const char * localName =
00466                                             getString(gamepadMgr, logical);
00467 
00468                                         std::string out;
00469                                         if (x) {
00470                                                 snprintf(buffer, bufsize,
00471                                                     "X:%d [%d, %d]", joy.x.value,
00472                                                     joy.x.minSeen, joy.x.maxSeen);
00473                                                 out += buffer;
00474                                                 out += "  ";
00475                                         }
00476                                         if (y) {
00477                                                 snprintf(buffer, bufsize,
00478                                                     "Y:%d [%d, %d]", joy.y.value,
00479                                                     joy.y.minSeen, joy.y.maxSeen);
00480                                                 out += buffer;
00481                                         }
00482                                         printf("joystick %d = %s = '%s': %s\n", i,
00483                                             logical, localName, out.c_str());
00484                                             
00485                                 }
00486                         }
00487 
00488                         int nPots = type->getInputCount(gamepad::eInput_Pot);
00489                         for (int i = 0; i < nPots; ++i) {
00490                                 gamepad::pot_value_t pv;
00491                                 gamepad->getPot(i, pv);
00492 
00493                                 int range = pv.maxSeen - pv.minSeen;
00494                                 if (range < 0)
00495                                         continue;       // skip this
00496                                 int threshold = (int) (0.1 * range) + 1;
00497                                 if (pv.value >= threshold) {
00498                                         const char * logical =
00499                                             type->getLogicalName(gamepad::eInput_Pot, i);
00500                                         const char * localName =
00501                                             getString(gamepadMgr, logical);
00502                                         printf("pot %d = %s = '%s': %d range=[%d, %d]\n",
00503                                             i, logical, localName, pv.value,
00504                                             pv.minSeen, pv.maxSeen);
00505                                 }
00506                         }
00507                 }
00508 
00509         } catch (std::exception& e) {
00510                 DPRINTF("EXCEPTION: %s", e.what());
00511                 retval = 1;
00512         }
00513 
00514         perf::dumpTimingSummary(std::cerr);
00515         return retval;
00516 }
00517