Files
fs2open.github.com/code/io/joy.cpp
T
2004-07-26 20:47:56 +00:00

1476 lines
35 KiB
C++

/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
/*
* $Logfile: /Freespace2/code/Io/Joy.cpp $
* $Revision: 2.3 $
* $Date: 2004-07-26 20:47:33 $
* $Author: Kazan $
*
* Code to read the joystick
*
* $Log: not supported by cvs2svn $
* Revision 2.2 2004/07/12 16:32:51 Kazan
* MCD - define _MCD_CHECK to use memory tracking
*
* Revision 2.1 2002/08/01 01:41:06 penguin
* The big include file move
*
* Revision 2.0 2002/06/03 04:02:24 penguin
* Warpcore CVS sync
*
* Revision 1.1 2002/05/02 18:03:08 mharris
* Initial checkin - converted filenames and includes to lower case
*
*
* 5 7/15/99 9:20a Andsager
* FS2_DEMO initial checkin
*
* 4 6/02/99 6:18p Dave
* Fixed TNT lockup problems! Wheeeee!
*
* 3 10/09/98 2:57p Dave
* Starting splitting up OS stuff.
*
* 2 10/07/98 10:53a Dave
* Initial checkin.
*
* 1 10/07/98 10:49a Dave
*
* 61 5/24/98 12:56a Mike
* Put in debug code, but comment out, to check joystick sensitivity.
*
* 60 5/19/98 6:54p Lawrance
* Set default joystick sensitivity to max
*
* 59 5/13/98 7:14p Hoffoss
* Made invalid axis return center position value.
*
* 58 5/13/98 1:17a Hoffoss
* Added joystick axes configurability.
*
* 57 5/07/98 3:15p Hoffoss
* Fixed typo.
*
* 56 5/07/98 12:41p Hoffoss
* Changed code to make joystick sensitivity default to center of range.
*
* 55 5/06/98 12:02a Hoffoss
* Fixed throttle problems with joysticks.
*
* 54 5/05/98 8:38p Hoffoss
* Added sensitivity adjustment to options menu and made it save to pilot
* file.
*
* 53 5/04/98 11:08p Hoffoss
* Expanded on Force Feedback code, and moved it all into Joy_ff.cpp.
* Updated references everywhere to it.
*
* 52 5/03/98 6:02p Hoffoss
* Added DirectInput support for joystick (only works with v5.0, so only
* Win95).
*
* 51 5/01/98 5:59p Hoffoss
* Checking in code so I can switch back to NT to work on this instead
* (Win95, bah!)
*
* 50 4/30/98 12:12p Hoffoss
* Changed code to explicitly use calibrated values from Windows for
* joystick axis.
*
* 49 4/29/98 12:13a Lawrance
* Add function for reading down count without resetting internal count.
* Add hook to reset demo trailer timer.
*
* 48 4/25/98 12:02p Lawrance
* take out crit sec code around joystick read in joy_process
*
* 47 4/22/98 9:03p John
* Put critical section around all the joygetposex functions. Had to set
* up events to signal the polling thread to end at the end of the
* program.
*
* 46 4/17/98 3:07p Jim
* added debug code to test joystick
*
* 45 4/13/98 10:16a John
* Switched gettime back to timer_get_milliseconds, which is now thread
* safe.
*
* 44 4/12/98 11:08p Lawrance
* switch back to using gettime() in separate threads
*
* 43 4/12/98 5:31p Lawrance
* use timer_get_milliseconds() instead of gettime()
*
* 42 3/21/98 11:29a John
* Made joy_flush work when a button is held down
*
* 41 3/12/98 10:44a Hoffoss
* Whoops, should probably use the actual define rather than 0.
*
* 40 3/12/98 10:38a Hoffoss
* Changed joystick default to first slot in never set up in FSLaunch
*
* 39 3/11/98 5:27p Hoffoss
* Added to FreeSpace usage of joystick specified through FSLaunch.
*
* 38 3/09/98 4:44p Sandeep
*
* 37 3/07/98 5:23p Sandeep
*
* 35 3/06/98 11:12a Hoffoss
* Fixed joystick deadzone bug.
*
* 33 3/06/98 10:02a Hoffoss
* Made dead zone adjustable, and defaulted it to 10% instead of 5%.
*
* 36 3/07/98 4:50p John
* Added code to allow toggling force feedback on/off in setup
*
* 35 3/06/98 11:12a Hoffoss
* Fixed joystick deadzone bug.
*
* 33 3/06/98 10:02a Hoffoss
* Made dead zone adjustable, and defaulted it to 10% instead of 5%.
*
* 32 2/11/98 9:56p Jim
* allender: from sandeep on Jim's machine -- some force feedback stuff
*
* 31 1/29/98 11:04a Sandeep
*
* 30 1/27/98 8:40p Sandeep
*
* 29 1/19/98 6:15p John
* Fixed all my Optimized Build compiler warnings
*
* 28 1/08/98 6:35p Hoffoss
* Fixed joystick undefined detection.
*
* 27 1/08/98 3:48p Dan
* Fixed bug with joystick axis reading thinking it's undefined at
* extremes.
*
* 26 10/16/97 5:37p Lawrance
* change thread priority from THREAD_PRIORITY_TIME_CRITICAL to
* THREAD_PRIORITY_HIGHEST
*
* 25 10/09/97 10:15a Johnson
* try to init several times if joystick init fails
*
* 24 10/07/97 10:42a Johnson
* zero out JOYINFOEX struct before setting dwSize
*
* 23 10/06/97 5:54p Johnson
* ALAN: fix nasty bug where dwSize member of JOYINFOEX was not being set,
* resulting in random failure
*
* 22 9/15/97 11:42p Lawrance
* change button_info to joy_button_info to avoid name conflict
*
* 21 8/07/97 11:26p Lawrance
* add support for 4th axis (rudder)
*
* 20 7/29/97 5:30p Lawrance
* move gettime() from keyboard module to timer module
*
* 19 7/11/97 11:43a Lawrance
* fix bug with joy_up_count
*
* 18 7/10/97 12:29a Lawrance
* fix problem with NT not recognizing an axis that was set under 95
*
* 17 7/09/97 11:41p Lawrance
* added throttle and hat support
*
* 16 6/19/97 9:50a John
* fixed bug where joy_close getting called out of order doesn't matter.
*
* 15 5/18/97 2:40p Lawrance
* added joy_get_caps()
*
* 14 4/22/97 10:56a John
* fixed some resource leaks.
*
* 13 2/27/97 2:23p Lawrance
* took out debug stmts
*
* 12 2/27/97 10:04a Lawrance
* fixed bug that was causing all joy buttons but 0 to be read incorrectly
*
* 11 2/17/97 5:18p John
* Added a bunch of RCS headers to a bunch of old files that don't have
* them.
*
* $NoKeywords: $
*/
#include <windows.h>
#include <windowsx.h>
#include "globalincs/pstypes.h"
#include "io/joy.h"
#include "math/fix.h"
#include "io/key.h"
#include "io/timer.h"
#include "osapi/osregistry.h"
#include "io/joy_ff.h"
#include "directx/vdinput.h"
#include "osapi/osapi.h"
#define PRECALIBRATED 1
static int Joy_inited = 0;
int joy_num_sticks = 0;
int Dead_zone_size = 10;
int Cur_joystick = -1; // joystick used for input or -1
int Joy_sensitivity = 9;
CRITICAL_SECTION joy_lock;
HANDLE joy_thread = NULL;
DWORD joy_thread_id;
int joy_pollrate = 1000 / 18; // poll at 18Hz
HANDLE Joy_tell_thread_to_end_event = NULL;
HANDLE Joy_thread_says_its_done_event = NULL;
static int Joy_last_x_reading = 0;
static int Joy_last_y_reading = 0;
int Joy_di_inited = 0;
static LPDIRECTINPUT Di_joystick_obj = NULL;
static LPDIRECTINPUTDEVICE2 Di_joystick = NULL;
typedef struct joy_button_info {
int actual_state; // Set if the button is physically down
int state; // Set when the button goes from up to down, cleared on down to up. Different than actual_state after a flush.
int down_count;
int up_count;
int down_time;
uint last_down_check; // timestamp in milliseconds of last
} joy_button_info;
Joy_info joystick;
joy_button_info joy_buttons[JOY_TOTAL_BUTTONS];
int joy_di_init();
int joy_di_shutdown();
int joystick_read_raw_axis_di(int num_axes, int *axis);
// --------------------------------------------------------------
// joy_flush()
//
// Clear the state of the joystick.
//
void joy_flush()
{
int i;
joy_button_info *bi;
if ( joy_num_sticks < 1 ) return;
EnterCriticalSection(&joy_lock);
for ( i = 0; i < JOY_TOTAL_BUTTONS; i++) {
bi = &joy_buttons[i];
bi->state = 0;
bi->down_count = 0;
bi->up_count = 0;
bi->down_time = 0;
bi->last_down_check = timer_get_milliseconds();
}
LeaveCriticalSection(&joy_lock);
}
// --------------------------------------------------------------
// joy_process()
//
// Runs as a separate thread, and updates the state of the joystick
//
DWORD joy_process(DWORD lparam)
{
MMRESULT rs;
JOYINFOEX ji;
int i,state;
joy_button_info *bi;
for ( i = 0; i < JOY_TOTAL_BUTTONS; i++) {
bi = &joy_buttons[i];
bi->actual_state = 0; // Don't set in flush code!
bi->state = 0;
bi->down_count = 0;
bi->up_count = 0;
bi->down_time = 0;
bi->last_down_check = timer_get_milliseconds();
}
while (1) {
// Wait for the thread to be signaled to end or 1/18th of a second to pass...
if ( WaitForSingleObject( Joy_tell_thread_to_end_event, joy_pollrate )==WAIT_OBJECT_0) {
break;
}
memset(&ji, 0, sizeof(ji));
ji.dwSize = sizeof(ji);
// ji.dwFlags = JOY_RETURNBUTTONS | JOY_RETURNRAWDATA;
ji.dwFlags = JOY_RETURNALL;
EnterCriticalSection(&joy_lock);
uint joy_state = 0;
if (Cur_joystick >= 0) {
rs = joyGetPosEx(Cur_joystick, &ji);
// If there's an error, assume all buttons down.
if (rs == JOYERR_NOERROR) {
joy_state = ji.dwButtons;
}
}
// Process ji.dwButtons
for (i=0; i<JOY_TOTAL_BUTTONS; i++) {
state = 0;
if (i < JOY_NUM_BUTTONS) {
state = joy_state & (1<<i);
} else {
// check for hat presses, which act like buttons
switch (i) {
case JOY_HATBACK:
if (ji.dwPOV == JOY_POVBACKWARD)
state = 1;
break;
case JOY_HATFORWARD:
if (ji.dwPOV == JOY_POVFORWARD)
state = 1;
break;
case JOY_HATLEFT:
if (ji.dwPOV == JOY_POVLEFT)
state = 1;
break;
case JOY_HATRIGHT:
if (ji.dwPOV == JOY_POVRIGHT)
state = 1;
break;
default:
Int3(); // should never happen
break;
} // end switch
} // end if
if (state != joy_buttons[i].actual_state) {
// Button position physically changed.
joy_buttons[i].actual_state = state;
if ( state ) {
// went from up to down
joy_buttons[i].down_count++;
joy_buttons[i].down_time = 0;
joy_buttons[i].state = 1;
////////////////////////////
/// SOMETHING TERRIBLE IS ABOUT TO HAPPEN. I FEEL THIS IS NECESSARY FOR THE DEMO, SINCE
/// I DON'T WANT TO CALL CRITICAL SECTION CODE EACH FRAME TO CHECK ALL THE JOYSTICK BUTTONS.
/// PLEASE SEE ALAN FOR MORE INFORMATION.
////////////////////////////
#ifdef FS2_DEMO
{
extern void demo_reset_trailer_timer();
demo_reset_trailer_timer();
}
#endif
////////////////////////////
/// IT'S OVER. SEE, IT WASN'T SO BAD RIGHT? IT'S IS VERY UGLY LOOKING, I KNOW.
////////////////////////////
} else {
// went from down to up
if ( joy_buttons[i].state ) {
joy_buttons[i].up_count++;
}
joy_buttons[i].state = 0;
}
} else {
// Didn't move... increment time down if down.
if (joy_buttons[i].state) {
joy_buttons[i].down_time += joy_pollrate;
}
}
} // end for
LeaveCriticalSection(&joy_lock);
}
SetEvent(Joy_thread_says_its_done_event);
return 0;
}
// --------------------------------------------------------------
// joy_close()
//
// Close the joystick system. Should be called at game exit.
//
void joy_close()
{
if (!Joy_inited)
return;
// joy_di_shutdown();
Joy_inited = 0;
joy_num_sticks = 0;
// Tell joystick polling thread to end
SetEvent(Joy_tell_thread_to_end_event);
// Wait for it to end
if ( WaitForSingleObject( Joy_thread_says_its_done_event, 5000 )==WAIT_TIMEOUT) { //INFINITE );
mprintf(( "Joy end thread wait timeout!\n" ));
}
CloseHandle(Joy_tell_thread_to_end_event);
CloseHandle(Joy_thread_says_its_done_event);
// It is now safe to release any resources use by the polling thread.
DeleteCriticalSection( &joy_lock );
if (joy_thread) {
CloseHandle(joy_thread);
joy_thread = NULL;
}
joy_ff_shutdown();
}
// --------------------------------------------------------------
// joy_get_caps()
//
// Determine the capabilities of the attached joysticks.
//
void joy_get_caps(int max)
{
JOYCAPS JoyCaps;
int j;
for (j=0; j<JOY_NUM_AXES; j++)
joystick.axis_valid[j] = 0;
for (j=JOYSTICKID1; j<JOYSTICKID1+max; j++) {
if (JOYERR_NOERROR == joyGetDevCaps (j, &JoyCaps, sizeof(JoyCaps))) {
nprintf(("JOYSTICK", "Joystick #%d: %s\n", j - JOYSTICKID1 + 1, JoyCaps.szPname));
if (j == Cur_joystick) {
joystick.axis_valid[0] = joystick.axis_valid[1] = 1;
if (JoyCaps.wCaps & JOYCAPS_HASZ)
joystick.axis_valid[2] = 1;
if (JoyCaps.wCaps & JOYCAPS_HASR)
joystick.axis_valid[3] = 1;
if (JoyCaps.wCaps & JOYCAPS_HASU)
joystick.axis_valid[4] = 1;
if (JoyCaps.wCaps & JOYCAPS_HASV)
joystick.axis_valid[5] = 1;
}
}
}
}
int joy_get_scaled_reading(int raw, int axn);
int joy_get_unscaled_reading(int raw, int axn);
DCF(joytest, "Test joystick")
{
if (Dc_command) {
while (!keyd_pressed[KEY_ESC]) {
int x, y, axis[JOY_NUM_AXES];
if (joy_num_sticks < 1)
return;
joystick_read_raw_axis(JOY_NUM_AXES, axis);
x = joy_get_scaled_reading(axis[0], 0);
y = joy_get_scaled_reading(axis[1], 1);
mprintf(("X=%5d Y=%5d Calibrated X=%6d Y=%6d\n", axis[0], axis[1], x, y));
Sleep(100);
}
}
}
DCF(joytest2, "Test joystick (extended)")
{
if (Dc_command) {
while (!keyd_pressed[KEY_ESC]) {
int x, y, z, r, axis[JOY_NUM_AXES];
if (joy_num_sticks < 1)
return;
joystick_read_raw_axis(JOY_NUM_AXES, axis);
x = joy_get_scaled_reading(axis[0], 0);
y = joy_get_scaled_reading(axis[1], 1);
z = joy_get_unscaled_reading(axis[2], 2);
r = joy_get_scaled_reading(axis[3], 3);
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));
Sleep(100);
}
}
}
// --------------------------------------------------------------
// joy_init()
//
// Initialize the joystick system. This is called once at game startup.
//
int joy_init()
{
int i, n, count;
MMRESULT rs;
JOYINFOEX ji;
if (Joy_inited)
return 0;
Joy_inited = 1;
n = joyGetNumDevs();
Cur_joystick = os_config_read_uint(NULL, "CurrentJoystick", JOYSTICKID1);
joy_get_caps(n);
if (n < 1) {
mprintf(("No joystick driver detected\n"));
return 0;
}
InitializeCriticalSection(&joy_lock);
atexit(joy_close);
joy_flush();
joy_num_sticks = 0;
memset(&ji, 0, sizeof(ji));
ji.dwSize = sizeof(ji);
ji.dwFlags = JOY_RETURNALL;
if (Cur_joystick >= 0) {
// AL: test code to try and find out why this call fails the first time
rs = 0;
for (count=0; count<20; count++) {
rs = joyGetPosEx(Cur_joystick, &ji);
if (rs == JOYERR_NOERROR)
break;
}
if (rs == JOYERR_NOERROR) {
joy_num_sticks++;
Joy_tell_thread_to_end_event = CreateEvent( NULL, FALSE, FALSE, NULL );
Joy_thread_says_its_done_event = CreateEvent( NULL, FALSE, FALSE, NULL );
joy_thread = CreateThread(NULL,
1024 * 32,
(LPTHREAD_START_ROUTINE) joy_process,
NULL,
0,
&joy_thread_id);
// SetThreadPriority(joy_thread, THREAD_PRIORITY_TIME_CRITICAL - 1);
SetThreadPriority(joy_thread, THREAD_PRIORITY_HIGHEST);
}
}
mprintf(("Windoze reported %d joysticks, we found %d\n", n, joy_num_sticks));
#ifdef PRECALIBRATED
// Fake a calibration
if (joy_num_sticks > 0) {
for (i=0; i<4; i++) {
joystick.axis_min[i] = 0;
joystick.axis_center[i] = 32768;
joystick.axis_max[i] = 65536;
}
}
#else
// Fake a calibration
if (joy_num_sticks > 0) {
joy_set_cen();
for (i=0; i<4; i++) {
joystick.axis_min[i] = 0;
joystick.axis_max[i] = joystick.axis_center[i]*2;
}
}
#endif
joy_ff_init();
// joy_di_init();
return joy_num_sticks;
}
// --------------------------------------------------------------
// joy_cheap_cal()
//
// Manual calibrate joystick routine
//
void joy_cheap_cal()
{
if ( joy_num_sticks < 1 ) return;
while(1) {
Sleep(50);
if ( key_inkey()) break;
if (joy_down_count(0)) break;
mprintf(( "Move stick to upper-left and hit button\n" ));
}
joy_set_ul();
while(1) {
Sleep(50);
if ( key_inkey()) break;
if (joy_down_count(0)) break;
mprintf(( "Move stick to lower-right and hit button\n" ));
}
joy_set_lr();
while(1) {
Sleep(50);
if ( key_inkey()) break;
if (joy_down_count(0)) break;
mprintf(( "Move stick to center and hit button\n" ));
}
joy_set_cen();
}
// --------------------------------------------------------------
// joy_get_pos_old()
//
// Get the position of the joystick axes
//
int joy_get_pos_old(int * x, int * y )
{
MMRESULT rs;
JOYINFOEX ji;
if ( joy_num_sticks < 1 ) {
if (x) *x = 0;
if (y) *y = 0;
return 0;
}
memset(&ji, 0, sizeof(ji));
ji.dwSize = sizeof(ji);
#ifdef PRECALIBRATED
//JOY_USEDEADZONE|
//JOY_RETURNCENTERED|
//JOY_RETURNX|JOY_RETURNY|JOY_RETURNRAWDATA;
ji.dwFlags = JOY_RETURNALL;
#else
ji.dwFlags = JOY_CAL_READXYONLY;
#endif
if (Cur_joystick >= 0) {
EnterCriticalSection(&joy_lock);
rs = joyGetPosEx(Cur_joystick, &ji);
LeaveCriticalSection(&joy_lock);
if (rs == JOYERR_NOERROR) {
#if 1
if (x)
*x = ji.dwXpos;
if (y)
*y = ji.dwYpos;
#else
if (x) {
*x = (ji.dwXpos - 32768) * 2;
if (*x < -65536)
*x = -65536;
else if (*x > 65536)
*x = 65536;
}
if (y) {
*y = (ji.dwYpos - 32768) * 2;
if (*y < -65536)
*y = -65536;
else if (*y > 65536)
*y = 65536;
}
#endif
return 1;
}
}
if (x)
*x = 0;
if (y)
*y = 0;
return 0;
}
// --------------------------------------------------------------
// joy_down_count()
//
// Return the number of times the joystick button has gone down since
// joy_down_count() was last called
//
// input: btn => button number to check
// reset_count => (default 1): if true reset down_count
//
// returns: number of times button 'btn' has gone down since last call
//
int joy_down_count(int btn, int reset_count)
{
int tmp;
if ( joy_num_sticks < 1 ) return 0;
if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0;
EnterCriticalSection(&joy_lock);
tmp = joy_buttons[btn].down_count;
if ( reset_count ) {
joy_buttons[btn].down_count = 0;
}
LeaveCriticalSection(&joy_lock);
return tmp;
}
// --------------------------------------------------------------
// joy_down()
//
// Return the state of button number 'btn'
//
// input: btn => button number to check
//
// returns: 0 => not pressed
// 1 => pressed
//
int joy_down(int btn)
{
int tmp;
if ( joy_num_sticks < 1 ) return 0;
if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS )) return 0;
EnterCriticalSection(&joy_lock);
tmp = joy_buttons[btn].state;
LeaveCriticalSection(&joy_lock);
return tmp;
}
// --------------------------------------------------------------
// joy_up_count()
//
// Return the number of times the joystick button has gone up since
// joy_up_count() was last called
//
// input: btn => button number to check
//
// returns: number of times button 'btn' has gone up since last call
//
int joy_up_count(int btn)
{
int tmp;
if ( joy_num_sticks < 1 ) return 0;
if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0;
EnterCriticalSection(&joy_lock);
tmp = joy_buttons[btn].up_count;
joy_buttons[btn].up_count = 0;
LeaveCriticalSection(&joy_lock);
return tmp;
}
// --------------------------------------------------------------
// joy_down_time()
//
// Return a number between 0 and 1. This number represents the percentage
// time that the joystick button has been down since it was last checked
//
// input: btn => button number to check
// returns: value between 0 and 1
//
float joy_down_time(int btn)
{
float rval;
unsigned int now;
joy_button_info *bi;
if ( joy_num_sticks < 1 ) return 0.0f;
if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0.0f;
bi = &joy_buttons[btn];
EnterCriticalSection(&joy_lock);
now = timer_get_milliseconds();
if ( bi->down_time == 0 && joy_down(btn) ) {
bi->down_time += joy_pollrate;
}
if ( (now - bi->last_down_check) > 0)
rval = i2fl(bi->down_time) / (now - bi->last_down_check);
else
rval = 0.0f;
bi->down_time = 0;
bi->last_down_check = now;
LeaveCriticalSection(&joy_lock);
if (rval < 0)
rval = 0.0f;
if (rval > 1)
rval = 1.0f;
return rval;
}
// --------------------------------------------------------------
// joy_get_cal_vals()
//
// Get the calibrated min, center, and max for all axes
//
// input: axis_min => OUTPUT PARAMETER: array of at least 4 ints to hold min axis values
// axis_center => OUTPUT PARAMETER: array of at least 4 ints to hold center axis values
// axis_min => OUTPUT PARAMETER: array of at least 4 ints to hold max axis values
//
void joy_get_cal_vals(int *axis_min, int *axis_center, int *axis_max)
{
int i;
for ( i = 0; i < 4; i++) {
axis_min[i] = joystick.axis_min[i];
axis_center[i] = joystick.axis_center[i];
axis_max[i] = joystick.axis_max[i];
}
}
// --------------------------------------------------------------
// joy_set_cal_vals()
//
// Get the calibrated min, center, and max for all axes
//
// input: axis_min => array of at 4 ints that hold min axis values
// axis_center => array of at 4 ints that hold center axis values
// axis_min => array of at 4 ints that hold max axis values
//
void joy_set_cal_vals(int *axis_min, int *axis_center, int *axis_max)
{
int i;
for (i=0; i<4; i++) {
joystick.axis_min[i] = axis_min[i];
joystick.axis_center[i] = axis_center[i];
joystick.axis_max[i] = axis_max[i];
}
}
// --------------------------------------------------------------
// joystick_read_raw_axis()
//
// Read the raw axis information for a specified number of axes.
//
// input: num_axes => number of axes to read. Note the axes go in the following order:
// dwXpos
// dwYpos
// dwZpos (throttle)
// dwRpos (rudder)
// dwUpos (5th axis)
// dwVpos (6th axis)
//
// axis => an array of at least 4 ints to hold axis data
//
int joystick_read_raw_axis(int num_axes, int *axis)
{
MMRESULT rs;
JOYINFOEX ji;
int i;
Assert(num_axes <= JOY_NUM_AXES);
for (i=0; i<num_axes; i++)
axis[i] = 32768;
// first, try reading with DirectInput, if we can
if (Joy_di_inited)
return joystick_read_raw_axis_di(num_axes, axis);
// No DirectInput joystick, fall back on older stuff
if (joy_num_sticks < 1)
return 0;
memset(&ji, 0, sizeof(ji));
ji.dwSize = sizeof(ji);
#ifdef PRECALIBRATED
ji.dwFlags = JOY_RETURNALL;
#else
ji.dwFlags = JOY_RETURNRAWDATA;
#endif
switch (num_axes) {
case 6:
ji.dwFlags |= JOY_RETURNV;
case 5:
ji.dwFlags |= JOY_RETURNU;
case 4:
ji.dwFlags |= JOY_RETURNR;
case 3:
ji.dwFlags |= JOY_RETURNZ;
case 2:
ji.dwFlags |= JOY_RETURNY;
case 1:
ji.dwFlags |= JOY_RETURNX;
break;
default:
Int3();
break;
}
if (Cur_joystick >= 0) {
EnterCriticalSection(&joy_lock);
rs = joyGetPosEx(Cur_joystick, &ji);
LeaveCriticalSection(&joy_lock);
} else
return 0;
if (rs != JOYERR_NOERROR)
return 0;
switch (num_axes) {
case 6:
if (joystick.axis_valid[5])
axis[5] = ji.dwVpos;
case 5:
if (joystick.axis_valid[4])
axis[4] = ji.dwUpos;
case 4:
if (joystick.axis_valid[3])
axis[3] = ji.dwRpos;
case 3:
if (joystick.axis_valid[2])
axis[2] = ji.dwZpos;
case 2:
if (joystick.axis_valid[1])
axis[1] = ji.dwYpos;
case 1:
if (joystick.axis_valid[0])
axis[0] = ji.dwXpos;
break;
default:
Int3();
break;
}
return 1;
}
// --------------------------------------------------------------
// joy_set_ul()
//
// Get the minimum axis information (namely, joystick in upper left).
// This is called by a manual calibration routine.
//
// NOTE: sets the values in joystick.axis_min[]
//
void joy_set_ul()
{
joystick_read_raw_axis( 2, joystick.axis_min );
}
// --------------------------------------------------------------
// joy_set_lr()
//
// Get the maximum axis information (namely, joystick in lower right).
// This is called by a manual calibration routine.
//
// NOTE: sets the values in joystick.axis_max[]
//
void joy_set_lr()
{
joystick_read_raw_axis( 2, joystick.axis_max );
}
// --------------------------------------------------------------
// joy_set_cen()
//
// Get the center axis information (namely, joystick in dead zone).
// This is called by a manual calibration routine.
//
// NOTE: sets the values in joystick.axis_center[]
//
void joy_set_cen()
{
joystick_read_raw_axis( 2, joystick.axis_center );
}
int joy_get_unscaled_reading(int raw, int axn)
{
int rng;
// Make sure it's calibrated properly.
if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5)
return 0;
if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5)
return 0;
rng = joystick.axis_max[axn] - joystick.axis_min[axn];
raw -= joystick.axis_min[axn]; // adjust for linear range starting at 0
// cap at limits
if (raw < 0)
raw = 0;
if (raw > rng)
raw = rng;
return (int) ((unsigned int) raw * (unsigned int) F1_0 / (unsigned int) rng); // convert to 0 - F1_0 range.
}
// --------------------------------------------------------------
// joy_get_scaled_reading()
//
// input: raw => the raw value for an axis position
// axn => axis number, numbered starting at 0
//
// return: joy_get_scaled_reading will return a value that represents
// the joystick pos from -1 to +1 for the specified axis number 'axn', and
// the raw value 'raw'
//
int joy_get_scaled_reading(int raw, int axn)
{
int x, d, dead_zone, rng;
float percent, sensitivity_percent, non_sensitivity_percent;
// Make sure it's calibrated properly.
if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5)
return 0;
if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5)
return 0;
raw -= joystick.axis_center[axn];
dead_zone = (joystick.axis_max[axn] - joystick.axis_min[axn]) * Dead_zone_size / 100;
if (raw < -dead_zone) {
rng = joystick.axis_center[axn] - joystick.axis_min[axn] - dead_zone;
d = -raw - dead_zone;
} else if (raw > dead_zone) {
rng = joystick.axis_max[axn] - joystick.axis_center[axn] - dead_zone;
d = raw - dead_zone;
} else
return 0;
if (d > rng)
d = rng;
Assert(Joy_sensitivity >= 0 && Joy_sensitivity <= 9);
// compute percentages as a range between 0 and 1
sensitivity_percent = (float) Joy_sensitivity / 9.0f;
non_sensitivity_percent = (float) (9 - Joy_sensitivity) / 9.0f;
// find percent of max axis is at
percent = (float) d / (float) rng;
// work sensitivity on axis value
percent = (percent * sensitivity_percent + percent * percent * percent * percent * percent * non_sensitivity_percent);
x = (int) ((float) F1_0 * percent);
//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));
if (raw < 0)
return -x;
return x;
}
// --------------------------------------------------------------
// joy_get_pos()
//
// input: x => OUTPUT PARAMETER: x-axis position of stick (-1 to 1)
// y => OUTPUT PARAMETER: y-axis position of stick (-1 to 1)
// z => OUTPUT PARAMETER: z-axis (throttle) position of stick (-1 to 1)
// r => OUTPUT PARAMETER: rudder position of stick (-1 to 1)
//
// return: success => 1
// failure => 0
//
int joy_get_pos(int *x, int *y, int *z, int *rx)
{
int axis[JOY_NUM_AXES];
if (x) *x = 0;
if (y) *y = 0;
if (z) *z = 0;
if (rx) *rx = 0;
if (joy_num_sticks < 1) return 0;
joystick_read_raw_axis( 6, axis );
// joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1
if (x && joystick.axis_valid[0])
*x = joy_get_scaled_reading(axis[0], 0);
if (y && joystick.axis_valid[1])
*y = joy_get_scaled_reading(axis[1], 1);
if (z && joystick.axis_valid[2])
*z = joy_get_unscaled_reading(axis[2], 2);
if (rx && joystick.axis_valid[3])
*rx = joy_get_scaled_reading(axis[3], 3);
if (x)
Joy_last_x_reading = *x;
if (y)
Joy_last_x_reading = *y;
return 1;
}
// change in joy position since last call
void joy_get_delta(int *dx, int *dy)
{
static int old_joy_x = 0;
static int old_joy_y = 0;
if ( !Joy_inited ) {
*dx = *dy = 0;
return;
}
*dx = Joy_last_x_reading - old_joy_x;
*dy = Joy_last_y_reading - old_joy_y;
old_joy_x = Joy_last_x_reading;
old_joy_y = Joy_last_y_reading;
}
//// This is the DirectInput joystick stuff
GUID Di_joy_guid;
int Di_joy_guid_valid = 0;
BOOL CALLBACK joy_di_enum(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
{
char buf[64];
nprintf(("Joystick", "Joystick detected: %s (%s)\n", lpddi->tszInstanceName, lpddi->tszProductName));
sprintf(buf, "Joystick %d", Cur_joystick + 1);
if (!stricmp(buf, lpddi->tszInstanceName)) {
Di_joy_guid = lpddi->guidInstance;
Di_joy_guid_valid = 1;
nprintf(("Joystick", " (Selected joystick)\n"));
}
return DIENUM_CONTINUE;
}
/*
BOOL FAR PASCAL InitJoystickInput(LPCDIDEVICEINSTANCE pdinst, LPVOID pvRef)
{
LPDIRECTINPUT pdi = pvRef;
LPDIRECTINPUTDEVICE pdev;
DIPROPRANGE diprg;
// create the DirectInput joystick device
if(pdi->lpVtbl->CreateDevice(pdi, &pdinst->guidInstance, &pdev, NULL) != DI_OK)
{
OutputDebugString("IDirectInput::CreateDevice FAILED\n");
return DIENUM_CONTINUE;
}
// set joystick data format
if (pdev->lpVtbl->SetDataFormat(pdev, &c_dfDIJoystick) != DI_OK)
{
OutputDebugString("IDirectInputDevice::SetDataFormat FAILED\n");
pdev->lpVtbl->Release(pdev);
return DIENUM_CONTINUE;
}
// set the cooperative level
if (pdev->lpVtbl->SetCooperativeLevel(pdev, hWndMain,
DISCL_NONEXCLUSIVE | DISCL_FOREGROUND) != DI_OK)
{
OutputDebugString("IDirectInputDevice::SetCooperativeLevel FAILED\n");
pdev->lpVtbl->Release(pdev);
return DIENUM_CONTINUE;
}
// set X-axis range to (-1000 ... +1000)
// This lets us test against 0 to see which way the stick is pointed.
diprg.diph.dwSize = sizeof(diprg);
diprg.diph.dwHeaderSize = sizeof(diprg.diph);
diprg.diph.dwObj = DIJOFS_X;
diprg.diph.dwHow = DIPH_BYOFFSET;
diprg.lMin = -1000;
diprg.lMax = +1000;
if (pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK)
{
OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n");
pdev->lpVtbl->Release(pdev);
return FALSE;
}
//
// And again for Y-axis range
//
diprg.diph.dwObj = DIJOFS_Y;
if (pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK)
{
OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n");
pdev->lpVtbl->Release(pdev);
return FALSE;
}
// set X axis dead zone to 50% (to avoid accidental turning)
// Units are ten thousandths, so 50% = 5000/10000.
if (SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_X, DIPH_BYOFFSET, 5000) != DI_OK)
{
OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n");
pdev->lpVtbl->Release(pdev);
return FALSE;
}
// set Y axis dead zone to 50% (to avoid accidental thrust)
// Units are ten thousandths, so 50% = 5000/10000.
if (SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_Y, DIPH_BYOFFSET, 5000) != DI_OK)
{
OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n");
pdev->lpVtbl->Release(pdev);
return FALSE;
}
// Add it to our list of devices. If AddInputDevice succeeds,
// he will do an AddRef.
AddInputDevice(pdev, pdinst);
hRes = pdev->lpVtbl->QueryInterface(
pdev, &IID_IDirectInputDevice2,
(LPVOID *)&g_rgpdevFound[g_cpdevFound]);
pdev->lpVtbl->Release(pdev);
return DIENUM_CONTINUE;
}
*/
int joy_di_init()
{
HRESULT hr;
LPDIRECTINPUTDEVICE pdev;
Joy_di_inited = 0;
hr = DirectInputCreate(GetModuleHandle(NULL), 0x500, &Di_joystick_obj, NULL);
if (FAILED(hr)) {
mprintf(( "DirectInputCreate() failed!\n" ));
return -1;
}
Di_joy_guid_valid = 0;
hr = Di_joystick_obj->EnumDevices(DIDEVTYPE_JOYSTICK, joy_di_enum, Di_joystick_obj, DIEDFL_ATTACHEDONLY);
if (FAILED(hr)) {
mprintf(( "EnumDevice() failed!\n" ));
return -1;
}
if (!Di_joy_guid_valid) {
mprintf(( "Correct joystick not found.\n" ));
return -1;
}
hr = Di_joystick_obj->CreateDevice(Di_joy_guid, &pdev, NULL);
if (FAILED(hr)) {
mprintf(( "CreateDevice() failed!\n" ));
return -1;
}
hr = pdev->SetDataFormat(&c_dfDIJoystick);
if (FAILED(hr)) {
mprintf(( "SetDataFormat() failed!\n" ));
if (hr == DIERR_ACQUIRED)
mprintf(( " (reason: DIERR_ACQUIRED)\n" ));
if (hr == DIERR_INVALIDPARAM)
mprintf(( " (reason: DIERR_INVALIDPARAM)\n" ));
if (hr == DIERR_NOTINITIALIZED)
mprintf(( " (reason: DIERR_NOTINITIALIZED)\n" ));
pdev->Release();
return -1;
}
hr = pdev->SetCooperativeLevel((HWND) os_get_window(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr)) {
mprintf(( "SetCooperativeLevel() failed!\n" ));
if (hr == DIERR_ACQUIRED)
mprintf(( " (reason: DIERR_ACQUIRED)\n" ));
if (hr == DIERR_INVALIDPARAM)
mprintf(( " (reason: DIERR_INVALIDPARAM)\n" ));
if (hr == DIERR_NOTINITIALIZED)
mprintf(( " (reason: DIERR_NOTINITIALIZED)\n" ));
pdev->Release();
return -1;
}
hr = pdev->QueryInterface(IID_IDirectInputDevice2, (LPVOID *) &Di_joystick);
if (FAILED(hr)) {
pdev->Release();
return -1;
}
Di_joystick->Acquire();
pdev->Release();
Joy_di_inited = 1;
nprintf(("Joystick", "DirectInput initialization of joystick succeeded\n"));
return 0;
}
int joy_di_shutdown()
{
// Destroy any lingering IDirectInputDevice object.
if (Di_joystick) {
// Unacquire the device one last time just in case we got really confused
// and tried to exit while the device is still acquired.
Di_joystick->Unacquire();
Di_joystick->Release();
Di_joystick = NULL;
}
// Destroy any lingering IDirectInput object.
if (Di_joystick_obj) {
Di_joystick_obj->Release();
Di_joystick_obj = NULL;
}
Joy_di_inited = 0;
return 0;
}
int joystick_read_raw_axis_di(int num_axes, int *axis)
{
int repeat = 1;
HRESULT hr = 0;
DIJOYSTATE joy_state;
if (!Joy_di_inited)
return 0;
repeat = 1;
while (repeat) {
repeat = 0;
hr = Di_joystick->Poll();
if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) {
// DirectInput is telling us that the input stream has
// been interrupted. We aren't tracking any state
// between polls, so we don't have any special reset
// that needs to be done. We just re-acquire and
// try again.
Sleep(1000); // Pause a second...
hr = Di_joystick->Acquire();
if (SUCCEEDED(hr))
repeat = 1;
}
}
repeat = 1;
memset(&joy_state, 0, sizeof(joy_state));
while (repeat) {
repeat = 0;
hr = Di_joystick->GetDeviceState(sizeof(joy_state), &joy_state);
if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) {
// DirectInput is telling us that the input stream has
// been interrupted. We aren't tracking any state
// between polls, so we don't have any special reset
// that needs to be done. We just re-acquire and
// try again.
Sleep(1000); // Pause a second...
hr = Di_joystick->Acquire();
if (SUCCEEDED(hr))
repeat = 1;
}
}
if (SUCCEEDED(hr)) {
switch (num_axes) {
case 6:
if (joystick.axis_valid[5])
axis[5] = joy_state.lRy;
case 5:
if (joystick.axis_valid[4])
axis[4] = joy_state.lRx;
case 4:
if (joystick.axis_valid[3])
axis[3] = joy_state.lRz;
case 3:
if (joystick.axis_valid[2])
axis[2] = joy_state.lZ;
case 2:
if (joystick.axis_valid[1])
axis[1] = joy_state.lY;
case 1:
if (joystick.axis_valid[0])
axis[0] = joy_state.lX;
break;
}
}
return 1;
}