Changes relating to input system:

- Added ability to configure axis min, centre and max values in INI file.  This allows some types of steering wheel pedals that use an inverted value range to work properly with the emulator.
 - Modified CINIFile to read and write signed numbers (needed for above change).
 - Added check at configuration start for "bad" input sources such as axes that are wrongly calibrated or buttons that are always triggering a value.  Otherwise they cause the configuration loop to wait indefinitely for the axis or button to be released.
 - Removed superfluous check for XInput devices when XInput is not enabled in CDirectInputSystem.
 - Improved force beedback code in CDirectInputSystem and also added the extra feedback effects needed so far for drive board emulation.
This commit is contained in:
Nik Henson 2011-06-22 13:06:52 +00:00
parent 5e247021be
commit e8782b98fa
18 changed files with 808 additions and 564 deletions

View file

@ -172,7 +172,7 @@ BOOL CINIFile::LookUpSetting(unsigned *idx, unsigned sectionIdx, string SettingN
// Assigns a value to the given setting, creating the setting if it does not exist. Nulls out the string (sets it to "").
void CINIFile::Set(string SectionName, string SettingName, unsigned value)
void CINIFile::Set(string SectionName, string SettingName, int value)
{
unsigned sectionIdx, settingIdx;
@ -234,7 +234,7 @@ void CINIFile::Set(string SectionName, string SettingName, string String)
}
// Obtains a numerical setting, if it exists, otherwise does nothing.
BOOL CINIFile::Get(string SectionName, string SettingName, unsigned& value)
BOOL CINIFile::Get(string SectionName, string SettingName, int& value)
{
unsigned sectionIdx, settingIdx;
@ -248,6 +248,17 @@ BOOL CINIFile::Get(string SectionName, string SettingName, unsigned& value)
return OKAY;
}
BOOL CINIFile::Get(string SectionName, string SettingName, unsigned& value)
{
int intVal;
if (Get(SectionName, SettingName, intVal) == FAIL || intVal < 0)
return FAIL;
value = (unsigned)intVal;
return OKAY;
}
// Obtains a string setting, if it exists, otherwise does nothing.
BOOL CINIFile::Get(string SectionName, string SettingName, string& String)
{
@ -323,15 +334,24 @@ CINIFile::CToken CINIFile::GetString(void)
return T;
}
// Fetch number (decimal or hexadecimal integer). linePtr must point to a character and therefore linePtr[1] is guaranteed to be within bounds.
// Fetch number (decimal or hexadecimal positive/negative integer).
// linePtr must point to a character and therefore linePtr[1] is guaranteed to be within bounds.
CINIFile::CToken CINIFile::GetNumber(void)
{
CToken T;
unsigned long long number = 0;
BOOL isNeg = FALSE;
int overflow = 0;
T.type = TOKEN_NUMBER;
// See if begins with minus sign
if (linePtr[0]=='-')
{
isNeg = TRUE;
linePtr++;
}
// Hexadecimal?
if ((linePtr[0]=='0') && ((linePtr[1]=='X') || (linePtr[1]=='x')))
{
@ -360,7 +380,7 @@ CINIFile::CToken CINIFile::GetNumber(void)
++linePtr;
// Check for overflows
if (number > 0x00000000FFFFFFFFULL)
if (!isNeg && number > 0x000000007FFFFFFFULL || isNeg && number > 0x0000000080000000ULL)
overflow = 1;
}
else if (IsBlank(linePtr[0]))
@ -387,7 +407,7 @@ CINIFile::CToken CINIFile::GetNumber(void)
++linePtr;
// Check for overflows
if (number > 0x00000000FFFFFFFFULL)
if (!isNeg && number > 0x000000007FFFFFFFULL || isNeg && number > 0x0000000080000000ULL)
overflow = 1;
}
else if (IsBlank(linePtr[0]))
@ -404,7 +424,7 @@ CINIFile::CToken CINIFile::GetNumber(void)
//if (overflow)
// printf("tokenizer: number exceeds 32 bits and has been truncated\n");
T.number = (unsigned) number;
T.number = (isNeg ? -(int)number : (int)number);
return T;
}

View file

@ -63,6 +63,7 @@ public:
* OKAY if the setting was found, FAIL otherwise. The type is not
* checked.
*/
BOOL Get(string SectionName, string SettingName, int& value);
BOOL Get(string SectionName, string SettingName, unsigned& value);
BOOL Get(string SectionName, string SettingName, string& String);
@ -82,7 +83,7 @@ public:
* value Value to write. String will be set to "".
* String String to write. Value will be set to 0.
*/
void Set(string SectionName, string SettingName, unsigned value);
void Set(string SectionName, string SettingName, int value);
void Set(string SectionName, string SettingName, string String);
/*
@ -146,7 +147,7 @@ private:
{
public:
int type; // token type (defined privately in INIFile.cpp)
unsigned number; // numbers and bools
int number; // numbers and bools
string String; // strings and identifiers
// Constructor (initialize to null token)
@ -177,7 +178,7 @@ private:
{
string Name; // setting name
BOOL isNumber; // internal flag: true if the setting is a number, false if it is a string
unsigned value; // value of number
int value; // value of number
string String; // string
Setting(void)

View file

@ -7,11 +7,18 @@ CInput::CInput(const char *inputId, const char *inputLabel, unsigned inputFlags,
ResetToDefaultMapping();
}
CInput::~CInput()
{
// Release source, if any
if (m_source != NULL)
m_source->Release();
}
void CInput::CreateSource()
{
// If already have a source, then release it now
if (m_system != NULL && m_source != NULL)
m_system->ReleaseSource(m_source);
if (m_source != NULL)
m_source->Release();
// If no system set yet or mapping is empty or NONE, then set source to NULL
if (m_system == NULL || m_mapping[0] == '\0' || stricmp(m_mapping, "NONE") == 0)
@ -21,14 +28,20 @@ void CInput::CreateSource()
// Otherwise, ask system to parse mapping into appropriate input source
m_source = m_system->ParseSource(m_mapping, !!(flags & INPUT_FLAGS_AXIS));
// Check that mapping was parsed okay and if not then fall back to default mapping
if (m_source == NULL && stricmp(m_mapping, m_defaultMapping) != 0)
// Check that mapping was parsed okay and if so acquire it
if (m_source != NULL)
m_source->Acquire();
else
{
// Otherwise, fall back to default mapping
if (stricmp(m_mapping, m_defaultMapping) != 0)
{
ErrorLog("Unable to map input %s to [%s] - switching to default [%s].\n", id, m_mapping, m_defaultMapping);
ResetToDefaultMapping();
}
}
}
}
void CInput::Initialize(CInputSystem *system)
@ -72,8 +85,8 @@ void CInput::ClearMapping()
void CInput::SetMapping(const char *mapping)
{
strncpy(m_mapping, mapping, MAX_MAPPING_LENGTH - 1);
m_mapping[MAX_MAPPING_LENGTH - 1] = '\0';
strncpy(m_mapping, mapping, MAX_MAPPING_LENGTH);
m_mapping[MAX_MAPPING_LENGTH] = '\0';
CreateSource();
}
@ -99,13 +112,23 @@ void CInput::ResetToDefaultMapping()
void CInput::ReadFromINIFile(CINIFile *ini, const char *section)
{
if (!IsConfigurable())
return;
// See if input is configurable
if (IsConfigurable())
{
// If so, check INI file for mapping string
string key("Input");
key.append(id);
string mapping;
if (ini->Get(section, key, mapping) == OKAY)
{
// If found, then set mapping string
SetMapping(mapping.c_str());
return;
}
}
// If input has not been configured, then force recreation of source anyway since input system settings may have changed
CreateSource();
}
void CInput::WriteToINIFile(CINIFile *ini, const char *section)
@ -134,7 +157,7 @@ bool CInput::Changed()
return value != prevValue;
}
bool CInput::SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd)
bool CInput::SendForceFeedbackCmd(ForceFeedbackCmd ffCmd)
{
if (m_source == NULL)
return false;

View file

@ -18,10 +18,14 @@ class CINIFile;
#define MAX_MAPPING_LENGTH 255
#define NUM_FF_EFFECTS 3
enum EForceFeedback
{
FFStop,
FFConstantForce
FFStop = -1,
FFSelfCenter = 0,
FFConstantForce = 1,
FFVibrate = 2
};
struct ForceFeedbackCmd
@ -37,7 +41,7 @@ class CInput
{
private:
// Current mapping(s) for input, eg JOY1_XAXIS_POS
char m_mapping[MAX_MAPPING_LENGTH];
char m_mapping[MAX_MAPPING_LENGTH + 1];
// Default mapping for input
const char *m_defaultMapping;
@ -54,7 +58,15 @@ protected:
// Current input source
CInputSource *m_source;
/*
* Constructs an input with the given identifier, label, flags, game flags, default mapping and initial value.
*/
CInput(const char *inputId, const char *inputLabel, unsigned inputFlags, unsigned inputGameFlags,
const char *defaultMapping = "NONE", UINT16 initValue = 0);
public:
virtual ~CInput();
// Input identifier
const char *id;
@ -73,12 +85,6 @@ public:
// Previous input value
UINT16 prevValue;
/*
* Constructs an input with the given identifier, label, flags, game flags, default mapping and initial value.
*/
CInput(const char *inputId, const char *inputLabel, unsigned inputFlags, unsigned inputGameFlags,
const char *defaultMapping = "NONE", UINT16 initValue = 0);
/*
* Initializes this input with the given input system. Must be called before any other methods are used.
*/
@ -161,7 +167,7 @@ public:
/*
* Sends a force feedback command to the input source of this input.
*/
bool SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd);
bool SendForceFeedbackCmd(ForceFeedbackCmd ffCmd);
};
//

View file

