Panda3D
|
00001 // Filename: dxInput9.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_wdxdisplay9.h" 00016 #include "dxInput9.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 DInput9Info::DInput9Info() { 00036 _pDInput9 = NULL; 00037 _hDInputDLL = NULL; 00038 _JoystickPollTimer = NULL; 00039 } 00040 00041 DInput9Info::~DInput9Info() { 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(_pDInput9); 00052 if(_hDInputDLL) { 00053 FreeLibrary(_hDInputDLL); 00054 _hDInputDLL=NULL; 00055 } 00056 } 00057 00058 bool DInput9Info::InitDirectInput() { 00059 HRESULT hr; 00060 00061 // assumes dx9 exists 00062 // use dynamic load so non-dinput programs don't have to load dinput 00063 #define DLLNAME "dinput9.dll" 00064 #define DINPUTCREATE "DirectInput9Create" 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 * LPDIRECTINPUT9CREATE)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter); 00073 LPDIRECTINPUT9CREATE pDInputCreate9; 00074 00075 pDInputCreate9 = (LPDIRECTINPUT9CREATE) GetProcAddress(_hDInputDLL,DINPUTCREATE); 00076 if(pDInputCreate9 == 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 = (*pDInputCreate9)(GetModuleHandle(NULL), DIRECTINPUT_VERSION, 00085 IID_IDirectInput9, (VOID**)&_pDInput9, 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 = _pDInput9->EnumDevices(DI9DEVCLASS_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 DInput9Info::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==DI9DEVTYPE_GAMEPAD) ||(devType==DI9DEVTYPE_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 LPDIRECTINPUTDEVICE9 pJoyDevice; 00121 00122 // Obtain an interface to the enumerated joystick. 00123 HRESULT hr = _pDInput9->CreateDevice(_DevInfos[devnum].guidInstance, &pJoyDevice, NULL ); 00124 if(FAILED(hr)) { 00125 errstr="CreateDevice"; 00126 goto handle_error; 00127 } 00128 00129 assert(pJoyDevice!=NULL); 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 assert(SUCCEEDED(hr)); 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 LPDIRECTINPUTDEVICE9 pJoyDevice = (LPDIRECTINPUTDEVICE9) 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 DInput9Info::ReadJoystick(int devnum, DIJOYSTATE2 &js) { 00216 LPDIRECTINPUTDEVICE9 pJoystick = _DeviceList[devnum]; 00217 assert(pJoystick!=NULL); 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 }