linux-input.cpp

Go to the documentation of this file.
00001 /*
00002  * linux-input.cpp
00003  *
00004  * Copyright (C) 2009  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  * Input support built on the linux/input.h system.  See linux-input.h
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "linux-input.h"                // always include our own header first
00036 
00037 #include <linux/input.h>
00038 #include <sstream>
00039 
00040 #include "perf/perf.h"
00041 #include "util/file.h"
00042 
00043 
00044 namespace gamepad {
00045 
00046 
00047 
00048 static const char * s_devicePath        = "/dev/input/";
00049 static const char * s_prefix            = "LinuxInput";
00050 
00051 static SetString s_devicePaths; // set of recognized device paths
00052 
00053 enum eFFState {
00054         eFF_NotLoaded           = 1,    // nothing done
00055         eFF_Loaded              = 2,    // loaded, not playing
00056         eFF_Playing             = 3,    // actively playing
00057 
00058         // keep this last
00059         eFF_Invalid             = 0
00060 };
00061 
00062 
00063 
00064 struct rumble_t {
00065         // constructor, manipulators
00066         rumble_t(void) throw() { this->clear(); }
00067         void clear(void) throw() {
00068                         state = eFF_Invalid;
00069                         id = -1;
00070                 }
00071 
00072         // data fields
00073         eFFState        state;
00074         int             id;
00075 };
00076 
00077 
00078 ////////////////////////////////////////////////////////////////////////////////
00079 //
00080 //      static helper methods
00081 //
00082 ////////////////////////////////////////////////////////////////////////////////
00083 
00084 static bool
00085 test_bit
00086 (
00087 IN int bit,
00088 IN const byte_t * array
00089 )
00090 {
00091         ASSERT(bit >= 0, "Bad bit: %d", bit);
00092         ASSERT(array, "null");
00093 
00094         int offset = bit / 8;
00095         byte_t b = array[offset];
00096         byte_t mask = 1 << (bit % 8);
00097         return b & mask;
00098 }
00099 
00100 
00101 
00102 #define DUMP_EVBIT(name, desc) {                                        \
00103         if (test_bit(EV_ ##name , pb)) { DPRINTF("  %s", desc); }       \
00104         }
00105 
00106 static void
00107 dumpCapabilities
00108 (
00109 IN const byte_t * pb
00110 )
00111 throw()
00112 {
00113         ASSERT(pb, "null");
00114 
00115         DPRINTF("Device capabilities:");        // examples
00116         DUMP_EVBIT(SYN, "Synchronizes");
00117         DUMP_EVBIT(KEY, "Buttons/keys");
00118         DUMP_EVBIT(REL, "Relative input");      // trackballs
00119         DUMP_EVBIT(ABS, "Absolute input");      // joysticks
00120         DUMP_EVBIT(MSC, "Miscellaneous");       // ???
00121         DUMP_EVBIT(SW,  "Switch");
00122         DUMP_EVBIT(LED, "LEDs");
00123         DUMP_EVBIT(SND, "Sound");
00124         DUMP_EVBIT(REP, "Repeat events");
00125         DUMP_EVBIT(FF,  "Force Feedback");
00126         DUMP_EVBIT(PWR, "Power management");
00127         DUMP_EVBIT(FF_STATUS, "Force feedback status");
00128 }
00129 
00130 
00131 
00132 static int
00133 getIndex
00134 (
00135 IN int value,
00136 IN const int * map,
00137 IN int size
00138 )
00139 {
00140         THROW(value >= 0, "Bad input index value: " << value);
00141         ASSERT(map, "null");
00142         ASSERT(size > 0, "Bad map size: %d", size);
00143 
00144         for (int i = 0; i < size; ++i) {
00145                 if (value == map[i])
00146                         return i;
00147         }
00148 
00149         // couldn't find input index in map!
00150 //      THROW(false, "Input index not found in map: " << value);
00151         DPRINTF("WARNING: input index not found in map?  %d", value);
00152 
00153         return -1;      // never get here
00154 }
00155 
00156 
00157 
00158 static bool
00159 isInputEntry
00160 (
00161 IN smart_fd& fd
00162 )
00163 {
00164         ASSERT(fd, "invalid");
00165 
00166         // what can it do?
00167         byte_t eventTypes[(EV_MAX + 7) / 8];
00168         if (ioctl(fd, EVIOCGBIT(0, sizeof(eventTypes)), eventTypes) < 0) {
00169                 return false;           // failed--assume not a joystick!
00170         }
00171 
00172         // we'll call it a "joystick" (or "gamepad-capable device") if it has
00173         //      buttons and potentiometers (absolute axes)
00174         if (!test_bit(EV_KEY, eventTypes) ||
00175             !test_bit(EV_ABS, eventTypes)) {
00176                 return false;
00177         }
00178 
00179         // touchpads and other devices sometimes claim absolute potentiometers!
00180         // require at least one interesting potentiometer and button
00181         byte_t potTypes[(ABS_MAX + 7) / 8];
00182         byte_t keyTypes[(KEY_MAX + 7) / 8];
00183         if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(potTypes)), potTypes) < 0 ||
00184             ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyTypes)), keyTypes) < 0) {
00185                 return false;   // failed to retrieve pot/key capabilities
00186         }
00187 
00188         if (!test_bit(ABS_X, potTypes) || !test_bit(ABS_Y, potTypes)) {
00189                 return false;   // no joystick at all
00190         }
00191         if (test_bit(BTN_TRIGGER, keyTypes) ||
00192             test_bit(BTN_A, keyTypes) ||
00193             test_bit(BTN_1, keyTypes)) {
00194                 return true;    // joystick plus at least one cool button
00195         }
00196 
00197         // got here?  No cool buttons
00198         return false;
00199 }
00200 
00201 
00202 
00203 const char *
00204 getInputIndex
00205 (
00206 IN const char * path
00207 )
00208 {
00209         ASSERT(path, "null");
00210 
00211         const char * key = "/dev/input/event";
00212 
00213         const char * p = strstr(path, key);
00214         THROW(p, "Bad joystick device path: " << path);
00215 
00216         p += strlen(key);
00217 
00218         DPRINTF("path: '%s' --> index '%s'", path, p);
00219         return p;
00220 }
00221 
00222 
00223 
00224 ////////////////////////////////////////////////////////////////////////////////
00225 //
00226 //      LIDevice -- class that implements the gamepad::SourceDevice interface
00227 //              for Linux Input objects (using the unified event model, input.h)
00228 //
00229 ////////////////////////////////////////////////////////////////////////////////
00230 
00231 class LIDevice : public SourceDevice {
00232 public:
00233         // constructor, destructor ---------------------------------------------
00234         LIDevice(void) throw();
00235         ~LIDevice(void) throw();
00236 
00237         // public class methods ------------------------------------------------
00238         void initialize(IN const char * path,
00239                                 IN bool wantForceFeedback);
00240 
00241         // gamepad::SourceDevice class interface methods -----------------------
00242         const char * getPublicName(void) throw() { return m_name.c_str(); }
00243         const char * getUniqueId(void) throw() { return m_uid.c_str(); }
00244         eDeviceState getState(void) throw();
00245         int getNumPots(void) throw() { return m_nPots; }
00246         int getNumButtons(void) throw() { return m_nButtons; }
00247         void poll(void) throw();
00248         const pot_value_t& getPotValue(IN int potIndex);
00249         eButton getButtonValue(IN int buttonIndex);
00250 /*
00251         // Jan 2011: removing all force feedback support for now
00252         int getNumRumblers(void) throw() { return m_nRumble; }
00253         bool playRumble(IN int rumbleIndex,
00254                                 IN uint16_t highFreqMagnitude,
00255                                 IN uint16_t lowFreqMagnitude,
00256                                 IN int durationMilliseconds);
00257  */
00258 
00259 private:
00260         // private helper methods ----------------------------------------------
00261         // private member data -------------------------------------------------
00262         std::string             m_path;         // device path
00263         std::string             m_name;         // public name
00264         std::string             m_uid;          // unique ID
00265         smart_fd                m_fd;           // device handle
00266         byte_t                  m_eventTypes[(EV_MAX + 7) / 8];// capabilities
00267         int                     m_nPots;        // count of potentiometers
00268         int                     m_nButtons;     // count of buttons
00269         std::vector<int>        m_buttonMap;    // local -> device
00270         std::vector<int>        m_potMap;       // local -> device
00271         std::vector<byte_t>     m_button;
00272         std::vector<pot_value_t> m_pot;
00273         std::vector<rumble_t>   m_rumble;
00274 };
00275 
00276 
00277 LIDevice::LIDevice(void)
00278 throw()
00279 {
00280         m_nPots = 0;
00281         m_nButtons = 0;
00282 }
00283 
00284 
00285 LIDevice::~LIDevice(void)
00286 throw()
00287 {
00288 }
00289 
00290 
00291 
00292 ////////////////////////////////////////////////////////////////////////////////
00293 //
00294 //      LIDevice -- public class methods
00295 //
00296 ////////////////////////////////////////////////////////////////////////////////
00297 
00298 void
00299 LIDevice::initialize
00300 (
00301 IN const char * path,
00302 IN bool wantForceFeedback
00303 )
00304 {
00305         perf::Timer timer("LinuxInputDevice::initialize()");
00306         ASSERT(path, "null");
00307 
00308         DPRINTF("Opening linux joystick source device: '%s'", path);
00309 
00310         // hopefully someday this can be removed
00311         if (wantForceFeedback) {
00312                 DPRINTF("Enabling force feedback.");
00313                 DPRINTF("Watch out for kernel crashes when you unplug the gamepad!");
00314         }
00315 
00316         // remember path
00317         m_path = path;
00318 
00319         // open device
00320         THROW(m_fd.open(path, O_RDWR),
00321             "Failed to open linux joystick device: " << path);
00322 
00323         // get the name of the device
00324         char buffer[256];
00325         THROW(ioctl(m_fd, EVIOCGNAME(sizeof(buffer)), buffer) >= 0,
00326             "Failed to acquire linux joystick device name");
00327         m_name = buffer;
00328         DPRINTF("  public name: '%s'", m_name.c_str());
00329 
00330         // get the device information
00331         struct input_id iid;
00332         THROW(ioctl(m_fd, EVIOCGID, &iid) >= 0,
00333             "Failed to acquire event input ID data");
00334 
00335         // construct the unique ID
00336         std::ostringstream out;
00337         out << s_prefix << ",";
00338         out << buffer << ",";
00339         out << "vendor:" << iid.vendor << ",";
00340         out << "product:" << iid.product << ",";
00341         out << "version:" << iid.version << ",";
00342         out << "idx:" << getInputIndex(path);
00343 
00344         m_uid = out.str();
00345         DPRINTF("  unique ID: '%s'", m_uid.c_str());
00346 
00347         // determine device capabilities
00348         THROW(ioctl(m_fd, EVIOCGBIT(0, sizeof(m_eventTypes)), m_eventTypes) >= 0,
00349             "Failed to acquire device capabilities");
00350         dumpCapabilities(m_eventTypes);
00351 
00352         // construct button map
00353         THROW(test_bit(EV_KEY, m_eventTypes),
00354             "Device reported no buttons/keys");
00355         m_buttonMap.clear();
00356         byte_t keyStats[(KEY_MAX + 7) / 8];
00357         THROW(ioctl(m_fd, EVIOCGBIT(EV_KEY, sizeof(keyStats)), keyStats) >= 0,
00358             "Failed to retrieve key/button bitfield");
00359         for (int i = 0; i < KEY_MAX; ++i) {
00360                 if (test_bit(i, keyStats)) {
00361                         m_buttonMap.push_back(i);
00362                         m_button.push_back(eButtonUp);
00363                 }
00364         }
00365         m_nButtons = m_buttonMap.size();
00366         DPRINTF("Found %d buttons on device", m_nButtons);
00367 
00368         // get count of potentiometers
00369         THROW(test_bit(EV_ABS, m_eventTypes),
00370             "Device reported no potentiometers");
00371         m_potMap.clear();
00372         byte_t potStats[(ABS_MAX + 7) / 8];
00373         THROW(ioctl(m_fd, EVIOCGBIT(EV_ABS, sizeof(potStats)), potStats) >= 0,
00374             "Failed to retrieve absolute potentiometer bitfield");
00375         for (int i = 0; i < ABS_MAX; ++i) {
00376                 if (test_bit(i, potStats)) {
00377                         m_potMap.push_back(i);
00378                         pot_value_t pv;
00379                         m_pot.push_back(pv);
00380                 }
00381         }
00382         m_nPots = m_potMap.size();
00383         DPRINTF("Found %d potentiometers on device", m_nPots);
00384 
00385 /*
00386         // Jan 2011: removing all force feedback support for now
00387         // get count of force feedback effects (just rumble for now)
00388         m_nRumble = 0;
00389         m_rumble.clear();
00390         int nEffects = 0;
00391         if (test_bit(EV_FF, m_eventTypes) &&
00392             ioctl(m_fd, EVIOCGEFFECTS, &nEffects) >= 0 &&
00393             nEffects > 0) {
00394                 byte_t ffStats[(FF_MAX + 7) / 8];
00395                 if (ioctl(m_fd, EVIOCGBIT(EV_FF, sizeof(ffStats)), ffStats) >= 0) {
00396                         if (test_bit(FF_RUMBLE, ffStats)) {
00397                                 m_nRumble = nEffects;
00398                         }
00399                 }
00400         }
00401         DPRINTF("Found %d rumble effects", m_nRumble);
00402 
00403         // Ouch!  In testing I've found that repeatedly loading, playing, and
00404         // unloading rumble effects results in a kernel crash when I unplug the
00405         // gamepad (!).
00406         // Observed 12/7/2009 with kernel 2.6.27.38-170.2.113.fc10.i686
00407         if (!wantForceFeedback) {
00408                 DPRINTF("Resetting rumble count to 0 to avoid kernel crashes");
00409                 m_nRumble = 0;
00410         }
00411         rumble_t r;
00412         r.state = eFF_NotLoaded;
00413         r.id = -1;
00414         for (int i = 0; i < m_nRumble; ++i) {
00415                 m_rumble.push_back(r);
00416         }
00417  */
00418 
00419         // last thing: set to non-blocking
00420         THROW(!fcntl(m_fd, F_SETFL, O_NONBLOCK),
00421             "Failed to set linux joystick device to nonblocking i/o");
00422 }
00423 
00424 
00425 
00426 ////////////////////////////////////////////////////////////////////////////////
00427 //
00428 //      LIDevice -- gamepad::SourceDevice class interface methods
00429 //
00430 ////////////////////////////////////////////////////////////////////////////////
00431 
00432 eDeviceState
00433 LIDevice::getState
00434 (
00435 void
00436 )
00437 throw()
00438 {
00439         if (s_devicePaths.end() == s_devicePaths.find(m_path.c_str())) {
00440                 // cannot find device!
00441                 return eDevice_Detached;
00442         }
00443 
00444         // device appears to be there...
00445         return eDevice_Attached;
00446 }
00447 
00448 
00449 
00450 void
00451 LIDevice::poll
00452 (
00453 void
00454 )
00455 throw()
00456 {
00457         ASSERT(m_fd, "need to open device first");
00458 
00459         // clear "was pushed" and "was released" bits for all buttons
00460         for (int i = 0; i < m_nButtons; ++i) {
00461                 m_button[i] &= ~eButtonWasPushed;
00462                 m_button[i] &= ~eButtonWasReleased;
00463         }
00464 
00465         // grab all available events
00466         struct input_event event[32];
00467         while (true) {
00468                 int len = read(m_fd, event, sizeof(event));
00469                 if (len <= 0) {
00470                         break;          // no more events!
00471                 }
00472 
00473                 int nEvents = len / sizeof(event[0]);
00474                 for (int i = 0; i < nEvents; ++i) {
00475                         int idx = -1;
00476                         switch (event[i].type) {
00477                         case EV_ABS:
00478                                 idx = getIndex(event[i].code, m_potMap.data(), m_nPots);
00479                                 if (idx >= 0 && idx < m_nPots) {
00480                                         pot_value_t& pv = m_pot[idx];
00481                                         pv.value = event[i].value;
00482                                         if (pv.value < pv.minSeen) {
00483                                                 pv.minSeen = pv.value;
00484                                         }
00485                                         if (pv.value > pv.maxSeen) {
00486                                                 pv.maxSeen = pv.value;
00487                                         }
00488                                 }
00489                                 break;
00490 
00491                         case EV_KEY:
00492                                 idx = getIndex(event[i].code, m_buttonMap.data(), m_nButtons);
00493                                 if (idx < 0 || idx >= m_nButtons)
00494                                         break;
00495                                 if (event[i].value) {
00496                                         // need to set "was pushed" and "is down" bits
00497                                         m_button[idx] |= eButtonWasPushed;
00498                                         m_button[idx] |= eButtonDown;
00499                                 } else {
00500                                         // need to clear "is down" bit
00501                                         m_button[idx] &= ~eButtonDown;
00502 
00503                                         // need to set "was released" bit
00504                                         m_button[idx] |= eButtonWasReleased;
00505                                 }
00506                                 break;
00507                         }
00508                 }
00509         }
00510 }
00511 
00512 
00513 
00514 const pot_value_t&
00515 LIDevice::getPotValue
00516 (
00517 IN int potIndex
00518 )
00519 {
00520         THROW(potIndex >= 0 && potIndex < m_nPots,
00521             "Bad pot index: " << potIndex);
00522         return m_pot[potIndex];
00523 }
00524 
00525 
00526 
00527 eButton
00528 LIDevice::getButtonValue
00529 (
00530 IN int buttonIndex
00531 )
00532 {
00533         THROW(buttonIndex >= 0 && buttonIndex < m_nButtons,
00534             "Bad button index: " << buttonIndex);
00535         return (eButton) m_button[buttonIndex];
00536 }
00537 
00538 
00539 
00540 /*
00541 bool
00542 LIDevice::playRumble
00543 (
00544 IN int rumbleIndex,
00545 IN uint16_t highFreqMagnitude,
00546 IN uint16_t lowFreqMagnitude,
00547 IN int durationMilliseconds
00548 )
00549 {
00550         THROW(rumbleIndex >= 0 && rumbleIndex < m_nRumble,
00551             "Invalid rumble index: " << rumbleIndex);
00552 
00553         rumble_t& r = m_rumble[rumbleIndex];
00554         bool wantPlay = (durationMilliseconds > 0) &&
00555             (highFreqMagnitude || lowFreqMagnitude);
00556 
00557         // stop if necessary
00558         if (eFF_Playing == r.state) {
00559                 input_event stop;
00560                 stop.type = EV_FF;
00561                 stop.code = r.id;
00562                 stop.value = 0;
00563                 if (write(m_fd, (const void *) &stop, sizeof(stop)) <= 0) {
00564                         DPRINTF("Failed to stop rumble");
00565                         return false;
00566                 }
00567                 r.state = eFF_Loaded;
00568         }
00569 
00570         // unload if necessary
00571         if (eFF_Loaded == r.state) {
00572                 if (ioctl(m_fd, EVIOCRMFF, r.id) < 0) {
00573                         DPRINTF("Failed to unload rumble");
00574                         return false;
00575                 }
00576                 r.state = eFF_NotLoaded;
00577                 r.id = -1;
00578         }
00579 
00580         // need to load?
00581         if (wantPlay && eFF_NotLoaded == r.state) {
00582                 // upload effect to device
00583                 ff_effect e;
00584                 e.type = FF_RUMBLE;
00585                 e.id = -1;
00586                 e.u.rumble.strong_magnitude = lowFreqMagnitude;
00587                 e.u.rumble.weak_magnitude = highFreqMagnitude;
00588                 e.replay.length = durationMilliseconds;
00589                 e.replay.delay = 0;
00590 
00591                 if (ioctl(m_fd, EVIOCSFF, &e) < 0) {
00592                         DPRINTF("Failed to upload rumble");
00593                         return false;
00594                 }
00595                 r.state = eFF_Loaded;
00596                 r.id = e.id;
00597         }
00598 
00599         // play!
00600         if (wantPlay && eFF_NotLoaded != r.state) {
00601                 input_event play;
00602                 play.type = EV_FF;
00603                 play.code = r.id;
00604                 play.value = 1;
00605                 if (write(m_fd, (const void *) &play, sizeof(play)) <= 0) {
00606                         DPRINTF("Failed to play rumble");
00607                         return false;
00608                 }
00609                 r.state = eFF_Playing;
00610         }
00611 
00612         // all done!
00613         return true;
00614 }
00615  */
00616 
00617 
00618 
00619 static void
00620 refreshLinuxInputDevicePaths
00621 (
00622 void
00623 )
00624 {
00625         perf::Timer timer("refreshLinuxInputDevicePaths");
00626 
00627         // get the count of joystick entries!
00628         smart_dir dir;
00629         dir.open(s_devicePath);
00630         ASSERT(dir, "should have a valid open directory now");
00631 
00632         // go ahead and parse the directory
00633         SetString newSet;
00634         struct dirent entry;
00635         while (dir.getNextEntry(entry)) {
00636                 // only look at entries beginning with "event"
00637                 if (strncmp("event", entry.d_name, 5)) {
00638                         continue;       // some other prefix
00639                 }
00640 
00641                 std::string path;
00642                 appendPath(s_devicePath, entry.d_name, path);
00643 
00644                 // quick check--already know about this one?
00645                 if (s_devicePaths.end() != s_devicePaths.find(path.c_str())) {
00646                         // yes, known good path
00647                         newSet.insert(path);
00648                         continue;
00649                 }
00650 
00651                 // new path--see if this is a joystick object
00652                 smart_fd fd;
00653                 if (fd.open(path.c_str(), O_RDONLY) && isInputEntry(fd)) {
00654                         newSet.insert(path);
00655                 }
00656         }
00657 
00658         // copy over
00659         s_devicePaths = newSet;
00660 }
00661 
00662 
00663 
00664 smart_ptr<SourceDevice>
00665 getLinuxInputSourceDevice
00666 (
00667 IN const char * path,
00668 IN bool wantForceFeedback
00669 )
00670 {
00671         ASSERT(path, "null");
00672 
00673         // okay, create device!
00674         smart_ptr<LIDevice> local = new LIDevice;
00675         ASSERT(local, "out of memory");
00676         local->initialize(path, wantForceFeedback);
00677 
00678         // all done
00679         return local;
00680 }
00681 
00682 
00683 
00684 ////////////////////////////////////////////////////////////////////////////////
00685 //
00686 //      Factory -- class that implements the gamepad::SourceDeviceFactory
00687 //              interface for linux input events.
00688 //
00689 ////////////////////////////////////////////////////////////////////////////////
00690 
00691 class Factory : public SourceDeviceFactory {
00692 public:
00693         // constructor, destructor ---------------------------------------------
00694         ~Factory(void) throw() { }
00695 
00696         // public class methods ------------------------------------------------
00697         void initialize(IN bool wantForceFeedback) {
00698                         m_wantForceFeedback = wantForceFeedback;
00699                 }
00700 
00701         // gamepad::SourceDeviceFactory class interface methods ----------------
00702         const char * getName(void) const throw()
00703                                 { return "Linux Input Factory"; }
00704         int getCount(void); 
00705         smart_ptr<SourceDevice> getSourceDevice(IN int index);
00706 
00707 private:
00708         // private typedefs ----------------------------------------------------
00709         typedef std::map<std::string, smart_ptr<SourceDevice> > device_map_t;
00710 
00711         // private member data -------------------------------------------------
00712         bool            m_wantForceFeedback;
00713         device_map_t    m_devices;      // instantiated devices (path -> device)
00714 };
00715 
00716 
00717 
00718 int
00719 Factory::getCount
00720 (
00721 void
00722 )
00723 {
00724         // refresh our set of device paths
00725         refreshLinuxInputDevicePaths();
00726 
00727         // get rid of any source devices we no longer recognize
00728         VecString badPaths;
00729         for (device_map_t::iterator i = m_devices.begin(); i != m_devices.end();
00730              ++i) {
00731                 const char * path = i->first.c_str();
00732                 if (s_devicePaths.end() == s_devicePaths.find(path)) {
00733                         // no longer recognize this path--device detached?
00734                         badPaths.push_back(path);
00735                 }
00736         }
00737         for (VecString::iterator i = badPaths.begin(); i != badPaths.end();
00738              ++i) {
00739                 device_map_t::iterator iBad = m_devices.find(*i);
00740                 ASSERT(m_devices.end() != iBad, "Just saw this in map!");
00741                 m_devices.erase(iBad);
00742         }
00743         ASSERT(m_devices.size() <= s_devicePaths.size(),
00744             "Should have devices for a subset of paths!");
00745 
00746         // return size
00747         return s_devicePaths.size();
00748 }
00749 
00750 
00751 
00752 smart_ptr<SourceDevice>
00753 Factory::getSourceDevice
00754 (
00755 IN int index
00756 )
00757 {
00758         // get path for this index
00759         const char * path = NULL;
00760         for (SetString::const_iterator i = s_devicePaths.begin();
00761              i != s_devicePaths.end(); ++i, --index) {
00762                 if (!index) {
00763                         path = i->c_str();
00764                         break;
00765                 }
00766         }
00767         if (!path) {
00768                 DPRINTF("Invalid source device index: %d", index);
00769                 return NULL;
00770         }
00771 
00772         // return device if we've already got it
00773         device_map_t::iterator i = m_devices.find(path);
00774         if (m_devices.end() != i) {
00775                 return i->second;
00776         }
00777 
00778         // don't know this device!  Add it
00779         smart_ptr<SourceDevice> device =
00780             getLinuxInputSourceDevice(path, m_wantForceFeedback);
00781         ASSERT(device, "failed to create linux input source device");
00782         m_devices[path] = device;
00783         return device;
00784 }
00785 
00786 
00787 
00788 ////////////////////////////////////////////////////////////////////////////////
00789 //
00790 //      public API
00791 //
00792 ////////////////////////////////////////////////////////////////////////////////
00793 
00794 smart_ptr<SourceDeviceFactory>
00795 getLinuxInputSourceDeviceFactory
00796 (
00797 IN bool wantForceFeedback
00798 )
00799 {
00800         smart_ptr<Factory> local = new Factory;
00801         ASSERT(local, "out of memory");
00802 
00803         local->initialize(wantForceFeedback);
00804 
00805         return local;
00806 }
00807 
00808 
00809 
00810 };      // gamepad namespace
00811