2 * $Logfile: /Freespace2/code/Io/Joy.cpp $
7 * Code to read the joystick
10 * Revision 1.1 2002/05/03 03:28:09 root
14 * 5 7/15/99 9:20a Andsager
15 * FS2_DEMO initial checkin
17 * 4 6/02/99 6:18p Dave
18 * Fixed TNT lockup problems! Wheeeee!
20 * 3 10/09/98 2:57p Dave
21 * Starting splitting up OS stuff.
23 * 2 10/07/98 10:53a Dave
26 * 1 10/07/98 10:49a Dave
28 * 61 5/24/98 12:56a Mike
29 * Put in debug code, but comment out, to check joystick sensitivity.
31 * 60 5/19/98 6:54p Lawrance
32 * Set default joystick sensitivity to max
34 * 59 5/13/98 7:14p Hoffoss
35 * Made invalid axis return center position value.
37 * 58 5/13/98 1:17a Hoffoss
38 * Added joystick axes configurability.
40 * 57 5/07/98 3:15p Hoffoss
43 * 56 5/07/98 12:41p Hoffoss
44 * Changed code to make joystick sensitivity default to center of range.
46 * 55 5/06/98 12:02a Hoffoss
47 * Fixed throttle problems with joysticks.
49 * 54 5/05/98 8:38p Hoffoss
50 * Added sensitivity adjustment to options menu and made it save to pilot
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.
57 * 52 5/03/98 6:02p Hoffoss
58 * Added DirectInput support for joystick (only works with v5.0, so only
61 * 51 5/01/98 5:59p Hoffoss
62 * Checking in code so I can switch back to NT to work on this instead
65 * 50 4/30/98 12:12p Hoffoss
66 * Changed code to explicitly use calibrated values from Windows for
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.
73 * 48 4/25/98 12:02p Lawrance
74 * take out crit sec code around joystick read in joy_process
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
81 * 46 4/17/98 3:07p Jim
82 * added debug code to test joystick
84 * 45 4/13/98 10:16a John
85 * Switched gettime back to timer_get_milliseconds, which is now thread
88 * 44 4/12/98 11:08p Lawrance
89 * switch back to using gettime() in separate threads
91 * 43 4/12/98 5:31p Lawrance
92 * use timer_get_milliseconds() instead of gettime()
94 * 42 3/21/98 11:29a John
95 * Made joy_flush work when a button is held down
97 * 41 3/12/98 10:44a Hoffoss
98 * Whoops, should probably use the actual define rather than 0.
100 * 40 3/12/98 10:38a Hoffoss
101 * Changed joystick default to first slot in never set up in FSLaunch
103 * 39 3/11/98 5:27p Hoffoss
104 * Added to FreeSpace usage of joystick specified through FSLaunch.
106 * 38 3/09/98 4:44p Sandeep
108 * 37 3/07/98 5:23p Sandeep
110 * 35 3/06/98 11:12a Hoffoss
111 * Fixed joystick deadzone bug.
113 * 33 3/06/98 10:02a Hoffoss
114 * Made dead zone adjustable, and defaulted it to 10% instead of 5%.
116 * 36 3/07/98 4:50p John
117 * Added code to allow toggling force feedback on/off in setup
119 * 35 3/06/98 11:12a Hoffoss
120 * Fixed joystick deadzone bug.
122 * 33 3/06/98 10:02a Hoffoss
123 * Made dead zone adjustable, and defaulted it to 10% instead of 5%.
125 * 32 2/11/98 9:56p Jim
126 * allender: from sandeep on Jim's machine -- some force feedback stuff
128 * 31 1/29/98 11:04a Sandeep
130 * 30 1/27/98 8:40p Sandeep
132 * 29 1/19/98 6:15p John
133 * Fixed all my Optimized Build compiler warnings
135 * 28 1/08/98 6:35p Hoffoss
136 * Fixed joystick undefined detection.
138 * 27 1/08/98 3:48p Dan
139 * Fixed bug with joystick axis reading thinking it's undefined at
142 * 26 10/16/97 5:37p Lawrance
143 * change thread priority from THREAD_PRIORITY_TIME_CRITICAL to
144 * THREAD_PRIORITY_HIGHEST
146 * 25 10/09/97 10:15a Johnson
147 * try to init several times if joystick init fails
149 * 24 10/07/97 10:42a Johnson
150 * zero out JOYINFOEX struct before setting dwSize
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
156 * 22 9/15/97 11:42p Lawrance
157 * change button_info to joy_button_info to avoid name conflict
159 * 21 8/07/97 11:26p Lawrance
160 * add support for 4th axis (rudder)
162 * 20 7/29/97 5:30p Lawrance
163 * move gettime() from keyboard module to timer module
165 * 19 7/11/97 11:43a Lawrance
166 * fix bug with joy_up_count
168 * 18 7/10/97 12:29a Lawrance
169 * fix problem with NT not recognizing an axis that was set under 95
171 * 17 7/09/97 11:41p Lawrance
172 * added throttle and hat support
174 * 16 6/19/97 9:50a John
175 * fixed bug where joy_close getting called out of order doesn't matter.
177 * 15 5/18/97 2:40p Lawrance
178 * added joy_get_caps()
180 * 14 4/22/97 10:56a John
181 * fixed some resource leaks.
183 * 13 2/27/97 2:23p Lawrance
184 * took out debug stmts
186 * 12 2/27/97 10:04a Lawrance
187 * fixed bug that was causing all joy buttons but 0 to be read incorrectly
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
197 #include <windowsx.h>
204 #include "osregistry.h"
209 #define PRECALIBRATED 1
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;
217 CRITICAL_SECTION joy_lock;
219 HANDLE joy_thread = NULL;
221 int joy_pollrate = 1000 / 18; // poll at 18Hz
223 HANDLE Joy_tell_thread_to_end_event = NULL;
224 HANDLE Joy_thread_says_its_done_event = NULL;
226 static int Joy_last_x_reading = 0;
227 static int Joy_last_y_reading = 0;
229 int Joy_di_inited = 0;
230 static LPDIRECTINPUT Di_joystick_obj = NULL;
231 static LPDIRECTINPUTDEVICE2 Di_joystick = NULL;
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.
239 uint last_down_check; // timestamp in milliseconds of last
244 joy_button_info joy_buttons[JOY_TOTAL_BUTTONS];
247 int joy_di_shutdown();
248 int joystick_read_raw_axis_di(int num_axes, int *axis);
250 // --------------------------------------------------------------
253 // Clear the state of the joystick.
260 if ( joy_num_sticks < 1 ) return;
262 EnterCriticalSection(&joy_lock);
263 for ( i = 0; i < JOY_TOTAL_BUTTONS; i++) {
264 bi = &joy_buttons[i];
269 bi->last_down_check = timer_get_milliseconds();
272 LeaveCriticalSection(&joy_lock);
275 // --------------------------------------------------------------
278 // Runs as a separate thread, and updates the state of the joystick
280 DWORD joy_process(DWORD lparam)
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!
294 bi->last_down_check = timer_get_milliseconds();
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) {
303 memset(&ji, 0, sizeof(ji));
304 ji.dwSize = sizeof(ji);
305 // ji.dwFlags = JOY_RETURNBUTTONS | JOY_RETURNRAWDATA;
306 ji.dwFlags = JOY_RETURNALL;
308 EnterCriticalSection(&joy_lock);
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;
319 // Process ji.dwButtons
320 for (i=0; i<JOY_TOTAL_BUTTONS; i++) {
322 if (i < JOY_NUM_BUTTONS) {
323 state = joy_state & (1<<i);
326 // check for hat presses, which act like buttons
329 if (ji.dwPOV == JOY_POVBACKWARD)
334 if (ji.dwPOV == JOY_POVFORWARD)
339 if (ji.dwPOV == JOY_POVLEFT)
344 if (ji.dwPOV == JOY_POVRIGHT)
349 Int3(); // should never happen
356 if (state != joy_buttons[i].actual_state) {
357 // Button position physically changed.
358 joy_buttons[i].actual_state = state;
361 // went from up to down
362 joy_buttons[i].down_count++;
363 joy_buttons[i].down_time = 0;
365 joy_buttons[i].state = 1;
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 ////////////////////////////
374 extern void demo_reset_trailer_timer();
375 demo_reset_trailer_timer();
378 ////////////////////////////
379 /// IT'S OVER. SEE, IT WASN'T SO BAD RIGHT? IT'S IS VERY UGLY LOOKING, I KNOW.
380 ////////////////////////////
384 // went from down to up
385 if ( joy_buttons[i].state ) {
386 joy_buttons[i].up_count++;
388 joy_buttons[i].state = 0;
392 // Didn't move... increment time down if down.
393 if (joy_buttons[i].state) {
394 joy_buttons[i].down_time += joy_pollrate;
400 LeaveCriticalSection(&joy_lock);
403 SetEvent(Joy_thread_says_its_done_event);
408 // --------------------------------------------------------------
411 // Close the joystick system. Should be called at game exit.
418 // joy_di_shutdown();
423 // Tell joystick polling thread to end
424 SetEvent(Joy_tell_thread_to_end_event);
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" ));
430 CloseHandle(Joy_tell_thread_to_end_event);
431 CloseHandle(Joy_thread_says_its_done_event);
433 // It is now safe to release any resources use by the polling thread.
434 DeleteCriticalSection( &joy_lock );
436 CloseHandle(joy_thread);
443 // --------------------------------------------------------------
446 // Determine the capabilities of the attached joysticks.
448 void joy_get_caps(int max)
453 for (j=0; j<JOY_NUM_AXES; j++)
454 joystick.axis_valid[j] = 0;
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));
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;
475 int joy_get_scaled_reading(int raw, int axn);
476 int joy_get_unscaled_reading(int raw, int axn);
478 DCF(joytest, "Test joystick")
481 while (!keyd_pressed[KEY_ESC]) {
482 int x, y, axis[JOY_NUM_AXES];
484 if (joy_num_sticks < 1)
487 joystick_read_raw_axis(JOY_NUM_AXES, axis);
489 x = joy_get_scaled_reading(axis[0], 0);
490 y = joy_get_scaled_reading(axis[1], 1);
492 mprintf(("X=%5d Y=%5d Calibrated X=%6d Y=%6d\n", axis[0], axis[1], x, y));
498 DCF(joytest2, "Test joystick (extended)")
501 while (!keyd_pressed[KEY_ESC]) {
502 int x, y, z, r, axis[JOY_NUM_AXES];
504 if (joy_num_sticks < 1)
507 joystick_read_raw_axis(JOY_NUM_AXES, axis);
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);
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));
520 // --------------------------------------------------------------
523 // Initialize the joystick system. This is called once at game startup.
537 Cur_joystick = os_config_read_uint(NULL, "CurrentJoystick", JOYSTICKID1);
542 mprintf(("No joystick driver detected\n"));
546 InitializeCriticalSection(&joy_lock);
552 memset(&ji, 0, sizeof(ji));
553 ji.dwSize = sizeof(ji);
554 ji.dwFlags = JOY_RETURNALL;
556 if (Cur_joystick >= 0) {
557 // AL: test code to try and find out why this call fails the first time
559 for (count=0; count<20; count++) {
560 rs = joyGetPosEx(Cur_joystick, &ji);
561 if (rs == JOYERR_NOERROR)
565 if (rs == JOYERR_NOERROR) {
568 Joy_tell_thread_to_end_event = CreateEvent( NULL, FALSE, FALSE, NULL );
569 Joy_thread_says_its_done_event = CreateEvent( NULL, FALSE, FALSE, NULL );
571 joy_thread = CreateThread(NULL,
573 (LPTHREAD_START_ROUTINE) joy_process,
578 // SetThreadPriority(joy_thread, THREAD_PRIORITY_TIME_CRITICAL - 1);
579 SetThreadPriority(joy_thread, THREAD_PRIORITY_HIGHEST);
583 mprintf(("Windoze reported %d joysticks, we found %d\n", n, joy_num_sticks));
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;
595 // Fake a calibration
596 if (joy_num_sticks > 0) {
598 for (i=0; i<4; i++) {
599 joystick.axis_min[i] = 0;
600 joystick.axis_max[i] = joystick.axis_center[i]*2;
608 return joy_num_sticks;
611 // --------------------------------------------------------------
614 // Manual calibrate joystick routine
618 if ( joy_num_sticks < 1 ) return;
622 if ( key_inkey()) break;
623 if (joy_down_count(0)) break;
624 mprintf(( "Move stick to upper-left and hit button\n" ));
630 if ( key_inkey()) break;
631 if (joy_down_count(0)) break;
632 mprintf(( "Move stick to lower-right and hit button\n" ));
638 if ( key_inkey()) break;
639 if (joy_down_count(0)) break;
640 mprintf(( "Move stick to center and hit button\n" ));
645 // --------------------------------------------------------------
648 // Get the position of the joystick axes
650 int joy_get_pos_old(int * x, int * y )
655 if ( joy_num_sticks < 1 ) {
661 memset(&ji, 0, sizeof(ji));
662 ji.dwSize = sizeof(ji);
666 //JOY_RETURNCENTERED|
667 //JOY_RETURNX|JOY_RETURNY|JOY_RETURNRAWDATA;
668 ji.dwFlags = JOY_RETURNALL;
670 ji.dwFlags = JOY_CAL_READXYONLY;
673 if (Cur_joystick >= 0) {
674 EnterCriticalSection(&joy_lock);
675 rs = joyGetPosEx(Cur_joystick, &ji);
676 LeaveCriticalSection(&joy_lock);
678 if (rs == JOYERR_NOERROR) {
686 *x = (ji.dwXpos - 32768) * 2;
694 *y = (ji.dwYpos - 32768) * 2;
714 // --------------------------------------------------------------
717 // Return the number of times the joystick button has gone down since
718 // joy_down_count() was last called
720 // input: btn => button number to check
721 // reset_count => (default 1): if true reset down_count
723 // returns: number of times button 'btn' has gone down since last call
725 int joy_down_count(int btn, int reset_count)
729 if ( joy_num_sticks < 1 ) return 0;
730 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0;
732 EnterCriticalSection(&joy_lock);
733 tmp = joy_buttons[btn].down_count;
735 joy_buttons[btn].down_count = 0;
737 LeaveCriticalSection(&joy_lock);
743 // --------------------------------------------------------------
746 // Return the state of button number 'btn'
748 // input: btn => button number to check
750 // returns: 0 => not pressed
753 int joy_down(int btn)
757 if ( joy_num_sticks < 1 ) return 0;
758 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS )) return 0;
760 EnterCriticalSection(&joy_lock);
761 tmp = joy_buttons[btn].state;
762 LeaveCriticalSection(&joy_lock);
767 // --------------------------------------------------------------
770 // Return the number of times the joystick button has gone up since
771 // joy_up_count() was last called
773 // input: btn => button number to check
775 // returns: number of times button 'btn' has gone up since last call
777 int joy_up_count(int btn)
781 if ( joy_num_sticks < 1 ) return 0;
782 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0;
784 EnterCriticalSection(&joy_lock);
785 tmp = joy_buttons[btn].up_count;
786 joy_buttons[btn].up_count = 0;
787 LeaveCriticalSection(&joy_lock);
792 // --------------------------------------------------------------
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
798 // input: btn => button number to check
799 // returns: value between 0 and 1
801 float joy_down_time(int btn)
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);
812 now = timer_get_milliseconds();
814 if ( bi->down_time == 0 && joy_down(btn) ) {
815 bi->down_time += joy_pollrate;
818 if ( (now - bi->last_down_check) > 0)
819 rval = i2fl(bi->down_time) / (now - bi->last_down_check);
824 bi->last_down_check = now;
826 LeaveCriticalSection(&joy_lock);
836 // --------------------------------------------------------------
837 // joy_get_cal_vals()
839 // Get the calibrated min, center, and max for all axes
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
845 void joy_get_cal_vals(int *axis_min, int *axis_center, int *axis_max)
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];
856 // --------------------------------------------------------------
857 // joy_set_cal_vals()
859 // Get the calibrated min, center, and max for all axes
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
865 void joy_set_cal_vals(int *axis_min, int *axis_center, int *axis_max)
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];
876 // --------------------------------------------------------------
877 // joystick_read_raw_axis()
879 // Read the raw axis information for a specified number of axes.
881 // input: num_axes => number of axes to read. Note the axes go in the following order:
889 // axis => an array of at least 4 ints to hold axis data
891 int joystick_read_raw_axis(int num_axes, int *axis)
897 Assert(num_axes <= JOY_NUM_AXES);
898 for (i=0; i<num_axes; i++)
901 // first, try reading with DirectInput, if we can
903 return joystick_read_raw_axis_di(num_axes, axis);
905 // No DirectInput joystick, fall back on older stuff
906 if (joy_num_sticks < 1)
909 memset(&ji, 0, sizeof(ji));
910 ji.dwSize = sizeof(ji);
913 ji.dwFlags = JOY_RETURNALL;
915 ji.dwFlags = JOY_RETURNRAWDATA;
920 ji.dwFlags |= JOY_RETURNV;
922 ji.dwFlags |= JOY_RETURNU;
924 ji.dwFlags |= JOY_RETURNR;
926 ji.dwFlags |= JOY_RETURNZ;
928 ji.dwFlags |= JOY_RETURNY;
930 ji.dwFlags |= JOY_RETURNX;
938 if (Cur_joystick >= 0) {
939 EnterCriticalSection(&joy_lock);
940 rs = joyGetPosEx(Cur_joystick, &ji);
941 LeaveCriticalSection(&joy_lock);
946 if (rs != JOYERR_NOERROR)
951 if (joystick.axis_valid[5])
955 if (joystick.axis_valid[4])
959 if (joystick.axis_valid[3])
963 if (joystick.axis_valid[2])
967 if (joystick.axis_valid[1])
971 if (joystick.axis_valid[0])
984 // --------------------------------------------------------------
987 // Get the minimum axis information (namely, joystick in upper left).
988 // This is called by a manual calibration routine.
990 // NOTE: sets the values in joystick.axis_min[]
994 joystick_read_raw_axis( 2, joystick.axis_min );
997 // --------------------------------------------------------------
1000 // Get the maximum axis information (namely, joystick in lower right).
1001 // This is called by a manual calibration routine.
1003 // NOTE: sets the values in joystick.axis_max[]
1007 joystick_read_raw_axis( 2, joystick.axis_max );
1010 // --------------------------------------------------------------
1013 // Get the center axis information (namely, joystick in dead zone).
1014 // This is called by a manual calibration routine.
1016 // NOTE: sets the values in joystick.axis_center[]
1020 joystick_read_raw_axis( 2, joystick.axis_center );
1023 int joy_get_unscaled_reading(int raw, int axn)
1027 // Make sure it's calibrated properly.
1028 if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5)
1031 if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5)
1034 rng = joystick.axis_max[axn] - joystick.axis_min[axn];
1035 raw -= joystick.axis_min[axn]; // adjust for linear range starting at 0
1043 return (int) ((unsigned int) raw * (unsigned int) F1_0 / (unsigned int) rng); // convert to 0 - F1_0 range.
1046 // --------------------------------------------------------------
1047 // joy_get_scaled_reading()
1049 // input: raw => the raw value for an axis position
1050 // axn => axis number, numbered starting at 0
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'
1056 int joy_get_scaled_reading(int raw, int axn)
1058 int x, d, dead_zone, rng;
1059 float percent, sensitivity_percent, non_sensitivity_percent;
1061 // Make sure it's calibrated properly.
1062 if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5)
1065 if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5)
1068 raw -= joystick.axis_center[axn];
1070 dead_zone = (joystick.axis_max[axn] - joystick.axis_min[axn]) * Dead_zone_size / 100;
1072 if (raw < -dead_zone) {
1073 rng = joystick.axis_center[axn] - joystick.axis_min[axn] - dead_zone;
1074 d = -raw - dead_zone;
1076 } else if (raw > dead_zone) {
1077 rng = joystick.axis_max[axn] - joystick.axis_center[axn] - dead_zone;
1078 d = raw - dead_zone;
1086 Assert(Joy_sensitivity >= 0 && Joy_sensitivity <= 9);
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;
1092 // find percent of max axis is at
1093 percent = (float) d / (float) rng;
1095 // work sensitivity on axis value
1096 percent = (percent * sensitivity_percent + percent * percent * percent * percent * percent * non_sensitivity_percent);
1098 x = (int) ((float) F1_0 * percent);
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));
1108 // --------------------------------------------------------------
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)
1116 // return: success => 1
1119 int joy_get_pos(int *x, int *y, int *z, int *rx)
1121 int axis[JOY_NUM_AXES];
1128 if (joy_num_sticks < 1) return 0;
1130 joystick_read_raw_axis( 6, axis );
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);
1143 Joy_last_x_reading = *x;
1146 Joy_last_x_reading = *y;
1151 // change in joy position since last call
1152 void joy_get_delta(int *dx, int *dy)
1154 static int old_joy_x = 0;
1155 static int old_joy_y = 0;
1157 if ( !Joy_inited ) {
1162 *dx = Joy_last_x_reading - old_joy_x;
1163 *dy = Joy_last_y_reading - old_joy_y;
1165 old_joy_x = Joy_last_x_reading;
1166 old_joy_y = Joy_last_y_reading;
1169 //// This is the DirectInput joystick stuff
1172 int Di_joy_guid_valid = 0;
1174 BOOL CALLBACK joy_di_enum(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
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"));
1186 return DIENUM_CONTINUE;
1189 BOOL FAR PASCAL InitJoystickInput(LPCDIDEVICEINSTANCE pdinst, LPVOID pvRef)
1191 LPDIRECTINPUT pdi = pvRef;
1192 LPDIRECTINPUTDEVICE pdev;
1195 // create the DirectInput joystick device
1196 if(pdi->lpVtbl->CreateDevice(pdi, &pdinst->guidInstance, &pdev, NULL) != DI_OK)
1198 OutputDebugString("IDirectInput::CreateDevice FAILED\n");
1199 return DIENUM_CONTINUE;
1202 // set joystick data format
1203 if (pdev->lpVtbl->SetDataFormat(pdev, &c_dfDIJoystick) != DI_OK)
1205 OutputDebugString("IDirectInputDevice::SetDataFormat FAILED\n");
1206 pdev->lpVtbl->Release(pdev);
1207 return DIENUM_CONTINUE;
1210 // set the cooperative level
1211 if (pdev->lpVtbl->SetCooperativeLevel(pdev, hWndMain,
1212 DISCL_NONEXCLUSIVE | DISCL_FOREGROUND) != DI_OK)
1214 OutputDebugString("IDirectInputDevice::SetCooperativeLevel FAILED\n");
1215 pdev->lpVtbl->Release(pdev);
1216 return DIENUM_CONTINUE;
1219 // set X-axis range to (-1000 ... +1000)
1220 // This lets us test against 0 to see which way the stick is pointed.
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;
1229 if (pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK)
1231 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n");
1232 pdev->lpVtbl->Release(pdev);
1237 // And again for Y-axis range
1239 diprg.diph.dwObj = DIJOFS_Y;
1241 if (pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK)
1243 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n");
1244 pdev->lpVtbl->Release(pdev);
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)
1252 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n");
1253 pdev->lpVtbl->Release(pdev);
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)
1262 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n");
1263 pdev->lpVtbl->Release(pdev);
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]);
1275 pdev->lpVtbl->Release(pdev);
1277 return DIENUM_CONTINUE;
1283 LPDIRECTINPUTDEVICE pdev;
1286 hr = DirectInputCreate(GetModuleHandle(NULL), 0x500, &Di_joystick_obj, NULL);
1288 mprintf(( "DirectInputCreate() failed!\n" ));
1292 Di_joy_guid_valid = 0;
1293 hr = Di_joystick_obj->EnumDevices(DIDEVTYPE_JOYSTICK, joy_di_enum, Di_joystick_obj, DIEDFL_ATTACHEDONLY);
1295 mprintf(( "EnumDevice() failed!\n" ));
1299 if (!Di_joy_guid_valid) {
1300 mprintf(( "Correct joystick not found.\n" ));
1304 hr = Di_joystick_obj->CreateDevice(Di_joy_guid, &pdev, NULL);
1306 mprintf(( "CreateDevice() failed!\n" ));
1310 hr = pdev->SetDataFormat(&c_dfDIJoystick);
1312 mprintf(( "SetDataFormat() failed!\n" ));
1313 if (hr == DIERR_ACQUIRED)
1314 mprintf(( " (reason: DIERR_ACQUIRED)\n" ));
1316 if (hr == DIERR_INVALIDPARAM)
1317 mprintf(( " (reason: DIERR_INVALIDPARAM)\n" ));
1319 if (hr == DIERR_NOTINITIALIZED)
1320 mprintf(( " (reason: DIERR_NOTINITIALIZED)\n" ));
1326 hr = pdev->SetCooperativeLevel((HWND) os_get_window(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
1328 mprintf(( "SetCooperativeLevel() failed!\n" ));
1329 if (hr == DIERR_ACQUIRED)
1330 mprintf(( " (reason: DIERR_ACQUIRED)\n" ));
1332 if (hr == DIERR_INVALIDPARAM)
1333 mprintf(( " (reason: DIERR_INVALIDPARAM)\n" ));
1335 if (hr == DIERR_NOTINITIALIZED)
1336 mprintf(( " (reason: DIERR_NOTINITIALIZED)\n" ));
1342 hr = pdev->QueryInterface(IID_IDirectInputDevice2, (LPVOID *) &Di_joystick);
1348 Di_joystick->Acquire();
1352 nprintf(("Joystick", "DirectInput initialization of joystick succeeded\n"));
1356 int joy_di_shutdown()
1358 // Destroy any lingering IDirectInputDevice object.
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();
1364 Di_joystick->Release();
1368 // Destroy any lingering IDirectInput object.
1369 if (Di_joystick_obj) {
1370 Di_joystick_obj->Release();
1371 Di_joystick_obj = NULL;
1378 int joystick_read_raw_axis_di(int num_axes, int *axis)
1382 DIJOYSTATE joy_state;
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
1398 Sleep(1000); // Pause a second...
1399 hr = Di_joystick->Acquire();
1406 memset(&joy_state, 0, sizeof(joy_state));
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
1417 Sleep(1000); // Pause a second...
1418 hr = Di_joystick->Acquire();
1424 if (SUCCEEDED(hr)) {
1427 if (joystick.axis_valid[5])
1428 axis[5] = joy_state.lRy;
1431 if (joystick.axis_valid[4])
1432 axis[4] = joy_state.lRx;
1435 if (joystick.axis_valid[3])
1436 axis[3] = joy_state.lRz;
1439 if (joystick.axis_valid[2])
1440 axis[2] = joy_state.lZ;
1443 if (joystick.axis_valid[1])
1444 axis[1] = joy_state.lY;
1447 if (joystick.axis_valid[0])
1448 axis[0] = joy_state.lX;