gamepad-config.cpp

Go to the documentation of this file.
00001 /*
00002  * gamepad-config.cpp
00003  *
00004  * Copyright (C) 2010  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Implementation of gamepad configuration object.
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "gamepad-config.h"     // always include our own header first
00036 
00037 #include "datahash/datahash_text.h"
00038 #include "datahash/datahash_util.h"
00039 #include "util/uuid.h"
00040 
00041 
00042 namespace gamepad {
00043 
00044 
00045 // interface destructors
00046 Configurator::~Configurator(void) throw() { }
00047 
00048 
00049 struct instruct_rec_t {
00050         eInstruction    instruct;
00051         const char *    token;
00052 };
00053 
00054 #define INSTRUCT_ENTRY(name, token)                                     \
00055         { eInstruction_ ## name , token } ,
00056 
00057 static const instruct_rec_t s_instructionRecords[] = {
00058 
00059         INSTRUCT_ENTRY(Calibrate,       "calibrate")
00060         INSTRUCT_ENTRY(ButtonPress,     "buttonPress")
00061         INSTRUCT_ENTRY(DpadCenter,      "dpadCenter")
00062         INSTRUCT_ENTRY(DpadLeft,        "dpadLeft")
00063         INSTRUCT_ENTRY(DpadRight,       "dpadRight")
00064         INSTRUCT_ENTRY(DpadUp,          "dpadUp")
00065         INSTRUCT_ENTRY(DpadDown,        "dpadDown")
00066         INSTRUCT_ENTRY(JoyCenter,       "joyCenter")
00067         INSTRUCT_ENTRY(JoyLeft,         "joyLeft")
00068         INSTRUCT_ENTRY(JoyUp,           "joyUp")
00069         INSTRUCT_ENTRY(PotCenter,       "potCenter")
00070         INSTRUCT_ENTRY(PotExtreme,      "potExtreme")
00071 
00072         // keep this last!
00073         { eInstruction_Invalid, NULL }
00074 };
00075 
00076 typedef std::multimap<int, std::string> priority_map_t;
00077 
00078 
00079 static const int s_interleave           = 8;
00080 
00081 static const float s_buttonThreshold    = 0.95;
00082 
00083 ////////////////////////////////////////////////////////////////////////////////
00084 //
00085 //      static helper methods
00086 //
00087 ////////////////////////////////////////////////////////////////////////////////
00088 
00089 static const instruct_rec_t *
00090 getInstructionRecord
00091 (
00092 IN eInstruction instruct
00093 )
00094 {
00095         for (const instruct_rec_t * p = s_instructionRecords; p->token; ++p) {
00096                 if (p->instruct == instruct)
00097                         return p;
00098         }
00099 
00100         // not found!
00101         ASSERT_THROW(false, "Invalid instruction value: " << instruct);
00102         return NULL;    // never get here
00103 }
00104 
00105 
00106 void
00107 addToMultimap
00108 (
00109 IN priority_map_t& map,
00110 IN int key,
00111 IN const char * val
00112 )
00113 {
00114         ASSERT(key >= 0, "Bad key: %d", key);
00115         ASSERT(val, "null");
00116 
00117         map.insert(std::pair<int, std::string>(key, val));
00118 }
00119 
00120 
00121 
00122 static bool
00123 notEqual
00124 (
00125 IN const char * string1,
00126 IN const char * string2A,
00127 IN const char * string2B
00128 )
00129 {
00130         ASSERT(string1, "null");
00131         ASSERT(string2A, "null");
00132         ASSERT(string2B, "null");
00133 
00134         int n2A = strlen(string2A);
00135         if (strncmp(string1, string2A, n2A)) {
00136                 DPRINTF("Compared first %d characters of '%s' and '%s'",
00137                     n2A, string1, string2A);
00138                 DPRINTF("Mismatch!");
00139                 return true;    // doesn't match
00140         }
00141         DPRINTF("Comparing '%s' and '%s'", string1 + n2A, string2B);
00142         return (0 != strcmp(string1 + n2A, string2B));
00143 }
00144 
00145 
00146 
00147 ////////////////////////////////////////////////////////////////////////////////
00148 //
00149 //      Config -- class that implements the gamepad::Configurator interface
00150 //
00151 ////////////////////////////////////////////////////////////////////////////////
00152 
00153 class Config : public Configurator {
00154 public:
00155         // constructor, destructor ---------------------------------------------
00156         ~Config(void) throw() { }
00157 
00158         // public class methods ------------------------------------------------
00159         void initialize(IN smart_ptr<Manager>& mgr,
00160                                 IN smart_ptr<SourceDevice>& device,
00161                                 IN smart_ptr<Type>& type,
00162                                 IN const char * name);
00163 
00164         // gamepad::Configurator class interface methods -----------------------
00165         void reset(void);
00166         bool update(OUT config_status_t& status);
00167         void calibrationComplete(void);
00168         smart_ptr<Map> getMapping(void);
00169 
00170 private:
00171         // private typedefs ----------------------------------------------------
00172         typedef std::map<int, std::string> map_int_t;   // input --> logical
00173         typedef std::map<std::string, int> map_logical_t;//logical name->configd
00174 
00175         // private helper methods ----------------------------------------------
00176         const char * getLogicalName(void) const throw();
00177         void setInstruction(void);
00178         void handleRelease(IN int buttonReleased,
00179                                 IN int potWithMaxValue,
00180                                 IN const device_state_t& state);
00181         bool calibrating(void);
00182         int getConfigured(IN const char * logical) const throw();
00183         void updateConfigured(IN const char * logical,
00184                                 IN eInputType type);
00185 
00186         // private member data -------------------------------------------------
00187         smart_ptr<Manager>              m_mgr;
00188         smart_ptr<SourceDevice>         m_device;
00189         smart_ptr<Type>                 m_type;
00190         smart_ptr<Datahash>             m_map;  // output gamepad mapping
00191         smart_ptr<Datahash>             m_sub;  // current subhash for map
00192         std::string                     m_mapName; // name of mapping
00193         priority_map_t                  m_logicalNames; // for type
00194         const char *                    m_primary;      // primary action button
00195         device_state_t                  m_state;
00196         map_int_t                       m_buttonsSeen;  // seen these
00197         map_int_t                       m_potsSeen;
00198         map_logical_t                   m_configured;
00199         priority_map_t::const_iterator  m_currInput;
00200         int                             m_currStep;
00201         int                             m_currSubstep;
00202         int                             m_maxInputs;
00203         int                             m_primaryIndex;
00204         int                             m_minValue;
00205         int                             m_DpadUsesPots;
00206         eInstruction                    m_instruct;
00207 };
00208 
00209 
00210 void
00211 Config::initialize
00212 (
00213 IN smart_ptr<Manager>& mgr,
00214 IN smart_ptr<SourceDevice>& device,
00215 IN smart_ptr<Type>& type,
00216 IN const char * name
00217 )
00218 {
00219         ASSERT(mgr, "null");
00220         ASSERT(device, "null");
00221         ASSERT(type, "null");
00222         ASSERT(name, "null");
00223 
00224         m_mgr = mgr;
00225         m_device = device;
00226         m_type = type;
00227         m_mapName = name;
00228         m_primary = m_type->getMainButton();
00229 
00230         VecString logicalNames;
00231         m_type->getAllInputs(logicalNames);
00232         DPRINTF("Type '%s' contains %d logical inputs",
00233             m_type->getName(), (int) logicalNames.size());
00234 
00235         // now add to priority map
00236         for (VecString::const_iterator i = logicalNames.begin();
00237              i != logicalNames.end(); ++i) {
00238                 const char * lname = i->c_str();
00239                 eInputType intype = m_type->getInputTypes(lname);
00240 
00241                 // add to priority map based on type
00242                 //     (low priority == high number)
00243                 if (eInput_Button & intype) {
00244                         DPRINTF("%s is a button", lname);
00245                         int pri = 10;   // default priority
00246                         if (!strcmp(m_primary, lname)) {
00247                                 pri = 0;        // primary button
00248                         }
00249                         addToMultimap(m_logicalNames, pri, lname);
00250                 } else if (eInput_Joystick & intype) {
00251                         DPRINTF("%s is a joystick", lname);
00252                         addToMultimap(m_logicalNames,  20, lname);
00253                 } else if (eInput_Pot & intype) {
00254                         DPRINTF("%s is a potentiometer", lname);
00255                         addToMultimap(m_logicalNames,  30, lname);
00256                 } else if (eInput_Dpad & intype) {
00257                         DPRINTF("%s is a dpad", lname);
00258                         addToMultimap(m_logicalNames,  40, lname);
00259                 } else {
00260                         ASSERT_THROW(false, "Unknown input type: " << lname);
00261                 }
00262         }
00263         m_maxInputs = (int) m_logicalNames.size() + 1;
00264 
00265         // reset
00266         this->reset();
00267 }
00268 
00269 
00270 
00271 ////////////////////////////////////////////////////////////////////////////////
00272 //
00273 //      Config -- gamepad::Configurator class interface methods
00274 //
00275 ////////////////////////////////////////////////////////////////////////////////
00276 
00277 void
00278 Config::reset
00279 (
00280 void
00281 )
00282 {
00283         // reset map
00284         m_map = new Datahash;
00285         ASSERT(m_map, "out of memory");
00286 
00287         // initialize map with important UUIDs
00288         char buffer[eUUID_Length];
00289         const char * uuid = generateUUID(buffer);
00290         m_map->insert("id", uuid);
00291         m_map->insert("typeId", m_type->getId());
00292         m_map->insert("name", m_mapName);
00293         m_map->insert("deviceName", m_device->getPublicName());
00294 
00295         // clear state
00296         m_sub = NULL;
00297         m_currStep = 0;
00298         m_currSubstep = 0;
00299         snapshotState(m_device, m_state);
00300         m_buttonsSeen.clear();
00301         m_potsSeen.clear();
00302         m_configured.clear();
00303 
00304         // clear primary
00305         m_primaryIndex = -1;
00306 
00307         // reset iterator
00308         m_currInput = m_logicalNames.begin();
00309 
00310         // start with calibration!
00311         m_instruct = eInstruction_Calibrate;
00312 
00313         // reset this flag (only used when configuring a dpad)
00314         m_DpadUsesPots = -1;    // unset
00315 }
00316 
00317 
00318 
00319 bool
00320 Config::update
00321 (
00322 OUT config_status_t& status
00323 )
00324 {
00325         ASSERT(m_mgr, "null");
00326         ASSERT(m_device, "null");
00327         ASSERT(m_type, "null");
00328         status.clear();
00329 
00330         // get new state snapshot, look for differences
00331         device_state_t state;
00332         snapshotState(m_device, state);
00333         if (eInstruction_Calibrate != m_instruct) {
00334                 // pots are (apparently) calibrated
00335                 int br = buttonReleased(state);
00336                 int mv = maxPotValue(state, s_buttonThreshold);
00337 
00338                 if (br > -1 || mv > -1) {
00339                         //DPRINTF("button: %d  pot: %d", br, mv);
00340 
00341                         // handle release
00342                         this->handleRelease(br, mv, state);
00343 
00344                         // now set updated instructions
00345                         this->setInstruction();
00346                 }
00347         }
00348 
00349         // update status based on current phase
00350         status.logical = this->getLogicalName();
00351         status.primary = m_primary;
00352         status.currStep = m_currStep * s_interleave + m_currSubstep;
00353         status.maxSteps = m_maxInputs * s_interleave;
00354         status.instruct = m_instruct;
00355 
00356         // we're done when we get an invalid instruction
00357         return (eInstruction_Invalid != m_instruct);
00358 }
00359 
00360 
00361 
00362 void
00363 Config::calibrationComplete
00364 (
00365 void
00366 )
00367 {
00368         if (eInstruction_Calibrate != m_instruct) {
00369                 DPRINTF("WARNING: not in calibration mode--why complete?");
00370                 return;
00371         }
00372 
00373         // okay, exit calibration!
00374         m_currStep = 1;
00375         this->setInstruction();
00376 }
00377 
00378 
00379 
00380 smart_ptr<Map>
00381 Config::getMapping
00382 (
00383 void
00384 )
00385 {
00386         DPRINTF("Caller is asking for mapping!");
00387         writeHashToStream(m_map, std::cerr);
00388         std::cerr << "\n";
00389 
00390         // construct parent datahash containing map
00391         smart_ptr<Datahash> parent = new Datahash;
00392         ASSERT(parent, "null");
00393         parent->insert("map", m_map);
00394 
00395         // create gamepad::Map object from parent datahash
00396         smart_ptr<Map> map = readMap(parent);
00397         ASSERT(map, "failed to read map from hash");
00398 
00399         return map;
00400 }
00401 
00402 
00403 
00404 ////////////////////////////////////////////////////////////////////////////////
00405 //
00406 //      Config -- private helper methods
00407 //
00408 ////////////////////////////////////////////////////////////////////////////////
00409 
00410 const char *
00411 Config::getLogicalName
00412 (
00413 void
00414 )
00415 const
00416 throw()
00417 {
00418         if (m_logicalNames.end() == m_currInput) {
00419                 return NULL;
00420         }
00421         return m_currInput->second.c_str();
00422 }
00423 
00424 
00425 
00426 void
00427 Config::setInstruction
00428 (
00429 void
00430 )
00431 {
00432         // done?
00433         if (m_logicalNames.end() == m_currInput) {
00434                 m_instruct = eInstruction_Invalid;
00435                 return;
00436         }
00437 
00438         // okay, given current state, what should the user do?
00439         const char * logical = this->getLogicalName();
00440         ASSERT(logical, "null");
00441         //DPRINTF("setInstruction: %s", logical);
00442         eInputType type = m_type->getInputTypes(logical);
00443         if (eInput_Button & type) {
00444                 m_instruct = eInstruction_ButtonPress;
00445                 if (!m_sub) {
00446                         m_sub = new Datahash;
00447                         ASSERT(m_sub, "out of memory");
00448                         m_sub->insert("logical", logical);
00449                 }
00450         } else if (eInput_Dpad & type) {
00451                 switch (m_currSubstep) {
00452                 case 0: 
00453                         m_instruct = eInstruction_DpadCenter;
00454                         m_DpadUsesPots = -1;    // unknown
00455                         m_sub = new Datahash;
00456                         ASSERT(m_sub, "out of memory");
00457                         m_sub->insert("logical", logical);
00458                         break;
00459 
00460                 case 1:
00461                         m_instruct = eInstruction_DpadLeft;
00462                         break;
00463 
00464                 case 2:
00465                         m_instruct = eInstruction_DpadCenter;
00466                         break;
00467 
00468                 case 3:
00469                         m_instruct = eInstruction_DpadRight;
00470                         break;
00471 
00472                 case 4:
00473                         m_instruct = eInstruction_DpadCenter;
00474                         break;
00475 
00476                 case 5:
00477                         m_instruct = eInstruction_DpadUp;
00478                         break;
00479 
00480                 case 6:
00481                         m_instruct = eInstruction_DpadCenter;
00482                         break;
00483 
00484                 case 7:
00485                         m_instruct = eInstruction_DpadDown;
00486                         break;
00487 
00488                 default:
00489                         ASSERT_THROW(false, "Bad substep: " << m_currSubstep);
00490                 }
00491         } else if (eInput_Joystick & type) {
00492                 switch (m_currSubstep) {
00493                 case 0:
00494                         m_instruct = eInstruction_JoyCenter;
00495                         m_sub = new Datahash;
00496                         ASSERT(m_sub, "out of memory");
00497                         m_sub->insert("logical", logical);
00498                         break;
00499 
00500                 case 1:
00501                         m_instruct = eInstruction_JoyLeft;
00502                         break;
00503 
00504                 case 2:
00505                         m_instruct = eInstruction_JoyCenter;
00506                         break;
00507 
00508                 case 3:
00509                         m_instruct = eInstruction_JoyUp;
00510                         break;
00511 
00512                 default:
00513                         ASSERT_THROW(false, "bad substep: " << m_currSubstep);
00514                 }
00515         } else if (eInput_Pot & type) {
00516                 switch (m_currSubstep) {
00517                 case 0:
00518                         m_instruct = eInstruction_PotCenter;
00519                         m_sub = new Datahash;
00520                         ASSERT(m_sub, "out of memory");
00521                         m_sub->insert("logical", logical);
00522                         break;
00523 
00524                 case 1:
00525                         m_instruct = eInstruction_PotExtreme;
00526                         break;
00527 
00528                 default:
00529                         ASSERT_THROW(false, "bad substep: " << m_currSubstep);
00530                 }
00531         } else {
00532                 ASSERT_THROW(false, "NYI");
00533         }
00534 }
00535 
00536 
00537 
00538 void
00539 Config::handleRelease
00540 (
00541 IN int br,                              // button released
00542 IN int mv,                              // pot with max value
00543 IN const device_state_t& state          // new device state
00544 )
00545 {
00546         // handy data
00547         const char * logical = this->getLogicalName();
00548         if (!logical) {
00549                 return;
00550         }
00551         const int bufsize = 16;
00552         char buffer[bufsize];
00553 
00554 //      DPRINTF("handleRelease: %s", logical);
00555 
00556         // what were we waiting for?
00557         if (eInstruction_Calibrate == m_instruct) {
00558                 // okay, calibrated!
00559                 m_currInput = m_logicalNames.begin();
00560                 m_currStep = 0;
00561                 m_currSubstep = 0;
00562         } else if (eInstruction_ButtonPress == m_instruct) {
00563                 eInputType intype = m_type->getInputTypes(logical);
00564                 ASSERT(m_sub, "null");
00565 
00566                 // is this button actually a potentiometer?
00567                 if ((eInput_Pot & intype) &&
00568                     !(eInput_Pot & this->getConfigured(logical))){
00569                         if (mv < 0)
00570                                 return;         // waiting for pot to change
00571 
00572                         DPRINTF("Logical button '%s' is physical pot %d",
00573                             logical, mv);
00574 
00575                         // already seen this?
00576                         if (!m_currSubstep &&
00577                             m_potsSeen.end() != m_potsSeen.find(mv)) {
00578                                 DPRINTF("already seen this pot!");
00579                                 return;
00580                         }
00581                         m_potsSeen[mv] = logical;
00582                         this->updateConfigured(logical, eInput_Pot);
00583 
00584                         smart_ptr<Datahash> pot = new Datahash;
00585                         ASSERT(pot, "out of memory");
00586                         pot->insert("logical", logical);
00587                         snprintf(buffer, bufsize, "%d", mv);
00588                         pot->insert("index", buffer);
00589                         m_map->insert("pot", pot);
00590                 } 
00591                 if (eInput_Button & intype) {
00592                         if (br < 0)
00593                                 return;         // waiting for a button release
00594                         // we wanted a button press, and we got one!
00595                         DPRINTF("Logical button '%s' is physical button %d",
00596                             logical, br);
00597 
00598                         // already seen this?
00599                         if (!m_currSubstep &&
00600                             m_buttonsSeen.end() != m_buttonsSeen.find(br)) {
00601                                 DPRINTF("Already seen this button!");
00602                                 return;
00603                         }
00604                         m_buttonsSeen[br] = logical;
00605                         this->updateConfigured(logical, eInput_Button);
00606 
00607                         if (!strcmp(logical, m_primary)) {
00608                                 m_primaryIndex = br;
00609                         }
00610 
00611                         snprintf(buffer, bufsize, "%d", br);
00612                         m_sub->insert("button", buffer);
00613                 }
00614 
00615                 // are we done?
00616                 if (intype == this->getConfigured(logical)) {
00617                         // insert subhash
00618                         m_map->insert("button", m_sub);
00619                         m_sub = NULL;
00620 
00621                         // what's next?
00622                         ++m_currInput;
00623                         ++m_currStep;
00624                         m_currSubstep = 0;
00625                 }
00626         } else if (eInstruction_DpadCenter == m_instruct) {
00627                 if (m_primaryIndex != br)
00628                         return;
00629                 m_state = state;
00630                 ++m_currSubstep;
00631         } else if (eInstruction_DpadLeft == m_instruct ||
00632                    eInstruction_DpadRight == m_instruct ||
00633                    eInstruction_DpadUp == m_instruct ||
00634                    eInstruction_DpadDown == m_instruct) {
00635                 if (m_primaryIndex != br)
00636                         return; // not the right button released
00637 
00638                 // TODO: this is *horribly* messy.  Configuring a dpad requires
00639                 //      maintaining a lot of state.  Also, after I built this
00640                 //      routine to handle pot-based dpads, I discovered that
00641                 //      Windows sometimes exposes dpads as buttons instead.  So
00642                 //      this code tries to allow configuring dpads which are
00643                 //      represented at the hardware layer by buttons OR pots.
00644                 // Ideally, this code should be pulled out and restructured in
00645                 //      a way that is remotely maintainable.
00646 
00647                 int mp = maxPotChanged(m_state, state);
00648                 if (mp < 0) {
00649                         // no pots changed--or many pots changed!
00650 
00651                         // see if any buttons are being held down instead
00652                         int bd = buttonDown(state);
00653                         if (-1 == m_DpadUsesPots) {
00654                                 // we don't yet know if this dpad is driven by
00655                                 //      button presses, or potentiometers
00656                                 if (bd > -1) {
00657                                         // apparently, this dpad uses buttons
00658                                         m_sub->insert("type", "button");
00659                                         m_DpadUsesPots = 0;
00660                                 }
00661                         }
00662 
00663                         // if we're not sure, or definitely using pots, go back
00664                         if (m_DpadUsesPots) {
00665                                 --m_currSubstep;
00666                                 return;
00667                         }
00668 
00669                         // okay, we're supposed to configure this dpad based on
00670                         //      button presses
00671                         DPRINTF("Button pressed: %d", bd);
00672 
00673                         // is this button taken?
00674                         map_int_t::iterator i = m_buttonsSeen.find(bd);
00675                         if (m_buttonsSeen.end() != i) {
00676                                 DPRINTF("Already seen this button!");
00677                                 DPRINTF("  button %d --> '%s'", bd, i->second.c_str());
00678                                 --m_currStep;
00679                                 return;
00680                         }
00681         
00682                         // update mapping sub-hash with button
00683                         const char * label = NULL;
00684                         if (eInstruction_DpadLeft == m_instruct) {
00685                                 label = "left";
00686                         } else if (eInstruction_DpadRight == m_instruct) {
00687                                 label = "right";
00688                         } else if (eInstruction_DpadUp == m_instruct) {
00689                                 label = "up";
00690                         } else if (eInstruction_DpadDown == m_instruct) {
00691                                 label = "down";
00692                         }
00693                         std::string full = logical;
00694                         full += label;
00695                         m_buttonsSeen[bd] = full;
00696 
00697                         DPRINTF("Dpad '%s': '%s' is button %d",
00698                             logical, label, bd);
00699 
00700                         ASSERT(m_sub, "null");
00701                         snprintf(buffer, bufsize, "%d", bd);
00702                         m_sub->insert(label, buffer);
00703 
00704                         // next!
00705                         if (eInstruction_DpadDown != m_instruct) {
00706                                 ++m_currSubstep;
00707                         } else {
00708                                 // this is Down: last substep!
00709                                 m_map->insert("dpad", m_sub);
00710                                 m_sub = NULL;
00711 
00712                                 // next!
00713                                 ++m_currInput;
00714                                 ++m_currStep;
00715                                 m_currSubstep = 0;
00716                         }
00717                         return;
00718                 }
00719 
00720                 // okay, we believe a pot changed.  Should we do anything?
00721                 if (-1 == m_DpadUsesPots) {
00722                         // at the moment, it is uncertain whether this dpad uses
00723                         //      buttons or potentiometers at the device layer.
00724                         // we now decide that yes, this dpad uses potentiometers
00725                         m_DpadUsesPots = 1;
00726                         m_sub->insert("type", "pot");
00727                 }
00728                 if (!m_DpadUsesPots) {
00729                         // we're supposed to be using buttons!  Ignore pot
00730                         --m_currSubstep;
00731                         return;
00732                 }
00733 
00734                 // okay, configure this dpad based on the pot that changed
00735                 map_int_t::const_iterator i = m_potsSeen.find(mp);
00736                 bool sameAxis = false;
00737                 if (m_potsSeen.end() != i) {
00738                         // okay, this is annoying.  Some controllers (xbox 360)
00739                         //   have 2 pots per dpad, others (playstation 3) have
00740                         //   4 pots per dpad.  And who knows what some adapters
00741                         //   will do!  So we may legitimately see duplicates,
00742                         //   although only for the same axis.
00743                         const char * other = i->second.c_str();
00744                         DPRINTF("Pot already in use by '%s'", other);
00745                         if (eInstruction_DpadLeft == m_instruct ||
00746                             eInstruction_DpadUp == m_instruct) {
00747                                 // Left and Up shouldn't have pots in use
00748                                 return;
00749                         }
00750                         if (eInstruction_DpadRight == m_instruct &&
00751                             notEqual(other, logical, "left")) {
00752                                 // pot in use isn't the correct one
00753                                 DPRINTF("Calibrating right, not left?");
00754                                 return;
00755                         }
00756                         if (eInstruction_DpadDown == m_instruct &&
00757                             notEqual(other, logical, "up")) {
00758                                 // not the correct one
00759                                 DPRINTF("Calibrating down, not up?");
00760                                 return;
00761                         }
00762                         // must be okay!
00763                         sameAxis = true;
00764                 }
00765                 const pot_value_t& pv = m_device->getPotValue(mp);
00766                 const char * flipAxis = NULL;
00767                 const char * label = NULL;
00768                 if (eInstruction_DpadLeft == m_instruct) {
00769                         m_minValue = pv.value;
00770                         label = "left";
00771                 } else if (eInstruction_DpadRight == m_instruct) {
00772                         if (sameAxis && pv.value < m_minValue) {
00773                                 flipAxis = "flipX";
00774                         }
00775                         label = "right";
00776                 } else if (eInstruction_DpadUp == m_instruct) {
00777                         m_minValue = pv.value;
00778                         label = "up";
00779                 } else if (eInstruction_DpadDown == m_instruct) {
00780                         if (sameAxis && pv.value < m_minValue) {
00781                                 flipAxis = "flipY";
00782                         }
00783                         label = "down";
00784                 }
00785                 std::string full = logical;
00786                 full += label;
00787                 m_potsSeen[mp] = full;
00788 
00789                 DPRINTF("Dpad '%s': '%s' is potentiometer %d",
00790                     logical, label, mp);
00791 
00792                 ASSERT(m_sub, "null");
00793                 snprintf(buffer, bufsize, "%d", mp);
00794                 m_sub->insert(label, buffer);
00795 
00796                 if (flipAxis) {
00797                         DPRINTF("Flipping axis!  '%s'", flipAxis);
00798                         m_sub->insert(flipAxis, "true");
00799                 }
00800 
00801                 // next!
00802                 if (eInstruction_DpadDown != m_instruct) {
00803                         ++m_currSubstep;
00804                 } else {
00805                         // this is Down: last substep!
00806                         m_map->insert("dpad", m_sub);
00807                         m_sub = NULL;
00808 
00809                         // next!
00810                         ++m_currInput;
00811                         ++m_currStep;
00812                         m_currSubstep = 0;
00813                 }
00814         } else if (eInstruction_JoyCenter == m_instruct) {
00815                 if (m_primaryIndex != br)
00816                         return;
00817                 m_state = state;
00818                 ++m_currSubstep;
00819         } else if (eInstruction_JoyLeft == m_instruct) {
00820                 if (m_primaryIndex != br) {
00821                         DPRINTF("Wrong button press?");
00822                         return;
00823                 }
00824                 int mp = maxPotChanged(m_state, state);
00825                 if (mp < 0) {
00826                         DPRINTF("No change?  Step back...");
00827                         --m_currSubstep;        // back up
00828                         return;
00829                 }
00830                 if (m_potsSeen.end() != m_potsSeen.find(mp)) {
00831                         DPRINTF("Already seen pot %d", mp);
00832                         return;
00833                 }
00834                 m_potsSeen[mp] = logical;
00835                 DPRINTF("Joystick '%s' x-axis is potentiometer %d",
00836                     logical, mp);
00837                 ASSERT(m_sub, "null");
00838                 snprintf(buffer, bufsize, "%d", mp);
00839                 m_sub->insert("xAxis", buffer);
00840                 ++m_currSubstep;        // next
00841         } else if (eInstruction_JoyUp == m_instruct) {
00842                 if (m_primaryIndex != br)
00843                         return;
00844                 int mp = maxPotChanged(m_state, state);
00845                 if (mp < 0) {
00846                         --m_currSubstep;        // back up
00847                         return;
00848                 }
00849                 if (m_potsSeen.end() != m_potsSeen.find(mp)) {
00850                         DPRINTF("Already seen pot %d", mp);
00851                         return;
00852                 }
00853                 m_potsSeen[mp] = logical;
00854                 DPRINTF("Joystick '%s' y-axis is potentiometer %d",
00855                     logical, mp);
00856                 ASSERT(m_sub, "null");
00857                 snprintf(buffer, bufsize, "%d", mp);
00858                 m_sub->insert("yAxis", buffer);
00859                 m_map->insert("joystick", m_sub);
00860                 m_sub = NULL;
00861 
00862                 // next input!
00863                 ++m_currInput;
00864                 ++m_currStep;
00865                 m_currSubstep = 0;
00866         } else if (eInstruction_PotCenter == m_instruct) {
00867                 if (m_primaryIndex != br)
00868                         return;
00869 
00870                 // okay, snapshot state and move ahead
00871                 m_state = state;
00872                 m_currSubstep++;
00873         } else if (eInstruction_PotExtreme == m_instruct) {
00874                 if (m_primaryIndex != br)
00875                         return;
00876                 int mp = maxPotChanged(m_state, state);
00877                 if (mp < 0)
00878                         return;
00879                 if (m_potsSeen.end() != m_potsSeen.find(mp)) {
00880                         DPRINTF("Already seen pot %d", mp);
00881                         return;
00882                 }
00883                 m_potsSeen[mp] = logical;
00884                 DPRINTF("Pot '%s' is potentiometer %d", logical, mp);
00885                 ASSERT(m_sub, "null");
00886                 snprintf(buffer, bufsize, "%d", mp);
00887                 m_sub->insert("index", buffer);
00888                 m_map->insert("pot", m_sub);
00889                 m_sub = NULL;
00890 
00891                 // next input!
00892                 ++m_currInput;
00893                 ++m_currStep;
00894                 m_currSubstep = 0;
00895         } else {
00896                 ASSERT_THROW(false, "NYI");
00897         }
00898 }
00899 
00900 
00901 
00902 int
00903 Config::getConfigured
00904 (
00905 IN const char * logical
00906 )
00907 const
00908 throw()
00909 {
00910         ASSERT(logical, "null");
00911 
00912         map_logical_t::const_iterator i = m_configured.find(logical);
00913         if (m_configured.end() == i) {
00914                 return 0;       // nothing configured!
00915         }
00916         return i->second;
00917 }
00918 
00919 
00920 
00921 void
00922 Config::updateConfigured
00923 (
00924 IN const char * logical,
00925 IN eInputType type
00926 )
00927 {
00928         ASSERT(logical, "null");
00929 
00930         map_logical_t::iterator i = m_configured.find(logical);
00931         if (m_configured.end() == i) {
00932                 // not a recognized logical name--add this
00933                 m_configured[logical] = type;
00934         } else {
00935                 // update existing record
00936                 int val = i->second;
00937                 val |= type;
00938                 m_configured[logical] = val;
00939         }
00940 }
00941 
00942 
00943 
00944 ////////////////////////////////////////////////////////////////////////////////
00945 //
00946 //      public API
00947 //
00948 ////////////////////////////////////////////////////////////////////////////////
00949 
00950 smart_ptr<Configurator>
00951 Configurator::create
00952 (
00953 IN smart_ptr<Manager>& mgr,
00954 IN smart_ptr<SourceDevice>& device,
00955 IN smart_ptr<Type>& type,
00956 IN const char * name
00957 )
00958 {
00959         ASSERT(mgr, "null");
00960         ASSERT(device, "null");
00961         ASSERT(type, "null");
00962         ASSERT(name, "null");
00963 
00964         smart_ptr<Config> local = new Config;
00965         ASSERT(local, "out of memory");
00966 
00967         local->initialize(mgr, device, type, name);
00968 
00969         return local;
00970 }
00971 
00972 
00973 
00974 const char *
00975 getInstructionToken
00976 (
00977 IN eInstruction instruct
00978 )
00979 {
00980         const instruct_rec_t * rec = getInstructionRecord(instruct);
00981         ASSERT(rec, "null");
00982 
00983         return rec->token;
00984 }
00985 
00986 
00987 
00988 };      // gamepad namespace
00989