Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Joystick.cpp
Go to the documentation of this file.
1 /* Project nGenEx
2  Destroyer Studios LLC
3  Copyright (C) 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: nGenEx.lib
6  FILE: Joystick.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Joystick Input class
13 */
14 
15 #include "MemDebug.h"
16 #include "Joystick.h"
17 #include "MachineInfo.h"
18 #include "Game.h"
19 
20 #define DIRECTINPUT_VERSION 0x0700
21 
22 #include <dinput.h>
23 
24 #define JOY_POVUPRIGHT 4500
25 #define JOY_POVDNRIGHT 13500
26 #define JOY_POVDNLEFT 22500
27 #define JOY_POVUPLEFT 31500
28 
29 // +--------------------------------------------------------------------+
30 // DIRECT INPUT SUPPORT
31 
32 const int MAX_DEVICES = 8;
33 
34 static LPDIRECTINPUT7 pdi = 0;
35 static LPDIRECTINPUTDEVICE7 pdev = 0;
36 static DIDEVICEINSTANCE devices[MAX_DEVICES];
37 static int ndev = 0;
38 static int idev = -1;
39 static int strikes = 3;
40 
41 static Joystick* joystick = 0;
42 
43 void DirectInputError(const char* msg, HRESULT err);
44 char* DIErrStr(HRESULT hr);
45 void ReleaseDirectInput();
46 
47 // +--------------------------------------------------------------------+
48 
50 : x(0), y(0), z(0), p(0), r(0), w(0), t(0)
51 {
52  if (!joystick)
53  joystick = this;
54 
55  select = 1;
56  rudder = 0;
57  throttle = 1;
58  sensitivity = 25;
59  dead_zone = 100;
60 
61  for (int i = 0; i < MotionController::MaxActions; i++)
62  action[i] = false;
63 
64  for (int i = 0; i < KEY_MAP_SIZE; i++)
65  map[i] = 0;
66 
67  for (int i = 0; i < 4; i++) {
68  for (int j = 0; j < 4; j++) {
69  hat[i][j] = false;
70  }
71  }
72 
77 
78  inv_axis[0] = false;
79  inv_axis[1] = false;
80  inv_axis[2] = false;
81  inv_axis[3] = false;
82 
84  Print("Joystick: DI7 not found, using multimedia library\n");
85  pdi = 0;
86  pdev = 0;
87  }
88 
89  else if (!pdi) {
90  HRESULT hr = DirectInputCreateEx(Game::GetHINST(),
92  IID_IDirectInput7,
93  (void**)&pdi,
94  NULL);
95  if FAILED(hr) {
96  DirectInputError("Failed to initialize DI7", hr);
97  pdi = 0;
98  pdev = 0;
99  }
100  else {
101  Print("Joystick: initialized DI7 pdi = %08x\n", (DWORD) pdi);
102  }
103  }
104 }
105 
107 {
109  joystick = 0;
110 }
111 
113 {
114  if (pdev) {
115  pdev->Unacquire();
116  pdev->Release();
117  pdev = 0;
118  }
119 
120  if (pdi) {
121  pdi->Release();
122  pdi = 0;
123  }
124 }
125 
127 {
128  return joystick;
129 }
130 
131 // +--------------------------------------------------------------------+
132 
133 void
134 Joystick::MapKeys(KeyMapEntry* mapping, int nkeys)
135 {
136  ZeroMemory(map, sizeof(map));
137 
138  for (int i = 0; i < nkeys; i++) {
139  KeyMapEntry k = mapping[i];
140 
141  if (k.act >= KEY_MAP_FIRST && k.act < KEY_MAP_LAST) {
142  if (k.act == KEY_JOY_SENSE)
143  sensitivity = k.key;
144 
145  else if (k.act == KEY_JOY_DEAD_ZONE)
146  dead_zone = k.key;
147 
148  else if (k.act == KEY_JOY_SWAP)
149  swapped = k.key;
150 
151  else if (k.act == KEY_JOY_RUDDER)
152  rudder = k.key;
153 
154  else if (k.act == KEY_JOY_THROTTLE)
155  throttle = k.key;
156 
157  else if (k.act == KEY_JOY_SELECT)
158  select = k.key;
159 
160 
161  else if (k.act == KEY_AXIS_YAW)
162  map_axis[0] = k.key;
163 
164  else if (k.act == KEY_AXIS_PITCH)
165  map_axis[1] = k.key;
166 
167  else if (k.act == KEY_AXIS_ROLL)
168  map_axis[2] = k.key;
169 
170  else if (k.act == KEY_AXIS_THROTTLE)
171  map_axis[3] = k.key;
172 
173 
174  else if (k.act == KEY_AXIS_YAW_INVERT)
175  inv_axis[0] = k.key ? true : false;
176 
177  else if (k.act == KEY_AXIS_PITCH_INVERT)
178  inv_axis[1] = k.key ? true : false;
179 
180  else if (k.act == KEY_AXIS_ROLL_INVERT)
181  inv_axis[2] = k.key ? true : false;
182 
183  else if (k.act == KEY_AXIS_THROTTLE_INVERT)
184  inv_axis[3] = k.key ? true : false;
185 
186  else if (k.key >= KEY_JOY_1 && k.key <= KEY_JOY_32)
187  map[k.act] = k.key;
188 
189  else if (k.alt >= KEY_JOY_1 && k.alt <= KEY_JOY_32)
190  map[k.act] = k.alt;
191 
192  else if (k.joy >= KEY_JOY_1 && k.joy <= KEY_JOY_32)
193  map[k.act] = k.joy;
194 
195  else if (k.key >= KEY_POV_0_UP && k.key <= KEY_POV_3_RIGHT)
196  map[k.act] = k.key;
197 
198  else if (k.alt >= KEY_POV_0_UP && k.alt <= KEY_POV_3_RIGHT)
199  map[k.act] = k.alt;
200 
201  else if (k.joy >= KEY_POV_0_UP && k.joy <= KEY_POV_3_RIGHT)
202  map[k.act] = k.joy;
203  }
204  }
205 }
206 
207 // +--------------------------------------------------------------------+
208 
209 static inline double sqr(double a) { return a*a; }
210 
211 BOOL FAR PASCAL EnumJoystick(LPCDIDEVICEINSTANCE pdinst, LPVOID pvSelect)
212 {
213  CopyMemory(&devices[ndev++], pdinst, pdinst->dwSize);
214 
215  ::Print("EnumJoystick %d: '%s'\n", ndev, pdinst->tszInstanceName);
216  ::Print(" guid: {%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x} \n",
217  (DWORD) pdinst->guidInstance.Data1,
218  (WORD) pdinst->guidInstance.Data2,
219  (WORD) pdinst->guidInstance.Data3,
220  (BYTE) pdinst->guidInstance.Data4[0],
221  (BYTE) pdinst->guidInstance.Data4[1],
222  (BYTE) pdinst->guidInstance.Data4[2],
223  (BYTE) pdinst->guidInstance.Data4[3],
224  (BYTE) pdinst->guidInstance.Data4[4],
225  (BYTE) pdinst->guidInstance.Data4[5]);
226 
227  if (ndev < MAX_DEVICES)
228  return DIENUM_CONTINUE;
229 
230  return DIENUM_STOP;
231 }
232 
233 bool CreateDevice(int select)
234 {
235  if (!pdi || ndev < select)
236  return false;
237 
238  LPCDIDEVICEINSTANCE pdinst = &devices[select-1];
239 
240  ::Print("Joystick CreateDevice(%d)\n", select);
241  ::Print(" name: %s\n\n", pdinst->tszInstanceName);
242 
243  // release current device before trying to get another:
244  if (pdev) {
245  pdev->Unacquire();
246  pdev->Release();
247  pdev = 0;
248  }
249 
250  HRESULT hr = DI_OK;
251  // Create the DirectInput joystick device:
252  hr = pdi->CreateDeviceEx(pdinst->guidInstance,
253  IID_IDirectInputDevice7,
254  (void**)&pdev,
255  NULL);
256 
257  if (hr != DI_OK || pdev == 0) {
258  DirectInputError("Create Device Ex failed", hr);
259  return false;
260  }
261 
262  // Set the data format:
263  hr = pdev->SetDataFormat(&c_dfDIJoystick);
264 
265  if (hr != DI_OK) {
266  DirectInputError("Set Data Format failed", hr);
267  pdev->Release();
268  pdev = 0;
269  return false;
270  }
271 
272  // Set the coop level:
273  hr = pdev->SetCooperativeLevel(Game::GetHWND(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
274 
275  if (hr != DI_OK) {
276  DirectInputError("Set Cooperative Level failed", hr);
277  pdev->Release();
278  return false;
279  }
280 
281  // Set data ranges
282  DIPROPRANGE diprg;
283  diprg.lMin = -32768;
284  diprg.lMax = +32768;
285 
286  diprg.diph.dwSize = sizeof(diprg);
287  diprg.diph.dwHeaderSize = sizeof(diprg.diph);
288  diprg.diph.dwObj = DIJOFS_X;
289  diprg.diph.dwHow = DIPH_BYOFFSET;
290  pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
291 
292  diprg.diph.dwObj = DIJOFS_Y;
293  pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
294 
295  diprg.diph.dwObj = DIJOFS_Z;
296  pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
297 
298  diprg.diph.dwObj = DIJOFS_RX;
299  pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
300 
301  diprg.diph.dwObj = DIJOFS_RY;
302  pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
303 
304  diprg.diph.dwObj = DIJOFS_RZ;
305  pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
306 
307  diprg.diph.dwObj = DIJOFS_SLIDER(0);
308  pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
309 
310  diprg.diph.dwObj = DIJOFS_SLIDER(1);
311  pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
312 
313  ::Print("Created joystick %d (pdev = %08x)\n", select, (DWORD) pdev);
314  idev = select;
315  return true;
316 }
317 
318 void
320 {
321  if (!pdi) {
322  Print("Joystick: no DI7, unable to enumerate devices\n");
323  ndev = 0;
324  }
325 
326  else if (ndev < 1) {
327  Print("Joystick: preparing to enumerate devices\n");
328 
329  ndev = 0;
330  HRESULT hr =
331  pdi->EnumDevices(DIDEVTYPE_JOYSTICK,
332  EnumJoystick,
333  (LPVOID) 0,
334  DIEDFL_ATTACHEDONLY);
335 
336  if (FAILED(hr)) {
337  DirectInputError("Failed to enumerate devices", hr);
339  }
340 
341  else if (ndev < 1) {
342  Print("Joystick: no devices found\n");
344  }
345  }
346 }
347 
348 int
350 {
351  return ndev;
352 }
353 
354 const char*
356 {
357  if (i >= 0 && i < ndev)
358  return devices[i].tszInstanceName;
359 
360  return 0;
361 }
362 
363 // +--------------------------------------------------------------------+
364 
365 static DIJOYSTATE joystate;
366 static JOYINFOEX joyinfo;
367 
368 int
370 {
371  if (!joystick)
372  return 0;
373 
374  int result = 0;
375 
376  if (pdev) {
377  switch (a) {
378  case KEY_JOY_AXIS_X: result = joystate.lX; break;
379  case KEY_JOY_AXIS_Y: result = joystate.lY; break;
380  case KEY_JOY_AXIS_Z: result = joystate.lZ; break;
381  case KEY_JOY_AXIS_RX: result = joystate.lRx; break;
382  case KEY_JOY_AXIS_RY: result = joystate.lRy; break;
383  case KEY_JOY_AXIS_RZ: result = joystate.lRz; break;
384  case KEY_JOY_AXIS_S0: result = joystate.rglSlider[0]; break;
385  case KEY_JOY_AXIS_S1: result = joystate.rglSlider[1]; break;
386  }
387  }
388 
389  else {
390  switch (a) {
391  case KEY_JOY_AXIS_X:
392  if (joyinfo.dwFlags & JOY_RETURNX)
393  result = joyinfo.dwXpos;
394  break;
395 
396  case KEY_JOY_AXIS_Y:
397  if (joyinfo.dwFlags & JOY_RETURNY)
398  result = joyinfo.dwYpos;
399  break;
400 
401  case KEY_JOY_AXIS_Z:
402  if (joyinfo.dwFlags & JOY_RETURNZ)
403  result = joyinfo.dwZpos;
404  break;
405 
406  case KEY_JOY_AXIS_RZ:
407  if (joyinfo.dwFlags & JOY_RETURNR)
408  result = joyinfo.dwRpos;
409  break;
410  }
411  }
412 
413  return result;
414 }
415 
416 double
418 {
419  if (a < 0 || a > 3)
420  return 0;
421 
422  double result = 0;
423 
424  switch (map_axis[a]) {
425  case KEY_JOY_AXIS_X: result = joystate.lX; break;
426  case KEY_JOY_AXIS_Y: result = joystate.lY; break;
427  case KEY_JOY_AXIS_Z: result = joystate.lZ; break;
428  case KEY_JOY_AXIS_RX: result = joystate.lRx; break;
429  case KEY_JOY_AXIS_RY: result = joystate.lRy; break;
430  case KEY_JOY_AXIS_RZ: result = joystate.lRz; break;
431  case KEY_JOY_AXIS_S0: result = joystate.rglSlider[0]; break;
432  case KEY_JOY_AXIS_S1: result = joystate.rglSlider[1]; break;
433  }
434 
435  if (a < 3) {
436  // ignore small movements:
437  if (result > dead_zone) result -= dead_zone;
438  else if (result < -dead_zone) result += dead_zone;
439  else result = 0;
440 
441  double scale = 1.0 / (32768.0-dead_zone);
442 
443  if (result >= 0)
444  result = sqr(result * scale);
445  else
446  result = sqr(result * scale) * -1.0;
447 
448  if (inv_axis[a])
449  result = -result;
450  }
451  else {
452  result = (result+32768.0) / 65536.0;
453 
454  if (inv_axis[a])
455  result = 1 - result;
456  }
457 
458 
459  return result;
460 }
461 
462 double
464 {
465  if (a < 0 || a > 3)
466  return 0;
467 
468  double result = 0;
469 
470  switch (map_axis[a]) {
471  case KEY_JOY_AXIS_X:
472  if (joyinfo.dwFlags & JOY_RETURNX)
473  result = joyinfo.dwXpos - 32768;
474  break;
475 
476  case KEY_JOY_AXIS_Y:
477  if (joyinfo.dwFlags & JOY_RETURNY)
478  result = joyinfo.dwYpos - 32768;
479  break;
480 
481  case KEY_JOY_AXIS_Z:
482  if (joyinfo.dwFlags & JOY_RETURNZ)
483  result = joyinfo.dwZpos - 32768;
484  break;
485 
486  case KEY_JOY_AXIS_RZ:
487  if (joyinfo.dwFlags & JOY_RETURNR)
488  result = joyinfo.dwRpos - 32768;
489  break;
490  }
491 
492  if (a < 3) {
493  // ignore small movements:
494  if (result > dead_zone) result -= dead_zone;
495  else if (result < -dead_zone) result += dead_zone;
496  else result = 0;
497 
498  double scale = 1.0 / (32768.0-dead_zone);
499 
500  if (result >= 0)
501  result = sqr(result * scale);
502  else
503  result = sqr(result * scale) * -1.0;
504 
505  if (inv_axis[a])
506  result = -result;
507  }
508  else {
509  result = (result+32768.0) / 65536.0;
510 
511  if (inv_axis[a])
512  result = 1 - result;
513  }
514 
515  return result;
516 }
517 
518 // +--------------------------------------------------------------------+
519 
520 void
522 {
523  t = x = y = z = p = r = w = 0;
524  for (int i = 0; i < MotionController::MaxActions; i++)
525  action[i] = false;
526 
527  for (int i = 0; i < 4; i++)
528  for (int j = 0; j < 4; j++)
529  hat[i][j] = false;
530 
531  if (select == 0)
532  return;
533 
534  //============================================================
535  //
536  // FIRST TRY DIRECT INPUT
537 
538  bool acquired = false;
539 
540  if (pdi) {
541  if (idev != select) {
542  if (ndev < 1)
544 
545  if (CreateDevice(select) && pdev)
546  pdev->Acquire();
547  }
548 
549  if (pdev) {
550  HRESULT hr = 0;
551 
552  hr = pdev->Poll();
553  hr = pdev->GetDeviceState(sizeof(joystate), &joystate);
554 
555  if (hr == DIERR_INPUTLOST) {
556  pdev->Acquire();
557 
558  hr = pdev->Poll();
559  hr = pdev->GetDeviceState(sizeof(joystate), &joystate);
560 
561  if (FAILED(hr)) {
562  strikes--;
563  ::Print("Joystick could not re-acquire joystick (%08x)\n", hr);
564 
565  // give up before you hurt yourself:
566  if (strikes <= 0) {
568  ndev = 0;
569  select = 0;
570  }
571 
572  return;
573  }
574  }
575 
576  for (int i = 0; i < 32; i++)
577  action[i] = (joystate.rgbButtons[i] & 0x80) != 0;
578 
579  double joy_x = ReadAxisDI(0);
580  double joy_y = ReadAxisDI(1);
581  double joy_r = rudder ? ReadAxisDI(2) : 0;
582  double joy_t = throttle ? ReadAxisDI(3) : 0;
583 
584  int joy_p = joystate.rgdwPOV[0];
585 
586  ProcessAxes(joy_x, joy_y, joy_r, joy_t);
587 
588  for (int i = 0; i < 4; i++)
589  ProcessHat(i, joystate.rgdwPOV[i]);
590 
591  acquired = true;
592  }
593  }
594 
595  //============================================================
596  //
597  // THEN TRY WINDOWS MULTIMEDIA LIBRARY
598 
599  if (!acquired) {
600  memset(&joyinfo, 0, sizeof(JOYINFOEX));
601  joyinfo.dwSize = sizeof(JOYINFOEX);
602  joyinfo.dwFlags = JOY_RETURNALL;
603 
604  HRESULT hr = 0;
605 
606  if (select == 1)
607  hr = joyGetPosEx(JOYSTICKID1, &joyinfo);
608 
609  else if (select == 2)
610  hr = joyGetPosEx(JOYSTICKID2, &joyinfo);
611 
612  if (hr != 0) {
613  Print("\nJoystick::Acquire() joyGetPosEx %d failed (err=%08x)\n\n", select, hr);
614  select = 0;
615  }
616 
617  action[0] = (joyinfo.dwButtons & JOY_BUTTON1) ? true : false;
618  action[1] = (joyinfo.dwButtons & JOY_BUTTON2) ? true : false;
619  action[2] = (joyinfo.dwButtons & JOY_BUTTON3) ? true : false;
620  action[3] = (joyinfo.dwButtons & JOY_BUTTON4) ? true : false;
621 
622  double joy_x = ReadAxisMM(0);
623  double joy_y = ReadAxisMM(1);
624  double joy_r = rudder ? ReadAxisMM(2) : 0;
625  double joy_t = throttle ? ReadAxisMM(3) : 0;
626 
627  ProcessAxes(joy_x, joy_y, joy_r, joy_t);
628  ProcessHat(0, joyinfo.dwPOV);
629  }
630 
631  // lateral translations:
632  if (KeyDownMap(KEY_PLUS_Y)) y = 1;
633  else if (KeyDownMap(KEY_MINUS_Y)) y = -1;
634 
635  if (KeyDownMap(KEY_PLUS_Z)) z = 1;
636  else if (KeyDownMap(KEY_MINUS_Z)) z = -1;
637 
638  if (KeyDownMap(KEY_MINUS_X)) x = -1;
639  else if (KeyDownMap(KEY_PLUS_X)) x = 1;
640 
641  // button-based turns:
642  const double steps=10;
643  static double p1=0, r1=0, w1=0;
644 
645  // if roll and yaw are swapped --------------------------
646  if (swapped) {
647  // yaw:
648  if (KeyDownMap(KEY_ROLL_LEFT)) { if (w1<steps) w1+=1; w = -sqr(w1/steps); }
649  else if (KeyDownMap(KEY_ROLL_RIGHT)) { if (w1<steps) w1+=1; w = sqr(w1/steps); }
650 
651  // roll:
652  if (KeyDownMap(KEY_YAW_LEFT)) { if (r1<steps) r1+=1; r = sqr(r1/steps); }
653  else if (KeyDownMap(KEY_YAW_RIGHT)) { if (r1<steps) r1+=1; r = -sqr(r1/steps); }
654  else w1 = 0;
655  }
656 
657  // else roll and yaw are NOT swapped ---------------------
658  else {
659  // roll:
660  if (KeyDownMap(KEY_ROLL_LEFT)) { if (r1<steps) r1+=1; r = sqr(r1/steps); }
661  else if (KeyDownMap(KEY_ROLL_RIGHT)) { if (r1<steps) r1+=1; r = -sqr(r1/steps); }
662 
663  // yaw left-right
664  if (KeyDownMap(KEY_YAW_LEFT)) { if (w1<steps) w1+=1; w = -sqr(w1/steps); }
665  else if (KeyDownMap(KEY_YAW_RIGHT)) { if (w1<steps) w1+=1; w = sqr(w1/steps); }
666  else w1 = 0;
667  }
668 
669  // pitch --------------------------------------------------
670  if (KeyDownMap(KEY_PITCH_UP)) { if (p1<steps) p1+=1; p = -sqr(p1/steps); }
671  else if (KeyDownMap(KEY_PITCH_DOWN)) { if (p1<steps) p1+=1; p = sqr(p1/steps); }
672  else p1 = 0;
673 }
674 
675 // +--------------------------------------------------------------------+
676 
677 void
678 Joystick::ProcessAxes(double joy_x, double joy_y, double joy_r, double joy_t)
679 {
680  int roll_enable = 0;
681 
682  joy_y *= -1;
683  joy_t = 1 - joy_t;
684 
685  if (map[KEY_ROLL_ENABLE])
686  roll_enable = action[map[KEY_ROLL_ENABLE] - KEY_JOY_1];
687 
688  // if roll and yaw are swapped --------------------------
689  if (swapped) {
690  if (roll_enable) {
691  w = joy_x;
692  r = joy_r;
693  }
694  else {
695  w = joy_r;
696  r = -joy_x;
697  }
698  }
699 
700  // else roll and yaw are NOT swapped ---------------------
701  else {
702  if (roll_enable) {
703  w = joy_r;
704  r = joy_x;
705  }
706  else {
707  w = joy_x;
708  r = -joy_r;
709  }
710  }
711 
712  p = joy_y;
713 
714  // read throttle:
715  if (throttle) {
716  static double init_throttle = -1;
717  static bool latch_throttle = false;
718 
719  if (init_throttle < 0)
720  init_throttle = joy_t;
721  else if (init_throttle != joy_t)
722  latch_throttle = true;
723 
724  if (latch_throttle)
725  t = joy_t;
726  else
727  t = 0;
728  }
729  else {
730  t = 0;
731  }
732 }
733 
734 void
735 Joystick::ProcessHat(int i, DWORD joy_pov)
736 {
737  if (i < 0 || i > 3) return;
738 
739  if (LOWORD(joy_pov) == 0xFFFF)
740  return;
741 
742  switch (joy_pov) {
743  case JOY_POVFORWARD: hat[i][0] = true; break;
744  case JOY_POVBACKWARD: hat[i][1] = true; break;
745  case JOY_POVLEFT: hat[i][2] = true; break;
746  case JOY_POVRIGHT: hat[i][3] = true; break;
747 
748  case JOY_POVUPRIGHT: hat[i][0] = true;
749  hat[i][3] = true; break;
750 
751  case JOY_POVDNRIGHT: hat[i][1] = true;
752  hat[i][3] = true; break;
753 
754  case JOY_POVDNLEFT: hat[i][1] = true;
755  hat[i][2] = true; break;
756 
757  case JOY_POVUPLEFT: hat[i][0] = true;
758  hat[i][2] = true; break;
759 
760  default: break;
761  }
762 }
763 
764 // +--------------------------------------------------------------------+
765 
766 bool Joystick::KeyDown(int key)
767 {
768  if (!joystick)
769  return false;
770 
771  if (key >= KEY_JOY_1 && key <= KEY_JOY_32)
772  return joystick->action[key - KEY_JOY_1];
773 
774  else if (key >= KEY_POV_0_UP && key <= KEY_POV_0_RIGHT)
775  return joystick->hat[0][key - KEY_POV_0_UP];
776 
777  else if (key >= KEY_POV_1_UP && key <= KEY_POV_1_RIGHT)
778  return joystick->hat[1][key - KEY_POV_1_UP];
779 
780  else if (key >= KEY_POV_2_UP && key <= KEY_POV_2_RIGHT)
781  return joystick->hat[2][key - KEY_POV_2_UP];
782 
783  else if (key >= KEY_POV_3_UP && key <= KEY_POV_3_RIGHT)
784  return joystick->hat[3][key - KEY_POV_3_UP];
785 
786  return false;
787 }
788 
789 // +--------------------------------------------------------------------+
790 
791 bool Joystick::KeyDownMap(int key)
792 {
793  if (!joystick)
794  return false;
795 
796  if (key >= KEY_MAP_FIRST && key <= KEY_MAP_LAST && joystick->map[key])
797  return KeyDown(joystick->map[key]);
798 
799  return false;
800 }
801 
802 // +--------------------------------------------------------------------+
803 
805 {
806  if (!joystick || n < 0 || n > 3)
807  return -1;
808 
809  return joystick->map_axis[n];
810 }
811 
813 {
814  if (!joystick || n < 0 || n > 3)
815  return -1;
816 
817  return joystick->inv_axis[n];
818 }
819 
820 // +--------------------------------------------------------------------+
821 
822 void
823 DirectInputError(const char* msg, HRESULT err)
824 {
825  static int report = 50;
826  if (report > 0)
827  report--;
828  else
829  return;
830 
831  Print(" DirectInput7: %s. [%s]\n", msg, DIErrStr(err));
832 }
833 
834 static char errstrbuf[128];
835 
836 char* DIErrStr(HRESULT hr)
837 {
838  switch (hr) {
839  default:
840  sprintf_s(errstrbuf, "Unrecognized error value = %08x.", hr);
841  return errstrbuf;
842 
843  case DI_OK:
844  return "No error.";
845 
846  case DI_BUFFEROVERFLOW:
847  return "The device buffer overflowed and some input was lost. This value is equal to the S_FALSE standard COM return value.";
848  case DI_DOWNLOADSKIPPED:
849  return "The parameters of the effect were successfully updated, but the effect could not be downloaded because the associated device was not acquired in exclusive mode.";
850  case DI_EFFECTRESTARTED:
851  return "The effect was stopped, the parameters were updated, and the effect was restarted.";
852  case DI_POLLEDDEVICE:
853  return "The device is a polled device. As a result, device buffering does not collect any data and event notifications is not signaled until the IDirectInputDevice7::Poll method is called.";
854  case DI_TRUNCATED:
855  return "The parameters of the effect were successfully updated, but some of them were beyond the capabilities of the device and were truncated to the nearest supported value.";
856  case DI_TRUNCATEDANDRESTARTED:
857  return "Equal to DI_EFFECTRESTARTED | DI_TRUNCATED";
858  case DIERR_ACQUIRED:
859  return "The operation cannot be performed while the device is acquired.";
860  case DIERR_ALREADYINITIALIZED:
861  return "This object is already initialized";
862  case DIERR_BADDRIVERVER:
863  return "The object could not be created due to an incompatible driver version or mismatched or incomplete driver components.";
864  case DIERR_BETADIRECTINPUTVERSION:
865  return "The application was written for an unsupported prerelease version of DirectInput.";
866  case DIERR_DEVICEFULL:
867  return "The device is full.";
868  case DIERR_DEVICENOTREG:
869  return "The device or device instance is not registered with DirectInput. This value is equal to the REGDB_E_CLASSNOTREG standard COM return value.";
870  case DIERR_EFFECTPLAYING:
871  return "The parameters were updated in memory but were not downloaded to the device because the device does not support updating an effect while it is still playing.";
872  case DIERR_HASEFFECTS:
873  return "The device cannot be reinitialized because there are still effects attached to it.";
874  case DIERR_GENERIC:
875  return "An undetermined error occurred inside the DirectInput subsystem. This value is equal to the E_FAIL standard COM return value.";
876  case DIERR_HANDLEEXISTS:
877  return "The device already has an event notification associated with it. This value is equal to the E_ACCESSDENIED standard COM return value.";
878  case DIERR_INCOMPLETEEFFECT:
879  return "The effect could not be downloaded because essential information is missing. For example, no axes have been associated with the effect, or no type-specific information has been supplied.";
880  case DIERR_INPUTLOST:
881  return "Access to the input device has been lost. It must be reacquired.";
882  case DIERR_INVALIDPARAM:
883  return "An invalid parameter was passed to the returning function, or the object was not in a state that permitted the function to be called. This value is equal to the E_INVALIDARG standard COM return value.";
884  case DIERR_MOREDATA:
885  return "Not all the requested information fit into the buffer.";
886  case DIERR_NOAGGREGATION:
887  return "This object does not support aggregation.";
888  case DIERR_NOINTERFACE:
889  return "The specified interface is not supported by the object. This value is equal to the E_NOINTERFACE standard COM return value.";
890  case DIERR_NOTACQUIRED:
891  return "The operation cannot be performed unless the device is acquired.";
892  case DIERR_NOTBUFFERED:
893  return "The device is not buffered. Set the DIPROP_BUFFERSIZE property to enable buffering.";
894  case DIERR_NOTDOWNLOADED:
895  return "The effect is not downloaded.";
896  case DIERR_NOTEXCLUSIVEACQUIRED:
897  return "The operation cannot be performed unless the device is acquired in DISCL_EXCLUSIVE mode.";
898  case DIERR_NOTFOUND:
899  return "The requested object does not exist.";
900  case DIERR_NOTINITIALIZED:
901  return "This object has not been initialized.";
902  case DIERR_OLDDIRECTINPUTVERSION:
903  return "The application requires a newer version of DirectInput.";
904  case DIERR_OUTOFMEMORY:
905  return "The DirectInput subsystem could not allocate sufficient memory to complete the call. This value is equal to the E_OUTOFMEMORY standard COM return value.";
906  case DIERR_REPORTFULL:
907  return "More information was requested to be sent than can be sent to the device.";
908  case DIERR_UNPLUGGED:
909  return "The operation could not be completed because the device is not plugged in.";
910  case DIERR_UNSUPPORTED:
911  return "The function called is not supported at this time. This value is equal to the E_NOTIMPL standard COM return value.";
912  }
913 }
914