Changes to input system:

- Joystick axis saturation values can now range from 1-200 rather than just 1-100.  This lets the user make axis less sensitive, if required.
- Added joystick axes calibration menus to -config-inputs.
- Moved DirectInput & XInput feedback settings out of CDirectInputSystem and into COSDConfig (so that they can be configured per-game).
- DirectInput mode now only grabs background access to keyboard, mice and joystick when configuring inputs, otherwise it only grabs foreground access.
Other changes:
- Main.cpp now ensures all Model3 threads are paused before shutting down.
This commit is contained in:
Nik Henson 2011-09-18 21:44:40 +00:00
parent f0697dbc84
commit b9430cd988
11 changed files with 525 additions and 161 deletions

View file

@ -169,6 +169,12 @@ void CInput::WriteToINIFile(CINIFile *ini, const char *section)
ini->Set(section, key, m_mapping);
}
void CInput::InputSystemChanged()
{
// If input system or its settings have changed, then force recreation of source
CreateSource();
}
bool CInput::Configure(bool append, const char *escapeMapping)
{
char mapping[MAX_MAPPING_LENGTH];

View file

@ -161,6 +161,8 @@ public:
*/
virtual void WriteToINIFile(CINIFile *ini, const char *section);
void InputSystemChanged();
/*
* Returns true if the input is a UI input.
*/

View file

@ -24,9 +24,17 @@
*
* Implementation of CInputSystem, the base input system class.
*/
// TODO
// - switch to using more C++ strings
// - simplify handling of configuration settings because it is a mess
// - think more about where config, calibrate and debug methods should go - OSD classes, CInputs or here?
// - open up API to allow direct access to keyboard, mouse and joystick values
// - add GetKey method that is easier to use than reading keyboard with ReadMapping
#include "Supermodel.h"
#include <cmath>
#include <string>
#include <algorithm>
#include <vector>
@ -377,6 +385,11 @@ JoyPartsStruct CInputSystem::s_joyParts[] =
const char *CInputSystem::s_axisNames[] = { "X", "Y", "Z", "RX", "RY", "RZ" };
const char *CInputSystem::GetDefaultAxisName(int axisNum)
{
return (axisNum >= 0 && axisNum < 6 ? s_axisNames[axisNum] : "");
}
CInputSystem::CInputSystem(const char *systemName) :
name(systemName), m_dispX(0), m_dispY(0), m_dispW(0), m_dispH(0), m_grabMouse(false)
{
@ -1523,6 +1536,8 @@ bool CInputSystem::Initialize()
// Create cache to hold input sources
CreateSourceCache();
GrabMouse();
return true;
}
@ -1663,20 +1678,22 @@ bool CInputSystem::ReadMapping(char *buffer, unsigned bufSize, bool fullAxisOnly
{
// Map given escape mapping to an input source
CInputSource *escape = ParseSource(escapeMapping);
escape->Acquire();
if (escape)
escape->Acquire();
string badMapping;
string mapping;
vector<CInputSource*> badSources;
vector<CInputSource*> sources;
bool mseCentered = false;
bool cancelled = false;
// See which sources activated to begin with and from here on ignore these (this stops badly calibrated axes that are constantly "active"
// from preventing the user from exiting read loop)
if (!Poll())
{
escape->Release();
return false;
cancelled = true;
goto Finish;
}
CheckAllSources(readFlags, fullAxisOnly, mseCentered, badSources, badMapping, sources);
@ -1687,25 +1704,25 @@ bool CInputSystem::ReadMapping(char *buffer, unsigned bufSize, bool fullAxisOnly
// Poll inputs
if (!Poll())
{
escape->Release();
return false;
cancelled = true;
goto Finish;
}
// Check if escape source was triggered
if (escape != NULL && escape->IsActive())
if (escape && escape->IsActive())
{
// If so, wait until source no longer active and then exit
while (escape->IsActive())
{
if (!Poll())
{
escape->Release();
return false;
cancelled = true;
goto Finish;
}
Wait(1000/60);
}
escape->Release();
return false;
cancelled = true;
goto Finish;
}
// Check all active sources
@ -1745,8 +1762,10 @@ bool CInputSystem::ReadMapping(char *buffer, unsigned bufSize, bool fullAxisOnly
strncpy(buffer, mapping.c_str(), bufSize - 1);
buffer[bufSize - 1] = '\0';
escape->Release();
return true;
Finish:
if (escape)
escape->Release();
return !cancelled;
}
void CInputSystem::GrabMouse()
@ -1770,6 +1789,233 @@ bool CInputSystem::SendForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCm
return ProcessForceFeedbackCmd(joyNum, axisNum, ffCmd);
}
bool CInputSystem::CalibrateJoystickAxis(unsigned joyNum, unsigned axisNum, const char *escapeMapping, const char *confirmMapping)
{
const JoyDetails *joyDetails = GetJoyDetails(joyNum);
if (joyDetails == NULL || axisNum >= NUM_JOY_AXES || !joyDetails->hasAxis[axisNum])
{
printf("No such axis or joystick");
return true;
}
// Map given escape mapping to input source
CInputSource *escape = ParseSource(escapeMapping);
CInputSource *output = ParseSource("KEY_SHIFT");
if (escape)
escape->Acquire();
if (output)
output->Acquire();
Repeat:
printf("Calibrating %s of joystick '%s'.\n\n", joyDetails->axisName[axisNum], joyDetails->name);
int posVal;
int negVal;
int offVal;
unsigned posRange;
unsigned negRange;
unsigned posOffRange;
unsigned negOffRange;
char mapping[50];
bool cancelled = false;
for (unsigned step = 0; step < 3; step++)
{
switch (step)
{
case 0:
puts("Step 1:");
puts(" Move axis now to its furthest positive/'on' position and hold, ie:");
if (axisNum == AXIS_X || axisNum == AXIS_RX || axisNum == AXIS_Z || axisNum == AXIS_RZ)
puts(" - for a joystick X-Axis, push it all the way to the right.");
if (axisNum == AXIS_Y || axisNum == AXIS_RY || axisNum == AXIS_Z || axisNum == AXIS_RZ)
puts(" - for a joystick Y-Axis, push it all the way downwards.");
puts(" - for a steering wheel, turn it all the way to the right.");
puts(" - for a pedal, press it all the way to the floor.");
break;
case 1:
puts("Step 2:");
puts(" Move axis the other way to its furthest negative position and hold, ie:");
if (axisNum == AXIS_X || axisNum == AXIS_RX || axisNum == AXIS_Z || axisNum == AXIS_RZ)
puts(" - for a joystick X-Axis, push it all the way to the left.");
if (axisNum == AXIS_Y || axisNum == AXIS_RY || axisNum == AXIS_Z || axisNum == AXIS_RZ)
puts(" - for a joystick Y-Axis, push it all the way updwards.");
puts(" - for a steering wheel, turn it all the way to the left.");
puts(" - for a pedal, let go of the pedal completely. If there is another pedal");
puts(" that shares the same axis then press that one all the way to the floor.");
break;
case 2:
puts("Step 3:");
puts(" Return axis to its center/'off' position and hold, ie:");
puts(" - for a joystick axis, let it return to the middle.");
puts(" - for a steering weel, turn it back to its central position.");
puts(" - for a pedal, let go of all pedals completely.");
break;
}
printf("\nPress Return when ready (or press Esc to cancel): ");
// Loop until user confirms or aborts
for (;;)
{
if (!ReadMapping(mapping, 50, false, READ_KEYBOARD|READ_MERGE, escapeMapping))
{
cancelled = true;
goto Finish;
}
if (stricmp(mapping, confirmMapping) == 0)
break;
}
printf("Calibrating... ");
// Loop until at least three seconds have elapsed or user aborts
int joyVal = GetJoyAxisValue(joyNum, axisNum);
int minVal = joyVal;
int maxVal = joyVal;
bool firstOut = true;
for (unsigned frames = 0; frames < 3 * 60; frames++)
{
if (!Poll())
{
cancelled = true;
goto Finish;
}
// Check if escape source was triggered
if (escape && escape->IsActive())
{
// If so, wait until source no longer active and then exit
while (escape->IsActive())
{
if (!Poll())
{
cancelled = true;
goto Finish;
}
Wait(1000/60);
}
cancelled = true;
goto Finish;
}
joyVal = GetJoyAxisValue(joyNum, axisNum);
minVal = min<int>(minVal, joyVal);
maxVal = max<int>(maxVal, joyVal);
// Check if output source is triggered, and if so output value for debugging
if (output != NULL && output->IsActive())
{
if (firstOut)
puts("");
printf(" [value: %d, min: %d, %max: %d]\n", joyVal, minVal, maxVal);
firstOut = false;
}
// Don't poll continuously
Wait(1000/60);
}
printf("Done\n\n");
switch (step)
{
case 0: posVal = (abs(maxVal) >= abs(minVal) ? maxVal : minVal); break;
case 1: negVal = (abs(minVal) >= abs(maxVal) ? minVal : maxVal); break;
case 2:
if (minVal <= 0 && maxVal >= 0)
offVal = 0;
else if (minVal == DEFAULT_JOY_AXISMINVAL)
offVal = DEFAULT_JOY_AXISMINVAL;
else if (maxVal == DEFAULT_JOY_AXISMAXVAL)
offVal = DEFAULT_JOY_AXISMAXVAL;
else
offVal = (minVal + maxVal) / 2;
posRange = abs(posVal - offVal);
negRange = abs(negVal - offVal);
posOffRange = (unsigned)(posVal > offVal ? maxVal - offVal : offVal - minVal);
negOffRange = (unsigned)(posVal > offVal ? offVal - minVal : maxVal - offVal);
break;
}
}
unsigned totalRange = posRange + negRange;
unsigned posDeadZone = (unsigned)ceil(100.0 * (double)posOffRange / (double)posRange);
unsigned negDeadZone = (unsigned)ceil(100.0 * (double)negOffRange / (double)negRange);
unsigned deadZone = max<unsigned>(1, max<unsigned>(negDeadZone, posDeadZone));
bool okay;
if (posVal > negVal)
okay = negVal <= offVal && offVal <= posVal && totalRange > 3000 && deadZone < 90;
else
okay = posVal <= offVal && offVal <= negVal && totalRange > 3000 && deadZone < 90;
if (okay)
{
JoySettings *commonSettings = GetJoySettings(ANY_JOYSTICK, true);
JoySettings *joySettings = GetJoySettings(joyNum, false);
if (joySettings == NULL)
{
joySettings = new JoySettings(*commonSettings);
m_joySettings.push_back(joySettings);
joySettings->joyNum = joyNum;
}
printf("Calibrated Axis Settings:\n\n");
printf(" Min Value = %d\n", negVal);
printf(" Center/Off Value = %d\n", offVal);
printf(" Max Value = %d\n", posVal);
printf(" Dead Zone = %d %%\n", deadZone);
printf("\nAccept these settings: y/n? ");
// Loop until user confirms or declines
while (ReadMapping(mapping, 50, false, READ_KEYBOARD|READ_MERGE, escapeMapping))
{
if (stricmp(mapping, "KEY_N") == 0)
break;
else if (stricmp(mapping, "KEY_Y") == 0)
{
joySettings->axisMinVals[axisNum] = negVal;
joySettings->axisMaxVals[axisNum] = posVal;
joySettings->axisOffVals[axisNum] = offVal;
joySettings->deadZones[axisNum] = deadZone;
ClearSourceCache();
puts("Accepted");
goto Finish;
}
}
cancelled = true;
}
else
{
puts("There was a problem calibrating the axis. This may be because the steps");
puts("were not followed correctly or the joystick is sending invalid data.");
printf("\nTry calibrating again: y/n? ");
// Loop until user confirms or declines
while (ReadMapping(mapping, 50, false, READ_KEYBOARD|READ_MERGE, escapeMapping))
{
if (stricmp(mapping, "KEY_N") == 0)
break;
else if (stricmp(mapping, "KEY_Y") == 0)
{
puts("[Cancelled]");
goto Repeat;
}
}
cancelled = true;
}
Finish:
if (cancelled)
puts("[Cancelled]");
if (escape)
escape->Release();
if (output)
output->Release();
return !cancelled;
}
void CInputSystem::PrintDevices()
{
puts("Keyboards:");
@ -1974,9 +2220,9 @@ CInputSystem::CJoyAxisInputSource::CJoyAxisInputSource(CInputSystem *system, int
{
m_axisInverted = m_axisMaxVal < m_axisMinVal;
// Calculate pos/neg deadzone and saturation points (joystick raw values range from axisMinVal to axisMasVal (centered/off at axisOffVal),
// deadzone given as percentage 0-99 and saturation given as percentage 1-100)
// deadzone given as percentage 0-99 and saturation given as percentage 1 - 200)
double dDeadZone = (double)Clamp((int)deadZone, 0, 99) / 100.0;
double dSaturation = (double)Clamp((int)saturation, (int)deadZone + 1, 100) / 100.0;
double dSaturation = (double)Clamp((int)saturation, (int)deadZone + 1, 200) / 100.0;
m_posDZone = m_axisOffVal + (int)(dDeadZone * (m_axisMaxVal - m_axisOffVal));
m_negDZone = m_axisOffVal + (int)(dDeadZone * (m_axisMinVal - m_axisOffVal));
m_posSat = m_axisOffVal + (int)(dSaturation * (m_axisMaxVal - m_axisOffVal));

View file

@ -29,7 +29,7 @@
#ifndef INCLUDED_INPUTSYSTEM_H
#define INCLUDED_INPUTSYSTEM_H
#include <stdio.h>
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
@ -264,7 +264,7 @@ struct JoySettings
int axisOffVals[NUM_JOY_AXES]; // Axis center/off value (default 0)
int axisMaxVals[NUM_JOY_AXES]; // Axis max raw value (default 32767)
unsigned deadZones[NUM_JOY_AXES]; // Axis dead zone as a percentage 0-99 of axis positive/negative ranges
unsigned saturations[NUM_JOY_AXES]; // Axis saturation as a percentage 1-100 of axis positive/negative ranges
unsigned saturations[NUM_JOY_AXES]; // Axis saturation as a percentage 1-200 of axis positive/negative ranges
/*
* Creates a JoySettings with default settings
@ -296,13 +296,14 @@ struct MouseDetails
struct JoyDetails
{
char name[MAX_NAME_LENGTH + 1]; // Joystick name (if available)
int numAxes; // Total number of axes on joystick
int numPOVs; // Total number of POV hat controllers on joystick
int numButtons; // Total number of buttons on joystick
bool hasFFeedback; // True if joystick supports force feedback
bool hasAxis[NUM_JOY_AXES]; // Flags to indicate which axes available on joystick
bool axisHasFF[NUM_JOY_AXES]; // Flags to indicate which axes are force feedback enabled
char name[MAX_NAME_LENGTH + 1]; // Joystick name (if available)
int numAxes; // Total number of axes on joystick
int numPOVs; // Total number of POV hat controllers on joystick
int numButtons; // Total number of buttons on joystick
bool hasFFeedback; // True if joystick supports force feedback
bool hasAxis[NUM_JOY_AXES]; // Flags to indicate which axes available on joystick
char axisName[NUM_JOY_AXES][MAX_NAME_LENGTH + 1]; // Axis names (if available)
bool axisHasFF[NUM_JOY_AXES]; // Flags to indicate which axes are force feedback enabled
};
/*
@ -722,6 +723,7 @@ public:
static unsigned totalSrcsAcquired;
static unsigned totalSrcsReleased;
#endif
static const char *GetDefaultAxisName(int axisNum);
// Name of this input system
const char *name;
@ -841,6 +843,8 @@ public:
virtual bool SendForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd);
bool CalibrateJoystickAxis(unsigned joyNum, unsigned axisNum, const char *escapeMapping = "KEY_ESCAPE", const char *confirmMapping = "KEY_RETURN");
void PrintDevices();
//

View file

@ -265,6 +265,7 @@ void CInputs::PrintConfigureInputsHelp()
puts(" r Reset current input mapping to default and remain there,");
puts(" Down Move onto next control,");
puts(" Up Go back to previous control,");
puts(" b Calibrate joystick axes,");
puts(" i Display information about input system and attached devices,");
puts(" h Display this help again,");
puts(" q Finish and save all changes,");
@ -374,6 +375,8 @@ bool CInputs::ConfigureInputs(const GameInfo *game)
const char *groupLabel = NULL;
bool cancelled = false;
// Loop through all the inputs to be configured
index = 0;
while (index < toConfigure.size())
@ -392,9 +395,9 @@ bool CInputs::ConfigureInputs(const GameInfo *game)
Redisplay:
// Print the input label, current input mapping and available options
if (index > 0)
printf(" %s [%s]: Ret/c/s/a/r/Up/Down/h/q/Esc? ", input->label, input->GetMapping());
printf(" %s [%s]: Ret/c/s/a/r/Up/Down/b/h/q/Esc? ", input->label, input->GetMapping());
else
printf(" %s [%s]: Ret/c/s/a/r/Down/h/q/Esc? ", input->label, input->GetMapping());
printf(" %s [%s]: Ret/c/s/a/r/Down/h/b/q/Esc? ", input->label, input->GetMapping());
fflush(stdout); // required on terminals that use buffering
// Loop until user has selected a valid option
@ -412,27 +415,26 @@ Redisplay:
(*it)->SetMapping(oldMappings[index].c_str());
index++;
}
puts("");
m_system->GrabMouse();
return false;
cancelled = true;
goto Finish;
}
if (stricmp(mapping, "KEY_RETURN") == 0 || stricmp(mapping, "KEY_S") == 0)
{
// Set the input mapping
printf("Setting...");
printf("Setting... ");
fflush(stdout); // required on terminals that use buffering
if (input->Configure(false, uiExit->GetMapping()))
{
printf(" %s\n", input->GetMapping());
puts(input->GetMapping());
if (stricmp(mapping, "KEY_RETURN") == 0)
index++;
done = true;
}
else
{
printf(" [Cancelled]\n");
puts("[Cancelled]");
goto Redisplay;
}
}
@ -442,23 +444,23 @@ Redisplay:
printf("Appending...");
fflush(stdout); // required on terminals that use buffering
if (input->Configure(true, uiExit->GetMapping()))
printf(" %s\n", input->GetMapping());
puts(input->GetMapping());
else
printf(" [Cancelled]\n");
puts("[Cancelled]");
goto Redisplay;
}
else if (stricmp(mapping, "KEY_C") == 0)
{
// Clear the input mapping(s)
input->SetMapping("NONE");
printf("Cleared\n");
puts("Cleared");
goto Redisplay;
}
else if (stricmp(mapping, "KEY_R") == 0)
{
// Reset the input mapping(s) to the default
input->ResetToDefaultMapping();
printf("Reset\n");
puts("Reset");
goto Redisplay;
}
else if (stricmp(mapping, "KEY_DOWN") == 0)
@ -485,6 +487,14 @@ Redisplay:
index = 0;
done = true;
}
else if (stricmp(mapping, "KEY_B") == 0)
{
// Calibrate joysticks
printf("\n\n");
CalibrateJoysticks();
puts("");
goto Redisplay;
}
else if (stricmp(mapping, "KEY_I") == 0)
{
// Print info about input system
@ -500,21 +510,15 @@ Redisplay:
goto Redisplay;
}
else if (stricmp(mapping, "KEY_Q") == 0)
{
// Finish configuration
puts("");
m_system->GrabMouse();
return true;
}
goto Finish;
}
}
// All inputs set, finish configuration
puts("");
Finish:
printf("\n\n");
m_system->GrabMouse();
return true;
return !cancelled;
}
bool CInputs::ConfigureInputs(const GameInfo *game, unsigned dispX, unsigned dispY, unsigned dispW, unsigned dispH)
@ -525,6 +529,85 @@ bool CInputs::ConfigureInputs(const GameInfo *game, unsigned dispX, unsigned dis
return ConfigureInputs(game);
}
void CInputs::CalibrateJoysticks()
{
unsigned numJoys = m_system->GetNumJoysticks();
if (numJoys == 0 || numJoys == ANY_JOYSTICK)
puts("No joysticks attached to calibrate!");
else
{
puts("Choose joystick to calibrate (or press Esc to cancel):");
if (numJoys > 10)
numJoys = 10;
for (int joyNum = 0; joyNum < numJoys; joyNum++)
{
const JoyDetails *joyDetails = m_system->GetJoyDetails(joyNum);
unsigned dispNum = (joyNum == 9 ? 0 : joyNum + 1);
printf(" %u: %s\n", dispNum, joyDetails->name);
}
char mapping[50];
while (m_system->ReadMapping(mapping, 50, false, READ_KEYBOARD|READ_MERGE, uiExit->GetMapping()))
{
if (strlen(mapping) != 5 || strncmp(mapping, "KEY_", 4) != 0)
continue;
char c = mapping[4];
if (!isdigit(c))
continue;
unsigned joyNum = c - '0';
joyNum = (joyNum == 0 ? 9 : joyNum - 1);
if (joyNum >= numJoys)
continue;
puts("");
CalibrateJoystick(joyNum);
return;
}
}
}
void CInputs::CalibrateJoystick(int joyNum)
{
const JoyDetails *joyDetails = m_system->GetJoyDetails(joyNum);
if (joyDetails == NULL || joyDetails->numAxes == 0)
{
printf("No axes available to calibrate on joystick!");
return;
}
printf("Calibrating joystick '%s'.\n\n", joyDetails->name);
puts("Choose axis to calibrate (or press Esc to cancel):");
vector<unsigned> axisNumList;
for (unsigned axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
{
if (!joyDetails->hasAxis[axisNum])
continue;
axisNumList.push_back(axisNum);
printf(" %u: %s\n", axisNumList.size(), joyDetails->axisName[axisNum]);
}
char mapping[50];
while (m_system->ReadMapping(mapping, 50, false, READ_KEYBOARD|READ_MERGE, uiExit->GetMapping()))
{
if (strlen(mapping) != 5 || strncmp(mapping, "KEY_", 4) != 0)
continue;
char c = mapping[4];
if (!isdigit(c))
continue;
unsigned optNum = c - '0';
if (optNum == 0 || optNum > axisNumList.size())
continue;
unsigned axisNum = axisNumList[optNum - 1];
puts("");
if (m_system->CalibrateJoystickAxis(joyNum, axisNum))
{
for (vector<CInput*>::iterator it = m_inputs.begin(); it != m_inputs.end(); it++)
(*it)->InputSystemChanged();
}
return;
}
}
void CInputs::PrintInputs(const GameInfo *game)
{
// Print header

View file

@ -246,6 +246,10 @@ public:
*/
bool ConfigureInputs(const GameInfo *game, unsigned dispX, unsigned dispY, unsigned dispW, unsigned dispH);
void CalibrateJoysticks();
void CalibrateJoystick(int joyNum);
/*
* Prints to stdout the current input mapping assignments for the given game, or all inputs if game is NULL.
*/

View file

@ -409,6 +409,20 @@ static void ApplySettings(CINIFile *INI, const char *section)
g_Config.showFPS = x ? true : false;
if (OKAY == INI->Get(section, "FlipStereo", x))
g_Config.flipStereo = x ? true : false;
#ifdef SUPERMODEL_WIN32
// DirectInput ForceFeedback
INI->Get(section, "DirectInputEffectsGain", g_Config.dInputEffectsGain);
INI->Get(section, "DirectInputConstForceMax", g_Config.dInputConstForceMax);
INI->Get(section, "DirectInputSelfCenterMax", g_Config.dInputSelfCenterMax);
INI->Get(section, "DirectInputFrictionMax", g_Config.dInputFrictionMax);
INI->Get(section, "DirectInputVibrateMax", g_Config.dInputVibrateMax);
// XInput ForceFeedback
INI->Get(section, "XInputConstForceThreshold", g_Config.xInputConstForceThreshold);
INI->Get(section, "XInputConstForceMax", g_Config.xInputConstForceMax);
INI->Get(section, "XInputVibrateMax", g_Config.xInputVibrateMax);
#endif // SUPERMODEL_WIN32
}
// Read settings (from a specific section) from the config file
@ -429,36 +443,50 @@ static void LogConfig(void)
InfoLog("Program settings:");
// COSDConfig
InfoLog("\tXResolution = %d", g_Config.xRes);
InfoLog("\tYResolution = %d", g_Config.yRes);
InfoLog("\tFullScreen = %d", g_Config.fullScreen);
InfoLog("\tThrottle = %d", g_Config.throttle);
InfoLog("\tShowFrameRate = %d", g_Config.showFPS);
InfoLog("\tXResolution = %d", g_Config.xRes);
InfoLog("\tYResolution = %d", g_Config.yRes);
InfoLog("\tFullScreen = %d", g_Config.fullScreen);
InfoLog("\tThrottle = %d", g_Config.throttle);
InfoLog("\tShowFrameRate = %d", g_Config.showFPS);
#ifdef SUPERMODEL_DEBUGGER
InfoLog("\tDisableDebugger = %d", g_Config.disableDebugger);
InfoLog("\tDisableDebugger = %d", g_Config.disableDebugger);
#endif
InfoLog("\tInputSystem = %s", g_Config.GetInputSystem());
InfoLog("\tFlipStereo = %d", g_Config.flipStereo);
InfoLog("\tInputSystem = %s", g_Config.GetInputSystem());
InfoLog("\tFlipStereo = %d", g_Config.flipStereo);
#ifdef SUPERMODEL_WIN32
// DirectInput ForceFeedback
InfoLog("\tDirectInputEffectsGain = %u", g_Config.dInputEffectsGain);
InfoLog("\tDirectInputConstForceMax = %u", g_Config.dInputConstForceMax);
InfoLog("\tDirectInputSelfCenterMax = %u", g_Config.dInputSelfCenterMax);
InfoLog("\tDirectInputFrictionMax = %u", g_Config.dInputFrictionMax);
InfoLog("\tDirectInputVibrateMax = %u", g_Config.dInputVibrateMax);
// XInput ForceFeedback
InfoLog("\tXInputConstForceThreshold = %u", g_Config.xInputConstForceThreshold);
InfoLog("\tXInputConstForceMax = %u", g_Config.xInputConstForceMax);
InfoLog("\tXInputVibrateMax = %u", g_Config.xInputVibrateMax);
#endif // SUPERMODEL_WIN32
// CModel3Config
InfoLog("\tMultiThreaded = %d", g_Config.multiThreaded);
InfoLog("\tPowerPCFrequency = %d", g_Config.GetPowerPCFrequency());
InfoLog("\tMultiThreaded = %d", g_Config.multiThreaded);
InfoLog("\tPowerPCFrequency = %d", g_Config.GetPowerPCFrequency());
// CSoundBoardConfig
InfoLog("\tEmulateSound = %d", g_Config.emulateSound);
InfoLog("\tEmulateSound = %d", g_Config.emulateSound);
// CDSBConfig
InfoLog("\tEmulateDSB = %d", g_Config.emulateDSB);
InfoLog("\tSoundVolume = %d", g_Config.GetSoundVolume());
InfoLog("\tMusicVolume = %d", g_Config.GetMusicVolume());
InfoLog("\tEmulateDSB = %d", g_Config.emulateDSB);
InfoLog("\tSoundVolume = %d", g_Config.GetSoundVolume());
InfoLog("\tMusicVolume = %d", g_Config.GetMusicVolume());
// CDriveBoardConfig
InfoLog("\tForceFeedback = %d", g_Config.forceFeedback);
InfoLog("\tForceFeedback = %d", g_Config.forceFeedback);
// CRender3DConfig
InfoLog("\tVertexShader = %s", g_Config.vertexShaderFile.c_str());
InfoLog("\tFragmentShader = %s", g_Config.fragmentShaderFile.c_str());
InfoLog("\tVertexShader = %s", g_Config.vertexShaderFile.c_str());
InfoLog("\tFragmentShader = %s", g_Config.fragmentShaderFile.c_str());
InfoLog("");
}
@ -1030,6 +1058,9 @@ int Supermodel(const char *zipFile, CInputs *Inputs, CINIFile *CmdLine)
}
}
// Make sure all threads are paused before shutting down
Model3->PauseThreads();
#ifdef SUPERMODEL_DEBUGGER
// If debugger was supplied, detach it from system and restore old logger
if (Debugger != NULL)
@ -1461,11 +1492,11 @@ int main(int argc, char **argv)
InputSystem = new CSDLInputSystem();
#ifdef SUPERMODEL_WIN32
else if (stricmp(g_Config.GetInputSystem(), "dinput") == 0)
InputSystem = new CDirectInputSystem(false, false, true);
InputSystem = new CDirectInputSystem(false, false);
else if (stricmp(g_Config.GetInputSystem(), "xinput") == 0)
InputSystem = new CDirectInputSystem(false, true, true);
InputSystem = new CDirectInputSystem(false, true);
else if (stricmp(g_Config.GetInputSystem(), "rawinput") == 0)
InputSystem = new CDirectInputSystem(true, false, true);
InputSystem = new CDirectInputSystem(true, false);
#endif // SUPERMODEL_WIN32
else
{

View file

@ -52,7 +52,18 @@ public:
#ifdef SUPERMODEL_DEBUGGER
bool disableDebugger; // disables the debugger (not stored in the config. file)
#endif
#ifdef SUPERMODEL_WIN32
unsigned dInputEffectsGain;
unsigned dInputConstForceMax;
unsigned dInputSelfCenterMax;
unsigned dInputFrictionMax;
unsigned dInputVibrateMax;
unsigned xInputConstForceThreshold;
unsigned xInputConstForceMax;
unsigned xInputVibrateMax;
#endif
// Input system
inline void SetInputSystem(const char *inpSysName)
{
@ -104,6 +115,14 @@ public:
#endif
#ifdef SUPERMODEL_WIN32
inputSystem = "dinput";
dInputEffectsGain = 100;
dInputConstForceMax = 100;
dInputSelfCenterMax = 100;
dInputFrictionMax = 100;
dInputVibrateMax = 100;
xInputConstForceThreshold = 30;
xInputConstForceMax = 100;
xInputVibrateMax = 100;
#else
inputSystem = "sdl";
#endif

View file

@ -215,6 +215,9 @@ void CSDLInputSystem::OpenJoysticks()
{
joyDetails.hasAxis[axisNum] = joyDetails.numAxes > axisNum;
joyDetails.axisHasFF[axisNum] = false; // SDL 1.2 does not support force feedback
char *axisName = joyDetails.axisName[axisNum];
strcpy(axisName, CInputSystem::GetDefaultAxisName(axisNum)); // SDL 1.2 does not support axis names
strcat(axisName, "-Axis");
}
joyDetails.numPOVs = SDL_JoystickNumHats(joystick);
joyDetails.numButtons = SDL_JoystickNumButtons(joystick);

View file

@ -319,6 +319,14 @@ BOOL CALLBACK DI8EnumAxesCallback(LPCDIDEVICEOBJECTINSTANCE instance, LPVOID con
JoyDetails *joyDetails = (JoyDetails*)context;
joyDetails->hasAxis[axisNum] = true;
joyDetails->axisHasFF[axisNum] = !!(instance->dwFlags & DIDOI_FFACTUATOR);
// Get axis name from DirectInput
char *axisName = joyDetails->axisName[axisNum];
strcpy(axisName, CInputSystem::GetDefaultAxisName(axisNum));
strcat(axisName, "-Axis (");
strncat(axisName, instance->tszName, MAX_NAME_LENGTH - strlen(axisName) - 1);
strcat(axisName, ")");
return DIENUM_CONTINUE;
}
@ -331,7 +339,7 @@ BOOL CALLBACK DI8EnumEffectsCallback(LPCDIEFFECTINFO effectInfo, LPVOID context)
return DIENUM_CONTINUE;
}
const char *CDirectInputSystem::ConstructName(bool useRawInput, bool useXInput, bool enableFFeedback)
const char *CDirectInputSystem::ConstructName(bool useRawInput, bool useXInput)
{
if (useRawInput)
return (useXInput ? "RawInput/XInput" : "RawInput/DirectInput");
@ -339,14 +347,12 @@ const char *CDirectInputSystem::ConstructName(bool useRawInput, bool useXInput,
return (useXInput ? "Xinput" : "DirectInput");
}
CDirectInputSystem::CDirectInputSystem(bool useRawInput, bool useXInput, bool enableFFeedback) :
CInputSystem(ConstructName(useRawInput, useXInput, enableFFeedback)),
m_useRawInput(useRawInput), m_useXInput(useXInput), m_enableFFeedback(enableFFeedback),
CDirectInputSystem::CDirectInputSystem(bool useRawInput, bool useXInput) :
CInputSystem(ConstructName(useRawInput, useXInput)),
m_useRawInput(useRawInput), m_useXInput(useXInput), m_enableFFeedback(true),
m_initializedCOM(false), m_activated(false), m_hwnd(NULL), m_screenW(0), m_screenH(0),
m_getRIDevListPtr(NULL), m_getRIDevInfoPtr(NULL), m_regRIDevsPtr(NULL), m_getRIDataPtr(NULL),
m_xiGetCapabilitiesPtr(NULL), m_xiGetStatePtr(NULL), m_xiSetStatePtr(NULL), m_di8(NULL), m_di8Keyboard(NULL), m_di8Mouse(NULL),
m_diEffectsGain(100), m_diConstForceMax(100), m_diSelfCenterMax(100), m_diFrictionMax(100), m_diVibrateMax(100),
m_xiConstForceThreshold(65), m_xiConstForceMax(100), m_xiVibrateMax(100)
m_xiGetCapabilitiesPtr(NULL), m_xiGetStatePtr(NULL), m_xiSetStatePtr(NULL), m_di8(NULL), m_di8Keyboard(NULL), m_di8Mouse(NULL)
{
// Reset initial states
memset(&m_combRawMseState, 0, sizeof(RawMseState));
@ -675,9 +681,17 @@ void CDirectInputSystem::ActivateKeyboardsAndMice()
// Set DirectInput cooperative level of keyboard and mouse
if (m_di8Keyboard != NULL)
m_di8Keyboard->SetCooperativeLevel(m_hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
{
m_di8Keyboard->Unacquire();
m_di8Keyboard->SetCooperativeLevel(m_hwnd, (m_grabMouse ? DISCL_FOREGROUND : DISCL_BACKGROUND) | DISCL_NONEXCLUSIVE);
m_di8Keyboard->Acquire();
}
if (m_di8Mouse != NULL)
m_di8Mouse->SetCooperativeLevel(m_hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
{
m_di8Mouse->Unacquire();
m_di8Mouse->SetCooperativeLevel(m_hwnd, (m_grabMouse ? DISCL_FOREGROUND : DISCL_BACKGROUND) | DISCL_NONEXCLUSIVE);
m_di8Mouse->Acquire();
}
}
void CDirectInputSystem::PollKeyboardsAndMice()
@ -1178,10 +1192,12 @@ void CDirectInputSystem::ActivateJoysticks()
if (!it->isXInput)
{
LPDIRECTINPUTDEVICE8 joystick = m_di8Joysticks[it->dInputNum];
if (m_joyDetails[joyNum].hasFFeedback)
joystick->Unacquire();
if (m_grabMouse)
joystick->SetCooperativeLevel(m_hwnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
else
joystick->SetCooperativeLevel(m_hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
joystick->Acquire();
}
joyNum++;
}
@ -1330,7 +1346,7 @@ HRESULT CDirectInputSystem::CreateJoystickEffect(LPDIRECTINPUTDEVICE8 joystick,
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.dwTriggerButton = DIEB_NOTRIGGER;
eff.dwTriggerRepeatInterval = 0;
eff.dwGain = min<LONG>(m_diEffectsGain * DI_EFFECTS_SCALE, DI_EFFECTS_MAX);
eff.dwGain = min<LONG>(g_Config.dInputEffectsGain * DI_EFFECTS_SCALE, DI_EFFECTS_MAX);
eff.cAxes = 1;
eff.rgdwAxes = &dwAxis;
eff.rglDirection = &lDirection;
@ -1651,12 +1667,12 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF
case FFConstantForce:
// Check if constant force effect is disabled
if (m_xiConstForceMax == 0)
if (g_Config.xInputConstForceMax == 0)
return false;
// Constant force effect is mapped to either left or right vibration motor depending on its direction
negForce = ffCmd.force < 0.0f;
absForce = (negForce ? -ffCmd.force : ffCmd.force);
threshold = (float)m_xiConstForceThreshold / 100.0f;
threshold = (float)g_Config.xInputConstForceThreshold / 100.0f;
// Check if constant force effect is being stopped or is below threshold
if (absForce == 0.0f || absForce < threshold)
{
@ -1667,14 +1683,14 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF
else if (negForce)
{
// If force is negative (to left), set left motor vibrating
pInfo->xiConstForceLeft = (WORD)(absForce * (float)(m_xiConstForceMax * XI_VIBRATE_SCALE));
pInfo->xiConstForceLeft = (WORD)(absForce * (float)(g_Config.xInputConstForceMax * XI_VIBRATE_SCALE));
pInfo->xiConstForceRight = 0;
}
else
{
// If force positive (to right), set right motor vibrating
pInfo->xiConstForceLeft = 0;
pInfo->xiConstForceRight = (WORD)(absForce * (float)(m_xiConstForceMax * XI_VIBRATE_SCALE));
pInfo->xiConstForceRight = (WORD)(absForce * (float)(g_Config.xInputConstForceMax * XI_VIBRATE_SCALE));
}
break;
@ -1685,7 +1701,7 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF
case FFVibrate:
// Check if vibration effect is disabled
if (m_xiVibrateMax == 0)
if (g_Config.xInputVibrateMax == 0)
return false;
// Check if vibration effect is being stopped
if (ffCmd.force == 0.0f)
@ -1696,7 +1712,7 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF
else
{
// Otherwise, set both motors vibrating
pInfo->xiVibrateBoth = (WORD)(ffCmd.force * (float)(m_xiVibrateMax * XI_VIBRATE_SCALE));
pInfo->xiVibrateBoth = (WORD)(ffCmd.force * (float)(g_Config.xInputVibrateMax * XI_VIBRATE_SCALE));
}
break;
@ -1748,9 +1764,9 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF
{
case FFConstantForce:
//printf("FFConstantForce %0.2f\n", 100.0f * ffCmd.force);
if (m_diConstForceMax == 0)
if (g_Config.dInputConstForceMax == 0)
return false;
dicf.lMagnitude = min<LONG>(-ffCmd.force * (float)(m_diConstForceMax * DI_EFFECTS_SCALE), DI_EFFECTS_MAX); // Invert sign for DirectInput effect
dicf.lMagnitude = min<LONG>(-ffCmd.force * (float)(g_Config.dInputConstForceMax * DI_EFFECTS_SCALE), DI_EFFECTS_MAX); // Invert sign for DirectInput effect
eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
eff.lpvTypeSpecificParams = &dicf;
@ -1758,11 +1774,11 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF
case FFSelfCenter:
//printf("FFSelfCenter %0.2f\n", 100.0f * ffCmd.force);
if (m_diSelfCenterMax == 0)
if (g_Config.dInputSelfCenterMax == 0)
return false;
dic.lOffset = 0;
dic.lPositiveCoefficient = min<LONG>(ffCmd.force * (float)(m_diSelfCenterMax * DI_EFFECTS_SCALE), DI_EFFECTS_MAX);
dic.lNegativeCoefficient = min<LONG>(ffCmd.force * (float)(m_diSelfCenterMax * DI_EFFECTS_SCALE), DI_EFFECTS_MAX);
dic.lPositiveCoefficient = min<LONG>(ffCmd.force * (float)(g_Config.dInputSelfCenterMax * DI_EFFECTS_SCALE), DI_EFFECTS_MAX);
dic.lNegativeCoefficient = min<LONG>(ffCmd.force * (float)(g_Config.dInputSelfCenterMax * DI_EFFECTS_SCALE), DI_EFFECTS_MAX);
dic.dwPositiveSaturation = DI_FFNOMINALMAX;
dic.dwNegativeSaturation = DI_FFNOMINALMAX;
dic.lDeadBand = (LONG)(0.05 * DI_FFNOMINALMAX);
@ -1773,11 +1789,11 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF
case FFFriction:
//printf("FFFriction %0.2f\n", 100.0f * ffCmd.force);
if (m_diFrictionMax == 0)
if (g_Config.dInputFrictionMax == 0)
return false;
dic.lOffset = 0;
dic.lPositiveCoefficient = min<LONG>(ffCmd.force * (float)(m_diFrictionMax * DI_EFFECTS_SCALE), DI_EFFECTS_MAX);
dic.lNegativeCoefficient = min<LONG>(ffCmd.force * (float)(m_diFrictionMax * DI_EFFECTS_SCALE), DI_EFFECTS_MAX);
dic.lPositiveCoefficient = min<LONG>(ffCmd.force * (float)(g_Config.dInputFrictionMax * DI_EFFECTS_SCALE), DI_EFFECTS_MAX);
dic.lNegativeCoefficient = min<LONG>(ffCmd.force * (float)(g_Config.dInputFrictionMax * DI_EFFECTS_SCALE), DI_EFFECTS_MAX);
dic.dwPositiveSaturation = DI_FFNOMINALMAX;
dic.dwNegativeSaturation = DI_FFNOMINALMAX;
dic.lDeadBand = 0;
@ -1788,9 +1804,9 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF
case FFVibrate:
//printf("FFVibrate %0.2f\n", 100.0f * ffCmd.force);
if (m_diVibrateMax == 0)
if (g_Config.dInputVibrateMax == 0)
return false;
dip.dwMagnitude = min<DWORD>(ffCmd.force * (float)(m_diVibrateMax * DI_EFFECTS_SCALE), DI_EFFECTS_MAX);
dip.dwMagnitude = min<DWORD>(ffCmd.force * (float)(g_Config.dInputVibrateMax * DI_EFFECTS_SCALE), DI_EFFECTS_MAX);
dip.lOffset = 0;
dip.dwPhase = 0;
dip.dwPeriod = (DWORD)(0.05 * DI_SECONDS); // 1/20th second
@ -1878,47 +1894,6 @@ const JoyDetails *CDirectInputSystem::GetJoyDetails(int joyNum)
return &m_joyDetails[joyNum];
}
void CDirectInputSystem::ReadFromINIFile(CINIFile *ini, const char *section)
{
CInputSystem::ReadFromINIFile(ini, section);
ini->Get(section, "DirectInputEffectsGain", m_diEffectsGain);
ini->Get(section, "DirectInputConstForceMax", m_diConstForceMax);
ini->Get(section, "DirectInputSelfCenterMax", m_diSelfCenterMax);
ini->Get(section, "DirectInputFrictionMax", m_diFrictionMax);
ini->Get(section, "DirectInputVibrateMax", m_diVibrateMax);
ini->Get(section, "XInputConstForceThreshold", m_xiConstForceThreshold);
ini->Get(section, "XInputConstForceMax", m_xiConstForceMax);
ini->Get(section, "XInputVibrateMax", m_xiVibrateMax);
}
void CDirectInputSystem::WriteToINIFile(CINIFile *ini, const char *section)
{
CInputSystem::WriteToINIFile(ini, section);
// Only write out those settings which have changed from default
/*
if (m_diEffectsGain != 100) ini->Set(section, "DirectInputEffectsGain", m_diEffectsGain);
if (m_diConstForceMax != 100) ini->Set(section, "DirectInputConstForceMax", m_diConstForceMax);
if (m_diSelfCenterMax != 100) ini->Set(section, "DirectInputSelfCenterMax", m_diSelfCenterMax);
if (m_diFrictionMax != 100) ini->Set(section, "DirectInputFrictionMax", m_diFrictionMax);
if (m_diVibrateMax != 100) ini->Set(section, "DirectInputVibrateMax", m_diVibrateMax);
if (m_xiConstForceThreshold != 65) ini->Set(section, "XInputConstForceThreshold", m_xiConstForceThreshold);
if (m_xiConstForceMax != 100) ini->Set(section, "XInputConstForceMax", m_xiConstForceMax);
if (m_xiVibrateMax != 100) ini->Set(section, "XInputVibrateMax", m_xiVibrateMax);
*/
// Always write out
ini->Set(section, "DirectInputEffectsGain", m_diEffectsGain);
ini->Set(section, "DirectInputConstForceMax", m_diConstForceMax);
ini->Set(section, "DirectInputSelfCenterMax", m_diSelfCenterMax);
ini->Set(section, "DirectInputFrictionMax", m_diFrictionMax);
ini->Set(section, "DirectInputVibrateMax", m_diVibrateMax);
ini->Set(section, "XInputConstForceThreshold", m_xiConstForceThreshold);
ini->Set(section, "XInputConstForceMax", m_xiConstForceMax);
ini->Set(section, "XInputVibrateMax", m_xiVibrateMax);
}
bool CDirectInputSystem::Poll()
{
// See if keyboard, mice and joysticks have been activated yet
@ -1997,18 +1972,25 @@ void CDirectInputSystem::GrabMouse()
{
CInputSystem::GrabMouse();
SetMouseVisibility(false);
if (m_useRawInput)
SetMouseVisibility(false);
// When grabbing mouse, make sure devices get re-activated
ActivateKeyboardsAndMice();
ActivateJoysticks();
if (m_activated)
{
ActivateKeyboardsAndMice();
ActivateJoysticks();
}
}
void CDirectInputSystem::UngrabMouse()
{
CInputSystem::UngrabMouse();
// When ungrabbing mouse place, make sure devices get re-activated
ActivateKeyboardsAndMice();
ActivateJoysticks();
// When ungrabbing mouse, make sure devices get re-activated
if (m_activated)
{
ActivateKeyboardsAndMice();
ActivateJoysticks();
}
}

View file

@ -126,7 +126,7 @@ private:
// Lookup table to map key names to DirectInput keycodes and Virtual keycodes
static DIKeyMapStruct s_keyMap[];
static const char *ConstructName(bool useRawInput, bool useXInput, bool enableFFeedback);
static const char *ConstructName(bool useRawInput, bool useXInput);
bool m_useRawInput;
bool m_useXInput;
@ -176,18 +176,6 @@ private:
vector<DIJoyInfo> m_diJoyInfos;
vector<DIJOYSTATE2> m_diJoyStates;
// DirectInput force feedback parameters (100% = max)
unsigned m_diEffectsGain;
unsigned m_diConstForceMax;
unsigned m_diSelfCenterMax;
unsigned m_diFrictionMax;
unsigned m_diVibrateMax;
// XInput force feedback parameters (100% = max)
unsigned m_xiConstForceThreshold;
unsigned m_xiConstForceMax;
unsigned m_xiVibrateMax;
bool GetRegString(HKEY regKey, const char *regPath, string &str);
bool GetRegDeviceName(const char *rawDevName, char *name);
@ -259,7 +247,7 @@ public:
* to the same shared axis and so cannot be distinguished when pressed together.
* If enableFFeedback is true then force feedback is enabled (for those joysticks which are force feedback capable).
*/
CDirectInputSystem(bool useRawInput, bool useXInput, bool enableFFeedback);
CDirectInputSystem(bool useRawInput, bool useXInput);
~CDirectInputSystem();
@ -275,10 +263,6 @@ public:
const JoyDetails *GetJoyDetails(int joyNum);
void ReadFromINIFile(CINIFile *ini, const char *section);
void WriteToINIFile(CINIFile *ini, const char *section);
bool Poll();
void GrabMouse();