2 * $Logfile: /Freespace2/code/Io/Joy.cpp $
7 * Code to read the joystick
10 * Revision 1.2 2002/05/07 03:16:46 theoddone33
11 * The Great Newline Fix
13 * Revision 1.1.1.1 2002/05/03 03:28:09 root
17 * 5 7/15/99 9:20a Andsager
18 * FS2_DEMO initial checkin
20 * 4 6/02/99 6:18p Dave
21 * Fixed TNT lockup problems! Wheeeee!
23 * 3 10/09/98 2:57p Dave
24 * Starting splitting up OS stuff.
26 * 2 10/07/98 10:53a Dave
29 * 1 10/07/98 10:49a Dave
31 * 61 5/24/98 12:56a Mike
32 * Put in debug code, but comment out, to check joystick sensitivity.
34 * 60 5/19/98 6:54p Lawrance
35 * Set default joystick sensitivity to max
37 * 59 5/13/98 7:14p Hoffoss
38 * Made invalid axis return center position value.
40 * 58 5/13/98 1:17a Hoffoss
41 * Added joystick axes configurability.
43 * 57 5/07/98 3:15p Hoffoss
46 * 56 5/07/98 12:41p Hoffoss
47 * Changed code to make joystick sensitivity default to center of range.
49 * 55 5/06/98 12:02a Hoffoss
50 * Fixed throttle problems with joysticks.
52 * 54 5/05/98 8:38p Hoffoss
53 * Added sensitivity adjustment to options menu and made it save to pilot
56 * 53 5/04/98 11:08p Hoffoss
57 * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp.
58 * Updated references everywhere to it.
60 * 52 5/03/98 6:02p Hoffoss
61 * Added DirectInput support for joystick (only works with v5.0, so only
64 * 51 5/01/98 5:59p Hoffoss
65 * Checking in code so I can switch back to NT to work on this instead
68 * 50 4/30/98 12:12p Hoffoss
69 * Changed code to explicitly use calibrated values from Windows for
72 * 49 4/29/98 12:13a Lawrance
73 * Add function for reading down count without resetting internal count.
74 * Add hook to reset demo trailer timer.
76 * 48 4/25/98 12:02p Lawrance
77 * take out crit sec code around joystick read in joy_process
79 * 47 4/22/98 9:03p John
80 * Put critical section around all the joygetposex functions. Had to set
81 * up events to signal the polling thread to end at the end of the
84 * 46 4/17/98 3:07p Jim
85 * added debug code to test joystick
87 * 45 4/13/98 10:16a John
88 * Switched gettime back to timer_get_milliseconds, which is now thread
91 * 44 4/12/98 11:08p Lawrance
92 * switch back to using gettime() in separate threads
94 * 43 4/12/98 5:31p Lawrance
95 * use timer_get_milliseconds() instead of gettime()
97 * 42 3/21/98 11:29a John
98 * Made joy_flush work when a button is held down
100 * 41 3/12/98 10:44a Hoffoss
101 * Whoops, should probably use the actual define rather than 0.
103 * 40 3/12/98 10:38a Hoffoss
104 * Changed joystick default to first slot in never set up in FSLaunch
106 * 39 3/11/98 5:27p Hoffoss
107 * Added to FreeSpace usage of joystick specified through FSLaunch.
109 * 38 3/09/98 4:44p Sandeep
111 * 37 3/07/98 5:23p Sandeep
113 * 35 3/06/98 11:12a Hoffoss
114 * Fixed joystick deadzone bug.
116 * 33 3/06/98 10:02a Hoffoss
117 * Made dead zone adjustable, and defaulted it to 10% instead of 5%.
119 * 36 3/07/98 4:50p John
120 * Added code to allow toggling force feedback on/off in setup
122 * 35 3/06/98 11:12a Hoffoss
123 * Fixed joystick deadzone bug.
125 * 33 3/06/98 10:02a Hoffoss
126 * Made dead zone adjustable, and defaulted it to 10% instead of 5%.
128 * 32 2/11/98 9:56p Jim
129 * allender: from sandeep on Jim's machine -- some force feedback stuff
131 * 31 1/29/98 11:04a Sandeep
133 * 30 1/27/98 8:40p Sandeep
135 * 29 1/19/98 6:15p John
136 * Fixed all my Optimized Build compiler warnings
138 * 28 1/08/98 6:35p Hoffoss
139 * Fixed joystick undefined detection.
141 * 27 1/08/98 3:48p Dan
142 * Fixed bug with joystick axis reading thinking it's undefined at
145 * 26 10/16/97 5:37p Lawrance
146 * change thread priority from THREAD_PRIORITY_TIME_CRITICAL to
147 * THREAD_PRIORITY_HIGHEST
149 * 25 10/09/97 10:15a Johnson
150 * try to init several times if joystick init fails
152 * 24 10/07/97 10:42a Johnson
153 * zero out JOYINFOEX struct before setting dwSize
155 * 23 10/06/97 5:54p Johnson
156 * ALAN: fix nasty bug where dwSize member of JOYINFOEX was not being set,
157 * resulting in random failure
159 * 22 9/15/97 11:42p Lawrance
160 * change button_info to joy_button_info to avoid name conflict
162 * 21 8/07/97 11:26p Lawrance
163 * add support for 4th axis (rudder)
165 * 20 7/29/97 5:30p Lawrance
166 * move gettime() from keyboard module to timer module
168 * 19 7/11/97 11:43a Lawrance
169 * fix bug with joy_up_count
171 * 18 7/10/97 12:29a Lawrance
172 * fix problem with NT not recognizing an axis that was set under 95
174 * 17 7/09/97 11:41p Lawrance
175 * added throttle and hat support
177 * 16 6/19/97 9:50a John
178 * fixed bug where joy_close getting called out of order doesn't matter.
180 * 15 5/18/97 2:40p Lawrance
181 * added joy_get_caps()
183 * 14 4/22/97 10:56a John
184 * fixed some resource leaks.
186 * 13 2/27/97 2:23p Lawrance
187 * took out debug stmts
189 * 12 2/27/97 10:04a Lawrance
190 * fixed bug that was causing all joy buttons but 0 to be read incorrectly
192 * 11 2/17/97 5:18p John
193 * Added a bunch of RCS headers to a bunch of old files that don't have
200 #include <windowsx.h>
207 #include "osregistry.h"
212 #define PRECALIBRATED 1
214 static int Joy_inited = 0;
215 int joy_num_sticks = 0;
216 int Dead_zone_size = 10;
217 int Cur_joystick = -1; // joystick used for input or -1
218 int Joy_sensitivity = 9;
220 CRITICAL_SECTION joy_lock;
222 HANDLE joy_thread = NULL;
224 int joy_pollrate = 1000 / 18; // poll at 18Hz
226 HANDLE Joy_tell_thread_to_end_event = NULL;
227 HANDLE Joy_thread_says_its_done_event = NULL;
229 static int Joy_last_x_reading = 0;
230 static int Joy_last_y_reading = 0;
232 int Joy_di_inited = 0;
233 static LPDIRECTINPUT Di_joystick_obj = NULL;
234 static LPDIRECTINPUTDEVICE2 Di_joystick = NULL;
236 typedef struct joy_button_info {
237 int actual_state; // Set if the button is physically down
238 int state; // Set when the button goes from up to down, cleared on down to up. Different than actual_state after a flush.
242 uint last_down_check; // timestamp in milliseconds of last
247 joy_button_info joy_buttons[JOY_TOTAL_BUTTONS];
250 int joy_di_shutdown();
251 int joystick_read_raw_axis_di(int num_axes, int *axis);
253 // --------------------------------------------------------------
256 // Clear the state of the joystick.
263 if ( joy_num_sticks < 1 ) return;
265 EnterCriticalSection(&joy_lock);
266 for ( i = 0; i < JOY_TOTAL_BUTTONS; i++) {
267 bi = &joy_buttons[i];
272 bi->last_down_check = timer_get_milliseconds();
275 LeaveCriticalSection(&joy_lock);
278 // --------------------------------------------------------------
281 // Runs as a separate thread, and updates the state of the joystick
283 DWORD joy_process(DWORD lparam)
290 for ( i = 0; i < JOY_TOTAL_BUTTONS; i++) {
291 bi = &joy_buttons[i];
292 bi->actual_state = 0; // Don't set in flush code!
297 bi->last_down_check = timer_get_milliseconds();
301 // Wait for the thread to be signaled to end or 1/18th of a second to pass...
302 if ( WaitForSingleObject( Joy_tell_thread_to_end_event, joy_pollrate )==WAIT_OBJECT_0) {
306 memset(&ji, 0, sizeof(ji));
307 ji.dwSize = sizeof(ji);
308 // ji.dwFlags = JOY_RETURNBUTTONS | JOY_RETURNRAWDATA;
309 ji.dwFlags = JOY_RETURNALL;
311 EnterCriticalSection(&joy_lock);
314 if (Cur_joystick >= 0) {
315 rs = joyGetPosEx(Cur_joystick, &ji);
316 // If there's an error, assume all buttons down.
317 if (rs == JOYERR_NOERROR) {
318 joy_state = ji.dwButtons;
322 // Process ji.dwButtons
323 for (i=0; i<JOY_TOTAL_BUTTONS; i++) {
325 if (i < JOY_NUM_BUTTONS) {
326 state = joy_state & (1<<i);
329 // check for hat presses, which act like buttons
332 if (ji.dwPOV == JOY_POVBACKWARD)
337 if (ji.dwPOV == JOY_POVFORWARD)
342 if (ji.dwPOV == JOY_POVLEFT)
347 if (ji.dwPOV == JOY_POVRIGHT)
352 Int3(); // should never happen
359 if (state != joy_buttons[i].actual_state) {
360 // Button position physically changed.
361 joy_buttons[i].actual_state = state;
364 // went from up to down
365 joy_buttons[i].down_count++;
366 joy_buttons[i].down_time = 0;
368 joy_buttons[i].state = 1;
370 ////////////////////////////
371 /// SOMETHING TERRIBLE IS ABOUT TO HAPPEN. I FEEL THIS IS NECESSARY FOR THE DEMO, SINCE
372 /// I DON'T WANT TO CALL CRITICAL SECTION CODE EACH FRAME TO CHECK ALL THE JOYSTICK BUTTONS.
373 /// PLEASE SEE ALAN FOR MORE INFORMATION.
374 ////////////////////////////
377 extern void demo_reset_trailer_timer();
378 demo_reset_trailer_timer();
381 ////////////////////////////
382 /// IT'S OVER. SEE, IT WASN'T SO BAD RIGHT? IT'S IS VERY UGLY LOOKING, I KNOW.
383 ////////////////////////////
387 // went from down to up
388 if ( joy_buttons[i].state ) {
389 joy_buttons[i].up_count++;
391 joy_buttons[i].state = 0;
395 // Didn't move... increment time down if down.
396 if (joy_buttons[i].state) {
397 joy_buttons[i].down_time += joy_pollrate;
403 LeaveCriticalSection(&joy_lock);
406 SetEvent(Joy_thread_says_its_done_event);
411 // --------------------------------------------------------------
414 // Close the joystick system. Should be called at game exit.
421 // joy_di_shutdown();
426 // Tell joystick polling thread to end
427 SetEvent(Joy_tell_thread_to_end_event);
429 // Wait for it to end
430 if ( WaitForSingleObject( Joy_thread_says_its_done_event, 5000 )==WAIT_TIMEOUT) { //INFINITE );
431 mprintf(( "Joy end thread wait timeout!\n" ));
433 CloseHandle(Joy_tell_thread_to_end_event);
434 CloseHandle(Joy_thread_says_its_done_event);
436 // It is now safe to release any resources use by the polling thread.
437 DeleteCriticalSection( &joy_lock );
439 CloseHandle(joy_thread);
446 // --------------------------------------------------------------
449 // Determine the capabilities of the attached joysticks.
451 void joy_get_caps(int max)
456 for (j=0; j<JOY_NUM_AXES; j++)
457 joystick.axis_valid[j] = 0;
459 for (j=JOYSTICKID1; j<JOYSTICKID1+max; j++) {
460 if (JOYERR_NOERROR == joyGetDevCaps (j, &JoyCaps, sizeof(JoyCaps))) {
461 nprintf(("JOYSTICK", "Joystick #%d: %s\n", j - JOYSTICKID1 + 1, JoyCaps.szPname));
463 if (j == Cur_joystick) {
464 joystick.axis_valid[0] = joystick.axis_valid[1] = 1;
465 if (JoyCaps.wCaps & JOYCAPS_HASZ)
466 joystick.axis_valid[2] = 1;
467 if (JoyCaps.wCaps & JOYCAPS_HASR)
468 joystick.axis_valid[3] = 1;
469 if (JoyCaps.wCaps & JOYCAPS_HASU)
470 joystick.axis_valid[4] = 1;
471 if (JoyCaps.wCaps & JOYCAPS_HASV)
472 joystick.axis_valid[5] = 1;
478 int joy_get_scaled_reading(int raw, int axn);
479 int joy_get_unscaled_reading(int raw, int axn);
481 DCF(joytest, "Test joystick")
484 while (!keyd_pressed[KEY_ESC]) {
485 int x, y, axis[JOY_NUM_AXES];
487 if (joy_num_sticks < 1)
490 joystick_read_raw_axis(JOY_NUM_AXES, axis);
492 x = joy_get_scaled_reading(axis[0], 0);
493 y = joy_get_scaled_reading(axis[1], 1);
495 mprintf(("X=%5d Y=%5d Calibrated X=%6d Y=%6d\n", axis[0], axis[1], x, y));
501 DCF(joytest2, "Test joystick (extended)")
504 while (!keyd_pressed[KEY_ESC]) {
505 int x, y, z, r, axis[JOY_NUM_AXES];
507 if (joy_num_sticks < 1)
510 joystick_read_raw_axis(JOY_NUM_AXES, axis);
512 x = joy_get_scaled_reading(axis[0], 0);
513 y = joy_get_scaled_reading(axis[1], 1);
514 z = joy_get_unscaled_reading(axis[2], 2);
515 r = joy_get_scaled_reading(axis[3], 3);
517 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));
523 // --------------------------------------------------------------
526 // Initialize the joystick system. This is called once at game startup.
540 Cur_joystick = os_config_read_uint(NULL, "CurrentJoystick", JOYSTICKID1);
545 mprintf(("No joystick driver detected\n"));
549 InitializeCriticalSection(&joy_lock);
555 memset(&ji, 0, sizeof(ji));
556 ji.dwSize = sizeof(ji);
557 ji.dwFlags = JOY_RETURNALL;
559 if (Cur_joystick >= 0) {
560 // AL: test code to try and find out why this call fails the first time
562 for (count=0; count<20; count++) {
563 rs = joyGetPosEx(Cur_joystick, &ji);
564 if (rs == JOYERR_NOERROR)
568 if (rs == JOYERR_NOERROR) {
571 Joy_tell_thread_to_end_event = CreateEvent( NULL, FALSE, FALSE, NULL );
572 Joy_thread_says_its_done_event = CreateEvent( NULL, FALSE, FALSE, NULL );
574 joy_thread = CreateThread(NULL,
576 (LPTHREAD_START_ROUTINE) joy_process,
581 // SetThreadPriority(joy_thread, THREAD_PRIORITY_TIME_CRITICAL - 1);
582 SetThreadPriority(joy_thread, THREAD_PRIORITY_HIGHEST);
586 mprintf(("Windoze reported %d joysticks, we found %d\n", n, joy_num_sticks));
589 // Fake a calibration
590 if (joy_num_sticks > 0) {
591 for (i=0; i<4; i++) {
592 joystick.axis_min[i] = 0;
593 joystick.axis_center[i] = 32768;
594 joystick.axis_max[i] = 65536;
598 // Fake a calibration
599 if (joy_num_sticks > 0) {
601 for (i=0; i<4; i++) {
602 joystick.axis_min[i] = 0;
603 joystick.axis_max[i] = joystick.axis_center[i]*2;
611 return joy_num_sticks;
614 // --------------------------------------------------------------
617 // Manual calibrate joystick routine
621 if ( joy_num_sticks < 1 ) return;
625 if ( key_inkey()) break;
626 if (joy_down_count(0)) break;
627 mprintf(( "Move stick to upper-left and hit button\n" ));
633 if ( key_inkey()) break;
634 if (joy_down_count(0)) break;
635 mprintf(( "Move stick to lower-right and hit button\n" ));
641 if ( key_inkey()) break;
642 if (joy_down_count(0)) break;
643 mprintf(( "Move stick to center and hit button\n" ));
648 // --------------------------------------------------------------
651 // Get the position of the joystick axes
653 int joy_get_pos_old(int * x, int * y )
658 if ( joy_num_sticks < 1 ) {
664 memset(&ji, 0, sizeof(ji));
665 ji.dwSize = sizeof(ji);
669 //JOY_RETURNCENTERED|
670 //JOY_RETURNX|JOY_RETURNY|JOY_RETURNRAWDATA;
671 ji.dwFlags = JOY_RETURNALL;
673 ji.dwFlags = JOY_CAL_READXYONLY;
676 if (Cur_joystick >= 0) {
677 EnterCriticalSection(&joy_lock);
678 rs = joyGetPosEx(Cur_joystick, &ji);
679 LeaveCriticalSection(&joy_lock);
681 if (rs == JOYERR_NOERROR) {
689 *x = (ji.dwXpos - 32768) * 2;
697 *y = (ji.dwYpos - 32768) * 2;
717 // --------------------------------------------------------------
720 // Return the number of times the joystick button has gone down since
721 // joy_down_count() was last called
723 // input: btn => button number to check
724 // reset_count => (default 1): if true reset down_count
726 // returns: number of times button 'btn' has gone down since last call
728 int joy_down_count(int btn, int reset_count)
732 if ( joy_num_sticks < 1 ) return 0;
733 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0;
735 EnterCriticalSection(&joy_lock);
736 tmp = joy_buttons[btn].down_count;
738 joy_buttons[btn].down_count = 0;
740 LeaveCriticalSection(&joy_lock);
746 // --------------------------------------------------------------
749 // Return the state of button number 'btn'
751 // input: btn => button number to check
753 // returns: 0 => not pressed
756 int joy_down(int btn)
760 if ( joy_num_sticks < 1 ) return 0;
761 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS )) return 0;
763 EnterCriticalSection(&joy_lock);
764 tmp = joy_buttons[btn].state;
765 LeaveCriticalSection(&joy_lock);
770 // --------------------------------------------------------------
773 // Return the number of times the joystick button has gone up since
774 // joy_up_count() was last called
776 // input: btn => button number to check
778 // returns: number of times button 'btn' has gone up since last call
780 int joy_up_count(int btn)
784 if ( joy_num_sticks < 1 ) return 0;
785 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0;
787 EnterCriticalSection(&joy_lock);
788 tmp = joy_buttons[btn].up_count;
789 joy_buttons[btn].up_count = 0;
790 LeaveCriticalSection(&joy_lock);
795 // --------------------------------------------------------------
798 // Return a number between 0 and 1. This number represents the percentage
799 // time that the joystick button has been down since it was last checked
801 // input: btn => button number to check
802 // returns: value between 0 and 1
804 float joy_down_time(int btn)
810 if ( joy_num_sticks < 1 ) return 0.0f;
811 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0.0f;
812 bi = &joy_buttons[btn];
813 EnterCriticalSection(&joy_lock);
815 now = timer_get_milliseconds();
817 if ( bi->down_time == 0 && joy_down(btn) ) {
818 bi->down_time += joy_pollrate;
821 if ( (now - bi->last_down_check) > 0)
822 rval = i2fl(bi->down_time) / (now - bi->last_down_check);
827 bi->last_down_check = now;
829 LeaveCriticalSection(&joy_lock);
839 // --------------------------------------------------------------
840 // joy_get_cal_vals()
842 // Get the calibrated min, center, and max for all axes
844 // input: axis_min => OUTPUT PARAMETER: array of at least 4 ints to hold min axis values
845 // axis_center => OUTPUT PARAMETER: array of at least 4 ints to hold center axis values
846 // axis_min => OUTPUT PARAMETER: array of at least 4 ints to hold max axis values
848 void joy_get_cal_vals(int *axis_min, int *axis_center, int *axis_max)
852 for ( i = 0; i < 4; i++) {
853 axis_min[i] = joystick.axis_min[i];
854 axis_center[i] = joystick.axis_center[i];
855 axis_max[i] = joystick.axis_max[i];
859 // --------------------------------------------------------------
860 // joy_set_cal_vals()
862 // Get the calibrated min, center, and max for all axes
864 // input: axis_min => array of at 4 ints that hold min axis values
865 // axis_center => array of at 4 ints that hold center axis values
866 // axis_min => array of at 4 ints that hold max axis values
868 void joy_set_cal_vals(int *axis_min, int *axis_center, int *axis_max)
872 for (i=0; i<4; i++) {
873 joystick.axis_min[i] = axis_min[i];
874 joystick.axis_center[i] = axis_center[i];
875 joystick.axis_max[i] = axis_max[i];
879 // --------------------------------------------------------------
880 // joystick_read_raw_axis()
882 // Read the raw axis information for a specified number of axes.
884 // input: num_axes => number of axes to read. Note the axes go in the following order:
892 // axis => an array of at least 4 ints to hold axis data
894 int joystick_read_raw_axis(int num_axes, int *axis)
900 Assert(num_axes <= JOY_NUM_AXES);
901 for (i=0; i<num_axes; i++)
904 // first, try reading with DirectInput, if we can
906 return joystick_read_raw_axis_di(num_axes, axis);
908 // No DirectInput joystick, fall back on older stuff
909 if (joy_num_sticks < 1)
912 memset(&ji, 0, sizeof(ji));
913 ji.dwSize = sizeof(ji);
916 ji.dwFlags = JOY_RETURNALL;
918 ji.dwFlags = JOY_RETURNRAWDATA;
923 ji.dwFlags |= JOY_RETURNV;
925 ji.dwFlags |= JOY_RETURNU;
927 ji.dwFlags |= JOY_RETURNR;
929 ji.dwFlags |= JOY_RETURNZ;
931 ji.dwFlags |= JOY_RETURNY;
933 ji.dwFlags |= JOY_RETURNX;
941 if (Cur_joystick >= 0) {
942 EnterCriticalSection(&joy_lock);
943 rs = joyGetPosEx(Cur_joystick, &ji);
944 LeaveCriticalSection(&joy_lock);
949 if (rs != JOYERR_NOERROR)
954 if (joystick.axis_valid[5])
958 if (joystick.axis_valid[4])
962 if (joystick.axis_valid[3])
966 if (joystick.axis_valid[2])
970 if (joystick.axis_valid[1])
974 if (joystick.axis_valid[0])
987 // --------------------------------------------------------------
990 // Get the minimum axis information (namely, joystick in upper left).
991 // This is called by a manual calibration routine.
993 // NOTE: sets the values in joystick.axis_min[]
997 joystick_read_raw_axis( 2, joystick.axis_min );
1000 // --------------------------------------------------------------
1003 // Get the maximum axis information (namely, joystick in lower right).
1004 // This is called by a manual calibration routine.
1006 // NOTE: sets the values in joystick.axis_max[]
1010 joystick_read_raw_axis( 2, joystick.axis_max );
1013 // --------------------------------------------------------------
1016 // Get the center axis information (namely, joystick in dead zone).
1017 // This is called by a manual calibration routine.
1019 // NOTE: sets the values in joystick.axis_center[]
1023 joystick_read_raw_axis( 2, joystick.axis_center );
1026 int joy_get_unscaled_reading(int raw, int axn)
1030 // Make sure it's calibrated properly.
1031 if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5)
1034 if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5)
1037 rng = joystick.axis_max[axn] - joystick.axis_min[axn];
1038 raw -= joystick.axis_min[axn]; // adjust for linear range starting at 0
1046 return (int) ((unsigned int) raw * (unsigned int) F1_0 / (unsigned int) rng); // convert to 0 - F1_0 range.
1049 // --------------------------------------------------------------
1050 // joy_get_scaled_reading()
1052 // input: raw => the raw value for an axis position
1053 // axn => axis number, numbered starting at 0
1055 // return: joy_get_scaled_reading will return a value that represents
1056 // the joystick pos from -1 to +1 for the specified axis number 'axn', and
1057 // the raw value 'raw'
1059 int joy_get_scaled_reading(int raw, int axn)
1061 int x, d, dead_zone, rng;
1062 float percent, sensitivity_percent, non_sensitivity_percent;
1064 // Make sure it's calibrated properly.
1065 if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5)
1068 if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5)
1071 raw -= joystick.axis_center[axn];
1073 dead_zone = (joystick.axis_max[axn] - joystick.axis_min[axn]) * Dead_zone_size / 100;
1075 if (raw < -dead_zone) {
1076 rng = joystick.axis_center[axn] - joystick.axis_min[axn] - dead_zone;
1077 d = -raw - dead_zone;
1079 } else if (raw > dead_zone) {
1080 rng = joystick.axis_max[axn] - joystick.axis_center[axn] - dead_zone;
1081 d = raw - dead_zone;
1089 Assert(Joy_sensitivity >= 0 && Joy_sensitivity <= 9);
1091 // compute percentages as a range between 0 and 1
1092 sensitivity_percent = (float) Joy_sensitivity / 9.0f;
1093 non_sensitivity_percent = (float) (9 - Joy_sensitivity) / 9.0f;
1095 // find percent of max axis is at
1096 percent = (float) d / (float) rng;
1098 // work sensitivity on axis value
1099 percent = (percent * sensitivity_percent + percent * percent * percent * percent * percent * non_sensitivity_percent);
1101 x = (int) ((float) F1_0 * percent);
1103 //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));
1111 // --------------------------------------------------------------
1114 // input: x => OUTPUT PARAMETER: x-axis position of stick (-1 to 1)
1115 // y => OUTPUT PARAMETER: y-axis position of stick (-1 to 1)
1116 // z => OUTPUT PARAMETER: z-axis (throttle) position of stick (-1 to 1)
1117 // r => OUTPUT PARAMETER: rudder position of stick (-1 to 1)
1119 // return: success => 1
1122 int joy_get_pos(int *x, int *y, int *z, int *rx)
1124 int axis[JOY_NUM_AXES];
1131 if (joy_num_sticks < 1) return 0;
1133 joystick_read_raw_axis( 6, axis );
1135 // joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1
1136 if (x && joystick.axis_valid[0])
1137 *x = joy_get_scaled_reading(axis[0], 0);
1138 if (y && joystick.axis_valid[1])
1139 *y = joy_get_scaled_reading(axis[1], 1);
1140 if (z && joystick.axis_valid[2])
1141 *z = joy_get_unscaled_reading(axis[2], 2);
1142 if (rx && joystick.axis_valid[3])
1143 *rx = joy_get_scaled_reading(axis[3], 3);
1146 Joy_last_x_reading = *x;
1149 Joy_last_x_reading = *y;
1154 // change in joy position since last call
1155 void joy_get_delta(int *dx, int *dy)
1157 static int old_joy_x = 0;
1158 static int old_joy_y = 0;
1160 if ( !Joy_inited ) {
1165 *dx = Joy_last_x_reading - old_joy_x;
1166 *dy = Joy_last_y_reading - old_joy_y;
1168 old_joy_x = Joy_last_x_reading;
1169 old_joy_y = Joy_last_y_reading;
1172 //// This is the DirectInput joystick stuff
1175 int Di_joy_guid_valid = 0;
1177 BOOL CALLBACK joy_di_enum(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
1181 nprintf(("Joystick", "Joystick detected: %s (%s)\n", lpddi->tszInstanceName, lpddi->tszProductName));
1182 sprintf(buf, "Joystick %d", Cur_joystick + 1);
1183 if (!stricmp(buf, lpddi->tszInstanceName)) {
1184 Di_joy_guid = lpddi->guidInstance;
1185 Di_joy_guid_valid = 1;
1186 nprintf(("Joystick", " (Selected joystick)\n"));
1189 return DIENUM_CONTINUE;
1192 BOOL FAR PASCAL InitJoystickInput(LPCDIDEVICEINSTANCE pdinst, LPVOID pvRef)
1194 LPDIRECTINPUT pdi = pvRef;
1195 LPDIRECTINPUTDEVICE pdev;
1198 // create the DirectInput joystick device
1199 if(pdi->lpVtbl->CreateDevice(pdi, &pdinst->guidInstance, &pdev, NULL) != DI_OK)
1201 OutputDebugString("IDirectInput::CreateDevice FAILED\n");
1202 return DIENUM_CONTINUE;
1205 // set joystick data format
1206 if (pdev->lpVtbl->SetDataFormat(pdev, &c_dfDIJoystick) != DI_OK)
1208 OutputDebugString("IDirectInputDevice::SetDataFormat FAILED\n");
1209 pdev->lpVtbl->Release(pdev);
1210 return DIENUM_CONTINUE;
1213 // set the cooperative level
1214 if (pdev->lpVtbl->SetCooperativeLevel(pdev, hWndMain,
1215 DISCL_NONEXCLUSIVE | DISCL_FOREGROUND) != DI_OK)
1217 OutputDebugString("IDirectInputDevice::SetCooperativeLevel FAILED\n");
1218 pdev->lpVtbl->Release(pdev);
1219 return DIENUM_CONTINUE;
1222 // set X-axis range to (-1000 ... +1000)
1223 // This lets us test against 0 to see which way the stick is pointed.
1225 diprg.diph.dwSize = sizeof(diprg);
1226 diprg.diph.dwHeaderSize = sizeof(diprg.diph);
1227 diprg.diph.dwObj = DIJOFS_X;
1228 diprg.diph.dwHow = DIPH_BYOFFSET;
1232 if (pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK)
1234 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n");
1235 pdev->lpVtbl->Release(pdev);
1240 // And again for Y-axis range
1242 diprg.diph.dwObj = DIJOFS_Y;
1244 if (pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK)
1246 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n");
1247 pdev->lpVtbl->Release(pdev);
1251 // set X axis dead zone to 50% (to avoid accidental turning)
1252 // Units are ten thousandths, so 50% = 5000/10000.
1253 if (SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_X, DIPH_BYOFFSET, 5000) != DI_OK)
1255 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n");
1256 pdev->lpVtbl->Release(pdev);
1261 // set Y axis dead zone to 50% (to avoid accidental thrust)
1262 // Units are ten thousandths, so 50% = 5000/10000.
1263 if (SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_Y, DIPH_BYOFFSET, 5000) != DI_OK)
1265 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n");
1266 pdev->lpVtbl->Release(pdev);
1271 // Add it to our list of devices. If AddInputDevice succeeds,
1272 // he will do an AddRef.
1273 AddInputDevice(pdev, pdinst);
1274 hRes = pdev->lpVtbl->QueryInterface(
1275 pdev, &IID_IDirectInputDevice2,
1276 (LPVOID *)&g_rgpdevFound[g_cpdevFound]);
1278 pdev->lpVtbl->Release(pdev);
1280 return DIENUM_CONTINUE;
1286 LPDIRECTINPUTDEVICE pdev;
1289 hr = DirectInputCreate(GetModuleHandle(NULL), 0x500, &Di_joystick_obj, NULL);
1291 mprintf(( "DirectInputCreate() failed!\n" ));
1295 Di_joy_guid_valid = 0;
1296 hr = Di_joystick_obj->EnumDevices(DIDEVTYPE_JOYSTICK, joy_di_enum, Di_joystick_obj, DIEDFL_ATTACHEDONLY);
1298 mprintf(( "EnumDevice() failed!\n" ));
1302 if (!Di_joy_guid_valid) {
1303 mprintf(( "Correct joystick not found.\n" ));
1307 hr = Di_joystick_obj->CreateDevice(Di_joy_guid, &pdev, NULL);
1309 mprintf(( "CreateDevice() failed!\n" ));
1313 hr = pdev->SetDataFormat(&c_dfDIJoystick);
1315 mprintf(( "SetDataFormat() failed!\n" ));
1316 if (hr == DIERR_ACQUIRED)
1317 mprintf(( " (reason: DIERR_ACQUIRED)\n" ));
1319 if (hr == DIERR_INVALIDPARAM)
1320 mprintf(( " (reason: DIERR_INVALIDPARAM)\n" ));
1322 if (hr == DIERR_NOTINITIALIZED)
1323 mprintf(( " (reason: DIERR_NOTINITIALIZED)\n" ));
1329 hr = pdev->SetCooperativeLevel((HWND) os_get_window(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
1331 mprintf(( "SetCooperativeLevel() failed!\n" ));
1332 if (hr == DIERR_ACQUIRED)
1333 mprintf(( " (reason: DIERR_ACQUIRED)\n" ));
1335 if (hr == DIERR_INVALIDPARAM)
1336 mprintf(( " (reason: DIERR_INVALIDPARAM)\n" ));
1338 if (hr == DIERR_NOTINITIALIZED)
1339 mprintf(( " (reason: DIERR_NOTINITIALIZED)\n" ));
1345 hr = pdev->QueryInterface(IID_IDirectInputDevice2, (LPVOID *) &Di_joystick);
1351 Di_joystick->Acquire();
1355 nprintf(("Joystick", "DirectInput initialization of joystick succeeded\n"));
1359 int joy_di_shutdown()
1361 // Destroy any lingering IDirectInputDevice object.
1363 // Unacquire the device one last time just in case we got really confused
1364 // and tried to exit while the device is still acquired.
1365 Di_joystick->Unacquire();
1367 Di_joystick->Release();
1371 // Destroy any lingering IDirectInput object.
1372 if (Di_joystick_obj) {
1373 Di_joystick_obj->Release();
1374 Di_joystick_obj = NULL;
1381 int joystick_read_raw_axis_di(int num_axes, int *axis)
1385 DIJOYSTATE joy_state;
1394 hr = Di_joystick->Poll();
1395 if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) {
1396 // DirectInput is telling us that the input stream has
1397 // been interrupted. We aren't tracking any state
1398 // between polls, so we don't have any special reset
1399 // that needs to be done. We just re-acquire and
1401 Sleep(1000); // Pause a second...
1402 hr = Di_joystick->Acquire();
1409 memset(&joy_state, 0, sizeof(joy_state));
1413 hr = Di_joystick->GetDeviceState(sizeof(joy_state), &joy_state);
1414 if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) {
1415 // DirectInput is telling us that the input stream has
1416 // been interrupted. We aren't tracking any state
1417 // between polls, so we don't have any special reset
1418 // that needs to be done. We just re-acquire and
1420 Sleep(1000); // Pause a second...
1421 hr = Di_joystick->Acquire();
1427 if (SUCCEEDED(hr)) {
1430 if (joystick.axis_valid[5])
1431 axis[5] = joy_state.lRy;
1434 if (joystick.axis_valid[4])
1435 axis[4] = joy_state.lRx;
1438 if (joystick.axis_valid[3])
1439 axis[3] = joy_state.lRz;
1442 if (joystick.axis_valid[2])
1443 axis[2] = joy_state.lZ;
1446 if (joystick.axis_valid[1])
1447 axis[1] = joy_state.lY;
1450 if (joystick.axis_valid[0])
1451 axis[0] = joy_state.lX;