Panda3D
 All Classes Functions Variables Enumerations
dxInput8.cxx
00001 // Filename: dxInput8.cxx
00002 // Created by:  angelina jolie (07Oct99)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "config_wdxdisplay8.h"
00016 #include "dxInput8.h"
00017 
00018 #define AXIS_RESOLUTION 2000   // use this many levels of resolution by default (could be more if needed and device supported it)
00019 #define AXIS_RANGE_CENTERED    // if defined, axis range is centered on 0, instead of starting on 0
00020 
00021 BOOL CALLBACK EnumGameCtrlsCallback( const DIDEVICEINSTANCE* pdidInstance,
00022                                      VOID* pContext ) {
00023     DI_DeviceInfos *pDevInfos = (DI_DeviceInfos *)pContext;
00024 
00025     (*pDevInfos).push_back(*pdidInstance);
00026 
00027     if(wdxdisplay_cat.is_debug())
00028         wdxdisplay_cat.debug() << "Found DevType 0x" << (void*)pdidInstance->dwDevType << ": " << pdidInstance->tszInstanceName << ": " << pdidInstance->tszProductName <<endl;
00029 
00030     return DIENUM_CONTINUE;
00031 }
00032 
00033 extern BOOL CALLBACK EnumObjectsCallbackJoystick(const DIDEVICEOBJECTINSTANCE* pdidoi,VOID* pContext);
00034 
00035 DInput8Info::DInput8Info() {
00036     _pDInput8 = NULL;
00037     _hDInputDLL = NULL;
00038     _JoystickPollTimer = NULL;
00039 }
00040 
00041 DInput8Info::~DInput8Info() {
00042   for(UINT i=0;i<_DeviceList.size();i++) {
00043       _DeviceList[i]->Unacquire();
00044       SAFE_RELEASE(_DeviceList[i]);
00045   }
00046 
00047   // bugbug: need to handle this
00048   // if(_JoystickPollTimer!=NULL)
00049   //   KillTimer(...)
00050 
00051   SAFE_RELEASE(_pDInput8);
00052   if(_hDInputDLL) {
00053       FreeLibrary(_hDInputDLL);
00054       _hDInputDLL=NULL;
00055   }
00056 }
00057 
00058 bool DInput8Info::InitDirectInput() {
00059     HRESULT hr;
00060 
00061     // assumes dx8 exists
00062     // use dynamic load so non-dinput programs don't have to load dinput
00063     #define DLLNAME "dinput8.dll"
00064     #define DINPUTCREATE "DirectInput8Create"
00065 
00066     HINSTANCE _hDInputDLL = LoadLibrary(DLLNAME);
00067     if(_hDInputDLL == 0) {
00068         wdxdisplay_cat.fatal() << "LoadLibrary(" << DLLNAME <<") failed!, error=" << GetLastError() << endl;
00069         exit(1);
00070     }
00071 
00072     typedef HRESULT (WINAPI * LPDIRECTINPUT8CREATE)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);
00073     LPDIRECTINPUT8CREATE pDInputCreate8;
00074 
00075     pDInputCreate8 = (LPDIRECTINPUT8CREATE) GetProcAddress(_hDInputDLL,DINPUTCREATE);
00076     if(pDInputCreate8 == NULL) {
00077         wdxdisplay_cat.fatal() << "GetProcAddr failed for " << DINPUTCREATE << endl;
00078         exit(1);
00079     }
00080 
00081     // Register with the DirectInput subsystem and get a pointer
00082     // to a IDirectInput interface we can use.
00083     // Create a DInput object
00084     if( FAILED( hr = (*pDInputCreate8)(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
00085                                          IID_IDirectInput8, (VOID**)&_pDInput8, NULL ) ) ) {
00086         wdxdisplay_cat.error() << DINPUTCREATE << "failed" << D3DERRORSTRING(hr);
00087         return false;
00088     }
00089 
00090     // enum all the joysticks,etc  (but not keybd/mouse)
00091     if( FAILED( hr = _pDInput8->EnumDevices(DI8DEVCLASS_GAMECTRL,
00092                                          EnumGameCtrlsCallback,
00093                                          (LPVOID)&_DevInfos, DIEDFL_ATTACHEDONLY ) ) ) {
00094         wdxdisplay_cat.error() << "EnumDevices failed" << D3DERRORSTRING(hr);
00095         return false;
00096     }
00097 
00098     return true;
00099 }
00100 
00101 bool DInput8Info::CreateJoystickOrPad(HWND _window) {
00102     bool bFoundDev = false;
00103     UINT devnum=0;
00104     char *errstr=NULL;
00105 
00106     // look through the list for the first joystick or gamepad
00107     for(;devnum<_DevInfos.size();devnum++) {
00108         DWORD devType = GET_DIDEVICE_TYPE(_DevInfos[devnum].dwDevType);
00109         if((devType==DI8DEVTYPE_GAMEPAD) ||(devType==DI8DEVTYPE_JOYSTICK)) {
00110           bFoundDev=true;
00111           break;
00112         }
00113     }
00114 
00115     if(!bFoundDev) {
00116         wdxdisplay_cat.error() << "Cant find an attached Joystick or GamePad!\n";
00117         return false;
00118     }
00119 
00120     LPDIRECTINPUTDEVICE8 pJoyDevice;
00121 
00122     // Obtain an interface to the enumerated joystick.
00123     HRESULT hr = _pDInput8->CreateDevice(_DevInfos[devnum].guidInstance, &pJoyDevice, NULL );
00124     if(FAILED(hr)) {
00125         errstr="CreateDevice";
00126         goto handle_error;
00127     }
00128 
00129     nassertr(pJoyDevice!=NULL, false);
00130     _DeviceList.push_back(pJoyDevice);
00131 
00132     // Set the data format to "simple joystick" - a predefined data format
00133     //
00134     // A data format specifies which controls on a device we are interested in,
00135     // and how they should be reported. This tells DInput that we will be
00136     // passing a DIJOYSTATE2 structure to IDirectInputDevice::GetDeviceState().
00137     hr = pJoyDevice->SetDataFormat(&c_dfDIJoystick2);
00138     if(FAILED(hr)) {
00139         errstr="SetDataFormat";
00140         goto handle_error;
00141     }
00142 
00143     // must be called AFTER SetDataFormat to get all the proper flags
00144     DX_DECLARE_CLEAN(DIDEVCAPS, DIDevCaps);
00145     hr = pJoyDevice->GetCapabilities(&DIDevCaps);
00146     nassertr(SUCCEEDED(hr), false);
00147 
00148     _DevCaps.push_back(DIDevCaps);
00149 
00150     if(wdxdisplay_cat.is_debug())
00151         wdxdisplay_cat.debug() << "Joy/Pad has " << DIDevCaps.dwAxes << " Axes, " <<  DIDevCaps.dwButtons << " Buttons, " <<  DIDevCaps.dwPOVs << " POVs" << endl;
00152 
00153     // Set the cooperative level to let DInput know how this device should
00154     // interact with the system and with other DInput applications.
00155     hr = pJoyDevice->SetCooperativeLevel( _window, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
00156     if(FAILED(hr)) {
00157         errstr="SetCooperativeLevel";
00158         goto handle_error;
00159     }
00160 
00161     // set the min/max values property for discovered axes.
00162     hr = pJoyDevice->EnumObjects(EnumObjectsCallbackJoystick, (LPVOID)pJoyDevice, DIDFT_AXIS);
00163     if(FAILED(hr)) {
00164         errstr="EnumObjects";
00165         goto handle_error;
00166     }
00167 
00168     return true;
00169 
00170   handle_error:
00171     wdxdisplay_cat.error() << errstr << " failed for (" << _DevInfos[devnum].tszInstanceName << ":" << _DevInfos[devnum].tszProductName << ")" << D3DERRORSTRING(hr);
00172     return false;
00173 }
00174 
00175 //-----------------------------------------------------------------------------
00176 // Name: EnumObjectsCallback()
00177 // Desc: Callback function for enumerating objects (axes, buttons, POVs) on a
00178 //       joystick. This function enables user interface elements for objects
00179 //       that are found to exist, and scales axes min/max values.
00180 //-----------------------------------------------------------------------------
00181 BOOL CALLBACK EnumObjectsCallbackJoystick( const DIDEVICEOBJECTINSTANCE* pdidoi,
00182                                    VOID* pContext ) {
00183 
00184     LPDIRECTINPUTDEVICE8 pJoyDevice = (LPDIRECTINPUTDEVICE8) pContext;
00185     HRESULT hr;
00186 
00187     // For axes that are returned, set the DIPROP_RANGE property for the
00188     // enumerated axis in order to scale min/max values.
00189     if( pdidoi->dwType & DIDFT_AXIS ) {
00190         DIPROPRANGE diprg;
00191         diprg.diph.dwSize       = sizeof(DIPROPRANGE);
00192         diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
00193         diprg.diph.dwHow        = DIPH_BYID;
00194         diprg.diph.dwObj        = pdidoi->dwType; // Specify the enumerated axis
00195 
00196      #ifdef AXIS_RANGE_CENTERED
00197         diprg.lMin              = -AXIS_RESOLUTION/2;
00198         diprg.lMax              = +AXIS_RESOLUTION/2;
00199      #else
00200         diprg.lMin              = 0;
00201         diprg.lMax              = +AXIS_RESOLUTION;
00202      #endif
00203 
00204         // Set the range for the axis
00205         hr = pJoyDevice->SetProperty( DIPROP_RANGE, &diprg.diph);
00206         if(FAILED(hr)) {
00207           wdxdisplay_cat.error() << "SetProperty on axis failed" << D3DERRORSTRING(hr);
00208           return DIENUM_STOP;
00209         }
00210     }
00211 
00212     return DIENUM_CONTINUE;
00213 }
00214 
00215 bool DInput8Info::ReadJoystick(int devnum, DIJOYSTATE2 &js) {
00216     LPDIRECTINPUTDEVICE8 pJoystick = _DeviceList[devnum];
00217     nassertr(pJoystick!=NULL, false);
00218     HRESULT hr;
00219     char *errstr;
00220 
00221     // Poll the device to read the current state
00222 
00223     hr = pJoystick->Poll();
00224 
00225     if( FAILED(hr) ) {
00226         // DInput is telling us that the input stream has been
00227         // interrupted. We aren't tracking any state between polls, so
00228         // we don't have any special reset that needs to be done. We
00229         // just re-acquire and try again.
00230 
00231         if((hr==DIERR_NOTACQUIRED)||(hr == DIERR_INPUTLOST)) {
00232             hr = pJoystick->Acquire();
00233 
00234             if(FAILED(hr)) {
00235                 if(wdxdisplay_cat.is_spam())
00236                    wdxdisplay_cat.spam() << "Acquire failed" << D3DERRORSTRING(hr);
00237 
00238                 // hr may be DIERR_OTHERAPPHASPRIO or other errors.  This
00239                 // may occur when the app is minimized or in the process of
00240                 // switching, so just try again later
00241                 return false;
00242             }
00243 
00244             hr = pJoystick->Poll();
00245             if(FAILED(hr)) {
00246                 // should never happen!
00247                 errstr = "Poll after successful Acquire failed";
00248                 goto handle_error;
00249             }
00250         } else {
00251             errstr =  "Unknown Poll failure";
00252             goto handle_error;
00253         }
00254     }
00255 
00256     // should we make a vector of devstate dataformats to generalize this fn for all device types?
00257 
00258     // Get the input's device state
00259     hr = pJoystick->GetDeviceState( sizeof(DIJOYSTATE2), &js);
00260     if(FAILED(hr)) {
00261         errstr =  "GetDeviceState failed";
00262         goto handle_error;
00263     }
00264 
00265     return true;
00266 
00267   handle_error:
00268      wdxdisplay_cat.fatal() << errstr << D3DERRORSTRING(hr);
00269      return false;
00270 }
00271 
00272 
 All Classes Functions Variables Enumerations