Panda3D
dxInput9.cxx
1 // Filename: dxInput9.cxx
2 // Created by: angelina jolie (07Oct99)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "config_wdxdisplay9.h"
16 #include "dxInput9.h"
17 
18 #define AXIS_RESOLUTION 2000 // use this many levels of resolution by default (could be more if needed and device supported it)
19 #define AXIS_RANGE_CENTERED // if defined, axis range is centered on 0, instead of starting on 0
20 
21 BOOL CALLBACK EnumGameCtrlsCallback( const DIDEVICEINSTANCE* pdidInstance,
22  VOID* pContext ) {
23  DI_DeviceInfos *pDevInfos = (DI_DeviceInfos *)pContext;
24 
25  (*pDevInfos).push_back(*pdidInstance);
26 
27  if(wdxdisplay_cat.is_debug())
28  wdxdisplay_cat.debug() << "Found DevType 0x" << (void*)pdidInstance->dwDevType << ": " << pdidInstance->tszInstanceName << ": " << pdidInstance->tszProductName <<endl;
29 
30  return DIENUM_CONTINUE;
31 }
32 
33 extern BOOL CALLBACK EnumObjectsCallbackJoystick(const DIDEVICEOBJECTINSTANCE* pdidoi,VOID* pContext);
34 
35 DInput9Info::DInput9Info() {
36  _pDInput9 = NULL;
37  _hDInputDLL = NULL;
38  _JoystickPollTimer = NULL;
39 }
40 
41 DInput9Info::~DInput9Info() {
42  for(UINT i=0;i<_DeviceList.size();i++) {
43  _DeviceList[i]->Unacquire();
44  SAFE_RELEASE(_DeviceList[i]);
45  }
46 
47  // bugbug: need to handle this
48  // if(_JoystickPollTimer!=NULL)
49  // KillTimer(...)
50 
51  SAFE_RELEASE(_pDInput9);
52  if(_hDInputDLL) {
53  FreeLibrary(_hDInputDLL);
54  _hDInputDLL=NULL;
55  }
56 }
57 
58 bool DInput9Info::InitDirectInput() {
59  HRESULT hr;
60 
61  // assumes dx9 exists
62  // use dynamic load so non-dinput programs don't have to load dinput
63  #define DLLNAME "dinput9.dll"
64  #define DINPUTCREATE "DirectInput9Create"
65 
66  HINSTANCE _hDInputDLL = LoadLibrary(DLLNAME);
67  if(_hDInputDLL == 0) {
68  wdxdisplay_cat.fatal() << "LoadLibrary(" << DLLNAME <<") failed!, error=" << GetLastError() << endl;
69  exit(1);
70  }
71 
72  typedef HRESULT (WINAPI * LPDIRECTINPUT9CREATE)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);
73  LPDIRECTINPUT9CREATE pDInputCreate9;
74 
75  pDInputCreate9 = (LPDIRECTINPUT9CREATE) GetProcAddress(_hDInputDLL,DINPUTCREATE);
76  if(pDInputCreate9 == NULL) {
77  wdxdisplay_cat.fatal() << "GetProcAddr failed for " << DINPUTCREATE << endl;
78  exit(1);
79  }
80 
81  // Register with the DirectInput subsystem and get a pointer
82  // to a IDirectInput interface we can use.
83  // Create a DInput object
84  if( FAILED( hr = (*pDInputCreate9)(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
85  IID_IDirectInput9, (VOID**)&_pDInput9, NULL ) ) ) {
86  wdxdisplay_cat.error() << DINPUTCREATE << "failed" << D3DERRORSTRING(hr);
87  return false;
88  }
89 
90  // enum all the joysticks,etc (but not keybd/mouse)
91  if( FAILED( hr = _pDInput9->EnumDevices(DI9DEVCLASS_GAMECTRL,
92  EnumGameCtrlsCallback,
93  (LPVOID)&_DevInfos, DIEDFL_ATTACHEDONLY ) ) ) {
94  wdxdisplay_cat.error() << "EnumDevices failed" << D3DERRORSTRING(hr);
95  return false;
96  }
97 
98  return true;
99 }
100 
101 bool DInput9Info::CreateJoystickOrPad(HWND _window) {
102  bool bFoundDev = false;
103  UINT devnum=0;
104  char *errstr=NULL;
105 
106  // look through the list for the first joystick or gamepad
107  for(;devnum<_DevInfos.size();devnum++) {
108  DWORD devType = GET_DIDEVICE_TYPE(_DevInfos[devnum].dwDevType);
109  if((devType==DI9DEVTYPE_GAMEPAD) ||(devType==DI9DEVTYPE_JOYSTICK)) {
110  bFoundDev=true;
111  break;
112  }
113  }
114 
115  if(!bFoundDev) {
116  wdxdisplay_cat.error() << "Cant find an attached Joystick or GamePad!\n";
117  return false;
118  }
119 
120  LPDIRECTINPUTDEVICE9 pJoyDevice;
121 
122  // Obtain an interface to the enumerated joystick.
123  HRESULT hr = _pDInput9->CreateDevice(_DevInfos[devnum].guidInstance, &pJoyDevice, NULL );
124  if(FAILED(hr)) {
125  errstr="CreateDevice";
126  goto handle_error;
127  }
128 
129  assert(pJoyDevice!=NULL);
130  _DeviceList.push_back(pJoyDevice);
131 
132  // Set the data format to "simple joystick" - a predefined data format
133  //
134  // A data format specifies which controls on a device we are interested in,
135  // and how they should be reported. This tells DInput that we will be
136  // passing a DIJOYSTATE2 structure to IDirectInputDevice::GetDeviceState().
137  hr = pJoyDevice->SetDataFormat(&c_dfDIJoystick2);
138  if(FAILED(hr)) {
139  errstr="SetDataFormat";
140  goto handle_error;
141  }
142 
143  // must be called AFTER SetDataFormat to get all the proper flags
144  DX_DECLARE_CLEAN(DIDEVCAPS, DIDevCaps);
145  hr = pJoyDevice->GetCapabilities(&DIDevCaps);
146  assert(SUCCEEDED(hr));
147 
148  _DevCaps.push_back(DIDevCaps);
149 
150  if(wdxdisplay_cat.is_debug())
151  wdxdisplay_cat.debug() << "Joy/Pad has " << DIDevCaps.dwAxes << " Axes, " << DIDevCaps.dwButtons << " Buttons, " << DIDevCaps.dwPOVs << " POVs" << endl;
152 
153  // Set the cooperative level to let DInput know how this device should
154  // interact with the system and with other DInput applications.
155  hr = pJoyDevice->SetCooperativeLevel( _window, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
156  if(FAILED(hr)) {
157  errstr="SetCooperativeLevel";
158  goto handle_error;
159  }
160 
161  // set the min/max values property for discovered axes.
162  hr = pJoyDevice->EnumObjects(EnumObjectsCallbackJoystick, (LPVOID)pJoyDevice, DIDFT_AXIS);
163  if(FAILED(hr)) {
164  errstr="EnumObjects";
165  goto handle_error;
166  }
167 
168  return true;
169 
170  handle_error:
171  wdxdisplay_cat.error() << errstr << " failed for (" << _DevInfos[devnum].tszInstanceName << ":" << _DevInfos[devnum].tszProductName << ")" << D3DERRORSTRING(hr);
172  return false;
173 }
174 
175 //-----------------------------------------------------------------------------
176 // Name: EnumObjectsCallback()
177 // Desc: Callback function for enumerating objects (axes, buttons, POVs) on a
178 // joystick. This function enables user interface elements for objects
179 // that are found to exist, and scales axes min/max values.
180 //-----------------------------------------------------------------------------
181 BOOL CALLBACK EnumObjectsCallbackJoystick( const DIDEVICEOBJECTINSTANCE* pdidoi,
182  VOID* pContext ) {
183 
184  LPDIRECTINPUTDEVICE9 pJoyDevice = (LPDIRECTINPUTDEVICE9) pContext;
185  HRESULT hr;
186 
187  // For axes that are returned, set the DIPROP_RANGE property for the
188  // enumerated axis in order to scale min/max values.
189  if( pdidoi->dwType & DIDFT_AXIS ) {
190  DIPROPRANGE diprg;
191  diprg.diph.dwSize = sizeof(DIPROPRANGE);
192  diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
193  diprg.diph.dwHow = DIPH_BYID;
194  diprg.diph.dwObj = pdidoi->dwType; // Specify the enumerated axis
195 
196  #ifdef AXIS_RANGE_CENTERED
197  diprg.lMin = -AXIS_RESOLUTION/2;
198  diprg.lMax = +AXIS_RESOLUTION/2;
199  #else
200  diprg.lMin = 0;
201  diprg.lMax = +AXIS_RESOLUTION;
202  #endif
203 
204  // Set the range for the axis
205  hr = pJoyDevice->SetProperty( DIPROP_RANGE, &diprg.diph);
206  if(FAILED(hr)) {
207  wdxdisplay_cat.error() << "SetProperty on axis failed" << D3DERRORSTRING(hr);
208  return DIENUM_STOP;
209  }
210  }
211 
212  return DIENUM_CONTINUE;
213 }
214 
215 bool DInput9Info::ReadJoystick(int devnum, DIJOYSTATE2 &js) {
216  LPDIRECTINPUTDEVICE9 pJoystick = _DeviceList[devnum];
217  assert(pJoystick!=NULL);
218  HRESULT hr;
219  char *errstr;
220 
221  // Poll the device to read the current state
222 
223  hr = pJoystick->Poll();
224 
225  if( FAILED(hr) ) {
226  // DInput is telling us that the input stream has been
227  // interrupted. We aren't tracking any state between polls, so
228  // we don't have any special reset that needs to be done. We
229  // just re-acquire and try again.
230 
231  if((hr==DIERR_NOTACQUIRED)||(hr == DIERR_INPUTLOST)) {
232  hr = pJoystick->Acquire();
233 
234  if(FAILED(hr)) {
235  if(wdxdisplay_cat.is_spam())
236  wdxdisplay_cat.spam() << "Acquire failed" << D3DERRORSTRING(hr);
237 
238  // hr may be DIERR_OTHERAPPHASPRIO or other errors. This
239  // may occur when the app is minimized or in the process of
240  // switching, so just try again later
241  return false;
242  }
243 
244  hr = pJoystick->Poll();
245  if(FAILED(hr)) {
246  // should never happen!
247  errstr = "Poll after successful Acquire failed";
248  goto handle_error;
249  }
250  } else {
251  errstr = "Unknown Poll failure";
252  goto handle_error;
253  }
254  }
255 
256  // should we make a vector of devstate dataformats to generalize this fn for all device types?
257 
258  // Get the input's device state
259  hr = pJoystick->GetDeviceState( sizeof(DIJOYSTATE2), &js);
260  if(FAILED(hr)) {
261  errstr = "GetDeviceState failed";
262  goto handle_error;
263  }
264 
265  return true;
266 
267  handle_error:
268  wdxdisplay_cat.fatal() << errstr << D3DERRORSTRING(hr);
269  return false;
270 }