direct-input.cpp

Go to the documentation of this file.
00001 /*
00002  * direct-input.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  * Input support built on the Microsoft DirectX DirectInput APIs
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "direct-input.h"               // always include our own header first
00036 #include "di-diagnostics.h"
00037 
00038 #include "perf/perf.h"
00039 #include "wave-windows/wave-windows.h"  // wavepacket windows helper library
00040 
00041 #include <iostream>
00042 
00043 
00044 namespace gamepad {
00045 
00046 
00047 // TODO: is this defined already somewhere in a DirectInput header?
00048 // this is the constant we need to decipher POV values
00049 static const int s_FortyFiveDegreesValue = 4500;
00050 
00051 
00052 ////////////////////////////////////////////////////////////////////////////////
00053 //
00054 //      static helper methods
00055 //
00056 ////////////////////////////////////////////////////////////////////////////////
00057 
00058 
00059 ////////////////////////////////////////////////////////////////////////////////
00060 //
00061 //      DirectInputDevice -- class that implements the gamepad::SourceDevice
00062 //              interface for Microsoft DirectInput devices.
00063 //
00064 ////////////////////////////////////////////////////////////////////////////////
00065 
00066 class DirectInputDevice : public SourceDevice {
00067 public:
00068         // constructor, destructor ---------------------------------------------
00069         DirectInputDevice(void) throw();
00070         ~DirectInputDevice(void) throw();
00071 
00072         // public class methods ------------------------------------------------
00073         // gamepad::SourceDevice class interface methods -----------------------
00074         virtual const char * getPublicName(void) throw() { return m_name.c_str(); }
00075         virtual const char * getUniqueId(void) throw() { return m_id.c_str(); }
00076         virtual eDeviceState getState(void) throw();
00077         virtual int getNumPots(void) throw() { return m_dcaps.nAxes; }
00078         virtual int getNumButtons(void) throw() { return m_dcaps.nButtons; }
00079         virtual int getNumRumblers(void) throw() { return 0; }
00080         virtual void poll(void) throw();
00081         virtual const pot_value_t& getPotValue(IN int potIndex);
00082         virtual eButton getButtonValue(IN int buttonIndex);
00083         virtual bool playRumble(IN int rumbleIndex,
00084                                 IN uint16_t highFreqMagnitude,
00085                                 IN uint16_t lowFreqMagnitude,
00086                                 IN int durationMilliseconds) { return false; }
00087 
00088         // public static methods (factories) -----------------------------------
00089         static smart_ptr<SourceDevice> create(IN IDirectInput8 * dinput,
00090                                 IN const DIDEVICEINSTANCE& ddi);
00091 
00092 private:
00093         // private typedefs ----------------------------------------------------
00094         typedef std::vector<pot_value_t> pot_vector_t;
00095         typedef std::vector<byte_t> byte_vector_t;
00096 
00097         // private helper methods ----------------------------------------------
00098         void initialize(IN IDirectInput8 * dinput,
00099                                 IN const DIDEVICEINSTANCE& di);
00100 
00101         // private member data -------------------------------------------------
00102         COM_ptr<IDirectInputDevice8>    m_device;
00103         std::string                     m_name;
00104         std::string                     m_id;
00105         device_caps_t                   m_dcaps;
00106         pot_vector_t                    m_pot;
00107         byte_vector_t                   m_button;
00108         byte_t *                        m_data; // raw gamepad data
00109 };
00110 
00111 
00112 
00113 DirectInputDevice::DirectInputDevice
00114 (
00115 void
00116 )
00117 throw()
00118 {
00119         m_data = NULL;
00120 }
00121 
00122 
00123 
00124 DirectInputDevice::~DirectInputDevice(void) throw()
00125 {
00126         if (m_data) {
00127                 delete[] m_data;
00128         }
00129 }
00130 
00131 
00132 
00133 ////////////////////////////////////////////////////////////////////////////////
00134 //
00135 //      DirectInputDevice -- gamepad::SourceDevice class interface methods
00136 //
00137 ////////////////////////////////////////////////////////////////////////////////
00138 
00139 eDeviceState
00140 DirectInputDevice::getState
00141 (
00142 void
00143 )
00144 throw()
00145 {
00146         return eDevice_Unknown;
00147 }
00148 
00149 
00150 
00151 void 
00152 DirectInputDevice::poll
00153 (
00154 void
00155 )
00156 throw()
00157 {
00158         ASSERT(m_device, "null");
00159 
00160         // get new data values
00161         HRESULT hr = m_device->Poll();
00162         DI_VERIFY(DI_OK == hr || DI_NOEFFECT == hr, hr,
00163             "Failed to poll device");
00164         hr = m_device->GetDeviceState(m_dcaps.dataBytes, m_data);
00165         DI_VERIFY(DI_OK == hr, hr,
00166             "Failed to get device state");
00167 
00168         // update potentiometers
00169         for (int i = 0; i < m_dcaps.nAxes; ++i) {
00170                 m_pot[i].update(getAxisValue(m_data, m_dcaps, i));
00171         }
00172 
00173         // update buttons
00174         for (int i = 0; i < m_dcaps.nButtons; ++i) {
00175                 // clear "was pushed" and "was released" bits
00176                 m_button[i] &= ~eButtonWasPushed;
00177                 m_button[i] &= ~eButtonWasReleased;
00178 
00179                 dword_t dwValue = gamepad::getButtonValue(m_data, m_dcaps, i);
00180                 m_button[i] = updateButton(m_button[i], dwValue != 0);
00181         }
00182 }
00183 
00184 
00185 
00186 const pot_value_t& 
00187 DirectInputDevice::getPotValue
00188 (
00189 IN int potIndex
00190 )
00191 {
00192         ASSERT(potIndex >= 0 && potIndex < m_dcaps.nAxes,
00193             "Invalid potentiometer index: %d", potIndex);
00194 
00195         return m_pot[potIndex];
00196 }
00197 
00198 
00199 
00200 eButton 
00201 DirectInputDevice::getButtonValue
00202 (
00203 IN int buttonIndex
00204 )
00205 {
00206         ASSERT(buttonIndex >= 0 && buttonIndex < m_dcaps.nButtons,
00207             "Bad button index: %d", buttonIndex);
00208 
00209         return (eButton) m_button[buttonIndex];
00210 }
00211 
00212 
00213 
00214 ////////////////////////////////////////////////////////////////////////////////
00215 //
00216 //      DirectInputDevice -- public class methods
00217 //
00218 ////////////////////////////////////////////////////////////////////////////////
00219 
00220 smart_ptr<SourceDevice>
00221 DirectInputDevice::create
00222 (
00223 IN IDirectInput8 * dinput,
00224 IN const DIDEVICEINSTANCE& ddi
00225 )
00226 {
00227         ASSERT(dinput, "null");
00228 
00229         smart_ptr<DirectInputDevice> local = new DirectInputDevice;
00230         ASSERT(local, "out of memory");
00231 
00232         local->initialize(dinput, ddi);
00233 
00234         return local;
00235 }
00236 
00237 
00238 
00239 
00240 void
00241 DirectInputDevice::initialize
00242 (
00243 IN IDirectInput8 * dinput,
00244 IN const DIDEVICEINSTANCE& ddi
00245 )
00246 {
00247         ASSERT(dinput, "null");
00248         ASSERT(!m_device, "already have a direct input device?");
00249 
00250         LPUNKNOWN punkOuter = NULL;
00251         IDirectInputDevice8 * pdev = NULL;
00252         HRESULT hr = dinput->CreateDevice(ddi.guidInstance, &pdev, punkOuter);
00253         DI_VERIFY(S_OK == hr && pdev, hr,
00254              "Failed to create direct input device for gamepad");
00255         m_device = pdev;
00256 
00257         // save some fields
00258         m_name = ddi.tszInstanceName;
00259         m_name += ": ";
00260         m_name += ddi.tszProductName;
00261 
00262         const int bufsize = 64;
00263         char buffer[bufsize];
00264         m_id = getGuidString(ddi.guidInstance, buffer, bufsize);
00265 
00266         // create custom data format
00267         m_data = getDataFormatForDevice(m_device, m_dcaps);
00268         DPRINTF("Just created data format for new DirectInput device");
00269         DPRINTF("  nAxes = nPots = %d", m_dcaps.nAxes);
00270         DPRINTF("  nPOVs = %d", m_dcaps.nPOVs);
00271         DPRINTF("  nButtons = %d", m_dcaps.nButtons);
00272         DPRINTF("  data size = %d bytes", m_dcaps.dataBytes);
00273 
00274         // set the data format
00275         hr = m_device->SetDataFormat((LPCDIDATAFORMAT) m_data);
00276         DI_VERIFY(DI_OK == hr, hr, "Failed to set data format on new device");
00277 
00278         // acquire the device
00279         hr = m_device->Acquire();
00280         DI_VERIFY(DI_OK == hr || S_FALSE == hr, hr,
00281             "Failed to Acquire() new device");
00282 
00283         // set up pot + button arrays
00284         m_pot.resize(m_dcaps.nAxes);
00285         m_button.resize(m_dcaps.nButtons, eButtonUp);
00286 }
00287 
00288 
00289 
00290 ////////////////////////////////////////////////////////////////////////////////
00291 //
00292 //      DirectInputFactory -- object that implements the SourceDeviceFactory
00293 //              interface based on the Microsoft DirectInput APIs
00294 //
00295 ////////////////////////////////////////////////////////////////////////////////
00296 
00297 class DirectInputFactory : public SourceDeviceFactory {
00298 public:
00299         // constructor, destructor ---------------------------------------------
00300         // public class methods ------------------------------------------------
00301         void initialize(void);
00302 
00303         // gamepad::SourceDeviceFactory class interface methods ----------------
00304         virtual const char * getName(void) const throw();
00305         virtual int getCount(void);
00306         virtual smart_ptr<SourceDevice> getSourceDevice(IN int index);
00307 
00308 private:
00309         // private typedefs ----------------------------------------------------
00310         struct enum_device_context_t {
00311                 // constructor, manipulators
00312                 enum_device_context_t(void) throw() { this->clear(); }
00313                 void clear(void) throw() {
00314                                 pThis = NULL;
00315                         }
00316 
00317                 // data fields
00318                 DirectInputFactory *    pThis;
00319         };
00320 
00321         typedef std::map<std::string, smart_ptr<SourceDevice> > device_map_t;
00322 
00323         // private helper methods ----------------------------------------------
00324         void refreshDevices(void);
00325         static BOOL CALLBACK deviceCallback(LPCDIDEVICEINSTANCEA lpddi,
00326                                 void * context);
00327         void addDevice(IN const DIDEVICEINSTANCEA& ddi);
00328 
00329         // private member data -------------------------------------------------
00330         COM_ptr<IDirectInput8>          m_dinput;
00331         device_map_t                    m_devices;
00332 };
00333 
00334 
00335 
00336 ////////////////////////////////////////////////////////////////////////////////
00337 //
00338 //      DirectInputFactory -- public class methods
00339 //
00340 ////////////////////////////////////////////////////////////////////////////////
00341 
00342 void
00343 DirectInputFactory::initialize
00344 (
00345 void
00346 )
00347 {
00348         DPRINTF("Creating a new DirectInput device factory...");
00349 
00350         ASSERT(!m_dinput, "already have a direct input object?");
00351 
00352         // create our DirectInput object
00353         HINSTANCE hinst = GetModuleHandle(NULL);
00354         dword_t dwVersion = DIRECTINPUT_VERSION;
00355         REFIID riid = IID_IDirectInput8;
00356         LPUNKNOWN punkOuter = NULL;
00357         HRESULT hr = DirectInput8Create(hinst, dwVersion, riid, m_dinput,
00358             punkOuter);
00359         DI_VERIFY(DI_OK == hr && m_dinput, hr,
00360             "Failed to create DirectInput object");
00361 }
00362 
00363 
00364 
00365 ////////////////////////////////////////////////////////////////////////////////
00366 //
00367 //      DirectInputFactory -- gamepad::SourceDeviceFactory methods
00368 //
00369 ////////////////////////////////////////////////////////////////////////////////
00370 
00371 const char *
00372 DirectInputFactory::getName
00373 (
00374 void
00375 )
00376 const
00377 throw()
00378 {
00379         return "DirectInput Factory";
00380 }
00381 
00382 
00383 
00384 int
00385 DirectInputFactory::getCount
00386 (
00387 void
00388 )
00389 {
00390         perf::Timer timer("DirectInputFactory::getCount");
00391         this->refreshDevices();
00392 
00393         return m_devices.size();
00394 }
00395 
00396 
00397 
00398 smart_ptr<SourceDevice>
00399 DirectInputFactory::getSourceDevice
00400 (
00401 IN int index
00402 )
00403 {
00404         ASSERT(index >= 0, "Bad device index: %d", index);
00405 
00406         if (index >= (int) m_devices.size()) {
00407                 DPRINTF("Device %d is now detached (?)", index);
00408                 return NULL;
00409         }
00410 
00411         device_map_t::iterator i = m_devices.begin();
00412         for (; index > 0; --index) {
00413                 ++i;
00414         }
00415 
00416         return i->second;
00417 }
00418 
00419 
00420 
00421 ////////////////////////////////////////////////////////////////////////////////
00422 //
00423 //      DirectInputFactory -- private helper methods
00424 //
00425 ////////////////////////////////////////////////////////////////////////////////
00426 
00427 void
00428 DirectInputFactory::refreshDevices
00429 (
00430 void
00431 )
00432 {
00433         ASSERT(m_dinput, "null");
00434 
00435         // enumerate all gamepads
00436         dword_t dwDevType = DI8DEVCLASS_GAMECTRL;
00437         dword_t dwFlags = DIEDFL_ALLDEVICES;
00438         enum_device_context_t edc;
00439         edc.pThis = this;
00440         HRESULT hr = m_dinput->EnumDevices(dwDevType, deviceCallback, &edc,
00441             dwFlags);
00442         DI_VERIFY(DI_OK == hr, hr, "Failed to enumerate DirectInput devices");
00443 }
00444 
00445 
00446 
00447 BOOL CALLBACK
00448 DirectInputFactory::deviceCallback
00449 (
00450 LPCDIDEVICEINSTANCEA lpddi,
00451 void * context
00452 )
00453 {
00454         ASSERT(lpddi, "null device instance?");
00455         enum_device_context_t * pedc = (enum_device_context_t *) context;
00456         ASSERT(pedc, "null context");
00457         ASSERT(pedc->pThis, "null");
00458 
00459         pedc->pThis->addDevice(*lpddi);
00460 
00461         return DIENUM_CONTINUE;
00462 }
00463 
00464 
00465 
00466 void
00467 DirectInputFactory::addDevice
00468 (
00469 IN const DIDEVICEINSTANCEA& ddi
00470 )
00471 {
00472         const int bufsize = 64;
00473         char guid[bufsize];
00474         getGuidString(ddi.guidInstance, guid, bufsize);
00475 //      DPRINTF("Checking device: %s", guid);
00476 
00477         // already have this device?
00478         if (m_devices.end() != m_devices.find(guid)) {
00479                 // already have it!
00480                 return;
00481         }
00482 
00483         // this is a device we haven't seen before
00484         DPRINTF("New direct input device: %s", guid);
00485         smart_ptr<SourceDevice> device =
00486             DirectInputDevice::create(m_dinput, ddi);
00487         ASSERT(device, "null");
00488 
00489         // add to map
00490         m_devices[guid] = device;
00491 }
00492 
00493 
00494 
00495 ////////////////////////////////////////////////////////////////////////////////
00496 //
00497 //      public API
00498 //
00499 ////////////////////////////////////////////////////////////////////////////////
00500 
00501 smart_ptr<SourceDeviceFactory>
00502 getDirectInputSourceDeviceFactory
00503 (
00504 void
00505 )
00506 {
00507         smart_ptr<DirectInputFactory> local = new DirectInputFactory;
00508         ASSERT(local, "out of memory");
00509 
00510         local->initialize();
00511 
00512         return local;
00513 }
00514 
00515 
00516 
00517 };      // gamepad namespace