Avionics
Dropship Simulator
Joystick.cpp
Go to the documentation of this file.
1 #include "Joystick.h"
2 
3 namespace Devices
4 {
5  //-----------------------------------------------------------------------------
6  // Name: EnumJoysticksCallback()
7  // Desc: Called once for each enumerated joystick. If we find one, create a
8  // device interface on it so we can play with it.
9  //-----------------------------------------------------------------------------
10  BOOL CALLBACK Joystick::EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, VOID* pContext)
11  {
12  Joystick* joystick = static_cast<Joystick*>(pContext);
13 
14  char msg[199];
15  sprintf_s(msg, 199, "Joystick::EnumJoysticksCallback {%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
16  pdidInstance->guidInstance.Data1, pdidInstance->guidInstance.Data2, pdidInstance->guidInstance.Data3,
17  pdidInstance->guidInstance.Data4[0], pdidInstance->guidInstance.Data4[1], pdidInstance->guidInstance.Data4[2], pdidInstance->guidInstance.Data4[3],
18  pdidInstance->guidInstance.Data4[4], pdidInstance->guidInstance.Data4[5], pdidInstance->guidInstance.Data4[6], pdidInstance->guidInstance.Data4[7]);
19  joystick->logger->Log(msg);
20 
21  if (joystick->config->serialno == GUID_NULL)
22  joystick->config->serialno = pdidInstance->guidInstance;
23 
24  joystick->guids.push_back(pdidInstance->guidInstance);
25 
26  return DIENUM_CONTINUE;
27  }
28 
29  //-----------------------------------------------------------------------------
30  // Name: EnumObjectsCallback()
31  // Desc: Callback function for enumerating objects (axes, buttons, POVs) on a
32  // joystick. This function enables user interface elements for objects
33  // that are found to exist, and scales axes min/max values.
34  //-----------------------------------------------------------------------------
35  BOOL CALLBACK Joystick::EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext)
36  {
37  Joystick* joystick = static_cast<Joystick*>(pContext);
38 
39  HRESULT hr;
40 
41  //static int nSliderCount = 0; // Number of returned slider controls
42  //static int nPOVCount = 0; // Number of returned POV controls
43 
44  // For axes that are returned, set the DIPROP_RANGE property for the
45  // enumerated axis in order to scale min/max values.
46  if (pdidoi->dwType & DIDFT_AXIS)
47  {
48  DIPROPRANGE diprg;
49  diprg.diph.dwSize = sizeof(DIPROPRANGE);
50  diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
51  diprg.diph.dwHow = DIPH_BYID;
52  diprg.diph.dwObj = pdidoi->dwType; // Specify the enumerated axis
53  for (UINT i = 0; i < joystick->config->axes.size(); i++)
54  {
55  if ((joystick->config->axes[i].index == 0 && pdidoi->guidType == GUID_XAxis) ||
56  (joystick->config->axes[i].index == 1 && pdidoi->guidType == GUID_YAxis) ||
57  (joystick->config->axes[i].index == 2 && pdidoi->guidType == GUID_ZAxis) ||
58  (joystick->config->axes[i].index == 3 && pdidoi->guidType == GUID_RxAxis) ||
59  (joystick->config->axes[i].index == 4 && pdidoi->guidType == GUID_RyAxis) ||
60  (joystick->config->axes[i].index == 5 && pdidoi->guidType == GUID_RzAxis))
61  {
62  diprg.lMin = joystick->config->axes[i].min;
63  diprg.lMax = joystick->config->axes[i].max;
64 
65  char msg[99];
66  sprintf_s(msg, 99, "Joystick::EnumObjectsCallback Configured %S with range %i to %i", pdidoi->tszName, diprg.lMin, diprg.lMax);
67  joystick->logger->Log(msg);
68 
69  // Set the range for the axis
70  if (FAILED(hr = joystick->g_pJoystick->SetProperty(DIPROP_RANGE, &diprg.diph)))
71  {
72  joystick->logger->Log("Joystick::EnumObjectsCallback SetProperty failed!", Logger::Error, hr);
73  return DIENUM_STOP;
74  }
75  else
76  {
77  return DIENUM_CONTINUE;
78  }
79  }
80  }
81  char msg[99];
82  sprintf_s(msg, 99, "Joystick::EnumObjectsCallback Unused %S", pdidoi->tszName);
83  joystick->logger->Log(msg);
84  }
85 
86  return DIENUM_CONTINUE;
87  }
88 
89  void Joystick::Initialize(Logger* prmLogger, JoystickConfig* prmConfig, Bus* prmBus, LPDIRECTINPUT8 g_pDI)
90  {
91  logger = prmLogger;
92  config = prmConfig;
93  bus = prmBus;
94 
95  HRESULT hr;
96 
97  logger->Log("Joystick::Initialize Calling EnumDevices...", Logger::Info);
98  if (FAILED(hr = g_pDI->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, this, DIEDFL_ATTACHEDONLY)))
99  {
100  logger->Log("Joystick::Initialize EnumDevices failed!", Logger::Error, hr);
101  config->enable = false;
102  return;
103  }
104 
106  bool found = false;
107  for (UINT i = 0; i < guids.size(); i++)
108  {
109  if (guids.at(i) == config->serialno)
110  {
111  found = true;
112  break;
113  }
114  }
115  if (!found)
116  {
117  logger->Log("Joystick::Initialize Could not find in list of enumerated GUIDs!", Logger::Error);
118  config->enable = false;
119  return;
120  }
121 
122  // Obtain an interface to the enumerated joystick.
123  if (FAILED(hr = g_pDI->CreateDevice(config->serialno, &g_pJoystick, NULL)))
124  {
125  logger->Log("Joystick::Initialize CreateDevice failed!", Logger::Error, hr);
126  config->enable = false;
127  return;
128  }
129 
130  // Set the data format to "simple joystick" - a predefined data format
131  // A data format specifies which controls on a device we are interested in,
132  // and how they should be reported. This tells DInput that we will be
133  // passing a DIJOYSTATE structure to IDirectInputDevice::GetDeviceState().
134  if (FAILED(hr = g_pJoystick->SetDataFormat(&c_dfDIJoystick)))
135  {
136  logger->Log("Joystick::SetDataFormat failed!", Logger::Error, hr);
137  config->enable = false;
138  return;
139  }
140 
141  // Set the cooperative level to let DInput know how this device should
142  // interact with the system and with other DInput applications.
143  if (FAILED(hr = g_pJoystick->SetCooperativeLevel(NULL, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND)))
144  {
145  logger->Log("Joystick::SetCooperativeLevel failed!", Logger::Error, hr);
146  config->enable = false;
147  return;
148  }
149 
150  char msg[199];
151  sprintf_s(msg, 199, "Joystick::EnumObjectsCallback {%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
152  config->serialno.Data1, config->serialno.Data2, config->serialno.Data3,
153  config->serialno.Data4[0], config->serialno.Data4[1], config->serialno.Data4[2], config->serialno.Data4[3],
154  config->serialno.Data4[4], config->serialno.Data4[5], config->serialno.Data4[6], config->serialno.Data4[7]);
155  logger->Log(msg);
156 
157  // Enumerate the joystick objects. The callback function enabled user
158  // interface elements for objects that are found, and sets the min/max
159  // values property for discovered axes.
160  if (FAILED(hr = g_pJoystick->EnumObjects(EnumObjectsCallback, this, DIDFT_ALL)))
161  {
162  logger->Log("Joystick::EnumObjects failed!", Logger::Error, hr);
163  config->enable = false;
164  return;
165  }
166 
167  axes.resize(7); // x y z rx ry rz slider0 slider1
168 
169  RtlZeroMemory(&jsOld, sizeof(DIJOYSTATE));
170  }
171 
173  {
174  if (g_pJoystick)
175  g_pJoystick->Unacquire();
176 
177  // Release any DirectInput objects.
178  SAFE_RELEASE(g_pJoystick);
179  }
180 
181  void Joystick::FrameMove(float fElapsed)
182  {
183  acquired = false;
184 
185  if (!config->enable)
186  return;
187 
188  if (g_pJoystick == nullptr)
189  return;
190 
191  HRESULT hr;
192 
194  DIJOYSTATE js;
195 
196  // Poll the device to read the current state
197  if (FAILED(hr = g_pJoystick->Poll()))
198  {
199  //logger->Log("Joystick::FrameMove Poll failed!", Logger::Warn, hr);
200 
201  // DInput is telling us that the input stream has been
202  // interrupted. We aren't tracking any state between polls, so
203  // we don't have any special reset that needs to be done. We
204  // just re-acquire and try again.
205  if (FAILED(hr = g_pJoystick->Acquire()))
206  {
207  //logger->Log("Joystick::FrameMove Acquire failed!", Logger::Warn, hr);
208  }
209 
210  // hr may be DIERR_OTHERAPPHASPRIO or other errors. This
211  // may occur when the app is minimized or in the process of
212  // switching, so just try again later
213  return;
214  }
215 
216  // Get the input's device state
217  if (FAILED(hr = g_pJoystick->GetDeviceState(sizeof(DIJOYSTATE), &js)))
218  {
219  logger->Log("Joystick::FrameMove GetDeviceState failed!", Logger::Warn, hr);
220  return;
221  }
222 
223  acquired = true;
224 
225  for (UINT i = 0; i < config->axes.size(); i++)
226  {
227  switch (config->axes.at(i).index)
228  {
229  case 0: axes.at(0) = static_cast<float>(js.lX);
230  break;
231  case 1: axes.at(1) = static_cast<float>(js.lY);
232  break;
233  case 2: axes.at(2) = static_cast<float>(js.lZ);
234  break;
235  case 3: axes.at(3) = static_cast<float>(js.lRx);
236  break;
237  case 4: axes.at(4) = static_cast<float>(js.lRy);
238  break;
239  case 5: axes.at(5) = static_cast<float>(js.lRz);
240  break;
241  case 6: axes.at(6) = static_cast<float>(js.rglSlider[0]);
242  break;
243  case 7: axes.at(7) = static_cast<float>(js.rglSlider[1]);
244  break;
245  }
246 
248  if (fabsf(axes.at(config->axes.at(i).index)) < config->axes.at(i).deadband)
249  axes.at(config->axes.at(i).index) = 0.0f;
250 
252  axes.at(config->axes.at(i).index) *= config->axes.at(i).scale;
253 
255  if (config->axes.at(i).inverse)
256  axes.at(config->axes.at(i).index) = 1.0f - axes.at(config->axes.at(i).index);
257  }
258 
260  for (UINT j = 0; j < config->buttons.size(); j++)
261  {
263  if (button->onPress == (js.rgbButtons[button->index] > 0))
264  {
265  button->durationElapsed += fElapsed;
266  if ((button->durationRequired == 0.0f && js.rgbButtons[button->index] != jsOld.rgbButtons[button->index]) ||
267  (button->durationRequired != 0.0f && button->durationElapsed >= button->durationRequired && !button->durationTripped))
268  {
269  button->durationTripped = true;
270  bus->AppendCommands(&button->commands);
271  char msg[99];
272  sprintf_s(msg, 99, "Joystick::FrameMove location %i button %i state %i elapsed %.1f", config->location, button->index, js.rgbButtons[button->index], button->durationElapsed);
273  logger->Log(msg, Logger::Level::Info);
274  }
275  }
276  else
277  {
278  button->durationElapsed = 0.0f;
279  button->durationTripped = false;
280  }
281  }
282 
283  jsOld = js;
284  }
285 
286  bool Joystick::IsActive() const
287  {
288  return acquired;
289  }
290 
291  float Joystick::GetAxis(UINT axis)
292  {
293  assert(axis < axes.size());
294  return axes.at(axis);
295  }
296 
297  bool Joystick::GetButton(int button) const
298  {
299  assert(button >= 0 && button < 32);
300  return jsOld.rgbButtons[button] > 0;
301  }
302 
303  DWORD Joystick::GetPOV(int pov)
304  {
305  assert(pov >= 0 && pov < 4);
306  return jsOld.rgdwPOV[pov];
307  }
308 }
std::vector< Axis > axes
Definition: Joystick.h:33
JoystickConfig * config
Definition: Joystick.h:52
Definition: Logger.h:5
LPDIRECTINPUT8 g_pDI
Definition: Avionics.cpp:16
void AppendCommands(std::vector< Command > *commands)
Definition: Bus.cpp:41
unsigned int location
Definition: Joystick.h:19
okay, the portable keyboard numbers don&#39;t work like the outside keypad because the outside keypad is ...
Definition: Analog.cpp:3
std::vector< GUID > guids
Definition: Joystick.h:63
Definition: Bus.h:12
bool GetButton(int) const
Definition: Joystick.cpp:297
DIJOYSTATE jsOld
Definition: Joystick.h:55
std::vector< Command > commands
Definition: Joystick.h:42
std::vector< float > axes
Definition: Joystick.h:56
void FrameMove(float fElapsed)
Definition: Joystick.cpp:181
static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE *pdidInstance, VOID *pContext)
Definition: Joystick.cpp:10
DWORD GetPOV(int)
Definition: Joystick.cpp:303
LPDIRECTINPUTDEVICE8 g_pJoystick
Definition: Joystick.h:59
void Log(const char *msg, Level level=Info, int errorCode=0)
These have to be in this order.
Definition: Logger.cpp:16
void Initialize(Logger *logger, JoystickConfig *config, Bus *prmBus, LPDIRECTINPUT8 g_pDI)
Definition: Joystick.cpp:89
bool IsActive() const
Definition: Joystick.cpp:286
std::vector< Button > buttons
Definition: Joystick.h:45
Logger * logger
Definition: Joystick.h:51
float GetAxis(UINT)
Definition: Joystick.cpp:291
static BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE *pdidoi, VOID *pContext)
Definition: Joystick.cpp:35