/* * 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.4 $ * $Date: 2006-09-20 05:05:07 $ * $Author: taylor $ * * Code to read the joystick * * $Log: not supported by cvs2svn $ * Revision 2.3 2004/07/26 20:47:33 Kazan * remove MCD complete * * 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 #include #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; EXECUTION_STATE last_exectution_state = 0; uint last_ssav_time = 0; // power management stuff, we need to handle this manually for joysticks { // give notification that we need both display and system (for multi) resources to remain available // NOTE: we'll have to restore the previous execution state before exiting this thread last_exectution_state = SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED); // turn of screen saver, but save the current timeout so that we can restore it later SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0, &last_ssav_time, 0); SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, 0, NULL, 0); } 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= 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= 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; }