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