@ -1,13 +1,32 @@
#include "InputSource.h"
#include "Supermodel.h"
#include <vector>
using namespace std;
CInputSource::CInputSource(ESourceType sourceType) : type(sourceType)
CInputSource::CInputSource(ESourceType sourceType) : type(sourceType), m_acquired(0)
{
//
}
void CInputSource::Acquire()
{
m_acquired++;
#ifdef DEBUG
CInputSystem::totalSrcsAcquired++;
#endif
}
void CInputSource::Release()
{
if (--m_acquired == 0)
delete this;
#ifdef DEBUG
CInputSystem::totalSrcsReleased++;
#endif
}
int CInputSource::Clamp(int val, int minVal, int maxVal)
{
if (val > maxVal) return maxVal;
@ -22,19 +41,19 @@ int CInputSource::Scale(int val, int fromMinVal, int fromMaxVal, int toMinVal, i
int CInputSource::Scale(int val, int fromMinVal, int fromOffVal, int fromMaxVal, int toMinVal, int toOffVal, int toMaxVal)
{
int fromRange;
double fromRange;
double frac;
if (fromMaxVal > fromMinVal)
{
val = Clamp(val, fromMinVal, fromMaxVal);
if (val > fromOffVal)
{
fromRange = fromMaxVal - fromOffVal;
fromRange = (double)(fromMaxVal - fromOffVal);
frac = (double)(val - fromOffVal) / fromRange;
}
else if (val < fromOffVal)
{
fromRange = fromOffVal - fromMinVal;
fromRange = (double)(fromOffVal - fromMinVal);
frac = (double)(val - fromOffVal) / fromRange;
}
else
@ -45,12 +64,12 @@ int CInputSource::Scale(int val, int fromMinVal, int fromOffVal, int fromMaxVal,
val = Clamp(val, fromMaxVal, fromMinVal);
if (val > fromOffVal)
{
fromRange = fromMinVal - fromOffVal;
fromRange = (double)(fromMinVal - fromOffVal);
frac = (double)(fromOffVal - val) / fromRange;
}
else if (val < fromOffVal)
{
fromRange = fromOffVal - fromMaxVal;
fromRange = (double)(fromOffVal - fromMaxVal);
frac = (double)(fromOffVal - val) / fromRange;
}
else
@ -83,7 +102,7 @@ bool CInputSource::IsActive()
return GetValueAsSwitch(boolVal);
}
bool CInputSource::SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd)
bool CInputSource::SendForceFeedbackCmd(ForceFeedbackCmd ffCmd)
{
return false;
}

View file

@ -25,6 +25,8 @@ enum ESourceType
class CInputSource
{
protected:
unsigned m_acquired;
CInputSource(ESourceType sourceType);
public:
@ -33,6 +35,10 @@ public:
*/
const ESourceType type;
virtual void Acquire();
virtual void Release();
//
// Static helper methods
//
@ -73,7 +79,7 @@ public:
/*
* Sends a force feedback command to the input source.
*/
virtual bool SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd);
virtual bool SendForceFeedbackCmd(ForceFeedbackCmd ffCmd);
};
#endif // INCLUDED_INPUTSOURCE_H

View file

@ -6,6 +6,11 @@
#include <sstream>
using namespace std;
#ifdef DEBUG
unsigned CInputSystem::totalSrcsAcquired = 0;
unsigned CInputSystem::totalSrcsReleased = 0;
#endif
const char *CInputSystem::s_validKeyNames[] =
{
// General keys
@ -343,17 +348,26 @@ JoyPartsStruct CInputSystem::s_joyParts[] =
{ NULL, JoyUnknown }
};
const char *CInputSystem::s_axisNames[] = { "X", "Y", "Z", "RX", "RY", "RZ" };
CInputSystem::CInputSystem(const char *systemName) :
name(systemName), m_dispX(0), m_dispY(0), m_dispW(0), m_dispH(0), m_grabMouse(false)
{
m_emptySource = new CMultiInputSource(this);
m_emptySource = new CMultiInputSource();
m_emptySource->Acquire();
}
CInputSystem::~CInputSystem()
{
DeleteSourceCache();
m_emptySource->Release();
ClearSettings();
delete m_emptySource;
ClearSourceCache(true);
#ifdef DEBUG
if (totalSrcsAcquired != totalSrcsReleased)
printf("WARNING - number of input source acquisitions (%u) does not equal number of releases (%u)\n", totalSrcsAcquired, totalSrcsReleased);
#endif
}
void CInputSystem::CreateSourceCache()
@ -398,141 +412,95 @@ void CInputSystem::CreateSourceCache()
}
}
bool CInputSystem::IsInSourceCache(CInputSource *source)
void CInputSystem::ClearSourceCache(bool deleteCache)
{
// Check keyboard source cache
// Clear cache of keyboard sources
if (m_anyKeySources != NULL)
{
for (int keyIndex = 0; keyIndex < NUM_VALID_KEYS; keyIndex++)
ReleaseSource(m_anyKeySources[keyIndex]);
if (deleteCache)
{
if (source == m_anyKeySources[keyIndex])
return true;
}
if (m_numKbds != ANY_KEYBOARD)
{
for (int kbdNum = 0; kbdNum < m_numKbds; kbdNum++)
{
for (int keyIndex = 0; keyIndex < NUM_VALID_KEYS; keyIndex++)
{
if (source == m_keySources[kbdNum][keyIndex])
return true;
}
}
}
}
// Check mouse source cache
if (m_anyMseSources != NULL)
{
for (int mseIndex = 0; mseIndex < NUM_MOUSE_PARTS; mseIndex++)
{
if (source == m_anyMseSources[mseIndex])
return true;
}
if (m_numMice != ANY_MOUSE)
{
for (int mseNum = 0; mseNum < m_numMice; mseNum++)
{
for (int mseIndex = 0; mseIndex < NUM_MOUSE_PARTS; mseIndex++)
{
if (source == m_mseSources[mseNum][mseIndex])
return true;
}
}
}
}
// Check joystick source cache
if (m_anyJoySources != NULL)
{
for (int joyIndex = 0; joyIndex < NUM_JOY_PARTS; joyIndex++)
{
if (source == m_anyJoySources[joyIndex])
return true;
}
if (m_numJoys != ANY_JOYSTICK)
{
for (int joyNum = 0; joyNum < m_numJoys; joyNum++)
{
for (int joyIndex = 0; joyIndex < NUM_JOY_PARTS; joyIndex++)
{
if (source == m_joySources[joyNum][joyIndex])
return true;
}
}
}
}
return false;
}
void CInputSystem::DeleteSourceCache()
{
// Delete cache for keyboard sources
if (m_anyKeySources != NULL)
{
for (int keyIndex = 0; keyIndex < NUM_VALID_KEYS; keyIndex++)
DeleteSource(m_anyKeySources[keyIndex]);
delete[] m_anyKeySources;
m_anyKeySources = NULL;
}
if (m_numKbds != ANY_KEYBOARD)
{
for (int kbdNum = 0; kbdNum < m_numKbds; kbdNum++)
{
for (int keyIndex = 0; keyIndex < NUM_VALID_KEYS; keyIndex++)
DeleteSource(m_keySources[kbdNum][keyIndex]);
ReleaseSource(m_keySources[kbdNum][keyIndex]);
if (deleteCache)
delete[] m_keySources[kbdNum];
}
if (deleteCache)
{
delete[] m_keySources;
m_keySources = NULL;
}
}
}
// Delete cache for mouse sources
// Clear cache of mouse sources
if (m_anyMseSources != NULL)
{
for (int mseIndex = 0; mseIndex < NUM_MOUSE_PARTS; mseIndex++)
DeleteSource(m_anyMseSources[mseIndex]);
ReleaseSource(m_anyMseSources[mseIndex]);
if (deleteCache)
{
delete[] m_anyMseSources;
m_anyMseSources = NULL;
}
if (m_numMice != ANY_MOUSE)
{
for (int mseNum = 0; mseNum < m_numMice; mseNum++)
{
for (int mseIndex = 0; mseIndex < NUM_MOUSE_PARTS; mseIndex++)
DeleteSource(m_mseSources[mseNum][mseIndex]);
ReleaseSource(m_mseSources[mseNum][mseIndex]);
if (deleteCache)
delete[] m_mseSources[mseNum];
}
if (deleteCache)
{
delete[] m_mseSources;
m_mseSources = NULL;
}
}
}
// Delete cache for joystick sources
// Clear cache of joystick sources
if (m_anyJoySources != NULL)
{
for (int joyIndex = 0; joyIndex < NUM_JOY_PARTS; joyIndex++)
DeleteSource(m_anyJoySources[joyIndex]);
ReleaseSource(m_anyJoySources[joyIndex]);
if (deleteCache)
{
delete[] m_anyJoySources;
m_anyJoySources = NULL;
}
if (m_numJoys != ANY_JOYSTICK)
{
for (int joyNum = 0; joyNum < m_numJoys; joyNum++)
{
for (int joyIndex = 0; joyIndex < NUM_JOY_PARTS; joyIndex++)
DeleteSource(m_joySources[joyNum][joyIndex]);
ReleaseSource(m_joySources[joyNum][joyIndex]);
if (deleteCache)
delete[] m_joySources[joyNum];
}
if (deleteCache)
{
delete[] m_joySources;
m_joySources = NULL;
}
}
}
}
void CInputSystem::DeleteSource(CInputSource *source)
void CInputSystem::ReleaseSource(CInputSource *&source)
{
if (source != NULL && source != m_emptySource)
delete source;
if (source != NULL)
source->Release();
source = NULL;
}
CInputSource *CInputSystem::GetKeySource(int kbdNum, int keyIndex)
@ -546,6 +514,7 @@ CInputSource *CInputSystem::GetKeySource(int kbdNum, int keyIndex)
m_anyKeySources[keyIndex] = CreateKeySource(ANY_KEYBOARD, keyIndex);
else
m_anyKeySources[keyIndex] = CreateAnyKeySource(keyIndex);
m_anyKeySources[keyIndex]->Acquire();
}
return m_anyKeySources[keyIndex];
}
@ -553,7 +522,10 @@ CInputSource *CInputSystem::GetKeySource(int kbdNum, int keyIndex)
{
// Check keyboard source cache first
if (m_keySources[kbdNum][keyIndex] == NULL)
{
m_keySources[kbdNum][keyIndex] = CreateKeySource(kbdNum, keyIndex);
m_keySources[kbdNum][keyIndex]->Acquire();
}
return m_keySources[kbdNum][keyIndex];
}
else
@ -572,6 +544,7 @@ CInputSource *CInputSystem::GetMouseSource(int mseNum, EMousePart msePart)
m_anyMseSources[mseIndex] = CreateMouseSource(ANY_MOUSE, msePart);
else
m_anyMseSources[mseIndex] = CreateAnyMouseSource(msePart);
m_anyMseSources[mseIndex]->Acquire();
}
return m_anyMseSources[mseIndex];
}
@ -579,7 +552,10 @@ CInputSource *CInputSystem::GetMouseSource(int mseNum, EMousePart msePart)
{
// Check mouse source cache first
if (m_mseSources[mseNum][mseIndex] == NULL)
{
m_mseSources[mseNum][mseIndex] = CreateMouseSource(mseNum, msePart);
m_mseSources[mseNum][mseIndex]->Acquire();
}
return m_mseSources[mseNum][mseIndex];
}
else
@ -598,6 +574,7 @@ CInputSource *CInputSystem::GetJoySource(int joyNum, EJoyPart joyPart)
m_anyJoySources[joyIndex] = CreateJoySource(ANY_JOYSTICK, joyPart);
else
m_anyJoySources[joyIndex] = CreateAnyJoySource(joyPart);
m_anyJoySources[joyIndex]->Acquire();
}
return m_anyJoySources[joyIndex];
}
@ -605,14 +582,63 @@ CInputSource *CInputSystem::GetJoySource(int joyNum, EJoyPart joyPart)
{
// Check joystick source cache first
if (m_joySources[joyNum][joyIndex] == NULL)
{
m_joySources[joyNum][joyIndex] = CreateJoySource(joyNum, joyPart);
m_joySources[joyNum][joyIndex]->Acquire();
}
return m_joySources[joyNum][joyIndex];
}
else
return m_emptySource;
}
void CInputSystem::CheckKeySources(int kbdNum, bool fullAxisOnly, vector<CInputSource*> &sources, string &mapping)
void CInputSystem::CheckAllSources(unsigned readFlags, bool fullAxisOnly, bool &mseCentered, vector<CInputSource*> &sources, string &mapping, vector<CInputSource*> &badSources)
{
// See if should read keyboards
if (readFlags & READ_KEYBOARD)
{
// Check all keyboard sources for inputs, merging devices if required
if ((readFlags & READ_MERGE_KEYBOARD) || m_numKbds == ANY_KEYBOARD)
CheckKeySources(ANY_KEYBOARD, fullAxisOnly, sources, mapping, badSources);
else
{
for (int kbdNum = 0; kbdNum < m_numKbds; kbdNum++)
CheckKeySources(kbdNum, fullAxisOnly, sources, mapping, badSources);
}
}
// See if should read mice
if (readFlags & READ_MOUSE)
{
// For mouse input, wait until mouse is in centre of display before parsing X- and Y-axis movements
if (!mseCentered)
mseCentered = ConfigMouseCentered();
// Check all mouse sources for input, merging devices if required
if ((readFlags & READ_MERGE_MOUSE) || m_numMice == ANY_MOUSE)
CheckMouseSources(ANY_MOUSE, fullAxisOnly, mseCentered, sources, mapping, badSources);
else
{
for (int mseNum = 0; mseNum < m_numMice; mseNum++)
CheckMouseSources(mseNum, fullAxisOnly, mseCentered, sources, mapping, badSources);
}
}
// See if should read joysticks
if (readFlags & READ_JOYSTICK)
{
// Check all joystick sources, merging devices if required
if ((readFlags & READ_MERGE_JOYSTICK) || m_numJoys == ANY_JOYSTICK)
CheckJoySources(ANY_JOYSTICK, fullAxisOnly, sources, mapping, badSources);
else
{
for (int joyNum = 0; joyNum < m_numJoys; joyNum++)
CheckJoySources(joyNum, fullAxisOnly, sources, mapping, badSources);
}
}
}
void CInputSystem::CheckKeySources(int kbdNum, bool fullAxisOnly, vector<CInputSource*> &sources, string &mapping, vector<CInputSource*> &badSources)
{
// Loop through all valid keys
for (int i = 0; i < NUM_VALID_KEYS; i++)
@ -621,9 +647,10 @@ void CInputSystem::CheckKeySources(int kbdNum, bool fullAxisOnly, vector<CInputS
int keyIndex = GetKeyIndex(keyName);
if (keyIndex < 0)
continue;
// Get key source for keyboard number and key and test it to see if it is active (but was not previously)
// Get key source for keyboard number and key and test to see if it is active (but was not previously) and that it is not a "bad" source
CInputSource *source = GetKeySource(kbdNum, keyIndex);
if (source != NULL && source->IsActive() && find(sources.begin(), sources.end(), source) == sources.end())
if (source != NULL && source->IsActive() && find(sources.begin(), sources.end(), source) == sources.end() &&
find(badSources.begin(), badSources.end(), source) == badSources.end())
{
// Update mapping string and add source to list
if (sources.size() == 0)
@ -639,7 +666,7 @@ void CInputSystem::CheckKeySources(int kbdNum, bool fullAxisOnly, vector<CInputS
}
}
void CInputSystem::CheckMouseSources(int mseNum, bool fullAxisOnly, bool mseCentered, vector<CInputSource*> &sources, string &mapping)
void CInputSystem::CheckMouseSources(int mseNum, bool fullAxisOnly, bool mseCentered, vector<CInputSource*> &sources, string &mapping, vector<CInputSource*> &badSources)
{
// Loop through all mouse parts
for (int mseIndex = 0; mseIndex < NUM_MOUSE_PARTS; mseIndex++)
@ -653,9 +680,10 @@ void CInputSystem::CheckMouseSources(int mseNum, bool fullAxisOnly, bool mseCent
// Ignore X- & Y-axes if mouse hasn't been centered yet and filter axes according to fullAxisOnly
if (isXYAxis && !mseCentered || isAxis && (IsFullAxis(msePart) && !fullAxisOnly || !IsFullAxis(msePart) && fullAxisOnly))
continue;
// Get mouse source for mouse number and part and test it to see if it is active (but was not previously)
// Get mouse source for mouse number and part and test to see if it is active (but was not previously) and that it is not a "bad" source
CInputSource *source = GetMouseSource(mseNum, msePart);
if (source != NULL && source->IsActive() && find(sources.begin(), sources.end(), source) == sources.end())
if (source != NULL && source->IsActive() && find(sources.begin(), sources.end(), source) == sources.end() &&
find(badSources.begin(), badSources.end(), source) == badSources.end())
{
// Otherwise, update mapping string and add source to list
const char *partName = LookupName(msePart);
@ -672,7 +700,7 @@ void CInputSystem::CheckMouseSources(int mseNum, bool fullAxisOnly, bool mseCent
}
}
void CInputSystem::CheckJoySources(int joyNum, bool fullAxisOnly, vector<CInputSource*> &sources, string &mapping)
void CInputSystem::CheckJoySources(int joyNum, bool fullAxisOnly, vector<CInputSource*> &sources, string &mapping, vector<CInputSource*> &badSources)
{
// Loop through all joystick parts
for (int joyIndex = 0; joyIndex < NUM_JOY_PARTS; joyIndex++)
@ -681,9 +709,10 @@ void CInputSystem::CheckJoySources(int joyNum, bool fullAxisOnly, vector<CInputS
// Filter axes according to fullAxisOnly
if (IsAxis(joyPart) && (IsFullAxis(joyPart) && !fullAxisOnly || !IsFullAxis(joyPart) && fullAxisOnly))
continue;
// Get joystick source for joystick number and part and test it to see if it is active (but was not previously)
// Get joystick source for joystick number and part and test to see if it is active (but was not previously) and that it is not a "bad" source
CInputSource *source = GetJoySource(joyNum, joyPart);
if (source != NULL && source->IsActive() && find(sources.begin(), sources.end(), source) == sources.end())
if (source != NULL && source->IsActive() && find(sources.begin(), sources.end(), source) == sources.end() &&
find(badSources.begin(), badSources.end(), source) == badSources.end())
{
// Otherwise, update mapping string and add source to list
const char *partName = LookupName(joyPart);
@ -870,7 +899,7 @@ CInputSource* CInputSystem::ParseMultiSource(string str, bool fullAxisOnly, bool
while (start < size);
// If only parsed a single source, return that, otherwise return a CMultiInputSource combining all the sources
return (isMulti ? new CMultiInputSource(this, isOr, sources) : source);
return (isMulti ? new CMultiInputSource(isOr, sources) : source);
}
CInputSource *CInputSystem::ParseSingleSource(string str)
@ -886,7 +915,7 @@ CInputSource *CInputSystem::ParseSingleSource(string str)
CInputSource *source = ParseSingleSource(str);
if (source != NULL && source != m_emptySource)
return new CNegInputSource(this, source);
return new CNegInputSource(source);
else
return source;
}
@ -924,7 +953,7 @@ CInputSource *CInputSystem::ParseSingleSource(string str)
sources.push_back(rightSource);
}
if (sources.size() > 0)
return new CMultiInputSource(this, true, sources);
return new CMultiInputSource(true, sources);
}
return m_emptySource;
}
@ -1037,9 +1066,12 @@ void CInputSystem::PrintMouseSettings(MouseSettings *settings)
}
// Print all common settings and any settings that are different to common/default settings
if (common == NULL || settings->xDeadZone != common->xDeadZone) printf(" X Dead Zone = %d %%\n", settings->xDeadZone);
if (common == NULL || settings->yDeadZone != common->yDeadZone) printf(" Y Dead Zone = %d %%\n", settings->yDeadZone);
if (common == NULL || settings->zDeadZone != common->zDeadZone) printf(" Z Dead Zone = %d %%\n", settings->zDeadZone);
for (int axisNum = 0; axisNum < NUM_MOUSE_AXES; axisNum++)
{
const char *axisName = s_axisNames[axisNum];
if (common == NULL || settings->deadZones[axisNum] != common->deadZones[axisNum])
printf(" %s-Axis Dead Zone = %d %%\n", axisName, settings->deadZones[axisNum]);
}
}
MouseSettings *CInputSystem::ReadMouseSettings(CINIFile *ini, const char *section, int mseNum)
@ -1054,9 +1086,11 @@ MouseSettings *CInputSystem::ReadMouseSettings(CINIFile *ini, const char *sectio
if (mseNum != ANY_MOUSE)
baseKey.append(IntToString(mseNum + 1));
bool read = false;
read |= ini->Get(section, baseKey + "XDeadZone", settings->xDeadZone) == OKAY;
read |= ini->Get(section, baseKey + "YDeadZone", settings->yDeadZone) == OKAY;
read |= ini->Get(section, baseKey + "ZDeadZone", settings->zDeadZone) == OKAY;
for (int axisNum = 0; axisNum < NUM_MOUSE_AXES; axisNum++)
{
const char *axisName = s_axisNames[axisNum];
read |= ini->Get(section, baseKey + axisName + "DeadZone", settings->deadZones[axisNum]) == OKAY;
}
if (read)
return settings;
delete settings;
@ -1072,9 +1106,12 @@ void CInputSystem::WriteMouseSettings(CINIFile *ini, const char *section, MouseS
string baseKey("InputMouse");
if (settings->mseNum != ANY_MOUSE)
baseKey.append(IntToString(settings->mseNum + 1));
if (settings->xDeadZone != common->xDeadZone) ini->Set(section, baseKey + "XDeadZone", settings->xDeadZone);
if (settings->yDeadZone != common->yDeadZone) ini->Set(section, baseKey + "YDeadZone", settings->yDeadZone);
if (settings->zDeadZone != common->zDeadZone) ini->Set(section, baseKey + "ZDeadZone", settings->zDeadZone);
for (int axisNum = 0; axisNum < NUM_MOUSE_AXES; axisNum++)
{
const char *axisName = s_axisNames[axisNum];
if (settings->deadZones[axisNum] != common->deadZones[axisNum])
ini->Set(section, baseKey + axisName + "DeadZone", settings->deadZones[axisNum]);
}
}
void CInputSystem::PrintJoySettings(JoySettings *settings)
@ -1093,18 +1130,20 @@ void CInputSystem::PrintJoySettings(JoySettings *settings)
}
// Print all common settings and any settings that are different to common/default settings
if (common == NULL || settings->xDeadZone != common->xDeadZone) printf(" X-Axis Dead Zone = %d %%\n", settings->xDeadZone);
if (common == NULL || settings->xSaturation != common->xSaturation) printf(" X-Axis Saturation = %d %%\n", settings->xSaturation);
if (common == NULL || settings->yDeadZone != common->yDeadZone) printf(" Y-Axis Dead Zone = %d %%\n", settings->yDeadZone);
if (common == NULL || settings->ySaturation != common->ySaturation) printf(" Y-Axis Saturation = %d %%\n", settings->ySaturation);
if (common == NULL || settings->zDeadZone != common->zDeadZone) printf(" Z-Axis Dead Zone = %d %%\n", settings->zDeadZone);
if (common == NULL || settings->zSaturation != common->zSaturation) printf(" Z-Axis Saturation = %d %%\n", settings->zSaturation);
if (common == NULL || settings->rxDeadZone != common->rxDeadZone) printf(" RX-Axis Dead Zone = %d %%\n", settings->rxDeadZone);
if (common == NULL || settings->rxSaturation != common->rxSaturation) printf(" RX-Axis Saturation = %d %%\n", settings->rxSaturation);
if (common == NULL || settings->ryDeadZone != common->ryDeadZone) printf(" RY-Axis Dead Zone = %d %%\n", settings->ryDeadZone);
if (common == NULL || settings->rySaturation != common->rySaturation) printf(" RY-Axis Saturation = %d %%\n", settings->rySaturation);
if (common == NULL || settings->rzDeadZone != common->rzDeadZone) printf(" RZ-Axis Dead Zone = %d %%\n", settings->rzDeadZone);
if (common == NULL || settings->rzSaturation != common->rzSaturation) printf(" RZ-Axis Saturation = %d %%\n", settings->rzSaturation);
for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
{
const char *axisName = s_axisNames[axisNum];
if (common == NULL || settings->axisMinVals[axisNum] != common->axisMinVals[axisNum])
printf(" %-2s-Axis Min Value = %d\n", axisName, settings->axisMinVals[axisNum]);
if (common == NULL || settings->axisOffVals[axisNum] != common->axisOffVals[axisNum])
printf(" %-2s-Axis Center/Off Value = %d\n", axisName, settings->axisOffVals[axisNum]);
if (common == NULL || settings->axisMaxVals[axisNum] != common->axisMaxVals[axisNum])
printf(" %-2s-Axis Max Value = %d\n", axisName, settings->axisMaxVals[axisNum]);
if (common == NULL || settings->deadZones[axisNum] != common->deadZones[axisNum])
printf(" %-2s-Axis Dead Zone = %d %%\n", axisName, settings->deadZones[axisNum]);
if (common == NULL || settings->saturations[axisNum] != common->saturations[axisNum])
printf(" %-2s-Axis Saturation = %d %%\n", axisName, settings->saturations[axisNum]);
}
}
JoySettings *CInputSystem::ReadJoySettings(CINIFile *ini, const char *section, int joyNum)
@ -1119,18 +1158,15 @@ JoySettings *CInputSystem::ReadJoySettings(CINIFile *ini, const char *section, i
if (joyNum != ANY_JOYSTICK)
baseKey.append(IntToString(joyNum + 1));
bool read = false;
read |= ini->Get(section, baseKey + "XDeadZone", settings->xDeadZone) == OKAY;
read |= ini->Get(section, baseKey + "XSaturation", settings->xSaturation) == OKAY;
read |= ini->Get(section, baseKey + "YDeadZone", settings->yDeadZone) == OKAY;
read |= ini->Get(section, baseKey + "YSaturation", settings->ySaturation) == OKAY;
read |= ini->Get(section, baseKey + "ZDeadZone", settings->zDeadZone) == OKAY;
read |= ini->Get(section, baseKey + "ZSaturation", settings->zSaturation) == OKAY;
read |= ini->Get(section, baseKey + "RXDeadZone", settings->rxDeadZone) == OKAY;
read |= ini->Get(section, baseKey + "RXSaturation", settings->rxSaturation) == OKAY;
read |= ini->Get(section, baseKey + "RYDeadZone", settings->ryDeadZone) == OKAY;
read |= ini->Get(section, baseKey + "RYSaturation", settings->rySaturation) == OKAY;
read |= ini->Get(section, baseKey + "RZDeadZone", settings->rzDeadZone) == OKAY;
read |= ini->Get(section, baseKey + "RZSaturation", settings->rzSaturation) == OKAY;
for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
{
const char *axisName = s_axisNames[axisNum];
read |= ini->Get(section, baseKey + axisName + "MinVal", settings->axisMinVals[axisNum]) == OKAY;
read |= ini->Get(section, baseKey + axisName + "OffVal", settings->axisOffVals[axisNum]) == OKAY;
read |= ini->Get(section, baseKey + axisName + "MaxVal", settings->axisMaxVals[axisNum]) == OKAY;
read |= ini->Get(section, baseKey + axisName + "DeadZone", settings->deadZones[axisNum]) == OKAY;
read |= ini->Get(section, baseKey + axisName + "Saturation", settings->saturations[axisNum]) == OKAY;
}
if (read)
return settings;
delete settings;
@ -1146,18 +1182,20 @@ void CInputSystem::WriteJoySettings(CINIFile *ini, const char *section, JoySetti
string baseKey("InputJoy");
if (settings->joyNum != ANY_JOYSTICK)
baseKey.append(IntToString(settings->joyNum + 1));
if (settings->xDeadZone != common->xDeadZone) ini->Set(section, baseKey + "XDeadZone", settings->xDeadZone);
if (settings->xSaturation != common->xSaturation) ini->Set(section, baseKey + "XSaturation", settings->xSaturation);
if (settings->yDeadZone != common->yDeadZone) ini->Set(section, baseKey + "YDeadZone", settings->yDeadZone);
if (settings->ySaturation != common->ySaturation) ini->Set(section, baseKey + "YSaturation", settings->ySaturation);
if (settings->zDeadZone != common->zDeadZone) ini->Set(section, baseKey + "ZDeadZone", settings->zDeadZone);
if (settings->zSaturation != common->zSaturation) ini->Set(section, baseKey + "ZSaturation", settings->zSaturation);
if (settings->rxDeadZone != common->rxDeadZone) ini->Set(section, baseKey + "RXDeadZone", settings->rxDeadZone);
if (settings->rxSaturation != common->rxSaturation) ini->Set(section, baseKey + "RXSaturation", settings->rxSaturation);
if (settings->ryDeadZone != common->ryDeadZone) ini->Set(section, baseKey + "RYDeadZone", settings->ryDeadZone);
if (settings->rySaturation != common->rySaturation) ini->Set(section, baseKey + "RYSaturation", settings->rySaturation);
if (settings->rzDeadZone != common->rzDeadZone) ini->Set(section, baseKey + "RZDeadZone", settings->rzDeadZone);
if (settings->rzSaturation != common->rzSaturation) ini->Set(section, baseKey + "RZSaturation", settings->rzSaturation);
for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
{
const char *axisName = s_axisNames[axisNum];
if (settings->axisMinVals[axisNum] != common->axisMinVals[axisNum])
ini->Set(section, baseKey + axisName + "MinVal", settings->axisMinVals[axisNum]);
if (settings->axisOffVals[axisNum] != common->axisOffVals[axisNum])
ini->Set(section, baseKey + axisName + "OffVal", settings->axisOffVals[axisNum]);
if (settings->axisMaxVals[axisNum] != common->axisMaxVals[axisNum])
ini->Set(section, baseKey + axisName + "MaxVal", settings->axisMaxVals[axisNum]);
if (settings->deadZones[axisNum] != common->deadZones[axisNum])
ini->Set(section, baseKey + axisName + "DeadZone", settings->deadZones[axisNum]);
if (settings->saturations[axisNum] != common->saturations[axisNum])
ini->Set(section, baseKey + axisName + "Saturation", settings->saturations[axisNum]);
}
}
KeySettings *CInputSystem::GetKeySettings(int kbdNum, bool useDefault)
@ -1338,7 +1376,7 @@ CInputSource *CInputSystem::CreateAnyKeySource(int keyIndex)
if (keySrc != NULL)
keySrcs.push_back(keySrc);
}
return new CMultiInputSource(this, true, keySrcs);
return new CMultiInputSource(true, keySrcs);
}
CInputSource *CInputSystem::CreateAnyMouseSource(EMousePart msePart)
@ -1351,7 +1389,7 @@ CInputSource *CInputSystem::CreateAnyMouseSource(EMousePart msePart)
if (mseSrc != NULL)
mseSrcs.push_back(mseSrc);
}
return new CMultiInputSource(this, true, mseSrcs);
return new CMultiInputSource(true, mseSrcs);
}
CInputSource *CInputSystem::CreateAnyJoySource(EJoyPart joyPart)
@ -1364,7 +1402,7 @@ CInputSource *CInputSystem::CreateAnyJoySource(EJoyPart joyPart)
if (joySrc != NULL)
joySrcs.push_back(joySrc);
}
return new CMultiInputSource(this, true, joySrcs);
return new CMultiInputSource(true, joySrcs);
}
CInputSource *CInputSystem::CreateKeySource(int kbdNum, int keyIndex)
@ -1386,16 +1424,8 @@ CInputSource *CInputSystem::CreateMouseSource(int mseNum, EMousePart msePart)
int axisDir;
if (GetAxisDetails(msePart, axisNum, axisDir))
{
// Part is mouse axis so get the deadzone setting for it
unsigned deadZone;
switch (axisNum)
{
case AXIS_X: deadZone = settings->xDeadZone; break;
case AXIS_Y: deadZone = settings->yDeadZone; break;
case AXIS_Z: deadZone = settings->zDeadZone; break;
default: return NULL; // Any other axis numbers are invalid
}
return new CMseAxisInputSource(this, mseNum, axisNum, axisDir, deadZone);
// Part is mouse axis so create axis source with appropriate deadzone setting
return new CMseAxisInputSource(this, mseNum, axisNum, axisDir, settings->deadZones[axisNum]);
}
else if (IsButton(msePart))
{
@ -1423,23 +1453,13 @@ CInputSource *CInputSystem::CreateJoySource(int joyNum, EJoyPart joyPart)
int povDir;
if (GetAxisDetails(joyPart, axisNum, axisDir))
{
// Part is joystick axis so get the deadzone and saturation settings for it
unsigned axisDZone;
unsigned axisSat;
switch (axisNum)
{
case AXIS_X: axisDZone = settings->xDeadZone; axisSat = settings->xSaturation; break;
case AXIS_Y: axisDZone = settings->yDeadZone; axisSat = settings->ySaturation; break;
case AXIS_Z: axisDZone = settings->zDeadZone; axisSat = settings->zSaturation; break;
case AXIS_RX: axisDZone = settings->rxDeadZone; axisSat = settings->rxSaturation; break;
case AXIS_RY: axisDZone = settings->ryDeadZone; axisSat = settings->rySaturation; break;
case AXIS_RZ: axisDZone = settings->rzDeadZone; axisSat = settings->rzSaturation; break;
default: return NULL; // Any other axis numbers are invalid
}
// See whether joystick has this axis
// Part is joystick axis, so see whether joystick actually has this axis
if (!joyDetails->hasAxis[axisNum])
return m_emptySource; // If joystick doesn't have axis, then return empty source rather than NULL as not really an error
return new CJoyAxisInputSource(this, joyNum, axisNum, axisDir, axisDZone, axisSat);
// Otherwise, create axis source with appropriate axis range, deadzone and saturation settings
return new CJoyAxisInputSource(this, joyNum, axisNum, axisDir,
settings->axisMinVals[axisNum], settings->axisOffVals[axisNum], settings->axisMaxVals[axisNum],
settings->deadZones[axisNum], settings->saturations[axisNum]);
}
else if (GetPOVDetails(joyPart, povNum, povDir))
{
@ -1452,7 +1472,7 @@ CInputSource *CInputSystem::CreateJoySource(int joyNum, EJoyPart joyPart)
{
// Part is joystick button so map it to a button number
int butNum = GetButtonNumber(joyPart);
if (butNum < 0)
if (butNum < 0 || butNum >= NUM_JOY_BUTTONS)
return NULL; // Buttons out of range are invalid
if (butNum >= joyDetails->numButtons)
return m_emptySource; // If joystick doesn't have button, then return empty source rather than NULL as not really an error
@ -1493,13 +1513,6 @@ CInputSource* CInputSystem::ParseSource(const char *mapping, bool fullAxisOnly)
return ParseMultiSource(mapping, fullAxisOnly, true);
}
void CInputSystem::ReleaseSource(CInputSource *source)
{
// If source is not being cached then delete it
if (!IsInSourceCache(source))
DeleteSource(source);
}
void CInputSystem::ClearSettings()
{
// Delete all key settings
@ -1568,6 +1581,7 @@ void CInputSystem::PrintSettings()
void CInputSystem::ReadFromINIFile(CINIFile *ini, const char *section)
{
ClearSettings();
ClearSourceCache();
// Read all key settings for attached keyboards
KeySettings *keySettings = ReadKeySettings(ini, section, ANY_KEYBOARD);
@ -1622,17 +1636,33 @@ bool CInputSystem::ReadMapping(char *buffer, unsigned bufSize, bool fullAxisOnly
{
// Map given escape mapping to an input source
CInputSource *escape = ParseSource(escapeMapping);
escape->Acquire();
string badMapping;
string mapping;
vector<CInputSource*> badSources;
vector<CInputSource*> sources;
bool mseCentered = 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;
}
CheckAllSources(readFlags, fullAxisOnly, mseCentered, badSources, badMapping, sources);
// Loop until have received meaningful inputs
for (;;)
{
// Poll inputs
if (!Poll())
{
escape->Release();
return false;
}
// Check if escape source was triggered
if (escape != NULL && escape->IsActive())
@ -1641,56 +1671,20 @@ bool CInputSystem::ReadMapping(char *buffer, unsigned bufSize, bool fullAxisOnly
while (escape->IsActive())
{
if (!Poll())
{
escape->Release();
return false;
}
Wait(1000/60);
}
escape->Release();
return false;
}
// See if should read keyboards
if (readFlags & READ_KEYBOARD)
{
// Check all keyboard sources for inputs, merging devices if required
if ((readFlags & READ_MERGE_KEYBOARD) || m_numKbds == ANY_KEYBOARD)
CheckKeySources(ANY_KEYBOARD, fullAxisOnly, sources, mapping);
else
{
for (int kbdNum = 0; kbdNum < m_numKbds; kbdNum++)
CheckKeySources(kbdNum, fullAxisOnly, sources, mapping);
}
}
// Check all active sources
CheckAllSources(readFlags, fullAxisOnly, mseCentered, sources, mapping, badSources);
// See if should read mice
if (readFlags & READ_MOUSE)
{
// For mouse input, wait until mouse is in centre of display before parsing X- and Y-axis movements
if (!mseCentered)
mseCentered = ConfigMouseCentered();
// Check all mouse sources for input, merging devices if required
if ((readFlags & READ_MERGE_MOUSE) || m_numMice == ANY_MOUSE)
CheckMouseSources(ANY_MOUSE, fullAxisOnly, mseCentered, sources, mapping);
else
{
for (int mseNum = 0; mseNum < m_numMice; mseNum++)
CheckMouseSources(mseNum, fullAxisOnly, mseCentered, sources, mapping);
}
}
// See if should read joysticks
if (readFlags & READ_JOYSTICK)
{
// Check all joystick sources, merging devices if required
if ((readFlags & READ_MERGE_JOYSTICK) || m_numJoys == ANY_JOYSTICK)
CheckJoySources(ANY_JOYSTICK, fullAxisOnly, sources, mapping);
else
{
for (int joyNum = 0; joyNum < m_numJoys; joyNum++)
CheckJoySources(joyNum, fullAxisOnly, sources, mapping);
}
}
// When some inputs have been activated, keep lopping until they have all been released again.
// When some inputs have been activated, keep looping until they have all been released again.
if (sources.size() > 0)
{
// Check each source is no longer active
@ -1723,6 +1717,8 @@ bool CInputSystem::ReadMapping(char *buffer, unsigned bufSize, bool fullAxisOnly
// Copy mapping to buffer and return
strncpy(buffer, mapping.c_str(), bufSize - 1);
buffer[bufSize - 1] = '\0';
escape->Release();
return true;
}
@ -1739,6 +1735,62 @@ void CInputSystem::UngrabMouse()
SetMouseVisibility(true);
}
bool CInputSystem::SendForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd)
{
const JoyDetails *joyDetails = GetJoyDetails(joyNum);
if (!joyDetails->hasFFeedback || !joyDetails->axisHasFF[axisNum])
return false;
return ProcessForceFeedbackCmd(joyNum, axisNum, ffCmd);
}
void CInputSystem::PrintDevices()
{
puts("Keyboards:");
if (m_numKbds == 0)
puts(" None");
else if (m_numKbds == ANY_KEYBOARD)
puts(" System Keyboard");
else
{
for (int kbdNum = 0; kbdNum < m_numKbds; kbdNum++)
{
const KeyDetails *keyDetails = GetKeyDetails(kbdNum);
printf(" %d: %s\n", kbdNum + 1, keyDetails->name);
}
}
puts("Mice:");
if (m_numMice == 0)
puts(" None");
else if (m_numMice == ANY_MOUSE)
puts(" System Mouse");
else
{
for (int mseNum = 0; mseNum < m_numMice; mseNum++)
{
const MouseDetails *mseDetails = GetMouseDetails(mseNum);
printf(" %d: %s\n", mseNum + 1, mseDetails->name);
}
}
puts("Joysticks:");
if (m_numJoys == 0)
puts(" None");
else if (m_numJoys == ANY_JOYSTICK)
puts(" System Joystick");
else
{
for (int joyNum = 0; joyNum < m_numJoys; joyNum++)
{
const JoyDetails *joyDetails = GetJoyDetails(joyNum);
if (joyDetails->hasFFeedback)
printf(" %d: %s [Force Feedback Available]\n", joyNum + 1, joyDetails->name);
else
printf(" %d: %s\n", joyNum + 1, joyDetails->name);
}
}
}
/*
* CInputSystem::CKeyInputSource
*/
@ -1888,26 +1940,28 @@ bool CInputSystem::CMseButInputSource::GetValueAsAnalog(int &val, int minVal, in
/*
* CInputSystem::CJoyAxisInputSource
*/
CInputSystem::CJoyAxisInputSource::CJoyAxisInputSource(CInputSystem *system, int joyNum, int axisNum, int axisDir, unsigned deadZone, unsigned saturation) :
CInputSystem::CJoyAxisInputSource::CJoyAxisInputSource(CInputSystem *system, int joyNum, int axisNum, int axisDir,
int axisMinVal, int axisOffVal, int axisMaxVal, unsigned deadZone, unsigned saturation) :
CInputSource(axisDir == AXIS_FULL || axisDir == AXIS_INVERTED ? SourceFullAxis : SourceHalfAxis),
m_system(system), m_joyNum(joyNum), m_axisNum(axisNum), m_axisDir(axisDir)
m_system(system), m_joyNum(joyNum), m_axisNum(axisNum), m_axisDir(axisDir), m_axisMinVal(axisMinVal), m_axisOffVal(axisOffVal), m_axisMaxVal(axisMaxVal)
{
// Calculate pos/neg deadzone and saturation points (joystick raw values range from -32768 to 32767, deadzone given as
// percentage 0-99 and saturation given as percentage 1-100)
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)
double dDeadZone = (double)Clamp((int)deadZone, 0, 99) / 100.0;
double dSaturation = (double)Clamp((int)saturation, (int)deadZone + 1, 100) / 100.0;
m_posDZone = (int)(dDeadZone * 32767.0);
m_negDZone = (int)(dDeadZone * -32768.0);
m_posSat = (int)(dSaturation * 32767.0);
m_negSat = (int)(dSaturation * -32868.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));
m_negSat = m_axisOffVal + (int)(dSaturation * (m_axisMinVal - m_axisOffVal));
}
int CInputSystem::CJoyAxisInputSource::ScaleAxisValue(int minVal, int offVal, int maxVal)
{
// Get raw axis value from input system (values range from -32768 to 32767)
// Get raw axis value from input system
int joyVal = m_system->GetJoyAxisValue(m_joyNum, m_axisNum);
// Check value is not zero
if (joyVal == 0)
// Check if value is at axis off value
if (joyVal == m_axisOffVal)
return offVal;
// Scale value between deadzone and saturation points, taking positive or negative values only or using the whole axis range as required
if (m_axisDir == AXIS_POS) return Scale(joyVal, m_posDZone, m_posDZone, m_posSat, minVal, offVal, maxVal);
@ -1915,14 +1969,18 @@ int CInputSystem::CJoyAxisInputSource::ScaleAxisValue(int minVal, int offVal, in
else if (m_axisDir == AXIS_FULL)
{
// Full axis range
if (joyVal > 0) return Scale(joyVal, m_posDZone, m_posDZone, m_posSat, minVal, offVal, maxVal);
else return Scale(joyVal, m_negSat, m_negDZone, m_negDZone, minVal, offVal, maxVal);
if (!m_axisInverted && joyVal > m_axisOffVal || m_axisInverted && joyVal < m_axisOffVal)
return Scale(joyVal, m_posDZone, m_posDZone, m_posSat, minVal, offVal, maxVal);
else
return Scale(joyVal, m_negSat, m_negDZone, m_negDZone, minVal, offVal, maxVal);
}
else
{
// Full axis range, but inverted
if (joyVal > 0) return Scale(joyVal, m_posDZone, m_posDZone, m_posSat, maxVal, offVal, minVal);
else return Scale(joyVal, m_negSat, m_negDZone, m_negDZone, maxVal, offVal, minVal);
if (!m_axisInverted && joyVal > m_axisOffVal || m_axisInverted && joyVal < m_axisOffVal)
return Scale(joyVal, m_posDZone, m_posDZone, m_posSat, maxVal, offVal, minVal);
else
return Scale(joyVal, m_negSat, m_negDZone, m_negDZone, maxVal, offVal, minVal);
}
}
@ -1943,7 +2001,7 @@ bool CInputSystem::CJoyAxisInputSource::GetValueAsAnalog(int &val, int minVal, i
return true;
}
bool CInputSystem::CJoyAxisInputSource::SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd)
bool CInputSystem::CJoyAxisInputSource::SendForceFeedbackCmd(ForceFeedbackCmd ffCmd)
{
return m_system->SendForceFeedbackCmd(m_joyNum, m_axisNum, ffCmd);
}

View file

@ -33,6 +33,9 @@ class CINIFile;
#define DEFAULT_KEY_SENSITIVITY 25
#define DEFAULT_KEY_DECAYSPEED 50
#define DEFAULT_MSE_DEADZONE 0
#define DEFAULT_JOY_AXISMINVAL -32768
#define DEFAULT_JOY_AXISOFFVAL 0
#define DEFAULT_JOY_AXISMAXVAL 32767
#define DEFAULT_JOY_DEADZONE 3
#define DEFAULT_JOY_SATURATION 100
@ -209,9 +212,7 @@ struct KeySettings
struct MouseSettings
{
int mseNum; // Mouse number (or ANY_MOUSE for settings that apply to all mice)
unsigned xDeadZone; // X-Axis dead zone as a percentage 0-99 of display width
unsigned yDeadZone; // Y-Axis dead zone as a percentage 0-99 of display height
unsigned zDeadZone; // Z-Axis dead zone as a percentage 0-99 of axis range
unsigned deadZones[NUM_MOUSE_AXES]; // Axis dead zone as a percentage 0-99 of display width (X)/height (Y) or axis range (Z)
/*
* Creates a MouseSettings with default settings
@ -219,9 +220,9 @@ struct MouseSettings
MouseSettings()
{
mseNum = ANY_MOUSE;
xDeadZone = DEFAULT_MSE_DEADZONE;
yDeadZone = DEFAULT_MSE_DEADZONE;
zDeadZone = 0;
deadZones[AXIS_X] = DEFAULT_MSE_DEADZONE;
deadZones[AXIS_Y] = DEFAULT_MSE_DEADZONE;
deadZones[AXIS_Z] = 0;
}
};
@ -231,18 +232,11 @@ struct MouseSettings
struct JoySettings
{
int joyNum; // Joystick number (or ANY_JOYSTICK for settings that apply to all joysticks)
unsigned xDeadZone; // X-Axis dead zone as a percentage 0-99 of axis positive/negative ranges
unsigned xSaturation; // X-Axis saturation as a percentage 1-100 of axis positive/negative ranges
unsigned yDeadZone; // Y-Axis dead zone as a percentage 0-99 of axis positive/negative ranges
unsigned ySaturation; // Y-Axis saturation as a percentage 1-100 of axis positive/negative ranges
unsigned zDeadZone; // Z-Axis dead zone as a percentage 0-99 of axis positive/negative ranges
unsigned zSaturation; // Z-Axis saturation as a percentage 1-100 of axis positive/negative ranges
unsigned rxDeadZone; // RX-Axis dead zone as a percentage 0-99 of axis positive/negative ranges
unsigned rxSaturation; // RX-Axis saturation as a percentage 1-100 of joystick axis positive/negative ranges
unsigned ryDeadZone; // RY-Axis dead zone as a percentage 0-99 of axis positive/negative ranges
unsigned rySaturation; // RY-Axis saturation as a percentage 1-100 of joystick axis positive/negative ranges
unsigned rzDeadZone; // RZ-Axis dead zone as a percentage 0-99 of axis positive/negative ranges
unsigned rzSaturation; // RZ-Axis saturation as a percentage 1-100 of joystick axis positive/negative ranges
int axisMinVals[NUM_JOY_AXES]; // Axis min raw value (default -32768)
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
/*
* Creates a JoySettings with default settings
@ -250,35 +244,31 @@ struct JoySettings
JoySettings()
{
joyNum = ANY_JOYSTICK;
xDeadZone = DEFAULT_JOY_DEADZONE;
xSaturation = DEFAULT_JOY_SATURATION;
yDeadZone = DEFAULT_JOY_DEADZONE;
ySaturation = DEFAULT_JOY_SATURATION;
zDeadZone = DEFAULT_JOY_DEADZONE;
zSaturation = DEFAULT_JOY_SATURATION;
rxDeadZone = DEFAULT_JOY_DEADZONE;
rxSaturation = DEFAULT_JOY_SATURATION;
ryDeadZone = DEFAULT_JOY_DEADZONE;
rySaturation = DEFAULT_JOY_SATURATION;
rzDeadZone = DEFAULT_JOY_DEADZONE;
rzSaturation = DEFAULT_JOY_SATURATION;
for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
{
axisMinVals[axisNum] = DEFAULT_JOY_AXISMINVAL;
axisOffVals[axisNum] = DEFAULT_JOY_AXISOFFVAL;
axisMaxVals[axisNum] = DEFAULT_JOY_AXISMAXVAL;
deadZones[axisNum] = DEFAULT_JOY_DEADZONE;
saturations[axisNum] = DEFAULT_JOY_SATURATION;
}
}
};
struct KeyDetails
{
char name[MAX_NAME_LENGTH]; // Keyboard name (if available)
char name[MAX_NAME_LENGTH + 1]; // Keyboard name (if available)
};
struct MouseDetails
{
char name[MAX_NAME_LENGTH]; // Mouse name (if available)
char name[MAX_NAME_LENGTH + 1]; // Mouse name (if available)
bool isAbsolute; // True if uses absolute positions (ie lightgun)
};
struct JoyDetails
{
char name[MAX_NAME_LENGTH]; // Joystick name (if available)
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
@ -303,6 +293,9 @@ private:
// Lookup table for translating joystick mapping strings to their respective joystick parts
static JoyPartsStruct s_joyParts[];
// Names of axes
static const char *s_axisNames[];
// Number of keyboards, mice and joysticks
int m_numKbds;
int m_numMice;
@ -334,24 +327,19 @@ private:
//
/*
* Creates cache for all sources.
* Creates source cache.
*/
void CreateSourceCache();
/*
* Returns true if the given source is in the source cache.
* Clears cache of all sources and optionally deletes cache itself.
*/
bool IsInSourceCache(CInputSource *source);
void ClearSourceCache(bool deleteCache = false);
/*
* Deletes cache for all sources.
* Releases a source from the cache.
*/
void DeleteSourceCache();
/*
* Deletes an input source.
*/
void DeleteSource(CInputSource *source);
void ReleaseSource(CInputSource *&source);
/*
* Returns a key source for the given keyboard number (or all keyboards if ANY_KEYBOARD supplied) and key index.
@ -371,26 +359,28 @@ private:
*/
CInputSource *GetJoySource(int joyNum, EJoyPart joyPart);
void CheckAllSources(unsigned readFlags, bool fullAxisOnly, bool &mseCentered, vector<CInputSource*> &sources, string &mapping, vector<CInputSource*> &badSources);
/*
* Finds any currently activated key sources for the given keyboard number (or all keyboards if ANY_KEYBOARD supplied)
* and adds them to the sources vector, aswell as constructing the corresponding mapping(s) in the given string.
* If fullAxisOnly is true, then only sources that represent a full axis range (eg MouseXAxis) are considered.
*/
void CheckKeySources(int kbdNum, bool fullAxisOnly, vector<CInputSource*> &sources, string &mapping);
void CheckKeySources(int kbdNum, bool fullAxisOnly, vector<CInputSource*> &sources, string &mapping, vector<CInputSource*> &badSources);
/*
* Finds any currently activated mouse sources for the given mouse number (or all mice if ANY_MOUSE supplied)
* and adds them to the sources vector, aswell as constructing the corresponding mapping(s) in the given string.
* If fullAxisOnly is true, then only sources that represent a full axis range (eg MouseXAxis) are considered.
*/
void CheckMouseSources(int mseNum, bool fullAxisOnly, bool mseCentered, vector<CInputSource*> &sources, string &mapping);
void CheckMouseSources(int mseNum, bool fullAxisOnly, bool mseCentered, vector<CInputSource*> &sources, string &mapping, vector<CInputSource*> &badSources);
/*
* Finds any currently activated joystick sources for the given joystick number (or all joysticks if ANY_JOYSTICK supplied)
* and adds them to the sources vector, aswell as constructing the corresponding mapping(s) in the given string.
* If fullAxisOnly is true, then only sources that represent a full axis range (eg MouseXAxis) are considered.
*/
void CheckJoySources(int joyNum, bool fullAxisOnly, vector<CInputSource*> &sources, string &mapping);
void CheckJoySources(int joyNum, bool fullAxisOnly, vector<CInputSource*> &sources, string &mapping, vector<CInputSource*> &badSources);
bool ParseInt(string str, int &num);
@ -653,7 +643,7 @@ protected:
/*
* Processes the given force feedback command for the given joystick and axis number.
*/
virtual bool ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd *ffCmd) = 0;
virtual bool ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd) = 0;
/*
* Waits for the given time in milliseconds
@ -700,6 +690,10 @@ protected:
virtual CInputSource *CreateJoySource(int joyNum, EJoyPart joyPart);
public:
#ifdef DEBUG
static unsigned totalSrcsAcquired;
static unsigned totalSrcsReleased;
#endif
// Name of this input system
const char *name;
@ -793,11 +787,6 @@ public:
*/
CInputSource* ParseSource(const char *mapping, bool fullAxisOnly = false);
/*
* Releases the given source when it is no longer in use.
*/
void ReleaseSource(CInputSource *source);
/*
* Waits for any input from the user and once received copies a mapping configuration representing the input (eg KEY_A or JOY1_AXIS_POS)
* into the given buffer.
@ -809,7 +798,7 @@ public:
bool ReadMapping(char *buffer, unsigned bufSize, bool fullAxisOnly = false, unsigned readFlags = READ_ALL, const char *escapeMapping = "KEY_ESCAPE");
/*
* Updates the current state of the input system (called by CInputs::Poll).
* Updates the current state of the input system (called by CInputs.Poll).
*/
virtual bool Poll() = 0;
@ -822,61 +811,9 @@ public:
*/
virtual void SetMouseVisibility(bool visible) = 0;
virtual bool SendForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd *ffCmd)
{
const JoyDetails *joyDetails = GetJoyDetails(joyNum);
if (!joyDetails->hasFFeedback || !joyDetails->axisHasFF[axisNum])
return false;
return ProcessForceFeedbackCmd(joyNum, axisNum, ffCmd);
}
virtual bool SendForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd);
void PrintDevices()
{
puts("Keyboards:");
if (m_numKbds == 0)
puts(" None");
else if (m_numKbds == ANY_KEYBOARD)
puts(" System Keyboard");
else
{
for (int kbdNum = 0; kbdNum < m_numKbds; kbdNum++)
{
const KeyDetails *keyDetails = GetKeyDetails(kbdNum);
printf(" %d: %s\n", kbdNum + 1, keyDetails->name);
}
}
puts("Mice:");
if (m_numMice == 0)
puts(" None");
else if (m_numMice == ANY_MOUSE)
puts(" System Mouse");
else
{
for (int mseNum = 0; mseNum < m_numMice; mseNum++)
{
const MouseDetails *mseDetails = GetMouseDetails(mseNum);
printf(" %d: %s\n", mseNum + 1, mseDetails->name);
}
}
puts("Joysticks:");
if (m_numJoys == 0)
puts(" None");
else if (m_numJoys == ANY_JOYSTICK)
puts(" System Joystick");
else
{
for (int joyNum = 0; joyNum < m_numJoys; joyNum++)
{
const JoyDetails *joyDetails = GetJoyDetails(joyNum);
if (joyDetails->hasFFeedback)
printf(" %d: %s [Force Feedback Available]\n", joyNum + 1, joyDetails->name);
else
printf(" %d: %s\n", joyNum + 1, joyDetails->name);
}
}
}
void PrintDevices();
//
// Nested Classes
@ -957,10 +894,14 @@ public:
int m_joyNum; // Joystick number
int m_axisNum; // Axis number (AXIS_X, AXIS_Y, AXIS_Z, AXIS_RX, AXIS_RY or AXIS_RZ)
int m_axisDir; // Axis direction (AXIS_FULL, AXIS_INVERTED, AXIS_POSITIVE or AXIS_NEGATIVE)
int m_posDZone; // Dead zone for positive range
int m_negDZone; // Dead zone for negative range
int m_posSat; // Saturation for positive range
int m_negSat; // Saturation for negative range
int m_axisMinVal; // Axis min raw value (default -32768)
int m_axisOffVal; // Axis center/off raw value (default 0)
int m_axisMaxVal; // Axis max raw value (default 32767)
bool m_axisInverted; // True if axis max raw value less than axis min raw value
int m_posDZone; // Dead zone for positive range (0-99%)
int m_negDZone; // Dead zone for negative range (0-99%)
int m_posSat; // Saturation for positive range (1-100%)
int m_negSat; // Saturation for negative range (1-100%)
/*
* Scales the joystick axis value to the given range.
@ -968,13 +909,14 @@ public:
int ScaleAxisValue(int minVal, int offVal, int maxVal);
public:
CJoyAxisInputSource(CInputSystem *system, int joyNum, int axisNum, int axisDir, unsigned deadZone, unsigned saturation);
CJoyAxisInputSource(CInputSystem *system, int joyNum, int axisNum, int axisDir, int axisMinVal, int axisOffVal, int axisMaxVal,
unsigned deadZone, unsigned saturation);
bool GetValueAsSwitch(bool &val);
bool GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal);
bool SendForceFeedbackCmd(ForceFeedbackCmd *fFeedback);
bool SendForceFeedbackCmd(ForceFeedbackCmd ffCmd);
};
/*

View file

@ -161,7 +161,7 @@ void CTriggerInput::ReadFromINIFile(CINIFile *ini, const char *section)
string key("Input");
key.append(id);
unsigned int autoTrigger;
unsigned autoTrigger;
if (ini->Get(section, key, autoTrigger) == OKAY)
m_autoTrigger = !!autoTrigger;
}

View file

@ -134,7 +134,7 @@ public:
/*
* Represents a trigger input, with both a trigger value and an offscreen value. If required, it can simulate pointing offscreen and pulling
* the trigger (in that order, which lightgun games require to reload properly) when just the offscreen input is activated. This makes
* the trigger (in that order, which lightgun games require to reload properly) just when the offscreen input is activated. This makes
* reloading the gun easier when playing with just the mouse for example.
*/
class CTriggerInput : public CInput

View file

@ -252,12 +252,16 @@ bool CInputs::Initialize()
void CInputs::ReadFromINIFile(CINIFile *ini, const char *section)
{
m_system->ReadFromINIFile(ini, section);
for (vector<CInput*>::iterator it = m_inputs.begin(); it != m_inputs.end(); it++)
(*it)->ReadFromINIFile(ini, section);
}
void CInputs::WriteToINIFile(CINIFile *ini, const char *section)
{
m_system->WriteToINIFile(ini, section);
for (vector<CInput*>::iterator it = m_inputs.begin(); it != m_inputs.end(); it++)
(*it)->WriteToINIFile(ini, section);
}

View file

@ -39,10 +39,13 @@ ESourceType CMultiInputSource::GetCombinedType(vector<CInputSource*> &sources)
else return SourceEmpty;
}
CMultiInputSource::CMultiInputSource(CInputSystem *system) : CInputSource(SourceEmpty), m_system(system), m_isOr(true), m_numSrcs(0), m_srcArray(NULL) { }
CMultiInputSource::CMultiInputSource() : CInputSource(SourceEmpty), m_isOr(true), m_numSrcs(0), m_srcArray(NULL)
{
//
}
CMultiInputSource::CMultiInputSource(CInputSystem *system, bool isOr, vector<CInputSource*> &sources) :
CInputSource(GetCombinedType(sources)), m_system(system), m_isOr(isOr), m_numSrcs(sources.size())
CMultiInputSource::CMultiInputSource(bool isOr, vector<CInputSource*> &sources) :
CInputSource(GetCombinedType(sources)), m_isOr(isOr), m_numSrcs(sources.size())
{
m_srcArray = new CInputSource*[m_numSrcs];
copy(sources.begin(), sources.end(), m_srcArray);
@ -51,13 +54,33 @@ CMultiInputSource::CMultiInputSource(CInputSystem *system, bool isOr, vector<CIn
CMultiInputSource::~CMultiInputSource()
{
if (m_srcArray != NULL)
{
for (int i = 0; i < m_numSrcs; i++)
m_system->ReleaseSource(m_srcArray[i]);
delete m_srcArray;
}
void CMultiInputSource::Acquire()
{
CInputSource::Acquire();
if (m_acquired == 1)
{
// Acquire all sources
for (int i = 0; i < m_numSrcs; i++)
m_srcArray[i]->Acquire();
}
}
void CMultiInputSource::Release()
{
if (m_acquired == 1)
{
// Release all sources
for (int i = 0; i < m_numSrcs; i++)
m_srcArray[i]->Release();
}
CInputSource::Release();
}
bool CMultiInputSource::GetValueAsSwitch(bool &val)
{
if (m_isOr)
@ -113,7 +136,7 @@ bool CMultiInputSource::GetValueAsAnalog(int &val, int minVal, int offVal, int m
}
}
bool CMultiInputSource::SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd)
bool CMultiInputSource::SendForceFeedbackCmd(ForceFeedbackCmd ffCmd)
{
bool result = false;
for (int i = 0; i < m_numSrcs; i++)
@ -121,11 +144,28 @@ bool CMultiInputSource::SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd)
return result;
}
CNegInputSource::CNegInputSource(CInputSystem *system, CInputSource *source) : CInputSource(source->type), m_system(system), m_source(source) { }
CNegInputSource::~CNegInputSource()
CNegInputSource::CNegInputSource(CInputSource *source) : CInputSource(source->type), m_source(source)
{
m_system->ReleaseSource(m_source);
// Acquire source
m_source->Acquire();
}
void CNegInputSource::Acquire()
{
CInputSource::Acquire();
// Acquire source
if (m_acquired == 1)
m_source->Acquire();
}
void CNegInputSource::Release()
{
// Release source
if (m_acquired == 1)
m_source->Release();
CInputSource::Release();
}
bool CNegInputSource::GetValueAsSwitch(bool &val)

View file

@ -9,15 +9,12 @@ using namespace std;
/*
* Represents a collection of input sources and combines their values into a single value.
* When multiple mappings are assigned to an input, this is the input source that is created.
* Can either represent a combination of multiple assignments that all map to the same input, eg KEY_ALT,JOY1_BUTTON1 or be
* used specify that controls must be activated together, eg KEY_ALT+KEY_P.
* Can represent either a combination of multiple assignments that all map to the same input, eg KEY_ALT,JOY1_BUTTON1 or to
* specify that controls must be combined together, eg KEY_ALT+KEY_P.
*/
class CMultiInputSource : public CInputSource
{
private:
// Input system that created this source
CInputSystem *m_system;
// Controls how the inputs sources are combined
bool m_isOr;
@ -36,7 +33,7 @@ public:
/*
* Constructs an 'empty' source (ie one which is always 'off').
*/
CMultiInputSource(CInputSystem *system);
CMultiInputSource();
/*
* Constructs a multiple input source from the given vector of sources.
@ -44,15 +41,19 @@ public:
* switch inputs must be active for this input to have a value (which will be the value of the first non-switch input in the list,
* or the first switch input if there are none).
*/
CMultiInputSource(CInputSystem *system, bool isOr, vector<CInputSource*> &sources);
CMultiInputSource(bool isOr, vector<CInputSource*> &sources);
~CMultiInputSource();
void Acquire();
void Release();
bool GetValueAsSwitch(bool &val);
bool GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal);
bool SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd);
bool SendForceFeedbackCmd(ForceFeedbackCmd ffCmd);
};
/*
@ -63,16 +64,15 @@ public:
class CNegInputSource : public CInputSource
{
private:
// Input system that created this source
CInputSystem *m_system;
// Input source being negated
CInputSource *m_source;
public:
CNegInputSource(CInputSystem *system, CInputSource *source);
CNegInputSource(CInputSource *source);
~CNegInputSource();
void Acquire();
void Release();
bool GetValueAsSwitch(bool &val);

View file

@ -181,7 +181,6 @@ static CInputs *CreateInputs(CInputSystem *InputSystem, BOOL configure)
INI.Parse();
Inputs->ReadFromINIFile(&INI, "Global");
InputSystem->ReadFromINIFile(&INI, "Global");
// If the user wants to configure the inputs, do that now
if (configure)
@ -199,7 +198,6 @@ static CInputs *CreateInputs(CInputSystem *InputSystem, BOOL configure)
{
// Write input configuration and input system settings to config file
Inputs->WriteToINIFile(&INI, "Global");
InputSystem->WriteToINIFile(&INI, "Global");
if (OKAY != INI.Write(CONFIG_FILE_COMMENT))
ErrorLog("Unable to save configuration to %s.", CONFIG_FILE_PATH);
@ -1090,10 +1088,10 @@ int main(int argc, char **argv)
exitCode = Supermodel(argv[fileIdx],Inputs,ppcFrequency,xRes,yRes,cmdFullScreen,cmdNoThrottle,cmdShowFPS,vsFile,fsFile);
Exit:
if (InputSystem != NULL)
delete InputSystem;
if (Inputs != NULL)
delete Inputs;
if (InputSystem != NULL)
delete InputSystem;
SDL_Quit();
return exitCode;
}

View file

@ -181,8 +181,8 @@ void CSDLInputSystem::OpenJoysticks()
// Gather joystick details (name, num POVs & buttons and which axes are available)
JoyDetails joyDetails;
const char *pName = SDL_JoystickName(joyNum);
strncpy(joyDetails.name, pName, MAX_NAME_LENGTH - 1);
joyDetails.name[MAX_NAME_LENGTH - 1] = '\0';
strncpy(joyDetails.name, pName, MAX_NAME_LENGTH);
joyDetails.name[MAX_NAME_LENGTH] = '\0';
joyDetails.numAxes = SDL_JoystickNumAxes(joystick);
for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
{
@ -313,7 +313,7 @@ bool CSDLInputSystem::IsJoyButPressed(int joyNum, int butNum)
return !!SDL_JoystickGetButton(joystick, butNum);
}
bool CSDLInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd *ffCmd)
bool CSDLInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd)
{
// SDL 1.2 does not support force feedback
return false;

View file

@ -81,7 +81,7 @@ protected:
bool IsJoyButPressed(int joyNum, int butNum);
bool ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd *ffCmd);
bool ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd);
void Wait(int ms);

View file

@ -160,6 +160,7 @@ DIKeyMapStruct CDirectInputSystem::s_keyMap[] =
bool IsXInputDevice(const GUID &devProdGUID)
{
// Following code taken from MSDN
IWbemLocator* pIWbemLocator = NULL;
IEnumWbemClassObject* pEnumDevices = NULL;
IWbemClassObject* pDevices[20] = {0};
@ -191,7 +192,7 @@ bool IsXInputDevice(const GUID &devProdGUID)
goto exit;
// Loop over all devices
while (true)
for (;;)
{
// Get 20 at a time
DWORD uReturned;
@ -259,12 +260,13 @@ exit:
BOOL CALLBACK DI8EnumDevicesCallback(LPCDIDEVICEINSTANCE instance, LPVOID context)
{
// Keep track of all joystick device GUIDs
vector<DIJoyInfo> *infos = (vector<DIJoyInfo>*)context;
DIEnumDevsContext *diContext = (DIEnumDevsContext*)context;
DIJoyInfo info;
memset(&info, 0, sizeof(DIJoyInfo));
info.guid = instance->guidInstance;
info.isXInput = IsXInputDevice(instance->guidProduct);
infos->push_back(info);
// If XInput is enabled, see if device is an XInput device
info.isXInput = diContext->useXInput && IsXInputDevice(instance->guidProduct);
diContext->infos->push_back(info);
return DIENUM_CONTINUE;
}
@ -295,7 +297,8 @@ BOOL CALLBACK DI8EnumAxesCallback(LPCDIDEVICEOBJECTINSTANCE instance, LPVOID con
BOOL CALLBACK DI8EnumEffectsCallback(LPCDIEFFECTINFO effectInfo, LPVOID context)
{
JoyDetails *joyDetails = (JoyDetails*)context;
if (effectInfo->dwEffType == DIEFT_CONSTANTFORCE)
// Check joystick has at least one of required types of effects
if (!!(effectInfo->dwEffType & (DIEFT_CONSTANTFORCE | DIEFT_PERIODIC | DIEFT_CONDITION)))
joyDetails->hasFFeedback = true;
return DIENUM_CONTINUE;
}
@ -651,7 +654,7 @@ void CDirectInputSystem::PollKeyboardsAndMice()
if (m_useRawInput)
{
// For RawInput, only thing to do is update wheelDir from wheelData for each mouse state. Everything else is updated via WM events.
for (vector<RawMseState>::iterator it = m_rawMseStates.begin(); it < m_rawMseStates.end(); it++)
for (vector<RawMseState>::iterator it = m_rawMseStates.begin(); it != m_rawMseStates.end(); it++)
{
if (it->wheelDelta != 0)
{
@ -745,7 +748,7 @@ void CDirectInputSystem::CloseKeyboardsAndMice()
}
// Delete storage for keyboards
for (vector<BOOL*>::iterator it = m_rawKeyStates.begin(); it < m_rawKeyStates.end(); it++)
for (vector<BOOL*>::iterator it = m_rawKeyStates.begin(); it != m_rawKeyStates.end(); it++)
delete[] *it;
m_keyDetails.clear();
m_rawKeyboards.clear();
@ -785,7 +788,7 @@ void CDirectInputSystem::ResetMice()
m_combRawMseState.x = p.x;
m_combRawMseState.y = p.y;
m_combRawMseState.z = 0;
for (vector<RawMseState>::iterator it = m_rawMseStates.begin(); it < m_rawMseStates.end(); it++)
for (vector<RawMseState>::iterator it = m_rawMseStates.begin(); it != m_rawMseStates.end(); it++)
{
it->x = p.x;
it->y = p.y;
@ -927,7 +930,7 @@ void CDirectInputSystem::ProcessRawInput(HRAWINPUT hInput)
}
m_combRawMseState.buttons = 0;
for (vector<RawMseState>::iterator it = m_rawMseStates.begin(); it < m_rawMseStates.end(); it++)
for (vector<RawMseState>::iterator it = m_rawMseStates.begin(); it != m_rawMseStates.end(); it++)
m_combRawMseState.buttons |= it->buttons;
}
}
@ -940,14 +943,17 @@ void CDirectInputSystem::ProcessRawInput(HRAWINPUT hInput)
void CDirectInputSystem::OpenJoysticks()
{
// Get the info about all attached joystick devices
DIEnumDevsContext diContext;
diContext.infos = &m_diJoyInfos;
diContext.useXInput = m_useXInput;
HRESULT hr;
if (FAILED(hr = m_di8->EnumDevices(DI8DEVCLASS_GAMECTRL, DI8EnumDevicesCallback, &m_diJoyInfos, DIEDFL_ATTACHEDONLY)))
if (FAILED(hr = m_di8->EnumDevices(DI8DEVCLASS_GAMECTRL, DI8EnumDevicesCallback, &diContext, DIEDFL_ATTACHEDONLY)))
return;
// Loop through those found
int joyNum = 0;
int xNum = 0;
for (vector<DIJoyInfo>::iterator it = m_diJoyInfos.begin(); it < m_diJoyInfos.end(); it++)
for (vector<DIJoyInfo>::iterator it = m_diJoyInfos.begin(); it != m_diJoyInfos.end(); it++)
{
joyNum++;
@ -955,14 +961,14 @@ void CDirectInputSystem::OpenJoysticks()
memset(&joyDetails, 0, sizeof(joyDetails));
// See if can use XInput for device
if (m_useXInput && it->isXInput)
if (it->isXInput)
{
// If so, set joystick details (currently XBox controller is only gamepad handled by XInput and so its capabilities are fixed)
sprintf(joyDetails.name, "Xbox 360 Controller %d (via XInput)", (xNum + 1));
joyDetails.numAxes = 6; // Left & right triggers are mapped to axes in addition to the two analog sticks, giving a total of 6 axes
joyDetails.numPOVs = 1; // Digital D-pad
joyDetails.numButtons = 10;
joyDetails.hasFFeedback = true;
joyDetails.hasFFeedback = m_enableFFeedback;
joyDetails.hasAxis[AXIS_X] = true;
joyDetails.hasAxis[AXIS_Y] = true;
joyDetails.hasAxis[AXIS_Z] = true;
@ -982,28 +988,28 @@ void CDirectInputSystem::OpenJoysticks()
else
{
// Otherwise, open joystick with DirectInput for given GUID and set its data format
LPDIRECTINPUTDEVICE8 di8Joystick;
if (FAILED(hr = m_di8->CreateDevice(it->guid, &di8Joystick, NULL)))
LPDIRECTINPUTDEVICE8 joystick;
if (FAILED(hr = m_di8->CreateDevice(it->guid, &joystick, NULL)))
{
ErrorLog("Unable to create DirectInput joystick device %d (error %d) - skipping joystick.\n", joyNum, hr);
continue;
}
if (FAILED(hr = di8Joystick->SetDataFormat(&c_dfDIJoystick2)))
if (FAILED(hr = joystick->SetDataFormat(&c_dfDIJoystick2)))
{
ErrorLog("Unable to set data format for DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release();
joystick->Release();
continue;
}
// Get joystick's capabilities
DIDEVCAPS devCaps;
devCaps.dwSize = sizeof(DIDEVCAPS);
if (FAILED(hr = di8Joystick->GetCapabilities(&devCaps)))
if (FAILED(hr = joystick->GetCapabilities(&devCaps)))
{
ErrorLog("Unable to query capabilities of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release();
joystick->Release();
continue;
}
@ -1013,11 +1019,11 @@ void CDirectInputSystem::OpenJoysticks()
didps.diph.dwHeaderSize = sizeof(DIPROPHEADER);
didps.diph.dwHow = DIPH_DEVICE;
didps.diph.dwObj = 0;
if (FAILED(hr = di8Joystick->GetProperty(DIPROP_INSTANCENAME, &didps.diph)))
if (FAILED(hr = joystick->GetProperty(DIPROP_INSTANCENAME, &didps.diph)))
{
ErrorLog("Unable to get name of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release();
joystick->Release();
continue;
}
// DInput returns name as Unicode, convert to ASCII
@ -1028,20 +1034,24 @@ void CDirectInputSystem::OpenJoysticks()
joyDetails.numButtons = devCaps.dwButtons;
// Enumerate axes
if (FAILED(hr = di8Joystick->EnumObjects(DI8EnumAxesCallback, &joyDetails, DIDFT_AXIS)))
if (FAILED(hr = joystick->EnumObjects(DI8EnumAxesCallback, &joyDetails, DIDFT_AXIS)))
{
ErrorLog("Unable to enumerate axes of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release();
joystick->Release();
continue;
}
joyDetails.numAxes = 0;
for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
joyDetails.numAxes += joyDetails.hasAxis[axisNum];
// See if force feedback available for joystick
if (FAILED(hr = di8Joystick->EnumEffects(DI8EnumEffectsCallback, &joyDetails, DIEFT_ALL)))
// See if force feedback enabled and is available for joystick
if (m_enableFFeedback && (devCaps.dwFlags & DIDC_FORCEFEEDBACK))
{
// If so, see what types of effects are available and for which axes
if (FAILED(hr = joystick->EnumEffects(DI8EnumEffectsCallback, &joyDetails, DIEFT_ALL)))
ErrorLog("Unable to enumerate effects of DirectInput joystick %d (error %d) - force feedback will be unavailable for joystick.\n", joyNum, hr);
}
// Configure axes, if any
if (joyDetails.numAxes > 0)
@ -1054,11 +1064,11 @@ void CDirectInputSystem::OpenJoysticks()
didpr.diph.dwObj = 0;
didpr.lMin = -32768;
didpr.lMax = 32767;
if (FAILED(hr = di8Joystick->SetProperty(DIPROP_RANGE, &didpr.diph)))
if (FAILED(hr = joystick->SetProperty(DIPROP_RANGE, &didpr.diph)))
{
ErrorLog("Unable to set axis range of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release();
joystick->Release();
continue;
}
@ -1069,40 +1079,40 @@ void CDirectInputSystem::OpenJoysticks()
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.diph.dwObj = 0;
dipdw.dwData = DIPROPAXISMODE_ABS;
if (FAILED(hr = di8Joystick->SetProperty(DIPROP_AXISMODE, &dipdw.diph)))
if (FAILED(hr = joystick->SetProperty(DIPROP_AXISMODE, &dipdw.diph)))
{
ErrorLog("Unable to set axis mode of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release();
joystick->Release();
continue;
}
// Turn off deadzone as handled by this class
dipdw.dwData = 0;
if (FAILED(hr = di8Joystick->SetProperty(DIPROP_DEADZONE, &dipdw.diph)))
if (FAILED(hr = joystick->SetProperty(DIPROP_DEADZONE, &dipdw.diph)))
{
ErrorLog("Unable to set deadzone of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release();
joystick->Release();
continue;
}
// Turn off saturation as handle by this class
dipdw.dwData = 10000;
if (FAILED(hr = di8Joystick->SetProperty(DIPROP_SATURATION, &dipdw.diph)))
if (FAILED(hr = joystick->SetProperty(DIPROP_SATURATION, &dipdw.diph)))
{
ErrorLog("Unable to set saturation of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release();
joystick->Release();
continue;
}
// If joystick force feedback is enabled and joystick has force feedback capabilities then disable auto-center
if (m_enableFFeedback && joyDetails.hasFFeedback)
// If joystick has force feedback capabilities then disable auto-center
if (joyDetails.hasFFeedback)
{
dipdw.dwData = FALSE;
if (FAILED(hr = di8Joystick->SetProperty(DIPROP_AUTOCENTER, &dipdw.diph)))
if (FAILED(hr = joystick->SetProperty(DIPROP_AUTOCENTER, &dipdw.diph)))
{
ErrorLog("Unable to unset auto-center of DirectInput joystick %d (error %d) - force feedback will be unavailable for joystick.\n", joyNum, hr);
@ -1114,7 +1124,7 @@ void CDirectInputSystem::OpenJoysticks()
// Keep track of DirectInput device number
it->dInputNum = m_di8Joysticks.size();
m_di8Joysticks.push_back(di8Joystick);
m_di8Joysticks.push_back(joystick);
}
// Create initial blank joystick state
@ -1131,20 +1141,31 @@ void CDirectInputSystem::OpenJoysticks()
void CDirectInputSystem::ActivateJoysticks()
{
// Set DirectInput cooperative level of joysticks
for (vector<LPDIRECTINPUTDEVICE8>::iterator it = m_di8Joysticks.begin(); it < m_di8Joysticks.end(); it++)
(*it)->SetCooperativeLevel(m_hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
unsigned joyNum = 0;
for (vector<DIJoyInfo>::iterator it = m_diJoyInfos.begin(); it != m_diJoyInfos.end(); it++)
{
if (!it->isXInput)
{
LPDIRECTINPUTDEVICE8 joystick = m_di8Joysticks[it->dInputNum];
if (m_joyDetails[joyNum].hasFFeedback)
joystick->SetCooperativeLevel(m_hwnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
else
joystick->SetCooperativeLevel(m_hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
}
joyNum++;
}
}
void CDirectInputSystem::PollJoysticks()
{
// Get current joystick states from XInput and DirectInput
int i = 0;
for (vector<DIJoyInfo>::iterator it = m_diJoyInfos.begin(); it < m_diJoyInfos.end(); it++)
for (vector<DIJoyInfo>::iterator it = m_diJoyInfos.begin(); it != m_diJoyInfos.end(); it++)
{
LPDIJOYSTATE2 pJoyState = &m_diJoyStates[i++];
HRESULT hr;
if (m_useXInput && it->isXInput)
if (it->isXInput)
{
// Use XInput to query joystick
XINPUT_STATE xState;
@ -1219,8 +1240,24 @@ void CDirectInputSystem::PollJoysticks()
void CDirectInputSystem::CloseJoysticks()
{
// Release any DirectInput force feedback effects that were created
for (vector<DIJoyInfo>::iterator it = m_diJoyInfos.begin(); it != m_diJoyInfos.end(); it++)
{
for (unsigned axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
{
for (unsigned effNum = 0; effNum < NUM_FF_EFFECTS; effNum++)
{
if (it->dInputEffects[axisNum][effNum] != NULL)
{
it->dInputEffects[axisNum][effNum]->Release();
it->dInputEffects[axisNum][effNum] = NULL;
}
}
}
}
// Release each DirectInput joystick
for (vector<LPDIRECTINPUTDEVICE8>::iterator it = m_di8Joysticks.begin(); it < m_di8Joysticks.end(); it++)
for (vector<LPDIRECTINPUTDEVICE8>::iterator it = m_di8Joysticks.begin(); it != m_di8Joysticks.end(); it++)
{
(*it)->Unacquire();
(*it)->Release();
@ -1232,47 +1269,102 @@ void CDirectInputSystem::CloseJoysticks()
m_di8Joysticks.clear();
}
HRESULT CDirectInputSystem::CreateJoystickEffect(LPDIRECTINPUTDEVICE8 di8Joystick, int axisNum, LPDIRECTINPUTEFFECT *di8Effect)
HRESULT CDirectInputSystem::CreateJoystickEffect(LPDIRECTINPUTDEVICE8 joystick, int axisNum, ForceFeedbackCmd ffCmd, LPDIRECTINPUTEFFECT *pEffect)
{
// TODO - following is not finished
// Map axis number to DI object offset
DWORD axisOfs;
switch (axisNum)
{
case AXIS_X: axisOfs = DIJOFS_X; break;
case AXIS_Y: axisNum = DIJOFS_Y; break;
case AXIS_Z: axisNum = DIJOFS_Z; break;
case AXIS_RX: axisNum = DIJOFS_RX; break;
case AXIS_RY: axisNum = DIJOFS_RY; break;
case AXIS_RZ: axisNum = DIJOFS_RZ; break;
case AXIS_Y: axisOfs = DIJOFS_Y; break;
case AXIS_Z: axisOfs = DIJOFS_Z; break;
case AXIS_RX: axisOfs = DIJOFS_RX; break;
case AXIS_RY: axisOfs = DIJOFS_RY; break;
case AXIS_RZ: axisOfs = DIJOFS_RZ; break;
default: return E_FAIL;
}
DWORD rgdwAxes[1] = { axisOfs };
LONG rglDirection[1] = { 0 };
DICONSTANTFORCE cf = { 0 };
DICONSTANTFORCE dicf;
DICONDITION dic;
DIPERIODIC dip;
DIENVELOPE die;
GUID guid;
// Set common effects parameters
DIEFFECT eff;
memset(&eff, 0, sizeof(eff));
eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.dwDuration = INFINITE;
eff.dwSamplePeriod = 0;
eff.dwGain = DI_FFNOMINALMAX;
eff.dwTriggerButton = DIEB_NOTRIGGER;
eff.dwTriggerRepeatInterval = 0;
eff.dwGain = DI_FFNOMINALMAX;
eff.cAxes = 1;
eff.rgdwAxes = rgdwAxes;
eff.rglDirection = rglDirection;
eff.lpEnvelope = 0;
eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
eff.lpvTypeSpecificParams = &cf;
eff.dwDuration = INFINITE;
eff.dwStartDelay = 0;
// Create the prepared effect
HRESULT hr;
if (FAILED(hr = di8Joystick->CreateEffect(GUID_ConstantForce, &eff, di8Effect, NULL)))
return hr;
if (di8Effect == NULL)
// Set specific effects parameters
switch (ffCmd.id)
{
case FFStop:
return E_FAIL;
case FFSelfCenter:
guid = GUID_Spring;
dic.lOffset = 0;
dic.lPositiveCoefficient = 0;
dic.lNegativeCoefficient = 0;
dic.dwPositiveSaturation = DI_FFNOMINALMAX;
dic.dwNegativeSaturation = DI_FFNOMINALMAX;
dic.lDeadBand = (LONG)0.05 * DI_FFNOMINALMAX;
eff.lpEnvelope = NULL;
eff.cbTypeSpecificParams = sizeof(DICONDITION);
eff.lpvTypeSpecificParams = &dic;
break;
case FFConstantForce:
guid = GUID_ConstantForce;
dicf.lMagnitude = 0;
eff.lpEnvelope = NULL;
eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
eff.lpvTypeSpecificParams = &dicf;
break;
case FFVibrate:
guid = GUID_Sine;
dip.dwMagnitude = 0;
dip.lOffset = 0;
dip.dwPhase = 0;
dip.dwPeriod = (DWORD)0.1 * DI_SECONDS; // 1/10th second
die.dwSize = sizeof(die);
die.dwAttackLevel = 0;
die.dwAttackTime = (DWORD)0.5 * DI_SECONDS;
die.dwFadeLevel = 0;
die.dwFadeTime = (DWORD)0.5 * DI_SECONDS;
eff.lpEnvelope = &die;
eff.cbTypeSpecificParams = sizeof(DIPERIODIC);
eff.lpvTypeSpecificParams = &dip;
break;
}
joystick->Acquire();
HRESULT hr;
if (FAILED(hr = joystick->CreateEffect(guid, &eff, pEffect, NULL)))
return hr;
if (*pEffect == NULL)
return E_FAIL;
(*pEffect)->Start(1, 0);
return S_OK;
}
@ -1490,18 +1582,15 @@ bool CDirectInputSystem::IsJoyButPressed(int joyNum, int butNum)
return !!m_diJoyStates[joyNum].rgbButtons[butNum];
}
bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd *ffCmd)
bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd)
{
if (!m_enableFFeedback)
return false;
DIJoyInfo *pInfo = &m_diJoyInfos[joyNum];
HRESULT hr;
if (m_useXInput && pInfo->isXInput)
if (pInfo->isXInput)
{
XINPUT_VIBRATION vibration;
switch (ffCmd->id)
switch (ffCmd.id)
{
case FFStop:
if (axisNum == AXIS_X || axisNum == AXIS_Y)
@ -1510,70 +1599,101 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF
vibration.wRightMotorSpeed = 0;
else
return false;
if (FAILED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration)))
return false;
break;
return SUCCEEDED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration));
case FFConstantForce:
if (axisNum == AXIS_X || axisNum == AXIS_Y)
vibration.wLeftMotorSpeed = ffCmd->data; // TODO - scale data to 0-65535
vibration.wLeftMotorSpeed = ffCmd.data; // TODO - scale data to 0-65535
else if (axisNum == AXIS_RX || axisNum == AXIS_RY)
vibration.wRightMotorSpeed = ffCmd->data; // TODO - scale data to 0-65535
vibration.wRightMotorSpeed = ffCmd.data; // TODO - scale data to 0-65535
else
return false;
if (FAILED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration)))
return SUCCEEDED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration));
default:
// TODO - other force feedback types
return false;
break;
}
}
else
{
LPDIRECTINPUTDEVICE8 di8Joystick = m_di8Joysticks[pInfo->dInputNum];
switch (ffCmd->id)
{
case FFStop:
if (FAILED(hr = di8Joystick->SendForceFeedbackCommand(DISFFC_STOPALL)))
{
// TODO
return false;
}
break;
LPDIRECTINPUTDEVICE8 joystick = m_di8Joysticks[pInfo->dInputNum];
case FFConstantForce:
LPDIRECTINPUTEFFECT di8Effect;
if (FAILED(hr = CreateJoystickEffect(di8Joystick, axisNum, &di8Effect)))
// See if command is to stop all force feedback
if (ffCmd.id == -1)
return SUCCEEDED(hr = joystick->SendForceFeedbackCommand(DISFFC_STOPALL));
// Create effect for given axis if has not already been created
int effNum = (int)ffCmd.id;
LPDIRECTINPUTEFFECT *pEffect = &pInfo->dInputEffects[axisNum][effNum];
if ((*pEffect) == NULL)
{
// TODO
if (FAILED(hr = CreateJoystickEffect(joystick, axisNum, ffCmd, pEffect)))
return false;
}
DICONSTANTFORCE cf;
cf.lMagnitude = ffCmd->data; // TODO - scale data to what?
LONG rglDirection[1] = { 0 };
DICONSTANTFORCE dicf;
DICONDITION dic;
DIPERIODIC dip;
DIENVELOPE die;
// Set common parameters
DIEFFECT eff;
memset(&eff, 0, sizeof(eff));
eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.cAxes = 1;
eff.rglDirection = rglDirection;
eff.lpEnvelope = 0;
eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
eff.lpvTypeSpecificParams = &cf;
eff.dwStartDelay = 0;
// Now set the new parameters and start the effect immediately.
if (FAILED(hr = di8Effect->SetParameters(&eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START)))
switch (ffCmd.id)
{
// TODO
case FFSelfCenter:
dic.lPositiveCoefficient = ffCmd.data;
dic.lNegativeCoefficient = ffCmd.data;
dic.dwPositiveSaturation = DI_FFNOMINALMAX;
dic.dwNegativeSaturation = DI_FFNOMINALMAX;
dic.lDeadBand = (LONG)0.05 * DI_FFNOMINALMAX;
eff.lpEnvelope = NULL;
eff.cbTypeSpecificParams = sizeof(DICONDITION);
eff.lpvTypeSpecificParams = &dic;
break;
case FFConstantForce:
dicf.lMagnitude = ffCmd.data; // TODO - scale & cap at +/- DI_FFNOMINALMAX
eff.lpEnvelope = NULL;
eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
eff.lpvTypeSpecificParams = &dicf;
break;
case FFVibrate:
dip.dwMagnitude = ffCmd.data;
dip.lOffset = 0;
dip.dwPhase = 0;
dip.dwPeriod = (DWORD)0.1 * DI_SECONDS; // 1/10th second
die.dwSize = sizeof(die);
die.dwAttackLevel = 0;
die.dwAttackTime = (DWORD)0.5 * DI_SECONDS;
die.dwFadeLevel = 0;
die.dwFadeTime = (DWORD)0.5 * DI_SECONDS;
eff.lpEnvelope = &die;
eff.cbTypeSpecificParams = sizeof(DIPERIODIC);
eff.lpvTypeSpecificParams = &dip;
break;
default:
return false;
}
//di8Effect->Release();
break;
// Now set the new parameters and start the effect immediately.
return SUCCEEDED(hr = (*pEffect)->SetParameters(&eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START));
}
}
return true;
}
void CDirectInputSystem::Wait(int ms)

View file

@ -46,9 +46,16 @@ struct DIJoyInfo
GUID guid;
bool isXInput;
int dInputNum;
LPDIRECTINPUTEFFECT dInputEffects[NUM_JOY_AXES][NUM_FF_EFFECTS];
int xInputNum;
};
struct DIEnumDevsContext
{
vector<DIJoyInfo> *infos;
bool useXInput;
};
// RawInput API
typedef /*WINUSERAPI*/ INT (WINAPI *GetRawInputDeviceListPtr)(OUT PRAWINPUTDEVICELIST pRawInputDeviceList, IN OUT PUINT puiNumDevices, IN UINT cbSize);
typedef /*WINUSERAPI*/ INT (WINAPI *GetRawInputDeviceInfoPtr)(IN HANDLE hDevice, IN UINT uiCommand, OUT LPVOID pData, IN OUT PUINT pcbSize);
@ -151,7 +158,7 @@ private:
void CloseJoysticks();
HRESULT CreateJoystickEffect(LPDIRECTINPUTDEVICE8 di8Joystick, int axisNum, LPDIRECTINPUTEFFECT *di8Effect);
HRESULT CreateJoystickEffect(LPDIRECTINPUTDEVICE8 di8Joystick, int axisNum, ForceFeedbackCmd ffCmd, LPDIRECTINPUTEFFECT *di8Effect);
protected:
/*
@ -177,7 +184,7 @@ protected:
bool IsJoyButPressed(int joyNum, int butNum);
bool ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd *ffCmd);
bool ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd);
void Wait(int ms);