]> icculus.org git repositories - taylor/freespace2.git/blob - src/io/joy.cpp
Initial revision
[taylor/freespace2.git] / src / io / joy.cpp
1 /*
2  * $Logfile: /Freespace2/code/Io/Joy.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Code to read the joystick
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:09  root
11  * Initial revision
12  *
13  * 
14  * 5     7/15/99 9:20a Andsager
15  * FS2_DEMO initial checkin
16  * 
17  * 4     6/02/99 6:18p Dave
18  * Fixed TNT lockup problems! Wheeeee!
19  * 
20  * 3     10/09/98 2:57p Dave
21  * Starting splitting up OS stuff.
22  * 
23  * 2     10/07/98 10:53a Dave
24  * Initial checkin.
25  * 
26  * 1     10/07/98 10:49a Dave
27  * 
28  * 61    5/24/98 12:56a Mike
29  * Put in debug code, but comment out, to check joystick sensitivity.
30  * 
31  * 60    5/19/98 6:54p Lawrance
32  * Set default joystick sensitivity to max
33  * 
34  * 59    5/13/98 7:14p Hoffoss
35  * Made invalid axis return center position value.
36  * 
37  * 58    5/13/98 1:17a Hoffoss
38  * Added joystick axes configurability.
39  * 
40  * 57    5/07/98 3:15p Hoffoss
41  * Fixed typo.
42  * 
43  * 56    5/07/98 12:41p Hoffoss
44  * Changed code to make joystick sensitivity default to center of range.
45  * 
46  * 55    5/06/98 12:02a Hoffoss
47  * Fixed throttle problems with joysticks.
48  * 
49  * 54    5/05/98 8:38p Hoffoss
50  * Added sensitivity adjustment to options menu and made it save to pilot
51  * file.
52  * 
53  * 53    5/04/98 11:08p Hoffoss
54  * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp.
55  * Updated references everywhere to it.
56  * 
57  * 52    5/03/98 6:02p Hoffoss
58  * Added DirectInput support for joystick (only works with v5.0, so only
59  * Win95).
60  * 
61  * 51    5/01/98 5:59p Hoffoss
62  * Checking in code so I can switch back to NT to work on this instead
63  * (Win95, bah!)
64  * 
65  * 50    4/30/98 12:12p Hoffoss
66  * Changed code to explicitly use calibrated values from Windows for
67  * joystick axis.
68  * 
69  * 49    4/29/98 12:13a Lawrance
70  * Add function for reading down count without resetting internal count.
71  * Add hook to reset demo trailer timer.
72  * 
73  * 48    4/25/98 12:02p Lawrance
74  * take out crit sec code around joystick read in joy_process
75  * 
76  * 47    4/22/98 9:03p John
77  * Put critical section around all the joygetposex functions.   Had to set
78  * up events to signal the polling thread to end at the end of the
79  * program.
80  * 
81  * 46    4/17/98 3:07p Jim
82  * added debug code to test joystick
83  * 
84  * 45    4/13/98 10:16a John
85  * Switched gettime back to timer_get_milliseconds, which is now thread
86  * safe.
87  * 
88  * 44    4/12/98 11:08p Lawrance
89  * switch back to using gettime() in separate threads
90  * 
91  * 43    4/12/98 5:31p Lawrance
92  * use timer_get_milliseconds() instead of gettime()
93  * 
94  * 42    3/21/98 11:29a John
95  * Made joy_flush work when a button is held down
96  * 
97  * 41    3/12/98 10:44a Hoffoss
98  * Whoops, should probably use the actual define rather than 0.
99  * 
100  * 40    3/12/98 10:38a Hoffoss
101  * Changed joystick default to first slot in never set up in FSLaunch
102  * 
103  * 39    3/11/98 5:27p Hoffoss
104  * Added to FreeSpace usage of joystick specified through FSLaunch.
105  * 
106  * 38    3/09/98 4:44p Sandeep
107  * 
108  * 37    3/07/98 5:23p Sandeep
109  * 
110  * 35    3/06/98 11:12a Hoffoss
111  * Fixed joystick deadzone bug.
112  * 
113  * 33    3/06/98 10:02a Hoffoss
114  * Made dead zone adjustable, and defaulted it to 10% instead of 5%.
115  * 
116  * 36    3/07/98 4:50p John
117  * Added code to allow toggling force feedback on/off in setup
118  * 
119  * 35    3/06/98 11:12a Hoffoss
120  * Fixed joystick deadzone bug.
121  * 
122  * 33    3/06/98 10:02a Hoffoss
123  * Made dead zone adjustable, and defaulted it to 10% instead of 5%.
124  * 
125  * 32    2/11/98 9:56p Jim
126  * allender: from sandeep on Jim's machine -- some force feedback stuff
127  * 
128  * 31    1/29/98 11:04a Sandeep
129  * 
130  * 30    1/27/98 8:40p Sandeep
131  * 
132  * 29    1/19/98 6:15p John
133  * Fixed all my Optimized Build compiler warnings
134  * 
135  * 28    1/08/98 6:35p Hoffoss
136  * Fixed joystick undefined detection.
137  * 
138  * 27    1/08/98 3:48p Dan
139  * Fixed bug with joystick axis reading thinking it's undefined at
140  * extremes.
141  * 
142  * 26    10/16/97 5:37p Lawrance
143  * change thread priority from THREAD_PRIORITY_TIME_CRITICAL to 
144  * THREAD_PRIORITY_HIGHEST
145  * 
146  * 25    10/09/97 10:15a Johnson
147  * try to init several times if joystick init fails
148  * 
149  * 24    10/07/97 10:42a Johnson
150  * zero out JOYINFOEX struct before setting dwSize
151  * 
152  * 23    10/06/97 5:54p Johnson
153  * ALAN: fix nasty bug where dwSize member of JOYINFOEX was not being set,
154  * resulting in random failure
155  * 
156  * 22    9/15/97 11:42p Lawrance
157  * change button_info to joy_button_info to avoid name conflict
158  * 
159  * 21    8/07/97 11:26p Lawrance
160  * add support for 4th axis (rudder)
161  * 
162  * 20    7/29/97 5:30p Lawrance
163  * move gettime() from keyboard module to timer module
164  * 
165  * 19    7/11/97 11:43a Lawrance
166  * fix bug with joy_up_count
167  * 
168  * 18    7/10/97 12:29a Lawrance
169  * fix problem with NT not recognizing an axis that was set under 95
170  * 
171  * 17    7/09/97 11:41p Lawrance
172  * added throttle and hat support
173  * 
174  * 16    6/19/97 9:50a John
175  * fixed bug where joy_close getting called out of order doesn't matter.
176  * 
177  * 15    5/18/97 2:40p Lawrance
178  * added joy_get_caps()
179  * 
180  * 14    4/22/97 10:56a John
181  * fixed some resource leaks.
182  * 
183  * 13    2/27/97 2:23p Lawrance
184  * took out debug stmts
185  * 
186  * 12    2/27/97 10:04a Lawrance
187  * fixed bug that was causing all joy buttons but 0 to be read incorrectly
188  * 
189  * 11    2/17/97 5:18p John
190  * Added a bunch of RCS headers to a bunch of old files that don't have
191  * them.
192  *
193  * $NoKeywords: $
194  */
195
196 #include <windows.h>
197 #include <windowsx.h>
198
199 #include "pstypes.h"
200 #include "joy.h"
201 #include "fix.h"
202 #include "key.h"
203 #include "timer.h"
204 #include "osregistry.h"
205 #include "joy_ff.h"
206 #include "vdinput.h"
207 #include "osapi.h"
208
209 #define PRECALIBRATED 1
210
211 static int Joy_inited = 0;
212 int joy_num_sticks = 0;
213 int Dead_zone_size = 10;
214 int Cur_joystick = -1;  // joystick used for input or -1
215 int Joy_sensitivity = 9;
216
217 CRITICAL_SECTION joy_lock;
218
219 HANDLE joy_thread = NULL;
220 DWORD joy_thread_id;
221 int joy_pollrate = 1000 / 18;  // poll at 18Hz
222
223 HANDLE Joy_tell_thread_to_end_event = NULL;
224 HANDLE Joy_thread_says_its_done_event = NULL;
225
226 static int Joy_last_x_reading = 0;
227 static int Joy_last_y_reading = 0;
228
229 int Joy_di_inited = 0;
230 static LPDIRECTINPUT                            Di_joystick_obj = NULL;
231 static LPDIRECTINPUTDEVICE2     Di_joystick = NULL;
232
233 typedef struct joy_button_info {
234         int     actual_state;           // Set if the button is physically down
235         int     state;                          // Set when the button goes from up to down, cleared on down to up.  Different than actual_state after a flush.
236         int     down_count;
237         int     up_count;
238         int     down_time;
239         uint    last_down_check;        // timestamp in milliseconds of last 
240 } joy_button_info;
241
242 Joy_info joystick;
243
244 joy_button_info joy_buttons[JOY_TOTAL_BUTTONS];
245
246 int joy_di_init();
247 int joy_di_shutdown();
248 int joystick_read_raw_axis_di(int num_axes, int *axis);
249
250 // --------------------------------------------------------------
251 //      joy_flush()
252 //
253 // Clear the state of the joystick.
254 //
255 void joy_flush()
256 {
257         int                     i;
258         joy_button_info *bi;
259
260         if ( joy_num_sticks < 1 ) return;
261
262         EnterCriticalSection(&joy_lock);
263         for ( i = 0; i < JOY_TOTAL_BUTTONS; i++) {
264                 bi = &joy_buttons[i];
265                 bi->state               = 0;
266                 bi->down_count  = 0;
267                 bi->up_count    = 0;
268                 bi->down_time   = 0;
269                 bi->last_down_check = timer_get_milliseconds();
270         }
271         
272         LeaveCriticalSection(&joy_lock);
273 }
274
275 // --------------------------------------------------------------
276 //      joy_process()
277 //
278 // Runs as a separate thread, and updates the state of the joystick
279 //
280 DWORD joy_process(DWORD lparam)
281 {
282         MMRESULT                rs;
283         JOYINFOEX       ji;
284         int                     i,state;
285         joy_button_info *bi;    
286
287         for ( i = 0; i < JOY_TOTAL_BUTTONS; i++) {
288                 bi = &joy_buttons[i];
289                 bi->actual_state = 0;           // Don't set in flush code!
290                 bi->state               = 0;
291                 bi->down_count  = 0;
292                 bi->up_count    = 0;
293                 bi->down_time   = 0;
294                 bi->last_down_check = timer_get_milliseconds();
295         }
296
297         while (1) {
298                 // Wait for the thread to be signaled to end or 1/18th of a second to pass...
299                 if ( WaitForSingleObject( Joy_tell_thread_to_end_event, joy_pollrate )==WAIT_OBJECT_0)  {
300                         break;
301                 }
302
303                 memset(&ji, 0, sizeof(ji));
304                 ji.dwSize = sizeof(ji);
305 //              ji.dwFlags = JOY_RETURNBUTTONS | JOY_RETURNRAWDATA;
306                 ji.dwFlags = JOY_RETURNALL;
307
308                 EnterCriticalSection(&joy_lock);
309
310                 uint joy_state = 0;
311                 if (Cur_joystick >= 0) {
312                         rs = joyGetPosEx(Cur_joystick, &ji);
313                         // If there's an error, assume all buttons down.
314                         if (rs == JOYERR_NOERROR) {
315                                 joy_state = ji.dwButtons;
316                         }
317                 }
318
319                 // Process ji.dwButtons
320                 for (i=0; i<JOY_TOTAL_BUTTONS; i++) {
321                         state = 0;
322                         if (i < JOY_NUM_BUTTONS) {
323                                 state = joy_state & (1<<i);
324
325                         } else {
326                                 // check for hat presses, which act like buttons
327                                 switch (i) {
328                                         case JOY_HATBACK:
329                                                 if (ji.dwPOV == JOY_POVBACKWARD)
330                                                         state = 1;
331                                                 break;
332
333                                         case JOY_HATFORWARD:
334                                                 if (ji.dwPOV == JOY_POVFORWARD)
335                                                         state = 1;
336                                                 break;
337
338                                         case JOY_HATLEFT:
339                                                 if (ji.dwPOV == JOY_POVLEFT)
340                                                         state = 1;
341                                                 break;
342
343                                         case JOY_HATRIGHT:
344                                                 if (ji.dwPOV == JOY_POVRIGHT)
345                                                         state = 1;
346                                                 break;
347
348                                         default:
349                                                 Int3(); // should never happen
350                                                 break;
351
352                                 }       // end switch
353                         }       // end if
354
355
356                         if (state != joy_buttons[i].actual_state) {
357                                 // Button position physically changed.
358                                 joy_buttons[i].actual_state = state;
359
360                                 if ( state )    {
361                                         // went from up to down
362                                         joy_buttons[i].down_count++;
363                                         joy_buttons[i].down_time = 0;
364
365                                         joy_buttons[i].state = 1;
366
367 ////////////////////////////
368 /// SOMETHING TERRIBLE IS ABOUT TO HAPPEN.  I FEEL THIS IS NECESSARY FOR THE DEMO, SINCE
369 /// I DON'T WANT TO CALL CRITICAL SECTION CODE EACH FRAME TO CHECK ALL THE JOYSTICK BUTTONS.
370 /// PLEASE SEE ALAN FOR MORE INFORMATION.
371 ////////////////////////////
372 #ifdef FS2_DEMO
373                                         {
374                                         extern void demo_reset_trailer_timer();
375                                         demo_reset_trailer_timer();
376                                         }
377 #endif
378 ////////////////////////////
379 /// IT'S OVER.  SEE, IT WASN'T SO BAD RIGHT?  IT'S IS VERY UGLY LOOKING, I KNOW.
380 ////////////////////////////
381
382
383                                 } else {
384                                         // went from down to up
385                                         if ( joy_buttons[i].state )     {
386                                                 joy_buttons[i].up_count++;
387                                         }
388                                         joy_buttons[i].state = 0;
389                                 }
390
391                         } else {
392                                 // Didn't move... increment time down if down.
393                                 if (joy_buttons[i].state) {
394                                         joy_buttons[i].down_time += joy_pollrate;
395                                 }
396                         }
397
398                 }  // end for
399
400                 LeaveCriticalSection(&joy_lock);
401         }
402
403         SetEvent(Joy_thread_says_its_done_event);       
404
405         return 0;
406 }
407
408 // --------------------------------------------------------------
409 //      joy_close()
410 //
411 // Close the joystick system.  Should be called at game exit.
412 //
413 void joy_close()
414 {
415         if (!Joy_inited)
416                 return;
417
418         // joy_di_shutdown();
419
420         Joy_inited = 0;
421         joy_num_sticks = 0;
422
423         // Tell joystick polling thread to end
424         SetEvent(Joy_tell_thread_to_end_event);
425
426         // Wait for it to end
427         if ( WaitForSingleObject( Joy_thread_says_its_done_event, 5000 )==WAIT_TIMEOUT) {                       //INFINITE );
428                 mprintf(( "Joy end thread wait timeout!\n" ));
429         }
430         CloseHandle(Joy_tell_thread_to_end_event);
431         CloseHandle(Joy_thread_says_its_done_event);
432
433         // It is now safe to release any resources use by the polling thread.
434         DeleteCriticalSection( &joy_lock );
435         if (joy_thread) {
436                 CloseHandle(joy_thread);
437                 joy_thread = NULL;
438         }
439
440         joy_ff_shutdown();
441 }
442
443 // --------------------------------------------------------------
444 //      joy_get_caps()
445 //
446 // Determine the capabilities of the attached joysticks.
447 //
448 void joy_get_caps(int max)
449 {
450         JOYCAPS JoyCaps;
451         int j;
452
453         for (j=0; j<JOY_NUM_AXES; j++)
454                 joystick.axis_valid[j] = 0;
455
456         for (j=JOYSTICKID1; j<JOYSTICKID1+max; j++) {
457                 if (JOYERR_NOERROR == joyGetDevCaps (j, &JoyCaps, sizeof(JoyCaps))) {
458                         nprintf(("JOYSTICK", "Joystick #%d: %s\n", j - JOYSTICKID1 + 1, JoyCaps.szPname));
459
460                         if (j == Cur_joystick) {
461                                 joystick.axis_valid[0] = joystick.axis_valid[1] = 1;
462                                 if (JoyCaps.wCaps & JOYCAPS_HASZ)
463                                         joystick.axis_valid[2] = 1;
464                                 if (JoyCaps.wCaps & JOYCAPS_HASR)
465                                         joystick.axis_valid[3] = 1;
466                                 if (JoyCaps.wCaps & JOYCAPS_HASU)
467                                         joystick.axis_valid[4] = 1;
468                                 if (JoyCaps.wCaps & JOYCAPS_HASV)
469                                         joystick.axis_valid[5] = 1;
470                         }
471                 }
472         }
473 }
474
475 int joy_get_scaled_reading(int raw, int axn);
476 int joy_get_unscaled_reading(int raw, int axn);
477
478 DCF(joytest, "Test joystick")
479 {
480         if (Dc_command) {
481                 while (!keyd_pressed[KEY_ESC]) {
482                         int x, y, axis[JOY_NUM_AXES];
483
484                         if (joy_num_sticks < 1)
485                                 return;
486
487                         joystick_read_raw_axis(JOY_NUM_AXES, axis);
488
489                         x = joy_get_scaled_reading(axis[0], 0);
490                         y = joy_get_scaled_reading(axis[1], 1);
491
492                         mprintf(("X=%5d Y=%5d  Calibrated X=%6d Y=%6d\n", axis[0], axis[1], x, y));
493                         Sleep(100);
494                 }
495         }
496 }
497
498 DCF(joytest2, "Test joystick (extended)")
499 {
500         if (Dc_command) {
501                 while (!keyd_pressed[KEY_ESC]) {
502                         int x, y, z, r, axis[JOY_NUM_AXES];
503
504                         if (joy_num_sticks < 1)
505                                 return;
506
507                         joystick_read_raw_axis(JOY_NUM_AXES, axis);
508
509                         x = joy_get_scaled_reading(axis[0], 0);
510                         y = joy_get_scaled_reading(axis[1], 1);
511                         z = joy_get_unscaled_reading(axis[2], 2);
512                         r = joy_get_scaled_reading(axis[3], 3);
513
514                         mprintf(("X=%5d Y=%5d Z=%5d Rx=%5d Ry=%5d Rz=%5d Cal X=%6d Y=%6d Z=%6d R=%6d\n", axis[0], axis[1], axis[2], axis[3], axis[4], axis[5], x, y, z, r));
515                         Sleep(100);
516                 }
517         }
518 }
519
520 // --------------------------------------------------------------
521 //      joy_init()
522 //
523 // Initialize the joystick system.  This is called once at game startup.
524 //
525
526 int joy_init()
527 {
528         int i, n, count;
529         MMRESULT rs;
530         JOYINFOEX ji;
531
532         if (Joy_inited)
533                 return 0;
534
535         Joy_inited = 1;
536         n = joyGetNumDevs();
537         Cur_joystick = os_config_read_uint(NULL, "CurrentJoystick", JOYSTICKID1);
538
539         joy_get_caps(n);
540
541         if (n < 1) {
542                 mprintf(("No joystick driver detected\n"));
543                 return 0;
544         }
545
546         InitializeCriticalSection(&joy_lock);
547         atexit(joy_close);
548
549         joy_flush();
550
551         joy_num_sticks = 0;
552         memset(&ji, 0, sizeof(ji));
553         ji.dwSize = sizeof(ji);
554         ji.dwFlags = JOY_RETURNALL;
555
556         if (Cur_joystick >= 0) {
557                 // AL: test code to try and find out why this call fails the first time
558                 rs = 0;
559                 for (count=0; count<20; count++) {
560                         rs = joyGetPosEx(Cur_joystick, &ji);
561                         if (rs == JOYERR_NOERROR)
562                                 break;
563                 }
564                         
565                 if (rs == JOYERR_NOERROR) {
566                         joy_num_sticks++;
567
568                         Joy_tell_thread_to_end_event = CreateEvent( NULL, FALSE, FALSE, NULL );
569                         Joy_thread_says_its_done_event = CreateEvent( NULL, FALSE, FALSE, NULL );
570
571                         joy_thread = CreateThread(NULL, 
572                                                                                         1024 * 32,
573                                                                                         (LPTHREAD_START_ROUTINE) joy_process,
574                                                                                         NULL,
575                                                                                         0,
576                                                                                         &joy_thread_id);
577
578                 //              SetThreadPriority(joy_thread, THREAD_PRIORITY_TIME_CRITICAL - 1);
579                         SetThreadPriority(joy_thread, THREAD_PRIORITY_HIGHEST);
580                 }
581         }
582
583         mprintf(("Windoze reported %d joysticks, we found %d\n", n, joy_num_sticks));
584
585 #ifdef PRECALIBRATED
586         // Fake a calibration
587         if (joy_num_sticks > 0) {
588                 for (i=0; i<4; i++) {
589                         joystick.axis_min[i] = 0;
590                         joystick.axis_center[i] = 32768;
591                         joystick.axis_max[i] = 65536;
592                 }
593         }
594 #else
595         // Fake a calibration
596         if (joy_num_sticks > 0) {
597                 joy_set_cen();
598                 for (i=0; i<4; i++) {
599                         joystick.axis_min[i] = 0;
600                         joystick.axis_max[i] = joystick.axis_center[i]*2;
601                 }
602         }
603 #endif
604
605         joy_ff_init();
606         // joy_di_init();
607
608         return joy_num_sticks;
609 }
610
611 // --------------------------------------------------------------
612 //      joy_cheap_cal()
613 //
614 //      Manual calibrate joystick routine
615 //
616 void joy_cheap_cal()
617 {
618         if ( joy_num_sticks < 1 ) return;
619
620         while(1)        {
621                 Sleep(50);
622                 if ( key_inkey()) break;
623                 if (joy_down_count(0)) break;
624                 mprintf(( "Move stick to upper-left and hit button\n" ));
625         }
626         joy_set_ul();
627
628         while(1)        {
629                 Sleep(50);
630                 if ( key_inkey()) break;
631                 if (joy_down_count(0)) break;
632                 mprintf(( "Move stick to lower-right and hit button\n" ));
633         }
634         joy_set_lr();
635
636         while(1)        {
637                 Sleep(50);
638                 if ( key_inkey()) break;
639                 if (joy_down_count(0)) break;
640                 mprintf(( "Move stick to center and hit button\n" ));
641         }
642         joy_set_cen();
643 }
644
645 // --------------------------------------------------------------
646 //      joy_get_pos_old()
647 //
648 //      Get the position of the joystick axes
649 //
650 int joy_get_pos_old(int * x, int * y )
651 {
652         MMRESULT rs;
653         JOYINFOEX ji;
654
655         if ( joy_num_sticks < 1 ) {
656                 if (x) *x = 0;
657                 if (y) *y = 0;
658                 return 0;
659         }
660
661         memset(&ji, 0, sizeof(ji));
662         ji.dwSize = sizeof(ji);
663
664 #ifdef PRECALIBRATED
665 //JOY_USEDEADZONE|
666 //JOY_RETURNCENTERED|
667 //JOY_RETURNX|JOY_RETURNY|JOY_RETURNRAWDATA;
668         ji.dwFlags = JOY_RETURNALL;
669 #else
670         ji.dwFlags = JOY_CAL_READXYONLY;
671 #endif
672
673         if (Cur_joystick >= 0) {
674                 EnterCriticalSection(&joy_lock);
675                 rs = joyGetPosEx(Cur_joystick, &ji);
676                 LeaveCriticalSection(&joy_lock);
677
678                 if (rs == JOYERR_NOERROR) {
679 #if 1
680                         if (x)
681                                 *x = ji.dwXpos;
682                         if (y)
683                                 *y = ji.dwYpos;
684 #else
685                         if (x) {        
686                                 *x = (ji.dwXpos - 32768) * 2;
687                                 if (*x < -65536)
688                                         *x = -65536;
689                                 else if (*x > 65536)
690                                         *x = 65536;
691                         }
692
693                         if (y) {
694                                 *y = (ji.dwYpos - 32768) * 2;
695                                 if (*y < -65536)
696                                         *y = -65536;
697                                 else if (*y > 65536)
698                                         *y = 65536;
699                         }
700 #endif
701                         return 1;
702                 }
703         }
704
705         if (x)
706                 *x = 0;
707         if (y)
708                 *y = 0;
709
710         return 0;
711 }
712
713
714 // --------------------------------------------------------------
715 //      joy_down_count()
716 //
717 //      Return the number of times the joystick button has gone down since
718 // joy_down_count() was last called
719 //
720 //      input:          btn                     =>              button number to check
721 //                                      reset_count     =>              (default 1): if true reset down_count
722 //
723 //      returns:                number of times button 'btn' has gone down since last call
724 //
725 int joy_down_count(int btn, int reset_count)
726 {
727         int tmp;
728
729         if ( joy_num_sticks < 1 ) return 0;
730         if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0;
731
732         EnterCriticalSection(&joy_lock);
733         tmp = joy_buttons[btn].down_count;
734         if ( reset_count ) {
735                 joy_buttons[btn].down_count = 0;
736         }
737         LeaveCriticalSection(&joy_lock);
738
739         return tmp;
740 }
741
742
743 // --------------------------------------------------------------
744 //      joy_down()
745 //
746 //      Return the state of button number 'btn'
747 //
748 //      input:          btn     =>              button number to check
749 //
750 //      returns:                0               =>              not pressed
751 //                                      1               =>              pressed
752 //
753 int joy_down(int btn)
754 {
755         int tmp;
756
757         if ( joy_num_sticks < 1 ) return 0;
758         if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS )) return 0;
759
760         EnterCriticalSection(&joy_lock);
761         tmp = joy_buttons[btn].state;
762         LeaveCriticalSection(&joy_lock);
763
764         return tmp;
765 }
766
767 // --------------------------------------------------------------
768 //      joy_up_count()
769 //
770 //      Return the number of times the joystick button has gone up since
771 // joy_up_count() was last called
772 //
773 //      input:          btn     =>              button number to check
774 //
775 //      returns:                number of times button 'btn' has gone up since last call
776 //
777 int joy_up_count(int btn)
778 {
779         int tmp;
780
781         if ( joy_num_sticks < 1 ) return 0;
782         if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0;
783
784         EnterCriticalSection(&joy_lock);
785         tmp = joy_buttons[btn].up_count;
786         joy_buttons[btn].up_count = 0;
787         LeaveCriticalSection(&joy_lock);
788
789         return tmp;
790 }
791
792 // --------------------------------------------------------------
793 //      joy_down_time()
794 //
795 //      Return a number between 0 and 1.  This number represents the percentage
796 // time that the joystick button has been down since it was last checked
797 //
798 //      input:          btn     =>              button number to check
799 //      returns:                value between 0 and 1
800 //
801 float joy_down_time(int btn)
802 {
803         float                           rval;
804         unsigned int    now;
805         joy_button_info         *bi;
806
807         if ( joy_num_sticks < 1 ) return 0.0f;
808         if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0.0f;
809         bi = &joy_buttons[btn];
810         EnterCriticalSection(&joy_lock);
811         
812         now = timer_get_milliseconds();
813
814         if ( bi->down_time == 0 && joy_down(btn) ) {
815                 bi->down_time += joy_pollrate;
816         }
817
818         if ( (now - bi->last_down_check) > 0)
819                 rval = i2fl(bi->down_time) / (now - bi->last_down_check);
820         else
821                 rval = 0.0f;
822
823         bi->down_time = 0;
824         bi->last_down_check = now;
825         
826         LeaveCriticalSection(&joy_lock);
827
828         if (rval < 0)
829                 rval = 0.0f;
830         if (rval > 1)
831                 rval = 1.0f;
832
833         return rval;
834 }
835
836 // --------------------------------------------------------------
837 //      joy_get_cal_vals()
838 //
839 //      Get the calibrated min, center, and max for all axes
840 //
841 //      input:  axis_min                =>              OUTPUT PARAMETER:       array of at least 4 ints to hold min axis values
842 //                              axis_center     =>              OUTPUT PARAMETER:       array of at least 4 ints to hold center axis values
843 //                              axis_min                =>              OUTPUT PARAMETER:       array of at least 4 ints to hold max axis values
844 //
845 void joy_get_cal_vals(int *axis_min, int *axis_center, int *axis_max)
846 {
847         int i;
848
849         for ( i = 0; i < 4; i++)                {
850                 axis_min[i] = joystick.axis_min[i];
851                 axis_center[i] = joystick.axis_center[i];
852                 axis_max[i] = joystick.axis_max[i];
853         }
854 }
855
856 // --------------------------------------------------------------
857 //      joy_set_cal_vals()
858 //
859 //      Get the calibrated min, center, and max for all axes
860 //
861 //      input:  axis_min                =>              array of at 4 ints that hold min axis values
862 //                              axis_center     =>              array of at 4 ints that hold center axis values
863 //                              axis_min                =>              array of at 4 ints that hold max axis values
864 //
865 void joy_set_cal_vals(int *axis_min, int *axis_center, int *axis_max)
866 {
867         int i;
868
869         for (i=0; i<4; i++)             {
870                 joystick.axis_min[i] = axis_min[i];
871                 joystick.axis_center[i] = axis_center[i];
872                 joystick.axis_max[i] = axis_max[i];
873         }
874 }
875
876 // --------------------------------------------------------------
877 //      joystick_read_raw_axis()
878 //
879 //      Read the raw axis information for a specified number of axes.
880 //
881 //      input:  num_axes        =>              number of axes to read.  Note the axes go in the following order:
882 //                                                                      dwXpos
883 //                                                                      dwYpos 
884 //                                                                      dwZpos (throttle)
885 //                                                                      dwRpos (rudder)
886 //                                                                      dwUpos (5th axis)
887 //                                                                      dwVpos (6th axis) 
888 //
889 //                              axis            =>              an array of at least 4 ints to hold axis data
890 //
891 int joystick_read_raw_axis(int num_axes, int *axis)
892 {
893         MMRESULT                rs;
894         JOYINFOEX       ji;
895         int                     i;
896
897         Assert(num_axes <= JOY_NUM_AXES);
898         for (i=0; i<num_axes; i++)
899                 axis[i] = 32768;
900
901         // first, try reading with DirectInput, if we can
902         if (Joy_di_inited)
903                 return joystick_read_raw_axis_di(num_axes, axis);
904
905         // No DirectInput joystick, fall back on older stuff
906         if (joy_num_sticks < 1)
907                 return 0;
908
909         memset(&ji, 0, sizeof(ji));
910         ji.dwSize = sizeof(ji);
911
912 #ifdef PRECALIBRATED
913         ji.dwFlags = JOY_RETURNALL;
914 #else
915         ji.dwFlags = JOY_RETURNRAWDATA;
916 #endif
917
918         switch (num_axes) {
919                 case    6:
920                         ji.dwFlags |= JOY_RETURNV;
921                 case    5:
922                         ji.dwFlags |= JOY_RETURNU;
923                 case    4:
924                         ji.dwFlags |= JOY_RETURNR;
925                 case    3:
926                         ji.dwFlags |= JOY_RETURNZ;
927                 case    2:
928                         ji.dwFlags |= JOY_RETURNY;
929                 case    1:
930                         ji.dwFlags |= JOY_RETURNX;
931                         break;
932
933                 default:
934                         Int3();
935                         break;
936         }
937
938         if (Cur_joystick >= 0) {
939                 EnterCriticalSection(&joy_lock);
940                 rs = joyGetPosEx(Cur_joystick, &ji);
941                 LeaveCriticalSection(&joy_lock);
942
943         } else
944                 return 0;
945
946         if (rs != JOYERR_NOERROR)
947                 return 0;
948
949         switch (num_axes) {
950                 case 6:
951                         if (joystick.axis_valid[5])
952                                 axis[5] = ji.dwVpos;
953
954                 case 5:
955                         if (joystick.axis_valid[4])
956                                 axis[4] = ji.dwUpos;
957
958                 case 4:
959                         if (joystick.axis_valid[3])
960                                 axis[3] = ji.dwRpos;
961
962                 case 3:
963                         if (joystick.axis_valid[2])
964                                 axis[2] = ji.dwZpos;
965
966                 case 2:
967                         if (joystick.axis_valid[1])
968                                 axis[1] = ji.dwYpos;
969
970                 case 1:
971                         if (joystick.axis_valid[0])
972                                 axis[0] = ji.dwXpos;
973
974                         break;
975
976                 default:
977                         Int3();
978                         break;
979         }
980
981         return 1;
982 }
983
984 // --------------------------------------------------------------
985 //      joy_set_ul()
986 //
987 //      Get the minimum axis information (namely, joystick in upper left).
988 // This is called by a manual calibration routine.
989 //
990 //      NOTE:   sets the values in joystick.axis_min[]
991 //
992 void joy_set_ul()       
993 {
994         joystick_read_raw_axis( 2, joystick.axis_min );
995 }
996
997 // --------------------------------------------------------------
998 //      joy_set_lr()
999 //
1000 //      Get the maximum axis information (namely, joystick in lower right).
1001 // This is called by a manual calibration routine.
1002 //
1003 //      NOTE:   sets the values in joystick.axis_max[]
1004 //
1005 void joy_set_lr()       
1006 {
1007         joystick_read_raw_axis( 2, joystick.axis_max );
1008 }
1009
1010 // --------------------------------------------------------------
1011 //      joy_set_cen()
1012 //
1013 //      Get the center axis information (namely, joystick in dead zone).
1014 // This is called by a manual calibration routine.
1015 //
1016 //      NOTE:   sets the values in joystick.axis_center[]
1017 //
1018 void joy_set_cen() 
1019 {
1020         joystick_read_raw_axis( 2, joystick.axis_center );
1021 }
1022
1023 int joy_get_unscaled_reading(int raw, int axn)
1024 {
1025         int rng;
1026
1027         // Make sure it's calibrated properly.
1028         if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5)
1029                 return 0;
1030
1031         if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5)
1032                 return 0;
1033
1034         rng = joystick.axis_max[axn] - joystick.axis_min[axn];
1035         raw -= joystick.axis_min[axn];  // adjust for linear range starting at 0
1036         
1037         // cap at limits
1038         if (raw < 0)
1039                 raw = 0;
1040         if (raw > rng)
1041                 raw = rng;
1042
1043         return (int) ((unsigned int) raw * (unsigned int) F1_0 / (unsigned int) rng);  // convert to 0 - F1_0 range.
1044 }
1045
1046 // --------------------------------------------------------------
1047 //      joy_get_scaled_reading()
1048 //
1049 //      input:  raw     =>      the raw value for an axis position
1050 //                              axn     =>      axis number, numbered starting at 0
1051 //
1052 // return:      joy_get_scaled_reading will return a value that represents
1053 //                              the joystick pos from -1 to +1 for the specified axis number 'axn', and
1054 //                              the raw value 'raw'
1055 //
1056 int joy_get_scaled_reading(int raw, int axn)
1057 {
1058         int x, d, dead_zone, rng;
1059         float percent, sensitivity_percent, non_sensitivity_percent;
1060
1061         // Make sure it's calibrated properly.
1062         if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5)
1063                 return 0;
1064
1065         if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5)
1066                 return 0;
1067
1068         raw -= joystick.axis_center[axn];
1069
1070         dead_zone = (joystick.axis_max[axn] - joystick.axis_min[axn]) * Dead_zone_size / 100;
1071
1072         if (raw < -dead_zone) {
1073                 rng = joystick.axis_center[axn] - joystick.axis_min[axn] - dead_zone;
1074                 d = -raw - dead_zone;
1075
1076         } else if (raw > dead_zone) {
1077                 rng = joystick.axis_max[axn] - joystick.axis_center[axn] - dead_zone;
1078                 d = raw - dead_zone;
1079
1080         } else
1081                 return 0;
1082
1083         if (d > rng)
1084                 d = rng;
1085
1086         Assert(Joy_sensitivity >= 0 && Joy_sensitivity <= 9);
1087
1088         // compute percentages as a range between 0 and 1
1089         sensitivity_percent = (float) Joy_sensitivity / 9.0f;
1090         non_sensitivity_percent = (float) (9 - Joy_sensitivity) / 9.0f;
1091
1092         // find percent of max axis is at
1093         percent = (float) d / (float) rng;
1094
1095         // work sensitivity on axis value
1096         percent = (percent * sensitivity_percent + percent * percent * percent * percent * percent * non_sensitivity_percent);
1097
1098         x = (int) ((float) F1_0 * percent);
1099
1100         //nprintf(("AI", "d=%6i, sens=%3i, percent=%6.3f, val=%6i, ratio=%6.3f\n", d, Joy_sensitivity, percent, (raw<0) ? -x : x, (float) d/x));
1101
1102         if (raw < 0)
1103                 return -x;
1104
1105         return x;
1106 }
1107
1108 // --------------------------------------------------------------
1109 //      joy_get_pos()
1110 //
1111 //      input:  x               =>              OUTPUT PARAMETER: x-axis position of stick (-1 to 1)
1112 //                              y               =>              OUTPUT PARAMETER: y-axis position of stick (-1 to 1)
1113 //                              z               =>              OUTPUT PARAMETER: z-axis (throttle) position of stick (-1 to 1)
1114 //                              r               =>              OUTPUT PARAMETER: rudder position of stick (-1 to 1)
1115 //
1116 //      return: success => 1
1117 //                              failure => 0
1118 //
1119 int joy_get_pos(int *x, int *y, int *z, int *rx)
1120 {
1121         int axis[JOY_NUM_AXES];
1122
1123         if (x) *x = 0;
1124         if (y) *y = 0;
1125         if (z) *z = 0;
1126         if (rx) *rx = 0;
1127
1128         if (joy_num_sticks < 1) return 0;
1129
1130         joystick_read_raw_axis( 6, axis );
1131
1132         //      joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1
1133         if (x && joystick.axis_valid[0])
1134                 *x = joy_get_scaled_reading(axis[0], 0);
1135         if (y && joystick.axis_valid[1]) 
1136                 *y = joy_get_scaled_reading(axis[1], 1);
1137         if (z && joystick.axis_valid[2])
1138                 *z = joy_get_unscaled_reading(axis[2], 2);
1139         if (rx && joystick.axis_valid[3])
1140                 *rx = joy_get_scaled_reading(axis[3], 3);
1141
1142         if (x)
1143                 Joy_last_x_reading = *x;
1144
1145         if (y)
1146                 Joy_last_x_reading = *y;
1147
1148         return 1;
1149 }
1150
1151 // change in joy position since last call
1152 void joy_get_delta(int *dx, int *dy)
1153 {
1154         static int old_joy_x = 0;
1155         static int old_joy_y = 0;
1156
1157         if ( !Joy_inited ) {
1158                 *dx = *dy = 0;
1159                 return;
1160         }
1161
1162         *dx = Joy_last_x_reading - old_joy_x;
1163         *dy = Joy_last_y_reading - old_joy_y;
1164
1165         old_joy_x = Joy_last_x_reading;
1166         old_joy_y = Joy_last_y_reading;
1167 }
1168
1169 ////  This is the DirectInput joystick stuff
1170
1171 GUID Di_joy_guid;
1172 int Di_joy_guid_valid = 0;
1173
1174 BOOL CALLBACK joy_di_enum(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
1175 {
1176         char buf[64];   
1177
1178         nprintf(("Joystick", "Joystick detected: %s (%s)\n", lpddi->tszInstanceName, lpddi->tszProductName));
1179         sprintf(buf, "Joystick %d", Cur_joystick + 1);
1180         if (!stricmp(buf, lpddi->tszInstanceName)) {
1181                 Di_joy_guid = lpddi->guidInstance;
1182                 Di_joy_guid_valid = 1;
1183                 nprintf(("Joystick", "   (Selected joystick)\n"));
1184         }
1185
1186         return DIENUM_CONTINUE;
1187 }
1188 /*
1189 BOOL FAR PASCAL InitJoystickInput(LPCDIDEVICEINSTANCE pdinst, LPVOID pvRef)
1190 {
1191    LPDIRECTINPUT pdi = pvRef;
1192    LPDIRECTINPUTDEVICE pdev;
1193    DIPROPRANGE diprg;
1194
1195    // create the DirectInput joystick device
1196    if(pdi->lpVtbl->CreateDevice(pdi, &pdinst->guidInstance, &pdev, NULL) != DI_OK)
1197    {
1198       OutputDebugString("IDirectInput::CreateDevice FAILED\n");
1199       return DIENUM_CONTINUE;
1200    }
1201
1202    // set joystick data format
1203    if (pdev->lpVtbl->SetDataFormat(pdev, &c_dfDIJoystick) != DI_OK)
1204    {
1205       OutputDebugString("IDirectInputDevice::SetDataFormat FAILED\n");
1206       pdev->lpVtbl->Release(pdev);
1207       return DIENUM_CONTINUE;
1208    }
1209
1210    // set the cooperative level
1211    if (pdev->lpVtbl->SetCooperativeLevel(pdev, hWndMain,
1212       DISCL_NONEXCLUSIVE | DISCL_FOREGROUND) != DI_OK)
1213    {
1214       OutputDebugString("IDirectInputDevice::SetCooperativeLevel FAILED\n");
1215       pdev->lpVtbl->Release(pdev);
1216       return DIENUM_CONTINUE;
1217    }
1218
1219    // set X-axis range to (-1000 ... +1000)
1220    // This lets us test against 0 to see which way the stick is pointed.
1221
1222    diprg.diph.dwSize       = sizeof(diprg);
1223    diprg.diph.dwHeaderSize = sizeof(diprg.diph);
1224    diprg.diph.dwObj        = DIJOFS_X;
1225    diprg.diph.dwHow        = DIPH_BYOFFSET;
1226    diprg.lMin              = -1000;
1227    diprg.lMax              = +1000;
1228
1229    if (pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK)
1230    {
1231       OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n");
1232       pdev->lpVtbl->Release(pdev);
1233       return FALSE;
1234    }
1235
1236    //
1237    // And again for Y-axis range
1238    //
1239    diprg.diph.dwObj        = DIJOFS_Y;
1240
1241    if (pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK)
1242    {
1243       OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n");
1244       pdev->lpVtbl->Release(pdev);
1245       return FALSE;
1246    }
1247
1248    // set X axis dead zone to 50% (to avoid accidental turning)
1249    // Units are ten thousandths, so 50% = 5000/10000.
1250    if (SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_X, DIPH_BYOFFSET, 5000) != DI_OK)
1251    {
1252       OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n");
1253       pdev->lpVtbl->Release(pdev);
1254       return FALSE;
1255    }
1256
1257
1258    // set Y axis dead zone to 50% (to avoid accidental thrust)
1259    // Units are ten thousandths, so 50% = 5000/10000.
1260    if (SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_Y, DIPH_BYOFFSET, 5000) != DI_OK)
1261    {
1262       OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n");
1263       pdev->lpVtbl->Release(pdev);
1264       return FALSE;
1265    }
1266
1267
1268    // Add it to our list of devices.  If AddInputDevice succeeds,
1269    // he will do an AddRef.
1270    AddInputDevice(pdev, pdinst);
1271         hRes = pdev->lpVtbl->QueryInterface(
1272                     pdev, &IID_IDirectInputDevice2,
1273                     (LPVOID *)&g_rgpdevFound[g_cpdevFound]);
1274
1275    pdev->lpVtbl->Release(pdev);
1276
1277    return DIENUM_CONTINUE;
1278 }
1279 */
1280 int joy_di_init()
1281 {
1282         HRESULT hr;
1283    LPDIRECTINPUTDEVICE pdev;
1284
1285         Joy_di_inited = 0;
1286         hr = DirectInputCreate(GetModuleHandle(NULL), 0x500, &Di_joystick_obj, NULL);
1287         if (FAILED(hr)) {
1288                 mprintf(( "DirectInputCreate() failed!\n" ));
1289                 return -1;
1290         }
1291
1292         Di_joy_guid_valid = 0;
1293         hr = Di_joystick_obj->EnumDevices(DIDEVTYPE_JOYSTICK, joy_di_enum, Di_joystick_obj, DIEDFL_ATTACHEDONLY);
1294         if (FAILED(hr)) {
1295                 mprintf(( "EnumDevice() failed!\n" ));
1296                 return -1;
1297         }
1298
1299         if (!Di_joy_guid_valid) {
1300                 mprintf(( "Correct joystick not found.\n" ));
1301                 return -1;
1302         }
1303
1304         hr = Di_joystick_obj->CreateDevice(Di_joy_guid, &pdev, NULL);
1305         if (FAILED(hr)) {
1306                 mprintf(( "CreateDevice() failed!\n" ));
1307                 return -1;
1308         }
1309
1310         hr = pdev->SetDataFormat(&c_dfDIJoystick);
1311         if (FAILED(hr)) {
1312                 mprintf(( "SetDataFormat() failed!\n" ));
1313                 if (hr == DIERR_ACQUIRED)
1314                         mprintf(( "   (reason: DIERR_ACQUIRED)\n" ));
1315
1316                 if (hr == DIERR_INVALIDPARAM)
1317                         mprintf(( "   (reason: DIERR_INVALIDPARAM)\n" ));
1318
1319                 if (hr == DIERR_NOTINITIALIZED)
1320                         mprintf(( "   (reason: DIERR_NOTINITIALIZED)\n" ));
1321
1322                 pdev->Release();
1323                 return -1;
1324         }
1325
1326         hr = pdev->SetCooperativeLevel((HWND) os_get_window(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
1327         if (FAILED(hr)) {
1328                 mprintf(( "SetCooperativeLevel() failed!\n" ));
1329                 if (hr == DIERR_ACQUIRED)
1330                         mprintf(( "   (reason: DIERR_ACQUIRED)\n" ));
1331
1332                 if (hr == DIERR_INVALIDPARAM)
1333                         mprintf(( "   (reason: DIERR_INVALIDPARAM)\n" ));
1334
1335                 if (hr == DIERR_NOTINITIALIZED)
1336                         mprintf(( "   (reason: DIERR_NOTINITIALIZED)\n" ));
1337
1338                 pdev->Release();
1339                 return -1;
1340         }
1341
1342         hr = pdev->QueryInterface(IID_IDirectInputDevice2, (LPVOID *) &Di_joystick);
1343         if (FAILED(hr)) {
1344                 pdev->Release();
1345                 return -1;
1346         }
1347
1348         Di_joystick->Acquire();
1349
1350         pdev->Release();
1351         Joy_di_inited = 1;
1352         nprintf(("Joystick", "DirectInput initialization of joystick succeeded\n"));
1353         return 0;
1354 }
1355
1356 int joy_di_shutdown()
1357 {
1358         // Destroy any lingering IDirectInputDevice object.
1359         if (Di_joystick) {
1360                 // Unacquire the device one last time just in case we got really confused
1361                 // and tried to exit while the device is still acquired.
1362                 Di_joystick->Unacquire();
1363
1364                 Di_joystick->Release();
1365                 Di_joystick = NULL;
1366         }
1367
1368         // Destroy any lingering IDirectInput object.
1369         if (Di_joystick_obj) {
1370                 Di_joystick_obj->Release();
1371                 Di_joystick_obj = NULL;
1372         }
1373
1374         Joy_di_inited = 0;
1375         return 0;
1376 }
1377
1378 int joystick_read_raw_axis_di(int num_axes, int *axis)
1379 {
1380         int repeat = 1;
1381         HRESULT hr = 0;
1382         DIJOYSTATE joy_state;
1383
1384         if (!Joy_di_inited)
1385                 return 0;
1386
1387         repeat = 1;
1388         while (repeat) {
1389                 repeat = 0;
1390
1391            hr = Di_joystick->Poll();
1392                 if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) {
1393                         // DirectInput is telling us that the input stream has
1394                         // been interrupted.  We aren't tracking any state
1395                         // between polls, so we don't have any special reset
1396                         // that needs to be done.  We just re-acquire and
1397                         // try again.
1398                         Sleep(1000);            // Pause a second...
1399                         hr = Di_joystick->Acquire();
1400                         if (SUCCEEDED(hr))
1401                                 repeat = 1;
1402                 }
1403         }
1404
1405         repeat = 1;
1406         memset(&joy_state, 0, sizeof(joy_state));
1407         while (repeat) {
1408                 repeat = 0;
1409
1410                 hr = Di_joystick->GetDeviceState(sizeof(joy_state), &joy_state);
1411                 if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) {
1412                         // DirectInput is telling us that the input stream has
1413                         // been interrupted.  We aren't tracking any state
1414                         // between polls, so we don't have any special reset
1415                         // that needs to be done.  We just re-acquire and
1416                         // try again.
1417                         Sleep(1000);            // Pause a second...
1418                         hr = Di_joystick->Acquire();
1419                         if (SUCCEEDED(hr))
1420                                 repeat = 1;
1421                 }
1422         }
1423
1424         if (SUCCEEDED(hr)) {
1425                 switch (num_axes) {
1426                         case 6:
1427                                 if (joystick.axis_valid[5])
1428                                         axis[5] = joy_state.lRy;
1429
1430                         case 5:
1431                                 if (joystick.axis_valid[4])
1432                                         axis[4] = joy_state.lRx;
1433
1434                         case 4:
1435                                 if (joystick.axis_valid[3])
1436                                         axis[3] = joy_state.lRz;
1437
1438                         case 3:
1439                                 if (joystick.axis_valid[2])
1440                                         axis[2] = joy_state.lZ;
1441
1442                         case 2:
1443                                 if (joystick.axis_valid[1])
1444                                         axis[1] = joy_state.lY;
1445
1446                         case 1:
1447                                 if (joystick.axis_valid[0])
1448                                         axis[0] = joy_state.lX;
1449
1450                                 break;
1451                 }
1452         }
1453
1454         return 1;
1455 }