2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Io/Joy.cpp $
15 * Code to read the joystick
18 * Revision 1.4 2003/05/25 02:30:42 taylor
21 * Revision 1.3 2002/06/09 04:41:21 relnev
22 * added copyright header
24 * Revision 1.2 2002/05/07 03:16:46 theoddone33
25 * The Great Newline Fix
27 * Revision 1.1.1.1 2002/05/03 03:28:09 root
31 * 5 7/15/99 9:20a Andsager
32 * FS2_DEMO initial checkin
34 * 4 6/02/99 6:18p Dave
35 * Fixed TNT lockup problems! Wheeeee!
37 * 3 10/09/98 2:57p Dave
38 * Starting splitting up OS stuff.
40 * 2 10/07/98 10:53a Dave
43 * 1 10/07/98 10:49a Dave
45 * 61 5/24/98 12:56a Mike
46 * Put in debug code, but comment out, to check joystick sensitivity.
48 * 60 5/19/98 6:54p Lawrance
49 * Set default joystick sensitivity to max
51 * 59 5/13/98 7:14p Hoffoss
52 * Made invalid axis return center position value.
54 * 58 5/13/98 1:17a Hoffoss
55 * Added joystick axes configurability.
57 * 57 5/07/98 3:15p Hoffoss
60 * 56 5/07/98 12:41p Hoffoss
61 * Changed code to make joystick sensitivity default to center of range.
63 * 55 5/06/98 12:02a Hoffoss
64 * Fixed throttle problems with joysticks.
66 * 54 5/05/98 8:38p Hoffoss
67 * Added sensitivity adjustment to options menu and made it save to pilot
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.
74 * 52 5/03/98 6:02p Hoffoss
75 * Added DirectInput support for joystick (only works with v5.0, so only
78 * 51 5/01/98 5:59p Hoffoss
79 * Checking in code so I can switch back to NT to work on this instead
82 * 50 4/30/98 12:12p Hoffoss
83 * Changed code to explicitly use calibrated values from Windows for
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.
90 * 48 4/25/98 12:02p Lawrance
91 * take out crit sec code around joystick read in joy_process
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
98 * 46 4/17/98 3:07p Jim
99 * added debug code to test joystick
101 * 45 4/13/98 10:16a John
102 * Switched gettime back to timer_get_milliseconds, which is now thread
105 * 44 4/12/98 11:08p Lawrance
106 * switch back to using gettime() in separate threads
108 * 43 4/12/98 5:31p Lawrance
109 * use timer_get_milliseconds() instead of gettime()
111 * 42 3/21/98 11:29a John
112 * Made joy_flush work when a button is held down
114 * 41 3/12/98 10:44a Hoffoss
115 * Whoops, should probably use the actual define rather than 0.
117 * 40 3/12/98 10:38a Hoffoss
118 * Changed joystick default to first slot in never set up in FSLaunch
120 * 39 3/11/98 5:27p Hoffoss
121 * Added to FreeSpace usage of joystick specified through FSLaunch.
123 * 38 3/09/98 4:44p Sandeep
125 * 37 3/07/98 5:23p Sandeep
127 * 35 3/06/98 11:12a Hoffoss
128 * Fixed joystick deadzone bug.
130 * 33 3/06/98 10:02a Hoffoss
131 * Made dead zone adjustable, and defaulted it to 10% instead of 5%.
133 * 36 3/07/98 4:50p John
134 * Added code to allow toggling force feedback on/off in setup
136 * 35 3/06/98 11:12a Hoffoss
137 * Fixed joystick deadzone bug.
139 * 33 3/06/98 10:02a Hoffoss
140 * Made dead zone adjustable, and defaulted it to 10% instead of 5%.
142 * 32 2/11/98 9:56p Jim
143 * allender: from sandeep on Jim's machine -- some force feedback stuff
145 * 31 1/29/98 11:04a Sandeep
147 * 30 1/27/98 8:40p Sandeep
149 * 29 1/19/98 6:15p John
150 * Fixed all my Optimized Build compiler warnings
152 * 28 1/08/98 6:35p Hoffoss
153 * Fixed joystick undefined detection.
155 * 27 1/08/98 3:48p Dan
156 * Fixed bug with joystick axis reading thinking it's undefined at
159 * 26 10/16/97 5:37p Lawrance
160 * change thread priority from THREAD_PRIORITY_TIME_CRITICAL to
161 * THREAD_PRIORITY_HIGHEST
163 * 25 10/09/97 10:15a Johnson
164 * try to init several times if joystick init fails
166 * 24 10/07/97 10:42a Johnson
167 * zero out JOYINFOEX struct before setting dwSize
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
173 * 22 9/15/97 11:42p Lawrance
174 * change button_info to joy_button_info to avoid name conflict
176 * 21 8/07/97 11:26p Lawrance
177 * add support for 4th axis (rudder)
179 * 20 7/29/97 5:30p Lawrance
180 * move gettime() from keyboard module to timer module
182 * 19 7/11/97 11:43a Lawrance
183 * fix bug with joy_up_count
185 * 18 7/10/97 12:29a Lawrance
186 * fix problem with NT not recognizing an axis that was set under 95
188 * 17 7/09/97 11:41p Lawrance
189 * added throttle and hat support
191 * 16 6/19/97 9:50a John
192 * fixed bug where joy_close getting called out of order doesn't matter.
194 * 15 5/18/97 2:40p Lawrance
195 * added joy_get_caps()
197 * 14 4/22/97 10:56a John
198 * fixed some resource leaks.
200 * 13 2/27/97 2:23p Lawrance
201 * took out debug stmts
203 * 12 2/27/97 10:04a Lawrance
204 * fixed bug that was causing all joy buttons but 0 to be read incorrectly
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
214 #include <windowsx.h>
221 #include "osregistry.h"
226 #define PRECALIBRATED 1
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;
234 CRITICAL_SECTION joy_lock;
236 HANDLE joy_thread = NULL;
238 int joy_pollrate = 1000 / 18; // poll at 18Hz
240 HANDLE Joy_tell_thread_to_end_event = NULL;
241 HANDLE Joy_thread_says_its_done_event = NULL;
243 static int Joy_last_x_reading = 0;
244 static int Joy_last_y_reading = 0;
246 int Joy_di_inited = 0;
247 static LPDIRECTINPUT Di_joystick_obj = NULL;
248 static LPDIRECTINPUTDEVICE2 Di_joystick = NULL;
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.
256 uint last_down_check; // timestamp in milliseconds of last
261 joy_button_info joy_buttons[JOY_TOTAL_BUTTONS];
264 int joy_di_shutdown();
265 int joystick_read_raw_axis_di(int num_axes, int *axis);
267 // --------------------------------------------------------------
270 // Clear the state of the joystick.
277 if ( joy_num_sticks < 1 ) return;
279 EnterCriticalSection(&joy_lock);
280 for ( i = 0; i < JOY_TOTAL_BUTTONS; i++) {
281 bi = &joy_buttons[i];
286 bi->last_down_check = timer_get_milliseconds();
289 LeaveCriticalSection(&joy_lock);
292 // --------------------------------------------------------------
295 // Runs as a separate thread, and updates the state of the joystick
297 DWORD joy_process(DWORD lparam)
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!
311 bi->last_down_check = timer_get_milliseconds();
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) {
320 memset(&ji, 0, sizeof(ji));
321 ji.dwSize = sizeof(ji);
322 // ji.dwFlags = JOY_RETURNBUTTONS | JOY_RETURNRAWDATA;
323 ji.dwFlags = JOY_RETURNALL;
325 EnterCriticalSection(&joy_lock);
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;
336 // Process ji.dwButtons
337 for (i=0; i<JOY_TOTAL_BUTTONS; i++) {
339 if (i < JOY_NUM_BUTTONS) {
340 state = joy_state & (1<<i);
343 // check for hat presses, which act like buttons
346 if (ji.dwPOV == JOY_POVBACKWARD)
351 if (ji.dwPOV == JOY_POVFORWARD)
356 if (ji.dwPOV == JOY_POVLEFT)
361 if (ji.dwPOV == JOY_POVRIGHT)
366 Int3(); // should never happen
373 if (state != joy_buttons[i].actual_state) {
374 // Button position physically changed.
375 joy_buttons[i].actual_state = state;
378 // went from up to down
379 joy_buttons[i].down_count++;
380 joy_buttons[i].down_time = 0;
382 joy_buttons[i].state = 1;
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)
391 extern void demo_reset_trailer_timer();
392 demo_reset_trailer_timer();
395 ////////////////////////////
396 /// IT'S OVER. SEE, IT WASN'T SO BAD RIGHT? IT'S IS VERY UGLY LOOKING, I KNOW.
397 ////////////////////////////
401 // went from down to up
402 if ( joy_buttons[i].state ) {
403 joy_buttons[i].up_count++;
405 joy_buttons[i].state = 0;
409 // Didn't move... increment time down if down.
410 if (joy_buttons[i].state) {
411 joy_buttons[i].down_time += joy_pollrate;
417 LeaveCriticalSection(&joy_lock);
420 SetEvent(Joy_thread_says_its_done_event);
425 // --------------------------------------------------------------
428 // Close the joystick system. Should be called at game exit.
435 // joy_di_shutdown();
440 // Tell joystick polling thread to end
441 SetEvent(Joy_tell_thread_to_end_event);
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" ));
447 CloseHandle(Joy_tell_thread_to_end_event);
448 CloseHandle(Joy_thread_says_its_done_event);
450 // It is now safe to release any resources use by the polling thread.
451 DeleteCriticalSection( &joy_lock );
453 CloseHandle(joy_thread);
460 // --------------------------------------------------------------
463 // Determine the capabilities of the attached joysticks.
465 void joy_get_caps(int max)
470 for (j=0; j<JOY_NUM_AXES; j++)
471 joystick.axis_valid[j] = 0;
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));
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;
492 int joy_get_scaled_reading(int raw, int axn);
493 int joy_get_unscaled_reading(int raw, int axn);
495 DCF(joytest, "Test joystick")
498 while (!keyd_pressed[KEY_ESC]) {
499 int x, y, axis[JOY_NUM_AXES];
501 if (joy_num_sticks < 1)
504 joystick_read_raw_axis(JOY_NUM_AXES, axis);
506 x = joy_get_scaled_reading(axis[0], 0);
507 y = joy_get_scaled_reading(axis[1], 1);
509 mprintf(("X=%5d Y=%5d Calibrated X=%6d Y=%6d\n", axis[0], axis[1], x, y));
515 DCF(joytest2, "Test joystick (extended)")
518 while (!keyd_pressed[KEY_ESC]) {
519 int x, y, z, r, axis[JOY_NUM_AXES];
521 if (joy_num_sticks < 1)
524 joystick_read_raw_axis(JOY_NUM_AXES, axis);
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);
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));
537 // --------------------------------------------------------------
540 // Initialize the joystick system. This is called once at game startup.
554 Cur_joystick = os_config_read_uint(NULL, "CurrentJoystick", JOYSTICKID1);
559 mprintf(("No joystick driver detected\n"));
563 InitializeCriticalSection(&joy_lock);
569 memset(&ji, 0, sizeof(ji));
570 ji.dwSize = sizeof(ji);
571 ji.dwFlags = JOY_RETURNALL;
573 if (Cur_joystick >= 0) {
574 // AL: test code to try and find out why this call fails the first time
576 for (count=0; count<20; count++) {
577 rs = joyGetPosEx(Cur_joystick, &ji);
578 if (rs == JOYERR_NOERROR)
582 if (rs == JOYERR_NOERROR) {
585 Joy_tell_thread_to_end_event = CreateEvent( NULL, FALSE, FALSE, NULL );
586 Joy_thread_says_its_done_event = CreateEvent( NULL, FALSE, FALSE, NULL );
588 joy_thread = CreateThread(NULL,
590 (LPTHREAD_START_ROUTINE) joy_process,
595 // SetThreadPriority(joy_thread, THREAD_PRIORITY_TIME_CRITICAL - 1);
596 SetThreadPriority(joy_thread, THREAD_PRIORITY_HIGHEST);
600 mprintf(("Windoze reported %d joysticks, we found %d\n", n, joy_num_sticks));
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;
612 // Fake a calibration
613 if (joy_num_sticks > 0) {
615 for (i=0; i<4; i++) {
616 joystick.axis_min[i] = 0;
617 joystick.axis_max[i] = joystick.axis_center[i]*2;
625 return joy_num_sticks;
628 // --------------------------------------------------------------
631 // Manual calibrate joystick routine
635 if ( joy_num_sticks < 1 ) return;
639 if ( key_inkey()) break;
640 if (joy_down_count(0)) break;
641 mprintf(( "Move stick to upper-left and hit button\n" ));
647 if ( key_inkey()) break;
648 if (joy_down_count(0)) break;
649 mprintf(( "Move stick to lower-right and hit button\n" ));
655 if ( key_inkey()) break;
656 if (joy_down_count(0)) break;
657 mprintf(( "Move stick to center and hit button\n" ));
662 // --------------------------------------------------------------
665 // Get the position of the joystick axes
667 int joy_get_pos_old(int * x, int * y )
672 if ( joy_num_sticks < 1 ) {
678 memset(&ji, 0, sizeof(ji));
679 ji.dwSize = sizeof(ji);
683 //JOY_RETURNCENTERED|
684 //JOY_RETURNX|JOY_RETURNY|JOY_RETURNRAWDATA;
685 ji.dwFlags = JOY_RETURNALL;
687 ji.dwFlags = JOY_CAL_READXYONLY;
690 if (Cur_joystick >= 0) {
691 EnterCriticalSection(&joy_lock);
692 rs = joyGetPosEx(Cur_joystick, &ji);
693 LeaveCriticalSection(&joy_lock);
695 if (rs == JOYERR_NOERROR) {
703 *x = (ji.dwXpos - 32768) * 2;
711 *y = (ji.dwYpos - 32768) * 2;
731 // --------------------------------------------------------------
734 // Return the number of times the joystick button has gone down since
735 // joy_down_count() was last called
737 // input: btn => button number to check
738 // reset_count => (default 1): if true reset down_count
740 // returns: number of times button 'btn' has gone down since last call
742 int joy_down_count(int btn, int reset_count)
746 if ( joy_num_sticks < 1 ) return 0;
747 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0;
749 EnterCriticalSection(&joy_lock);
750 tmp = joy_buttons[btn].down_count;
752 joy_buttons[btn].down_count = 0;
754 LeaveCriticalSection(&joy_lock);
760 // --------------------------------------------------------------
763 // Return the state of button number 'btn'
765 // input: btn => button number to check
767 // returns: 0 => not pressed
770 int joy_down(int btn)
774 if ( joy_num_sticks < 1 ) return 0;
775 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS )) return 0;
777 EnterCriticalSection(&joy_lock);
778 tmp = joy_buttons[btn].state;
779 LeaveCriticalSection(&joy_lock);
784 // --------------------------------------------------------------
787 // Return the number of times the joystick button has gone up since
788 // joy_up_count() was last called
790 // input: btn => button number to check
792 // returns: number of times button 'btn' has gone up since last call
794 int joy_up_count(int btn)
798 if ( joy_num_sticks < 1 ) return 0;
799 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0;
801 EnterCriticalSection(&joy_lock);
802 tmp = joy_buttons[btn].up_count;
803 joy_buttons[btn].up_count = 0;
804 LeaveCriticalSection(&joy_lock);
809 // --------------------------------------------------------------
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
815 // input: btn => button number to check
816 // returns: value between 0 and 1
818 float joy_down_time(int btn)
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);
829 now = timer_get_milliseconds();
831 if ( bi->down_time == 0 && joy_down(btn) ) {
832 bi->down_time += joy_pollrate;
835 if ( (now - bi->last_down_check) > 0)
836 rval = i2fl(bi->down_time) / (now - bi->last_down_check);
841 bi->last_down_check = now;
843 LeaveCriticalSection(&joy_lock);
853 // --------------------------------------------------------------
854 // joy_get_cal_vals()
856 // Get the calibrated min, center, and max for all axes
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
862 void joy_get_cal_vals(int *axis_min, int *axis_center, int *axis_max)
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];
873 // --------------------------------------------------------------
874 // joy_set_cal_vals()
876 // Get the calibrated min, center, and max for all axes
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
882 void joy_set_cal_vals(int *axis_min, int *axis_center, int *axis_max)
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];
893 // --------------------------------------------------------------
894 // joystick_read_raw_axis()
896 // Read the raw axis information for a specified number of axes.
898 // input: num_axes => number of axes to read. Note the axes go in the following order:
906 // axis => an array of at least 4 ints to hold axis data
908 int joystick_read_raw_axis(int num_axes, int *axis)
914 Assert(num_axes <= JOY_NUM_AXES);
915 for (i=0; i<num_axes; i++)
918 // first, try reading with DirectInput, if we can
920 return joystick_read_raw_axis_di(num_axes, axis);
922 // No DirectInput joystick, fall back on older stuff
923 if (joy_num_sticks < 1)
926 memset(&ji, 0, sizeof(ji));
927 ji.dwSize = sizeof(ji);
930 ji.dwFlags = JOY_RETURNALL;
932 ji.dwFlags = JOY_RETURNRAWDATA;
937 ji.dwFlags |= JOY_RETURNV;
939 ji.dwFlags |= JOY_RETURNU;
941 ji.dwFlags |= JOY_RETURNR;
943 ji.dwFlags |= JOY_RETURNZ;
945 ji.dwFlags |= JOY_RETURNY;
947 ji.dwFlags |= JOY_RETURNX;
955 if (Cur_joystick >= 0) {
956 EnterCriticalSection(&joy_lock);
957 rs = joyGetPosEx(Cur_joystick, &ji);
958 LeaveCriticalSection(&joy_lock);
963 if (rs != JOYERR_NOERROR)
968 if (joystick.axis_valid[5])
972 if (joystick.axis_valid[4])
976 if (joystick.axis_valid[3])
980 if (joystick.axis_valid[2])
984 if (joystick.axis_valid[1])
988 if (joystick.axis_valid[0])
1001 // --------------------------------------------------------------
1004 // Get the minimum axis information (namely, joystick in upper left).
1005 // This is called by a manual calibration routine.
1007 // NOTE: sets the values in joystick.axis_min[]
1011 joystick_read_raw_axis( 2, joystick.axis_min );
1014 // --------------------------------------------------------------
1017 // Get the maximum axis information (namely, joystick in lower right).
1018 // This is called by a manual calibration routine.
1020 // NOTE: sets the values in joystick.axis_max[]
1024 joystick_read_raw_axis( 2, joystick.axis_max );
1027 // --------------------------------------------------------------
1030 // Get the center axis information (namely, joystick in dead zone).
1031 // This is called by a manual calibration routine.
1033 // NOTE: sets the values in joystick.axis_center[]
1037 joystick_read_raw_axis( 2, joystick.axis_center );
1040 int joy_get_unscaled_reading(int raw, int axn)
1044 // Make sure it's calibrated properly.
1045 if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5)
1048 if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5)
1051 rng = joystick.axis_max[axn] - joystick.axis_min[axn];
1052 raw -= joystick.axis_min[axn]; // adjust for linear range starting at 0
1060 return (int) ((unsigned int) raw * (unsigned int) F1_0 / (unsigned int) rng); // convert to 0 - F1_0 range.
1063 // --------------------------------------------------------------
1064 // joy_get_scaled_reading()
1066 // input: raw => the raw value for an axis position
1067 // axn => axis number, numbered starting at 0
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'
1073 int joy_get_scaled_reading(int raw, int axn)
1075 int x, d, dead_zone, rng;
1076 float percent, sensitivity_percent, non_sensitivity_percent;
1078 // Make sure it's calibrated properly.
1079 if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5)
1082 if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5)
1085 raw -= joystick.axis_center[axn];
1087 dead_zone = (joystick.axis_max[axn] - joystick.axis_min[axn]) * Dead_zone_size / 100;
1089 if (raw < -dead_zone) {
1090 rng = joystick.axis_center[axn] - joystick.axis_min[axn] - dead_zone;
1091 d = -raw - dead_zone;
1093 } else if (raw > dead_zone) {
1094 rng = joystick.axis_max[axn] - joystick.axis_center[axn] - dead_zone;
1095 d = raw - dead_zone;
1103 Assert(Joy_sensitivity >= 0 && Joy_sensitivity <= 9);
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;
1109 // find percent of max axis is at
1110 percent = (float) d / (float) rng;
1112 // work sensitivity on axis value
1113 percent = (percent * sensitivity_percent + percent * percent * percent * percent * percent * non_sensitivity_percent);
1115 x = (int) ((float) F1_0 * percent);
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));
1125 // --------------------------------------------------------------
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)
1133 // return: success => 1
1136 int joy_get_pos(int *x, int *y, int *z, int *rx)
1138 int axis[JOY_NUM_AXES];
1145 if (joy_num_sticks < 1) return 0;
1147 joystick_read_raw_axis( 6, axis );
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);
1160 Joy_last_x_reading = *x;
1163 Joy_last_x_reading = *y;
1168 // change in joy position since last call
1169 void joy_get_delta(int *dx, int *dy)
1171 static int old_joy_x = 0;
1172 static int old_joy_y = 0;
1174 if ( !Joy_inited ) {
1179 *dx = Joy_last_x_reading - old_joy_x;
1180 *dy = Joy_last_y_reading - old_joy_y;
1182 old_joy_x = Joy_last_x_reading;
1183 old_joy_y = Joy_last_y_reading;
1186 //// This is the DirectInput joystick stuff
1189 int Di_joy_guid_valid = 0;
1191 BOOL CALLBACK joy_di_enum(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
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"));
1203 return DIENUM_CONTINUE;
1206 BOOL FAR PASCAL InitJoystickInput(LPCDIDEVICEINSTANCE pdinst, LPVOID pvRef)
1208 LPDIRECTINPUT pdi = pvRef;
1209 LPDIRECTINPUTDEVICE pdev;
1212 // create the DirectInput joystick device
1213 if(pdi->lpVtbl->CreateDevice(pdi, &pdinst->guidInstance, &pdev, NULL) != DI_OK)
1215 OutputDebugString("IDirectInput::CreateDevice FAILED\n");
1216 return DIENUM_CONTINUE;
1219 // set joystick data format
1220 if (pdev->lpVtbl->SetDataFormat(pdev, &c_dfDIJoystick) != DI_OK)
1222 OutputDebugString("IDirectInputDevice::SetDataFormat FAILED\n");
1223 pdev->lpVtbl->Release(pdev);
1224 return DIENUM_CONTINUE;
1227 // set the cooperative level
1228 if (pdev->lpVtbl->SetCooperativeLevel(pdev, hWndMain,
1229 DISCL_NONEXCLUSIVE | DISCL_FOREGROUND) != DI_OK)
1231 OutputDebugString("IDirectInputDevice::SetCooperativeLevel FAILED\n");
1232 pdev->lpVtbl->Release(pdev);
1233 return DIENUM_CONTINUE;
1236 // set X-axis range to (-1000 ... +1000)
1237 // This lets us test against 0 to see which way the stick is pointed.
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;
1246 if (pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK)
1248 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n");
1249 pdev->lpVtbl->Release(pdev);
1254 // And again for Y-axis range
1256 diprg.diph.dwObj = DIJOFS_Y;
1258 if (pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK)
1260 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n");
1261 pdev->lpVtbl->Release(pdev);
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)
1269 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n");
1270 pdev->lpVtbl->Release(pdev);
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)
1279 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n");
1280 pdev->lpVtbl->Release(pdev);
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]);
1292 pdev->lpVtbl->Release(pdev);
1294 return DIENUM_CONTINUE;
1300 LPDIRECTINPUTDEVICE pdev;
1303 hr = DirectInputCreate(GetModuleHandle(NULL), 0x500, &Di_joystick_obj, NULL);
1305 mprintf(( "DirectInputCreate() failed!\n" ));
1309 Di_joy_guid_valid = 0;
1310 hr = Di_joystick_obj->EnumDevices(DIDEVTYPE_JOYSTICK, joy_di_enum, Di_joystick_obj, DIEDFL_ATTACHEDONLY);
1312 mprintf(( "EnumDevice() failed!\n" ));
1316 if (!Di_joy_guid_valid) {
1317 mprintf(( "Correct joystick not found.\n" ));
1321 hr = Di_joystick_obj->CreateDevice(Di_joy_guid, &pdev, NULL);
1323 mprintf(( "CreateDevice() failed!\n" ));
1327 hr = pdev->SetDataFormat(&c_dfDIJoystick);
1329 mprintf(( "SetDataFormat() failed!\n" ));
1330 if (hr == DIERR_ACQUIRED)
1331 mprintf(( " (reason: DIERR_ACQUIRED)\n" ));
1333 if (hr == DIERR_INVALIDPARAM)
1334 mprintf(( " (reason: DIERR_INVALIDPARAM)\n" ));
1336 if (hr == DIERR_NOTINITIALIZED)
1337 mprintf(( " (reason: DIERR_NOTINITIALIZED)\n" ));
1343 hr = pdev->SetCooperativeLevel((HWND) os_get_window(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
1345 mprintf(( "SetCooperativeLevel() failed!\n" ));
1346 if (hr == DIERR_ACQUIRED)
1347 mprintf(( " (reason: DIERR_ACQUIRED)\n" ));
1349 if (hr == DIERR_INVALIDPARAM)
1350 mprintf(( " (reason: DIERR_INVALIDPARAM)\n" ));
1352 if (hr == DIERR_NOTINITIALIZED)
1353 mprintf(( " (reason: DIERR_NOTINITIALIZED)\n" ));
1359 hr = pdev->QueryInterface(IID_IDirectInputDevice2, (LPVOID *) &Di_joystick);
1365 Di_joystick->Acquire();
1369 nprintf(("Joystick", "DirectInput initialization of joystick succeeded\n"));
1373 int joy_di_shutdown()
1375 // Destroy any lingering IDirectInputDevice object.
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();
1381 Di_joystick->Release();
1385 // Destroy any lingering IDirectInput object.
1386 if (Di_joystick_obj) {
1387 Di_joystick_obj->Release();
1388 Di_joystick_obj = NULL;
1395 int joystick_read_raw_axis_di(int num_axes, int *axis)
1399 DIJOYSTATE joy_state;
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
1415 Sleep(1000); // Pause a second...
1416 hr = Di_joystick->Acquire();
1423 memset(&joy_state, 0, sizeof(joy_state));
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
1434 Sleep(1000); // Pause a second...
1435 hr = Di_joystick->Acquire();
1441 if (SUCCEEDED(hr)) {
1444 if (joystick.axis_valid[5])
1445 axis[5] = joy_state.lRy;
1448 if (joystick.axis_valid[4])
1449 axis[4] = joy_state.lRx;
1452 if (joystick.axis_valid[3])
1453 axis[3] = joy_state.lRz;
1456 if (joystick.axis_valid[2])
1457 axis[2] = joy_state.lZ;
1460 if (joystick.axis_valid[1])
1461 axis[1] = joy_state.lY;
1464 if (joystick.axis_valid[0])
1465 axis[0] = joy_state.lX;