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.3 2002/06/09 04:41:21 relnev
19 * added copyright header
21 * Revision 1.2 2002/05/07 03:16:46 theoddone33
22 * The Great Newline Fix
24 * Revision 1.1.1.1 2002/05/03 03:28:09 root
28 * 5 7/15/99 9:20a Andsager
29 * FS2_DEMO initial checkin
31 * 4 6/02/99 6:18p Dave
32 * Fixed TNT lockup problems! Wheeeee!
34 * 3 10/09/98 2:57p Dave
35 * Starting splitting up OS stuff.
37 * 2 10/07/98 10:53a Dave
40 * 1 10/07/98 10:49a Dave
42 * 61 5/24/98 12:56a Mike
43 * Put in debug code, but comment out, to check joystick sensitivity.
45 * 60 5/19/98 6:54p Lawrance
46 * Set default joystick sensitivity to max
48 * 59 5/13/98 7:14p Hoffoss
49 * Made invalid axis return center position value.
51 * 58 5/13/98 1:17a Hoffoss
52 * Added joystick axes configurability.
54 * 57 5/07/98 3:15p Hoffoss
57 * 56 5/07/98 12:41p Hoffoss
58 * Changed code to make joystick sensitivity default to center of range.
60 * 55 5/06/98 12:02a Hoffoss
61 * Fixed throttle problems with joysticks.
63 * 54 5/05/98 8:38p Hoffoss
64 * Added sensitivity adjustment to options menu and made it save to pilot
67 * 53 5/04/98 11:08p Hoffoss
68 * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp.
69 * Updated references everywhere to it.
71 * 52 5/03/98 6:02p Hoffoss
72 * Added DirectInput support for joystick (only works with v5.0, so only
75 * 51 5/01/98 5:59p Hoffoss
76 * Checking in code so I can switch back to NT to work on this instead
79 * 50 4/30/98 12:12p Hoffoss
80 * Changed code to explicitly use calibrated values from Windows for
83 * 49 4/29/98 12:13a Lawrance
84 * Add function for reading down count without resetting internal count.
85 * Add hook to reset demo trailer timer.
87 * 48 4/25/98 12:02p Lawrance
88 * take out crit sec code around joystick read in joy_process
90 * 47 4/22/98 9:03p John
91 * Put critical section around all the joygetposex functions. Had to set
92 * up events to signal the polling thread to end at the end of the
95 * 46 4/17/98 3:07p Jim
96 * added debug code to test joystick
98 * 45 4/13/98 10:16a John
99 * Switched gettime back to timer_get_milliseconds, which is now thread
102 * 44 4/12/98 11:08p Lawrance
103 * switch back to using gettime() in separate threads
105 * 43 4/12/98 5:31p Lawrance
106 * use timer_get_milliseconds() instead of gettime()
108 * 42 3/21/98 11:29a John
109 * Made joy_flush work when a button is held down
111 * 41 3/12/98 10:44a Hoffoss
112 * Whoops, should probably use the actual define rather than 0.
114 * 40 3/12/98 10:38a Hoffoss
115 * Changed joystick default to first slot in never set up in FSLaunch
117 * 39 3/11/98 5:27p Hoffoss
118 * Added to FreeSpace usage of joystick specified through FSLaunch.
120 * 38 3/09/98 4:44p Sandeep
122 * 37 3/07/98 5:23p Sandeep
124 * 35 3/06/98 11:12a Hoffoss
125 * Fixed joystick deadzone bug.
127 * 33 3/06/98 10:02a Hoffoss
128 * Made dead zone adjustable, and defaulted it to 10% instead of 5%.
130 * 36 3/07/98 4:50p John
131 * Added code to allow toggling force feedback on/off in setup
133 * 35 3/06/98 11:12a Hoffoss
134 * Fixed joystick deadzone bug.
136 * 33 3/06/98 10:02a Hoffoss
137 * Made dead zone adjustable, and defaulted it to 10% instead of 5%.
139 * 32 2/11/98 9:56p Jim
140 * allender: from sandeep on Jim's machine -- some force feedback stuff
142 * 31 1/29/98 11:04a Sandeep
144 * 30 1/27/98 8:40p Sandeep
146 * 29 1/19/98 6:15p John
147 * Fixed all my Optimized Build compiler warnings
149 * 28 1/08/98 6:35p Hoffoss
150 * Fixed joystick undefined detection.
152 * 27 1/08/98 3:48p Dan
153 * Fixed bug with joystick axis reading thinking it's undefined at
156 * 26 10/16/97 5:37p Lawrance
157 * change thread priority from THREAD_PRIORITY_TIME_CRITICAL to
158 * THREAD_PRIORITY_HIGHEST
160 * 25 10/09/97 10:15a Johnson
161 * try to init several times if joystick init fails
163 * 24 10/07/97 10:42a Johnson
164 * zero out JOYINFOEX struct before setting dwSize
166 * 23 10/06/97 5:54p Johnson
167 * ALAN: fix nasty bug where dwSize member of JOYINFOEX was not being set,
168 * resulting in random failure
170 * 22 9/15/97 11:42p Lawrance
171 * change button_info to joy_button_info to avoid name conflict
173 * 21 8/07/97 11:26p Lawrance
174 * add support for 4th axis (rudder)
176 * 20 7/29/97 5:30p Lawrance
177 * move gettime() from keyboard module to timer module
179 * 19 7/11/97 11:43a Lawrance
180 * fix bug with joy_up_count
182 * 18 7/10/97 12:29a Lawrance
183 * fix problem with NT not recognizing an axis that was set under 95
185 * 17 7/09/97 11:41p Lawrance
186 * added throttle and hat support
188 * 16 6/19/97 9:50a John
189 * fixed bug where joy_close getting called out of order doesn't matter.
191 * 15 5/18/97 2:40p Lawrance
192 * added joy_get_caps()
194 * 14 4/22/97 10:56a John
195 * fixed some resource leaks.
197 * 13 2/27/97 2:23p Lawrance
198 * took out debug stmts
200 * 12 2/27/97 10:04a Lawrance
201 * fixed bug that was causing all joy buttons but 0 to be read incorrectly
203 * 11 2/17/97 5:18p John
204 * Added a bunch of RCS headers to a bunch of old files that don't have
211 #include <windowsx.h>
218 #include "osregistry.h"
223 #define PRECALIBRATED 1
225 static int Joy_inited = 0;
226 int joy_num_sticks = 0;
227 int Dead_zone_size = 10;
228 int Cur_joystick = -1; // joystick used for input or -1
229 int Joy_sensitivity = 9;
231 CRITICAL_SECTION joy_lock;
233 HANDLE joy_thread = NULL;
235 int joy_pollrate = 1000 / 18; // poll at 18Hz
237 HANDLE Joy_tell_thread_to_end_event = NULL;
238 HANDLE Joy_thread_says_its_done_event = NULL;
240 static int Joy_last_x_reading = 0;
241 static int Joy_last_y_reading = 0;
243 int Joy_di_inited = 0;
244 static LPDIRECTINPUT Di_joystick_obj = NULL;
245 static LPDIRECTINPUTDEVICE2 Di_joystick = NULL;
247 typedef struct joy_button_info {
248 int actual_state; // Set if the button is physically down
249 int state; // Set when the button goes from up to down, cleared on down to up. Different than actual_state after a flush.
253 uint last_down_check; // timestamp in milliseconds of last
258 joy_button_info joy_buttons[JOY_TOTAL_BUTTONS];
261 int joy_di_shutdown();
262 int joystick_read_raw_axis_di(int num_axes, int *axis);
264 // --------------------------------------------------------------
267 // Clear the state of the joystick.
274 if ( joy_num_sticks < 1 ) return;
276 EnterCriticalSection(&joy_lock);
277 for ( i = 0; i < JOY_TOTAL_BUTTONS; i++) {
278 bi = &joy_buttons[i];
283 bi->last_down_check = timer_get_milliseconds();
286 LeaveCriticalSection(&joy_lock);
289 // --------------------------------------------------------------
292 // Runs as a separate thread, and updates the state of the joystick
294 DWORD joy_process(DWORD lparam)
301 for ( i = 0; i < JOY_TOTAL_BUTTONS; i++) {
302 bi = &joy_buttons[i];
303 bi->actual_state = 0; // Don't set in flush code!
308 bi->last_down_check = timer_get_milliseconds();
312 // Wait for the thread to be signaled to end or 1/18th of a second to pass...
313 if ( WaitForSingleObject( Joy_tell_thread_to_end_event, joy_pollrate )==WAIT_OBJECT_0) {
317 memset(&ji, 0, sizeof(ji));
318 ji.dwSize = sizeof(ji);
319 // ji.dwFlags = JOY_RETURNBUTTONS | JOY_RETURNRAWDATA;
320 ji.dwFlags = JOY_RETURNALL;
322 EnterCriticalSection(&joy_lock);
325 if (Cur_joystick >= 0) {
326 rs = joyGetPosEx(Cur_joystick, &ji);
327 // If there's an error, assume all buttons down.
328 if (rs == JOYERR_NOERROR) {
329 joy_state = ji.dwButtons;
333 // Process ji.dwButtons
334 for (i=0; i<JOY_TOTAL_BUTTONS; i++) {
336 if (i < JOY_NUM_BUTTONS) {
337 state = joy_state & (1<<i);
340 // check for hat presses, which act like buttons
343 if (ji.dwPOV == JOY_POVBACKWARD)
348 if (ji.dwPOV == JOY_POVFORWARD)
353 if (ji.dwPOV == JOY_POVLEFT)
358 if (ji.dwPOV == JOY_POVRIGHT)
363 Int3(); // should never happen
370 if (state != joy_buttons[i].actual_state) {
371 // Button position physically changed.
372 joy_buttons[i].actual_state = state;
375 // went from up to down
376 joy_buttons[i].down_count++;
377 joy_buttons[i].down_time = 0;
379 joy_buttons[i].state = 1;
381 ////////////////////////////
382 /// SOMETHING TERRIBLE IS ABOUT TO HAPPEN. I FEEL THIS IS NECESSARY FOR THE DEMO, SINCE
383 /// I DON'T WANT TO CALL CRITICAL SECTION CODE EACH FRAME TO CHECK ALL THE JOYSTICK BUTTONS.
384 /// PLEASE SEE ALAN FOR MORE INFORMATION.
385 ////////////////////////////
388 extern void demo_reset_trailer_timer();
389 demo_reset_trailer_timer();
392 ////////////////////////////
393 /// IT'S OVER. SEE, IT WASN'T SO BAD RIGHT? IT'S IS VERY UGLY LOOKING, I KNOW.
394 ////////////////////////////
398 // went from down to up
399 if ( joy_buttons[i].state ) {
400 joy_buttons[i].up_count++;
402 joy_buttons[i].state = 0;
406 // Didn't move... increment time down if down.
407 if (joy_buttons[i].state) {
408 joy_buttons[i].down_time += joy_pollrate;
414 LeaveCriticalSection(&joy_lock);
417 SetEvent(Joy_thread_says_its_done_event);
422 // --------------------------------------------------------------
425 // Close the joystick system. Should be called at game exit.
432 // joy_di_shutdown();
437 // Tell joystick polling thread to end
438 SetEvent(Joy_tell_thread_to_end_event);
440 // Wait for it to end
441 if ( WaitForSingleObject( Joy_thread_says_its_done_event, 5000 )==WAIT_TIMEOUT) { //INFINITE );
442 mprintf(( "Joy end thread wait timeout!\n" ));
444 CloseHandle(Joy_tell_thread_to_end_event);
445 CloseHandle(Joy_thread_says_its_done_event);
447 // It is now safe to release any resources use by the polling thread.
448 DeleteCriticalSection( &joy_lock );
450 CloseHandle(joy_thread);
457 // --------------------------------------------------------------
460 // Determine the capabilities of the attached joysticks.
462 void joy_get_caps(int max)
467 for (j=0; j<JOY_NUM_AXES; j++)
468 joystick.axis_valid[j] = 0;
470 for (j=JOYSTICKID1; j<JOYSTICKID1+max; j++) {
471 if (JOYERR_NOERROR == joyGetDevCaps (j, &JoyCaps, sizeof(JoyCaps))) {
472 nprintf(("JOYSTICK", "Joystick #%d: %s\n", j - JOYSTICKID1 + 1, JoyCaps.szPname));
474 if (j == Cur_joystick) {
475 joystick.axis_valid[0] = joystick.axis_valid[1] = 1;
476 if (JoyCaps.wCaps & JOYCAPS_HASZ)
477 joystick.axis_valid[2] = 1;
478 if (JoyCaps.wCaps & JOYCAPS_HASR)
479 joystick.axis_valid[3] = 1;
480 if (JoyCaps.wCaps & JOYCAPS_HASU)
481 joystick.axis_valid[4] = 1;
482 if (JoyCaps.wCaps & JOYCAPS_HASV)
483 joystick.axis_valid[5] = 1;
489 int joy_get_scaled_reading(int raw, int axn);
490 int joy_get_unscaled_reading(int raw, int axn);
492 DCF(joytest, "Test joystick")
495 while (!keyd_pressed[KEY_ESC]) {
496 int x, y, axis[JOY_NUM_AXES];
498 if (joy_num_sticks < 1)
501 joystick_read_raw_axis(JOY_NUM_AXES, axis);
503 x = joy_get_scaled_reading(axis[0], 0);
504 y = joy_get_scaled_reading(axis[1], 1);
506 mprintf(("X=%5d Y=%5d Calibrated X=%6d Y=%6d\n", axis[0], axis[1], x, y));
512 DCF(joytest2, "Test joystick (extended)")
515 while (!keyd_pressed[KEY_ESC]) {
516 int x, y, z, r, axis[JOY_NUM_AXES];
518 if (joy_num_sticks < 1)
521 joystick_read_raw_axis(JOY_NUM_AXES, axis);
523 x = joy_get_scaled_reading(axis[0], 0);
524 y = joy_get_scaled_reading(axis[1], 1);
525 z = joy_get_unscaled_reading(axis[2], 2);
526 r = joy_get_scaled_reading(axis[3], 3);
528 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));
534 // --------------------------------------------------------------
537 // Initialize the joystick system. This is called once at game startup.
551 Cur_joystick = os_config_read_uint(NULL, "CurrentJoystick", JOYSTICKID1);
556 mprintf(("No joystick driver detected\n"));
560 InitializeCriticalSection(&joy_lock);
566 memset(&ji, 0, sizeof(ji));
567 ji.dwSize = sizeof(ji);
568 ji.dwFlags = JOY_RETURNALL;
570 if (Cur_joystick >= 0) {
571 // AL: test code to try and find out why this call fails the first time
573 for (count=0; count<20; count++) {
574 rs = joyGetPosEx(Cur_joystick, &ji);
575 if (rs == JOYERR_NOERROR)
579 if (rs == JOYERR_NOERROR) {
582 Joy_tell_thread_to_end_event = CreateEvent( NULL, FALSE, FALSE, NULL );
583 Joy_thread_says_its_done_event = CreateEvent( NULL, FALSE, FALSE, NULL );
585 joy_thread = CreateThread(NULL,
587 (LPTHREAD_START_ROUTINE) joy_process,
592 // SetThreadPriority(joy_thread, THREAD_PRIORITY_TIME_CRITICAL - 1);
593 SetThreadPriority(joy_thread, THREAD_PRIORITY_HIGHEST);
597 mprintf(("Windoze reported %d joysticks, we found %d\n", n, joy_num_sticks));
600 // Fake a calibration
601 if (joy_num_sticks > 0) {
602 for (i=0; i<4; i++) {
603 joystick.axis_min[i] = 0;
604 joystick.axis_center[i] = 32768;
605 joystick.axis_max[i] = 65536;
609 // Fake a calibration
610 if (joy_num_sticks > 0) {
612 for (i=0; i<4; i++) {
613 joystick.axis_min[i] = 0;
614 joystick.axis_max[i] = joystick.axis_center[i]*2;
622 return joy_num_sticks;
625 // --------------------------------------------------------------
628 // Manual calibrate joystick routine
632 if ( joy_num_sticks < 1 ) return;
636 if ( key_inkey()) break;
637 if (joy_down_count(0)) break;
638 mprintf(( "Move stick to upper-left and hit button\n" ));
644 if ( key_inkey()) break;
645 if (joy_down_count(0)) break;
646 mprintf(( "Move stick to lower-right and hit button\n" ));
652 if ( key_inkey()) break;
653 if (joy_down_count(0)) break;
654 mprintf(( "Move stick to center and hit button\n" ));
659 // --------------------------------------------------------------
662 // Get the position of the joystick axes
664 int joy_get_pos_old(int * x, int * y )
669 if ( joy_num_sticks < 1 ) {
675 memset(&ji, 0, sizeof(ji));
676 ji.dwSize = sizeof(ji);
680 //JOY_RETURNCENTERED|
681 //JOY_RETURNX|JOY_RETURNY|JOY_RETURNRAWDATA;
682 ji.dwFlags = JOY_RETURNALL;
684 ji.dwFlags = JOY_CAL_READXYONLY;
687 if (Cur_joystick >= 0) {
688 EnterCriticalSection(&joy_lock);
689 rs = joyGetPosEx(Cur_joystick, &ji);
690 LeaveCriticalSection(&joy_lock);
692 if (rs == JOYERR_NOERROR) {
700 *x = (ji.dwXpos - 32768) * 2;
708 *y = (ji.dwYpos - 32768) * 2;
728 // --------------------------------------------------------------
731 // Return the number of times the joystick button has gone down since
732 // joy_down_count() was last called
734 // input: btn => button number to check
735 // reset_count => (default 1): if true reset down_count
737 // returns: number of times button 'btn' has gone down since last call
739 int joy_down_count(int btn, int reset_count)
743 if ( joy_num_sticks < 1 ) return 0;
744 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0;
746 EnterCriticalSection(&joy_lock);
747 tmp = joy_buttons[btn].down_count;
749 joy_buttons[btn].down_count = 0;
751 LeaveCriticalSection(&joy_lock);
757 // --------------------------------------------------------------
760 // Return the state of button number 'btn'
762 // input: btn => button number to check
764 // returns: 0 => not pressed
767 int joy_down(int btn)
771 if ( joy_num_sticks < 1 ) return 0;
772 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS )) return 0;
774 EnterCriticalSection(&joy_lock);
775 tmp = joy_buttons[btn].state;
776 LeaveCriticalSection(&joy_lock);
781 // --------------------------------------------------------------
784 // Return the number of times the joystick button has gone up since
785 // joy_up_count() was last called
787 // input: btn => button number to check
789 // returns: number of times button 'btn' has gone up since last call
791 int joy_up_count(int btn)
795 if ( joy_num_sticks < 1 ) return 0;
796 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0;
798 EnterCriticalSection(&joy_lock);
799 tmp = joy_buttons[btn].up_count;
800 joy_buttons[btn].up_count = 0;
801 LeaveCriticalSection(&joy_lock);
806 // --------------------------------------------------------------
809 // Return a number between 0 and 1. This number represents the percentage
810 // time that the joystick button has been down since it was last checked
812 // input: btn => button number to check
813 // returns: value between 0 and 1
815 float joy_down_time(int btn)
821 if ( joy_num_sticks < 1 ) return 0.0f;
822 if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0.0f;
823 bi = &joy_buttons[btn];
824 EnterCriticalSection(&joy_lock);
826 now = timer_get_milliseconds();
828 if ( bi->down_time == 0 && joy_down(btn) ) {
829 bi->down_time += joy_pollrate;
832 if ( (now - bi->last_down_check) > 0)
833 rval = i2fl(bi->down_time) / (now - bi->last_down_check);
838 bi->last_down_check = now;
840 LeaveCriticalSection(&joy_lock);
850 // --------------------------------------------------------------
851 // joy_get_cal_vals()
853 // Get the calibrated min, center, and max for all axes
855 // input: axis_min => OUTPUT PARAMETER: array of at least 4 ints to hold min axis values
856 // axis_center => OUTPUT PARAMETER: array of at least 4 ints to hold center axis values
857 // axis_min => OUTPUT PARAMETER: array of at least 4 ints to hold max axis values
859 void joy_get_cal_vals(int *axis_min, int *axis_center, int *axis_max)
863 for ( i = 0; i < 4; i++) {
864 axis_min[i] = joystick.axis_min[i];
865 axis_center[i] = joystick.axis_center[i];
866 axis_max[i] = joystick.axis_max[i];
870 // --------------------------------------------------------------
871 // joy_set_cal_vals()
873 // Get the calibrated min, center, and max for all axes
875 // input: axis_min => array of at 4 ints that hold min axis values
876 // axis_center => array of at 4 ints that hold center axis values
877 // axis_min => array of at 4 ints that hold max axis values
879 void joy_set_cal_vals(int *axis_min, int *axis_center, int *axis_max)
883 for (i=0; i<4; i++) {
884 joystick.axis_min[i] = axis_min[i];
885 joystick.axis_center[i] = axis_center[i];
886 joystick.axis_max[i] = axis_max[i];
890 // --------------------------------------------------------------
891 // joystick_read_raw_axis()
893 // Read the raw axis information for a specified number of axes.
895 // input: num_axes => number of axes to read. Note the axes go in the following order:
903 // axis => an array of at least 4 ints to hold axis data
905 int joystick_read_raw_axis(int num_axes, int *axis)
911 Assert(num_axes <= JOY_NUM_AXES);
912 for (i=0; i<num_axes; i++)
915 // first, try reading with DirectInput, if we can
917 return joystick_read_raw_axis_di(num_axes, axis);
919 // No DirectInput joystick, fall back on older stuff
920 if (joy_num_sticks < 1)
923 memset(&ji, 0, sizeof(ji));
924 ji.dwSize = sizeof(ji);
927 ji.dwFlags = JOY_RETURNALL;
929 ji.dwFlags = JOY_RETURNRAWDATA;
934 ji.dwFlags |= JOY_RETURNV;
936 ji.dwFlags |= JOY_RETURNU;
938 ji.dwFlags |= JOY_RETURNR;
940 ji.dwFlags |= JOY_RETURNZ;
942 ji.dwFlags |= JOY_RETURNY;
944 ji.dwFlags |= JOY_RETURNX;
952 if (Cur_joystick >= 0) {
953 EnterCriticalSection(&joy_lock);
954 rs = joyGetPosEx(Cur_joystick, &ji);
955 LeaveCriticalSection(&joy_lock);
960 if (rs != JOYERR_NOERROR)
965 if (joystick.axis_valid[5])
969 if (joystick.axis_valid[4])
973 if (joystick.axis_valid[3])
977 if (joystick.axis_valid[2])
981 if (joystick.axis_valid[1])
985 if (joystick.axis_valid[0])
998 // --------------------------------------------------------------
1001 // Get the minimum axis information (namely, joystick in upper left).
1002 // This is called by a manual calibration routine.
1004 // NOTE: sets the values in joystick.axis_min[]
1008 joystick_read_raw_axis( 2, joystick.axis_min );
1011 // --------------------------------------------------------------
1014 // Get the maximum axis information (namely, joystick in lower right).
1015 // This is called by a manual calibration routine.
1017 // NOTE: sets the values in joystick.axis_max[]
1021 joystick_read_raw_axis( 2, joystick.axis_max );
1024 // --------------------------------------------------------------
1027 // Get the center axis information (namely, joystick in dead zone).
1028 // This is called by a manual calibration routine.
1030 // NOTE: sets the values in joystick.axis_center[]
1034 joystick_read_raw_axis( 2, joystick.axis_center );
1037 int joy_get_unscaled_reading(int raw, int axn)
1041 // Make sure it's calibrated properly.
1042 if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5)
1045 if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5)
1048 rng = joystick.axis_max[axn] - joystick.axis_min[axn];
1049 raw -= joystick.axis_min[axn]; // adjust for linear range starting at 0
1057 return (int) ((unsigned int) raw * (unsigned int) F1_0 / (unsigned int) rng); // convert to 0 - F1_0 range.
1060 // --------------------------------------------------------------
1061 // joy_get_scaled_reading()
1063 // input: raw => the raw value for an axis position
1064 // axn => axis number, numbered starting at 0
1066 // return: joy_get_scaled_reading will return a value that represents
1067 // the joystick pos from -1 to +1 for the specified axis number 'axn', and
1068 // the raw value 'raw'
1070 int joy_get_scaled_reading(int raw, int axn)
1072 int x, d, dead_zone, rng;
1073 float percent, sensitivity_percent, non_sensitivity_percent;
1075 // Make sure it's calibrated properly.
1076 if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5)
1079 if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5)
1082 raw -= joystick.axis_center[axn];
1084 dead_zone = (joystick.axis_max[axn] - joystick.axis_min[axn]) * Dead_zone_size / 100;
1086 if (raw < -dead_zone) {
1087 rng = joystick.axis_center[axn] - joystick.axis_min[axn] - dead_zone;
1088 d = -raw - dead_zone;
1090 } else if (raw > dead_zone) {
1091 rng = joystick.axis_max[axn] - joystick.axis_center[axn] - dead_zone;
1092 d = raw - dead_zone;
1100 Assert(Joy_sensitivity >= 0 && Joy_sensitivity <= 9);
1102 // compute percentages as a range between 0 and 1
1103 sensitivity_percent = (float) Joy_sensitivity / 9.0f;
1104 non_sensitivity_percent = (float) (9 - Joy_sensitivity) / 9.0f;
1106 // find percent of max axis is at
1107 percent = (float) d / (float) rng;
1109 // work sensitivity on axis value
1110 percent = (percent * sensitivity_percent + percent * percent * percent * percent * percent * non_sensitivity_percent);
1112 x = (int) ((float) F1_0 * percent);
1114 //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));
1122 // --------------------------------------------------------------
1125 // input: x => OUTPUT PARAMETER: x-axis position of stick (-1 to 1)
1126 // y => OUTPUT PARAMETER: y-axis position of stick (-1 to 1)
1127 // z => OUTPUT PARAMETER: z-axis (throttle) position of stick (-1 to 1)
1128 // r => OUTPUT PARAMETER: rudder position of stick (-1 to 1)
1130 // return: success => 1
1133 int joy_get_pos(int *x, int *y, int *z, int *rx)
1135 int axis[JOY_NUM_AXES];
1142 if (joy_num_sticks < 1) return 0;
1144 joystick_read_raw_axis( 6, axis );
1146 // joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1
1147 if (x && joystick.axis_valid[0])
1148 *x = joy_get_scaled_reading(axis[0], 0);
1149 if (y && joystick.axis_valid[1])
1150 *y = joy_get_scaled_reading(axis[1], 1);
1151 if (z && joystick.axis_valid[2])
1152 *z = joy_get_unscaled_reading(axis[2], 2);
1153 if (rx && joystick.axis_valid[3])
1154 *rx = joy_get_scaled_reading(axis[3], 3);
1157 Joy_last_x_reading = *x;
1160 Joy_last_x_reading = *y;
1165 // change in joy position since last call
1166 void joy_get_delta(int *dx, int *dy)
1168 static int old_joy_x = 0;
1169 static int old_joy_y = 0;
1171 if ( !Joy_inited ) {
1176 *dx = Joy_last_x_reading - old_joy_x;
1177 *dy = Joy_last_y_reading - old_joy_y;
1179 old_joy_x = Joy_last_x_reading;
1180 old_joy_y = Joy_last_y_reading;
1183 //// This is the DirectInput joystick stuff
1186 int Di_joy_guid_valid = 0;
1188 BOOL CALLBACK joy_di_enum(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
1192 nprintf(("Joystick", "Joystick detected: %s (%s)\n", lpddi->tszInstanceName, lpddi->tszProductName));
1193 sprintf(buf, "Joystick %d", Cur_joystick + 1);
1194 if (!stricmp(buf, lpddi->tszInstanceName)) {
1195 Di_joy_guid = lpddi->guidInstance;
1196 Di_joy_guid_valid = 1;
1197 nprintf(("Joystick", " (Selected joystick)\n"));
1200 return DIENUM_CONTINUE;
1203 BOOL FAR PASCAL InitJoystickInput(LPCDIDEVICEINSTANCE pdinst, LPVOID pvRef)
1205 LPDIRECTINPUT pdi = pvRef;
1206 LPDIRECTINPUTDEVICE pdev;
1209 // create the DirectInput joystick device
1210 if(pdi->lpVtbl->CreateDevice(pdi, &pdinst->guidInstance, &pdev, NULL) != DI_OK)
1212 OutputDebugString("IDirectInput::CreateDevice FAILED\n");
1213 return DIENUM_CONTINUE;
1216 // set joystick data format
1217 if (pdev->lpVtbl->SetDataFormat(pdev, &c_dfDIJoystick) != DI_OK)
1219 OutputDebugString("IDirectInputDevice::SetDataFormat FAILED\n");
1220 pdev->lpVtbl->Release(pdev);
1221 return DIENUM_CONTINUE;
1224 // set the cooperative level
1225 if (pdev->lpVtbl->SetCooperativeLevel(pdev, hWndMain,
1226 DISCL_NONEXCLUSIVE | DISCL_FOREGROUND) != DI_OK)
1228 OutputDebugString("IDirectInputDevice::SetCooperativeLevel FAILED\n");
1229 pdev->lpVtbl->Release(pdev);
1230 return DIENUM_CONTINUE;
1233 // set X-axis range to (-1000 ... +1000)
1234 // This lets us test against 0 to see which way the stick is pointed.
1236 diprg.diph.dwSize = sizeof(diprg);
1237 diprg.diph.dwHeaderSize = sizeof(diprg.diph);
1238 diprg.diph.dwObj = DIJOFS_X;
1239 diprg.diph.dwHow = DIPH_BYOFFSET;
1243 if (pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK)
1245 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n");
1246 pdev->lpVtbl->Release(pdev);
1251 // And again for Y-axis range
1253 diprg.diph.dwObj = DIJOFS_Y;
1255 if (pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK)
1257 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n");
1258 pdev->lpVtbl->Release(pdev);
1262 // set X axis dead zone to 50% (to avoid accidental turning)
1263 // Units are ten thousandths, so 50% = 5000/10000.
1264 if (SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_X, DIPH_BYOFFSET, 5000) != DI_OK)
1266 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n");
1267 pdev->lpVtbl->Release(pdev);
1272 // set Y axis dead zone to 50% (to avoid accidental thrust)
1273 // Units are ten thousandths, so 50% = 5000/10000.
1274 if (SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_Y, DIPH_BYOFFSET, 5000) != DI_OK)
1276 OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n");
1277 pdev->lpVtbl->Release(pdev);
1282 // Add it to our list of devices. If AddInputDevice succeeds,
1283 // he will do an AddRef.
1284 AddInputDevice(pdev, pdinst);
1285 hRes = pdev->lpVtbl->QueryInterface(
1286 pdev, &IID_IDirectInputDevice2,
1287 (LPVOID *)&g_rgpdevFound[g_cpdevFound]);
1289 pdev->lpVtbl->Release(pdev);
1291 return DIENUM_CONTINUE;
1297 LPDIRECTINPUTDEVICE pdev;
1300 hr = DirectInputCreate(GetModuleHandle(NULL), 0x500, &Di_joystick_obj, NULL);
1302 mprintf(( "DirectInputCreate() failed!\n" ));
1306 Di_joy_guid_valid = 0;
1307 hr = Di_joystick_obj->EnumDevices(DIDEVTYPE_JOYSTICK, joy_di_enum, Di_joystick_obj, DIEDFL_ATTACHEDONLY);
1309 mprintf(( "EnumDevice() failed!\n" ));
1313 if (!Di_joy_guid_valid) {
1314 mprintf(( "Correct joystick not found.\n" ));
1318 hr = Di_joystick_obj->CreateDevice(Di_joy_guid, &pdev, NULL);
1320 mprintf(( "CreateDevice() failed!\n" ));
1324 hr = pdev->SetDataFormat(&c_dfDIJoystick);
1326 mprintf(( "SetDataFormat() failed!\n" ));
1327 if (hr == DIERR_ACQUIRED)
1328 mprintf(( " (reason: DIERR_ACQUIRED)\n" ));
1330 if (hr == DIERR_INVALIDPARAM)
1331 mprintf(( " (reason: DIERR_INVALIDPARAM)\n" ));
1333 if (hr == DIERR_NOTINITIALIZED)
1334 mprintf(( " (reason: DIERR_NOTINITIALIZED)\n" ));
1340 hr = pdev->SetCooperativeLevel((HWND) os_get_window(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
1342 mprintf(( "SetCooperativeLevel() failed!\n" ));
1343 if (hr == DIERR_ACQUIRED)
1344 mprintf(( " (reason: DIERR_ACQUIRED)\n" ));
1346 if (hr == DIERR_INVALIDPARAM)
1347 mprintf(( " (reason: DIERR_INVALIDPARAM)\n" ));
1349 if (hr == DIERR_NOTINITIALIZED)
1350 mprintf(( " (reason: DIERR_NOTINITIALIZED)\n" ));
1356 hr = pdev->QueryInterface(IID_IDirectInputDevice2, (LPVOID *) &Di_joystick);
1362 Di_joystick->Acquire();
1366 nprintf(("Joystick", "DirectInput initialization of joystick succeeded\n"));
1370 int joy_di_shutdown()
1372 // Destroy any lingering IDirectInputDevice object.
1374 // Unacquire the device one last time just in case we got really confused
1375 // and tried to exit while the device is still acquired.
1376 Di_joystick->Unacquire();
1378 Di_joystick->Release();
1382 // Destroy any lingering IDirectInput object.
1383 if (Di_joystick_obj) {
1384 Di_joystick_obj->Release();
1385 Di_joystick_obj = NULL;
1392 int joystick_read_raw_axis_di(int num_axes, int *axis)
1396 DIJOYSTATE joy_state;
1405 hr = Di_joystick->Poll();
1406 if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) {
1407 // DirectInput is telling us that the input stream has
1408 // been interrupted. We aren't tracking any state
1409 // between polls, so we don't have any special reset
1410 // that needs to be done. We just re-acquire and
1412 Sleep(1000); // Pause a second...
1413 hr = Di_joystick->Acquire();
1420 memset(&joy_state, 0, sizeof(joy_state));
1424 hr = Di_joystick->GetDeviceState(sizeof(joy_state), &joy_state);
1425 if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) {
1426 // DirectInput is telling us that the input stream has
1427 // been interrupted. We aren't tracking any state
1428 // between polls, so we don't have any special reset
1429 // that needs to be done. We just re-acquire and
1431 Sleep(1000); // Pause a second...
1432 hr = Di_joystick->Acquire();
1438 if (SUCCEEDED(hr)) {
1441 if (joystick.axis_valid[5])
1442 axis[5] = joy_state.lRy;
1445 if (joystick.axis_valid[4])
1446 axis[4] = joy_state.lRx;
1449 if (joystick.axis_valid[3])
1450 axis[3] = joy_state.lRz;
1453 if (joystick.axis_valid[2])
1454 axis[2] = joy_state.lZ;
1457 if (joystick.axis_valid[1])
1458 axis[1] = joy_state.lY;
1461 if (joystick.axis_valid[0])
1462 axis[0] = joy_state.lX;