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 ""). // 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; 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. // 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; unsigned sectionIdx, settingIdx;
@ -248,6 +248,17 @@ BOOL CINIFile::Get(string SectionName, string SettingName, unsigned& value)
return OKAY; 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. // Obtains a string setting, if it exists, otherwise does nothing.
BOOL CINIFile::Get(string SectionName, string SettingName, string& String) BOOL CINIFile::Get(string SectionName, string SettingName, string& String)
{ {
@ -323,15 +334,24 @@ CINIFile::CToken CINIFile::GetString(void)
return T; 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) CINIFile::CToken CINIFile::GetNumber(void)
{ {
CToken T; CToken T;
unsigned long long number = 0; unsigned long long number = 0;
BOOL isNeg = FALSE;
int overflow = 0; int overflow = 0;
T.type = TOKEN_NUMBER; T.type = TOKEN_NUMBER;
// See if begins with minus sign
if (linePtr[0]=='-')
{
isNeg = TRUE;
linePtr++;
}
// Hexadecimal? // Hexadecimal?
if ((linePtr[0]=='0') && ((linePtr[1]=='X') || (linePtr[1]=='x'))) if ((linePtr[0]=='0') && ((linePtr[1]=='X') || (linePtr[1]=='x')))
{ {
@ -360,7 +380,7 @@ CINIFile::CToken CINIFile::GetNumber(void)
++linePtr; ++linePtr;
// Check for overflows // Check for overflows
if (number > 0x00000000FFFFFFFFULL) if (!isNeg && number > 0x000000007FFFFFFFULL || isNeg && number > 0x0000000080000000ULL)
overflow = 1; overflow = 1;
} }
else if (IsBlank(linePtr[0])) else if (IsBlank(linePtr[0]))
@ -387,7 +407,7 @@ CINIFile::CToken CINIFile::GetNumber(void)
++linePtr; ++linePtr;
// Check for overflows // Check for overflows
if (number > 0x00000000FFFFFFFFULL) if (!isNeg && number > 0x000000007FFFFFFFULL || isNeg && number > 0x0000000080000000ULL)
overflow = 1; overflow = 1;
} }
else if (IsBlank(linePtr[0])) else if (IsBlank(linePtr[0]))
@ -404,7 +424,7 @@ CINIFile::CToken CINIFile::GetNumber(void)
//if (overflow) //if (overflow)
// printf("tokenizer: number exceeds 32 bits and has been truncated\n"); // printf("tokenizer: number exceeds 32 bits and has been truncated\n");
T.number = (unsigned) number; T.number = (isNeg ? -(int)number : (int)number);
return T; return T;
} }

View file

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

View file

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

View file

@ -18,10 +18,14 @@ class CINIFile;
#define MAX_MAPPING_LENGTH 255 #define MAX_MAPPING_LENGTH 255
#define NUM_FF_EFFECTS 3
enum EForceFeedback enum EForceFeedback
{ {
FFStop, FFStop = -1,
FFConstantForce FFSelfCenter = 0,
FFConstantForce = 1,
FFVibrate = 2
}; };
struct ForceFeedbackCmd struct ForceFeedbackCmd
@ -37,7 +41,7 @@ class CInput
{ {
private: private:
// Current mapping(s) for input, eg JOY1_XAXIS_POS // 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 // Default mapping for input
const char *m_defaultMapping; const char *m_defaultMapping;
@ -54,7 +58,15 @@ protected:
// Current input source // Current input source
CInputSource *m_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: public:
virtual ~CInput();
// Input identifier // Input identifier
const char *id; const char *id;
@ -73,12 +85,6 @@ public:
// Previous input value // Previous input value
UINT16 prevValue; 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. * 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. * 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> #include <vector>
using namespace std; 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) int CInputSource::Clamp(int val, int minVal, int maxVal)
{ {
if (val > maxVal) return 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 CInputSource::Scale(int val, int fromMinVal, int fromOffVal, int fromMaxVal, int toMinVal, int toOffVal, int toMaxVal)
{ {
int fromRange; double fromRange;
double frac; double frac;
if (fromMaxVal > fromMinVal) if (fromMaxVal > fromMinVal)
{ {
val = Clamp(val, fromMinVal, fromMaxVal); val = Clamp(val, fromMinVal, fromMaxVal);
if (val > fromOffVal) if (val > fromOffVal)
{ {
fromRange = fromMaxVal - fromOffVal; fromRange = (double)(fromMaxVal - fromOffVal);
frac = (double)(val - fromOffVal) / fromRange; frac = (double)(val - fromOffVal) / fromRange;
} }
else if (val < fromOffVal) else if (val < fromOffVal)
{ {
fromRange = fromOffVal - fromMinVal; fromRange = (double)(fromOffVal - fromMinVal);
frac = (double)(val - fromOffVal) / fromRange; frac = (double)(val - fromOffVal) / fromRange;
} }
else else
@ -45,12 +64,12 @@ int CInputSource::Scale(int val, int fromMinVal, int fromOffVal, int fromMaxVal,
val = Clamp(val, fromMaxVal, fromMinVal); val = Clamp(val, fromMaxVal, fromMinVal);
if (val > fromOffVal) if (val > fromOffVal)
{ {
fromRange = fromMinVal - fromOffVal; fromRange = (double)(fromMinVal - fromOffVal);
frac = (double)(fromOffVal - val) / fromRange; frac = (double)(fromOffVal - val) / fromRange;
} }
else if (val < fromOffVal) else if (val < fromOffVal)
{ {
fromRange = fromOffVal - fromMaxVal; fromRange = (double)(fromOffVal - fromMaxVal);
frac = (double)(fromOffVal - val) / fromRange; frac = (double)(fromOffVal - val) / fromRange;
} }
else else
@ -83,7 +102,7 @@ bool CInputSource::IsActive()
return GetValueAsSwitch(boolVal); return GetValueAsSwitch(boolVal);
} }
bool CInputSource::SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd) bool CInputSource::SendForceFeedbackCmd(ForceFeedbackCmd ffCmd)
{ {
return false; return false;
} }

View file

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

View file

@ -6,6 +6,11 @@
#include <sstream> #include <sstream>
using namespace std; using namespace std;
#ifdef DEBUG
unsigned CInputSystem::totalSrcsAcquired = 0;
unsigned CInputSystem::totalSrcsReleased = 0;
#endif
const char *CInputSystem::s_validKeyNames[] = const char *CInputSystem::s_validKeyNames[] =
{ {
// General keys // General keys
@ -343,17 +348,26 @@ JoyPartsStruct CInputSystem::s_joyParts[] =
{ NULL, JoyUnknown } { NULL, JoyUnknown }
}; };
const char *CInputSystem::s_axisNames[] = { "X", "Y", "Z", "RX", "RY", "RZ" };
CInputSystem::CInputSystem(const char *systemName) : CInputSystem::CInputSystem(const char *systemName) :
name(systemName), m_dispX(0), m_dispY(0), m_dispW(0), m_dispH(0), m_grabMouse(false) 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() CInputSystem::~CInputSystem()
{ {
DeleteSourceCache(); m_emptySource->Release();
ClearSettings(); 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() 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) if (m_anyKeySources != NULL)
{ {
for (int keyIndex = 0; keyIndex < NUM_VALID_KEYS; keyIndex++) 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; delete[] m_anyKeySources;
m_anyKeySources = NULL; m_anyKeySources = NULL;
}
if (m_numKbds != ANY_KEYBOARD) if (m_numKbds != ANY_KEYBOARD)
{ {
for (int kbdNum = 0; kbdNum < m_numKbds; kbdNum++) for (int kbdNum = 0; kbdNum < m_numKbds; kbdNum++)
{ {
for (int keyIndex = 0; keyIndex < NUM_VALID_KEYS; keyIndex++) 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]; delete[] m_keySources[kbdNum];
} }
if (deleteCache)
{
delete[] m_keySources; delete[] m_keySources;
m_keySources = NULL; m_keySources = NULL;
} }
} }
}
// Delete cache for mouse sources // Clear cache of mouse sources
if (m_anyMseSources != NULL) if (m_anyMseSources != NULL)
{ {
for (int mseIndex = 0; mseIndex < NUM_MOUSE_PARTS; mseIndex++) for (int mseIndex = 0; mseIndex < NUM_MOUSE_PARTS; mseIndex++)
DeleteSource(m_anyMseSources[mseIndex]); ReleaseSource(m_anyMseSources[mseIndex]);
if (deleteCache)
{
delete[] m_anyMseSources; delete[] m_anyMseSources;
m_anyMseSources = NULL; m_anyMseSources = NULL;
}
if (m_numMice != ANY_MOUSE) if (m_numMice != ANY_MOUSE)
{ {
for (int mseNum = 0; mseNum < m_numMice; mseNum++) for (int mseNum = 0; mseNum < m_numMice; mseNum++)
{ {
for (int mseIndex = 0; mseIndex < NUM_MOUSE_PARTS; mseIndex++) 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]; delete[] m_mseSources[mseNum];
} }
if (deleteCache)
{
delete[] m_mseSources; delete[] m_mseSources;
m_mseSources = NULL; m_mseSources = NULL;
} }
} }
}
// Delete cache for joystick sources // Clear cache of joystick sources
if (m_anyJoySources != NULL) if (m_anyJoySources != NULL)
{ {
for (int joyIndex = 0; joyIndex < NUM_JOY_PARTS; joyIndex++) for (int joyIndex = 0; joyIndex < NUM_JOY_PARTS; joyIndex++)
DeleteSource(m_anyJoySources[joyIndex]); ReleaseSource(m_anyJoySources[joyIndex]);
if (deleteCache)
{
delete[] m_anyJoySources; delete[] m_anyJoySources;
m_anyJoySources = NULL; m_anyJoySources = NULL;
}
if (m_numJoys != ANY_JOYSTICK) if (m_numJoys != ANY_JOYSTICK)
{ {
for (int joyNum = 0; joyNum < m_numJoys; joyNum++) for (int joyNum = 0; joyNum < m_numJoys; joyNum++)
{ {
for (int joyIndex = 0; joyIndex < NUM_JOY_PARTS; joyIndex++) 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]; delete[] m_joySources[joyNum];
} }
if (deleteCache)
{
delete[] m_joySources; delete[] m_joySources;
m_joySources = NULL; m_joySources = NULL;
} }
} }
}
} }
void CInputSystem::DeleteSource(CInputSource *source) void CInputSystem::ReleaseSource(CInputSource *&source)
{ {
if (source != NULL && source != m_emptySource) if (source != NULL)
delete source; source->Release();
source = NULL;
} }
CInputSource *CInputSystem::GetKeySource(int kbdNum, int keyIndex) 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); m_anyKeySources[keyIndex] = CreateKeySource(ANY_KEYBOARD, keyIndex);
else else
m_anyKeySources[keyIndex] = CreateAnyKeySource(keyIndex); m_anyKeySources[keyIndex] = CreateAnyKeySource(keyIndex);
m_anyKeySources[keyIndex]->Acquire();
} }
return m_anyKeySources[keyIndex]; return m_anyKeySources[keyIndex];
} }
@ -553,7 +522,10 @@ CInputSource *CInputSystem::GetKeySource(int kbdNum, int keyIndex)
{ {
// Check keyboard source cache first // Check keyboard source cache first
if (m_keySources[kbdNum][keyIndex] == NULL) if (m_keySources[kbdNum][keyIndex] == NULL)
{
m_keySources[kbdNum][keyIndex] = CreateKeySource(kbdNum, keyIndex); m_keySources[kbdNum][keyIndex] = CreateKeySource(kbdNum, keyIndex);
m_keySources[kbdNum][keyIndex]->Acquire();
}
return m_keySources[kbdNum][keyIndex]; return m_keySources[kbdNum][keyIndex];
} }
else else
@ -572,6 +544,7 @@ CInputSource *CInputSystem::GetMouseSource(int mseNum, EMousePart msePart)
m_anyMseSources[mseIndex] = CreateMouseSource(ANY_MOUSE, msePart); m_anyMseSources[mseIndex] = CreateMouseSource(ANY_MOUSE, msePart);
else else
m_anyMseSources[mseIndex] = CreateAnyMouseSource(msePart); m_anyMseSources[mseIndex] = CreateAnyMouseSource(msePart);
m_anyMseSources[mseIndex]->Acquire();
} }
return m_anyMseSources[mseIndex]; return m_anyMseSources[mseIndex];
} }
@ -579,7 +552,10 @@ CInputSource *CInputSystem::GetMouseSource(int mseNum, EMousePart msePart)
{ {
// Check mouse source cache first // Check mouse source cache first
if (m_mseSources[mseNum][mseIndex] == NULL) if (m_mseSources[mseNum][mseIndex] == NULL)
{
m_mseSources[mseNum][mseIndex] = CreateMouseSource(mseNum, msePart); m_mseSources[mseNum][mseIndex] = CreateMouseSource(mseNum, msePart);
m_mseSources[mseNum][mseIndex]->Acquire();
}
return m_mseSources[mseNum][mseIndex]; return m_mseSources[mseNum][mseIndex];
} }
else else
@ -598,6 +574,7 @@ CInputSource *CInputSystem::GetJoySource(int joyNum, EJoyPart joyPart)
m_anyJoySources[joyIndex] = CreateJoySource(ANY_JOYSTICK, joyPart); m_anyJoySources[joyIndex] = CreateJoySource(ANY_JOYSTICK, joyPart);
else else
m_anyJoySources[joyIndex] = CreateAnyJoySource(joyPart); m_anyJoySources[joyIndex] = CreateAnyJoySource(joyPart);
m_anyJoySources[joyIndex]->Acquire();
} }
return m_anyJoySources[joyIndex]; return m_anyJoySources[joyIndex];
} }
@ -605,14 +582,63 @@ CInputSource *CInputSystem::GetJoySource(int joyNum, EJoyPart joyPart)
{ {
// Check joystick source cache first // Check joystick source cache first
if (m_joySources[joyNum][joyIndex] == NULL) if (m_joySources[joyNum][joyIndex] == NULL)
{
m_joySources[joyNum][joyIndex] = CreateJoySource(joyNum, joyPart); m_joySources[joyNum][joyIndex] = CreateJoySource(joyNum, joyPart);
m_joySources[joyNum][joyIndex]->Acquire();
}
return m_joySources[joyNum][joyIndex]; return m_joySources[joyNum][joyIndex];
} }
else else
return m_emptySource; 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 // Loop through all valid keys
for (int i = 0; i < NUM_VALID_KEYS; i++) 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); int keyIndex = GetKeyIndex(keyName);
if (keyIndex < 0) if (keyIndex < 0)
continue; 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); 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 // Update mapping string and add source to list
if (sources.size() == 0) 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 // Loop through all mouse parts
for (int mseIndex = 0; mseIndex < NUM_MOUSE_PARTS; mseIndex++) 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 // 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)) if (isXYAxis && !mseCentered || isAxis && (IsFullAxis(msePart) && !fullAxisOnly || !IsFullAxis(msePart) && fullAxisOnly))
continue; 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); 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 // Otherwise, update mapping string and add source to list
const char *partName = LookupName(msePart); 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 // Loop through all joystick parts
for (int joyIndex = 0; joyIndex < NUM_JOY_PARTS; joyIndex++) 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 // Filter axes according to fullAxisOnly
if (IsAxis(joyPart) && (IsFullAxis(joyPart) && !fullAxisOnly || !IsFullAxis(joyPart) && fullAxisOnly)) if (IsAxis(joyPart) && (IsFullAxis(joyPart) && !fullAxisOnly || !IsFullAxis(joyPart) && fullAxisOnly))
continue; 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); 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 // Otherwise, update mapping string and add source to list
const char *partName = LookupName(joyPart); const char *partName = LookupName(joyPart);
@ -870,7 +899,7 @@ CInputSource* CInputSystem::ParseMultiSource(string str, bool fullAxisOnly, bool
while (start < size); while (start < size);
// If only parsed a single source, return that, otherwise return a CMultiInputSource combining all the sources // 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) CInputSource *CInputSystem::ParseSingleSource(string str)
@ -886,7 +915,7 @@ CInputSource *CInputSystem::ParseSingleSource(string str)
CInputSource *source = ParseSingleSource(str); CInputSource *source = ParseSingleSource(str);
if (source != NULL && source != m_emptySource) if (source != NULL && source != m_emptySource)
return new CNegInputSource(this, source); return new CNegInputSource(source);
else else
return source; return source;
} }
@ -924,7 +953,7 @@ CInputSource *CInputSystem::ParseSingleSource(string str)
sources.push_back(rightSource); sources.push_back(rightSource);
} }
if (sources.size() > 0) if (sources.size() > 0)
return new CMultiInputSource(this, true, sources); return new CMultiInputSource(true, sources);
} }
return m_emptySource; 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 // 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); for (int axisNum = 0; axisNum < NUM_MOUSE_AXES; axisNum++)
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); 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) 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) if (mseNum != ANY_MOUSE)
baseKey.append(IntToString(mseNum + 1)); baseKey.append(IntToString(mseNum + 1));
bool read = false; bool read = false;
read |= ini->Get(section, baseKey + "XDeadZone", settings->xDeadZone) == OKAY; for (int axisNum = 0; axisNum < NUM_MOUSE_AXES; axisNum++)
read |= ini->Get(section, baseKey + "YDeadZone", settings->yDeadZone) == OKAY; {
read |= ini->Get(section, baseKey + "ZDeadZone", settings->zDeadZone) == OKAY; const char *axisName = s_axisNames[axisNum];
read |= ini->Get(section, baseKey + axisName + "DeadZone", settings->deadZones[axisNum]) == OKAY;
}
if (read) if (read)
return settings; return settings;
delete settings; delete settings;
@ -1072,9 +1106,12 @@ void CInputSystem::WriteMouseSettings(CINIFile *ini, const char *section, MouseS
string baseKey("InputMouse"); string baseKey("InputMouse");
if (settings->mseNum != ANY_MOUSE) if (settings->mseNum != ANY_MOUSE)
baseKey.append(IntToString(settings->mseNum + 1)); baseKey.append(IntToString(settings->mseNum + 1));
if (settings->xDeadZone != common->xDeadZone) ini->Set(section, baseKey + "XDeadZone", settings->xDeadZone); for (int axisNum = 0; axisNum < NUM_MOUSE_AXES; axisNum++)
if (settings->yDeadZone != common->yDeadZone) ini->Set(section, baseKey + "YDeadZone", settings->yDeadZone); {
if (settings->zDeadZone != common->zDeadZone) ini->Set(section, baseKey + "ZDeadZone", settings->zDeadZone); 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) 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 // 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); for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
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); const char *axisName = s_axisNames[axisNum];
if (common == NULL || settings->ySaturation != common->ySaturation) printf(" Y-Axis Saturation = %d %%\n", settings->ySaturation); if (common == NULL || settings->axisMinVals[axisNum] != common->axisMinVals[axisNum])
if (common == NULL || settings->zDeadZone != common->zDeadZone) printf(" Z-Axis Dead Zone = %d %%\n", settings->zDeadZone); printf(" %-2s-Axis Min Value = %d\n", axisName, settings->axisMinVals[axisNum]);
if (common == NULL || settings->zSaturation != common->zSaturation) printf(" Z-Axis Saturation = %d %%\n", settings->zSaturation); if (common == NULL || settings->axisOffVals[axisNum] != common->axisOffVals[axisNum])
if (common == NULL || settings->rxDeadZone != common->rxDeadZone) printf(" RX-Axis Dead Zone = %d %%\n", settings->rxDeadZone); printf(" %-2s-Axis Center/Off Value = %d\n", axisName, settings->axisOffVals[axisNum]);
if (common == NULL || settings->rxSaturation != common->rxSaturation) printf(" RX-Axis Saturation = %d %%\n", settings->rxSaturation); if (common == NULL || settings->axisMaxVals[axisNum] != common->axisMaxVals[axisNum])
if (common == NULL || settings->ryDeadZone != common->ryDeadZone) printf(" RY-Axis Dead Zone = %d %%\n", settings->ryDeadZone); printf(" %-2s-Axis Max Value = %d\n", axisName, settings->axisMaxVals[axisNum]);
if (common == NULL || settings->rySaturation != common->rySaturation) printf(" RY-Axis Saturation = %d %%\n", settings->rySaturation); if (common == NULL || settings->deadZones[axisNum] != common->deadZones[axisNum])
if (common == NULL || settings->rzDeadZone != common->rzDeadZone) printf(" RZ-Axis Dead Zone = %d %%\n", settings->rzDeadZone); printf(" %-2s-Axis Dead Zone = %d %%\n", axisName, settings->deadZones[axisNum]);
if (common == NULL || settings->rzSaturation != common->rzSaturation) printf(" RZ-Axis Saturation = %d %%\n", settings->rzSaturation); 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) 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) if (joyNum != ANY_JOYSTICK)
baseKey.append(IntToString(joyNum + 1)); baseKey.append(IntToString(joyNum + 1));
bool read = false; bool read = false;
read |= ini->Get(section, baseKey + "XDeadZone", settings->xDeadZone) == OKAY; for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
read |= ini->Get(section, baseKey + "XSaturation", settings->xSaturation) == OKAY; {
read |= ini->Get(section, baseKey + "YDeadZone", settings->yDeadZone) == OKAY; const char *axisName = s_axisNames[axisNum];
read |= ini->Get(section, baseKey + "YSaturation", settings->ySaturation) == OKAY; read |= ini->Get(section, baseKey + axisName + "MinVal", settings->axisMinVals[axisNum]) == OKAY;
read |= ini->Get(section, baseKey + "ZDeadZone", settings->zDeadZone) == OKAY; read |= ini->Get(section, baseKey + axisName + "OffVal", settings->axisOffVals[axisNum]) == OKAY;
read |= ini->Get(section, baseKey + "ZSaturation", settings->zSaturation) == OKAY; read |= ini->Get(section, baseKey + axisName + "MaxVal", settings->axisMaxVals[axisNum]) == OKAY;
read |= ini->Get(section, baseKey + "RXDeadZone", settings->rxDeadZone) == OKAY; read |= ini->Get(section, baseKey + axisName + "DeadZone", settings->deadZones[axisNum]) == OKAY;
read |= ini->Get(section, baseKey + "RXSaturation", settings->rxSaturation) == OKAY; read |= ini->Get(section, baseKey + axisName + "Saturation", settings->saturations[axisNum]) == 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;
if (read) if (read)
return settings; return settings;
delete settings; delete settings;
@ -1146,18 +1182,20 @@ void CInputSystem::WriteJoySettings(CINIFile *ini, const char *section, JoySetti
string baseKey("InputJoy"); string baseKey("InputJoy");
if (settings->joyNum != ANY_JOYSTICK) if (settings->joyNum != ANY_JOYSTICK)
baseKey.append(IntToString(settings->joyNum + 1)); baseKey.append(IntToString(settings->joyNum + 1));
if (settings->xDeadZone != common->xDeadZone) ini->Set(section, baseKey + "XDeadZone", settings->xDeadZone); for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
if (settings->xSaturation != common->xSaturation) ini->Set(section, baseKey + "XSaturation", settings->xSaturation); {
if (settings->yDeadZone != common->yDeadZone) ini->Set(section, baseKey + "YDeadZone", settings->yDeadZone); const char *axisName = s_axisNames[axisNum];
if (settings->ySaturation != common->ySaturation) ini->Set(section, baseKey + "YSaturation", settings->ySaturation); if (settings->axisMinVals[axisNum] != common->axisMinVals[axisNum])
if (settings->zDeadZone != common->zDeadZone) ini->Set(section, baseKey + "ZDeadZone", settings->zDeadZone); ini->Set(section, baseKey + axisName + "MinVal", settings->axisMinVals[axisNum]);
if (settings->zSaturation != common->zSaturation) ini->Set(section, baseKey + "ZSaturation", settings->zSaturation); if (settings->axisOffVals[axisNum] != common->axisOffVals[axisNum])
if (settings->rxDeadZone != common->rxDeadZone) ini->Set(section, baseKey + "RXDeadZone", settings->rxDeadZone); ini->Set(section, baseKey + axisName + "OffVal", settings->axisOffVals[axisNum]);
if (settings->rxSaturation != common->rxSaturation) ini->Set(section, baseKey + "RXSaturation", settings->rxSaturation); if (settings->axisMaxVals[axisNum] != common->axisMaxVals[axisNum])
if (settings->ryDeadZone != common->ryDeadZone) ini->Set(section, baseKey + "RYDeadZone", settings->ryDeadZone); ini->Set(section, baseKey + axisName + "MaxVal", settings->axisMaxVals[axisNum]);
if (settings->rySaturation != common->rySaturation) ini->Set(section, baseKey + "RYSaturation", settings->rySaturation); if (settings->deadZones[axisNum] != common->deadZones[axisNum])
if (settings->rzDeadZone != common->rzDeadZone) ini->Set(section, baseKey + "RZDeadZone", settings->rzDeadZone); ini->Set(section, baseKey + axisName + "DeadZone", settings->deadZones[axisNum]);
if (settings->rzSaturation != common->rzSaturation) ini->Set(section, baseKey + "RZSaturation", settings->rzSaturation); if (settings->saturations[axisNum] != common->saturations[axisNum])
ini->Set(section, baseKey + axisName + "Saturation", settings->saturations[axisNum]);
}
} }
KeySettings *CInputSystem::GetKeySettings(int kbdNum, bool useDefault) KeySettings *CInputSystem::GetKeySettings(int kbdNum, bool useDefault)
@ -1338,7 +1376,7 @@ CInputSource *CInputSystem::CreateAnyKeySource(int keyIndex)
if (keySrc != NULL) if (keySrc != NULL)
keySrcs.push_back(keySrc); keySrcs.push_back(keySrc);
} }
return new CMultiInputSource(this, true, keySrcs); return new CMultiInputSource(true, keySrcs);
} }
CInputSource *CInputSystem::CreateAnyMouseSource(EMousePart msePart) CInputSource *CInputSystem::CreateAnyMouseSource(EMousePart msePart)
@ -1351,7 +1389,7 @@ CInputSource *CInputSystem::CreateAnyMouseSource(EMousePart msePart)
if (mseSrc != NULL) if (mseSrc != NULL)
mseSrcs.push_back(mseSrc); mseSrcs.push_back(mseSrc);
} }
return new CMultiInputSource(this, true, mseSrcs); return new CMultiInputSource(true, mseSrcs);
} }
CInputSource *CInputSystem::CreateAnyJoySource(EJoyPart joyPart) CInputSource *CInputSystem::CreateAnyJoySource(EJoyPart joyPart)
@ -1364,7 +1402,7 @@ CInputSource *CInputSystem::CreateAnyJoySource(EJoyPart joyPart)
if (joySrc != NULL) if (joySrc != NULL)
joySrcs.push_back(joySrc); joySrcs.push_back(joySrc);
} }
return new CMultiInputSource(this, true, joySrcs); return new CMultiInputSource(true, joySrcs);
} }
CInputSource *CInputSystem::CreateKeySource(int kbdNum, int keyIndex) CInputSource *CInputSystem::CreateKeySource(int kbdNum, int keyIndex)
@ -1386,16 +1424,8 @@ CInputSource *CInputSystem::CreateMouseSource(int mseNum, EMousePart msePart)
int axisDir; int axisDir;
if (GetAxisDetails(msePart, axisNum, axisDir)) if (GetAxisDetails(msePart, axisNum, axisDir))
{ {
// Part is mouse axis so get the deadzone setting for it // Part is mouse axis so create axis source with appropriate deadzone setting
unsigned deadZone; return new CMseAxisInputSource(this, mseNum, axisNum, axisDir, settings->deadZones[axisNum]);
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);
} }
else if (IsButton(msePart)) else if (IsButton(msePart))
{ {
@ -1423,23 +1453,13 @@ CInputSource *CInputSystem::CreateJoySource(int joyNum, EJoyPart joyPart)
int povDir; int povDir;
if (GetAxisDetails(joyPart, axisNum, axisDir)) if (GetAxisDetails(joyPart, axisNum, axisDir))
{ {
// Part is joystick axis so get the deadzone and saturation settings for it // Part is joystick axis, so see whether joystick actually has this axis
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
if (!joyDetails->hasAxis[axisNum]) 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 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)) 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 // Part is joystick button so map it to a button number
int butNum = GetButtonNumber(joyPart); int butNum = GetButtonNumber(joyPart);
if (butNum < 0) if (butNum < 0 || butNum >= NUM_JOY_BUTTONS)
return NULL; // Buttons out of range are invalid return NULL; // Buttons out of range are invalid
if (butNum >= joyDetails->numButtons) 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 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); 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() void CInputSystem::ClearSettings()
{ {
// Delete all key settings // Delete all key settings
@ -1568,6 +1581,7 @@ void CInputSystem::PrintSettings()
void CInputSystem::ReadFromINIFile(CINIFile *ini, const char *section) void CInputSystem::ReadFromINIFile(CINIFile *ini, const char *section)
{ {
ClearSettings(); ClearSettings();
ClearSourceCache();
// Read all key settings for attached keyboards // Read all key settings for attached keyboards
KeySettings *keySettings = ReadKeySettings(ini, section, ANY_KEYBOARD); 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 // Map given escape mapping to an input source
CInputSource *escape = ParseSource(escapeMapping); CInputSource *escape = ParseSource(escapeMapping);
escape->Acquire();
string badMapping;
string mapping; string mapping;
vector<CInputSource*> badSources;
vector<CInputSource*> sources; vector<CInputSource*> sources;
bool mseCentered = false; 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 // Loop until have received meaningful inputs
for (;;) for (;;)
{ {
// Poll inputs // Poll inputs
if (!Poll()) if (!Poll())
{
escape->Release();
return false; return false;
}
// Check if escape source was triggered // Check if escape source was triggered
if (escape != NULL && escape->IsActive()) if (escape != NULL && escape->IsActive())
@ -1641,56 +1671,20 @@ bool CInputSystem::ReadMapping(char *buffer, unsigned bufSize, bool fullAxisOnly
while (escape->IsActive()) while (escape->IsActive())
{ {
if (!Poll()) if (!Poll())
{
escape->Release();
return false; return false;
}
Wait(1000/60); Wait(1000/60);
} }
escape->Release();
return false; return false;
} }
// See if should read keyboards // Check all active sources
if (readFlags & READ_KEYBOARD) CheckAllSources(readFlags, fullAxisOnly, mseCentered, sources, mapping, badSources);
{
// 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);
}
}
// See if should read mice // When some inputs have been activated, keep looping until they have all been released again.
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.
if (sources.size() > 0) if (sources.size() > 0)
{ {
// Check each source is no longer active // 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 // Copy mapping to buffer and return
strncpy(buffer, mapping.c_str(), bufSize - 1); strncpy(buffer, mapping.c_str(), bufSize - 1);
buffer[bufSize - 1] = '\0'; buffer[bufSize - 1] = '\0';
escape->Release();
return true; return true;
} }
@ -1739,6 +1735,62 @@ void CInputSystem::UngrabMouse()
SetMouseVisibility(true); 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 * CInputSystem::CKeyInputSource
*/ */
@ -1888,26 +1940,28 @@ bool CInputSystem::CMseButInputSource::GetValueAsAnalog(int &val, int minVal, in
/* /*
* CInputSystem::CJoyAxisInputSource * 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), 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 m_axisInverted = m_axisMaxVal < m_axisMinVal;
// percentage 0-99 and saturation given as percentage 1-100) // 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 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, 100) / 100.0;
m_posDZone = (int)(dDeadZone * 32767.0); m_posDZone = m_axisOffVal + (int)(dDeadZone * (m_axisMaxVal - m_axisOffVal));
m_negDZone = (int)(dDeadZone * -32768.0); m_negDZone = m_axisOffVal + (int)(dDeadZone * (m_axisMinVal - m_axisOffVal));
m_posSat = (int)(dSaturation * 32767.0); m_posSat = m_axisOffVal + (int)(dSaturation * (m_axisMaxVal - m_axisOffVal));
m_negSat = (int)(dSaturation * -32868.0); m_negSat = m_axisOffVal + (int)(dSaturation * (m_axisMinVal - m_axisOffVal));
} }
int CInputSystem::CJoyAxisInputSource::ScaleAxisValue(int minVal, int offVal, int maxVal) 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); int joyVal = m_system->GetJoyAxisValue(m_joyNum, m_axisNum);
// Check value is not zero // Check if value is at axis off value
if (joyVal == 0) if (joyVal == m_axisOffVal)
return offVal; return offVal;
// Scale value between deadzone and saturation points, taking positive or negative values only or using the whole axis range as required // 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); 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) else if (m_axisDir == AXIS_FULL)
{ {
// Full axis range // Full axis range
if (joyVal > 0) return Scale(joyVal, m_posDZone, m_posDZone, m_posSat, minVal, offVal, maxVal); if (!m_axisInverted && joyVal > m_axisOffVal || m_axisInverted && joyVal < m_axisOffVal)
else return Scale(joyVal, m_negSat, m_negDZone, m_negDZone, minVal, offVal, maxVal); 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 else
{ {
// Full axis range, but inverted // Full axis range, but inverted
if (joyVal > 0) return Scale(joyVal, m_posDZone, m_posDZone, m_posSat, maxVal, offVal, minVal); if (!m_axisInverted && joyVal > m_axisOffVal || m_axisInverted && joyVal < m_axisOffVal)
else return Scale(joyVal, m_negSat, m_negDZone, m_negDZone, maxVal, offVal, minVal); 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; return true;
} }
bool CInputSystem::CJoyAxisInputSource::SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd) bool CInputSystem::CJoyAxisInputSource::SendForceFeedbackCmd(ForceFeedbackCmd ffCmd)
{ {
return m_system->SendForceFeedbackCmd(m_joyNum, m_axisNum, 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_SENSITIVITY 25
#define DEFAULT_KEY_DECAYSPEED 50 #define DEFAULT_KEY_DECAYSPEED 50
#define DEFAULT_MSE_DEADZONE 0 #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_DEADZONE 3
#define DEFAULT_JOY_SATURATION 100 #define DEFAULT_JOY_SATURATION 100
@ -209,9 +212,7 @@ struct KeySettings
struct MouseSettings struct MouseSettings
{ {
int mseNum; // Mouse number (or ANY_MOUSE for settings that apply to all mice) 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 deadZones[NUM_MOUSE_AXES]; // Axis dead zone as a percentage 0-99 of display width (X)/height (Y) or axis range (Z)
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
/* /*
* Creates a MouseSettings with default settings * Creates a MouseSettings with default settings
@ -219,9 +220,9 @@ struct MouseSettings
MouseSettings() MouseSettings()
{ {
mseNum = ANY_MOUSE; mseNum = ANY_MOUSE;
xDeadZone = DEFAULT_MSE_DEADZONE; deadZones[AXIS_X] = DEFAULT_MSE_DEADZONE;
yDeadZone = DEFAULT_MSE_DEADZONE; deadZones[AXIS_Y] = DEFAULT_MSE_DEADZONE;
zDeadZone = 0; deadZones[AXIS_Z] = 0;
} }
}; };
@ -231,18 +232,11 @@ struct MouseSettings
struct JoySettings struct JoySettings
{ {
int joyNum; // Joystick number (or ANY_JOYSTICK for settings that apply to all joysticks) 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 int axisMinVals[NUM_JOY_AXES]; // Axis min raw value (default -32768)
unsigned xSaturation; // X-Axis saturation as a percentage 1-100 of axis positive/negative ranges int axisOffVals[NUM_JOY_AXES]; // Axis center/off value (default 0)
unsigned yDeadZone; // Y-Axis dead zone as a percentage 0-99 of axis positive/negative ranges int axisMaxVals[NUM_JOY_AXES]; // Axis max raw value (default 32767)
unsigned ySaturation; // Y-Axis saturation as a percentage 1-100 of axis positive/negative ranges unsigned deadZones[NUM_JOY_AXES]; // Axis dead zone as a percentage 0-99 of axis positive/negative ranges
unsigned zDeadZone; // Z-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 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
/* /*
* Creates a JoySettings with default settings * Creates a JoySettings with default settings
@ -250,35 +244,31 @@ struct JoySettings
JoySettings() JoySettings()
{ {
joyNum = ANY_JOYSTICK; joyNum = ANY_JOYSTICK;
xDeadZone = DEFAULT_JOY_DEADZONE; for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
xSaturation = DEFAULT_JOY_SATURATION; {
yDeadZone = DEFAULT_JOY_DEADZONE; axisMinVals[axisNum] = DEFAULT_JOY_AXISMINVAL;
ySaturation = DEFAULT_JOY_SATURATION; axisOffVals[axisNum] = DEFAULT_JOY_AXISOFFVAL;
zDeadZone = DEFAULT_JOY_DEADZONE; axisMaxVals[axisNum] = DEFAULT_JOY_AXISMAXVAL;
zSaturation = DEFAULT_JOY_SATURATION; deadZones[axisNum] = DEFAULT_JOY_DEADZONE;
rxDeadZone = DEFAULT_JOY_DEADZONE; saturations[axisNum] = DEFAULT_JOY_SATURATION;
rxSaturation = DEFAULT_JOY_SATURATION; }
ryDeadZone = DEFAULT_JOY_DEADZONE;
rySaturation = DEFAULT_JOY_SATURATION;
rzDeadZone = DEFAULT_JOY_DEADZONE;
rzSaturation = DEFAULT_JOY_SATURATION;
} }
}; };
struct KeyDetails struct KeyDetails
{ {
char name[MAX_NAME_LENGTH]; // Keyboard name (if available) char name[MAX_NAME_LENGTH + 1]; // Keyboard name (if available)
}; };
struct MouseDetails 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) bool isAbsolute; // True if uses absolute positions (ie lightgun)
}; };
struct JoyDetails 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 numAxes; // Total number of axes on joystick
int numPOVs; // Total number of POV hat controllers on joystick int numPOVs; // Total number of POV hat controllers on joystick
int numButtons; // Total number of buttons 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 // Lookup table for translating joystick mapping strings to their respective joystick parts
static JoyPartsStruct s_joyParts[]; static JoyPartsStruct s_joyParts[];
// Names of axes
static const char *s_axisNames[];
// Number of keyboards, mice and joysticks // Number of keyboards, mice and joysticks
int m_numKbds; int m_numKbds;
int m_numMice; int m_numMice;
@ -334,24 +327,19 @@ private:
// //
/* /*
* Creates cache for all sources. * Creates source cache.
*/ */
void CreateSourceCache(); 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(); void ReleaseSource(CInputSource *&source);
/*
* Deletes an input source.
*/
void DeleteSource(CInputSource *source);
/* /*
* Returns a key source for the given keyboard number (or all keyboards if ANY_KEYBOARD supplied) and key index. * 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); 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) * 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. * 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. * 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) * 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. * 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. * 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) * 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. * 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. * 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); bool ParseInt(string str, int &num);
@ -653,7 +643,7 @@ protected:
/* /*
* Processes the given force feedback command for the given joystick and axis number. * 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 * Waits for the given time in milliseconds
@ -700,6 +690,10 @@ protected:
virtual CInputSource *CreateJoySource(int joyNum, EJoyPart joyPart); virtual CInputSource *CreateJoySource(int joyNum, EJoyPart joyPart);
public: public:
#ifdef DEBUG
static unsigned totalSrcsAcquired;
static unsigned totalSrcsReleased;
#endif
// Name of this input system // Name of this input system
const char *name; const char *name;
@ -793,11 +787,6 @@ public:
*/ */
CInputSource* ParseSource(const char *mapping, bool fullAxisOnly = false); 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) * 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. * 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"); 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; virtual bool Poll() = 0;
@ -822,61 +811,9 @@ public:
*/ */
virtual void SetMouseVisibility(bool visible) = 0; virtual void SetMouseVisibility(bool visible) = 0;
virtual bool SendForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd *ffCmd) 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);
}
void PrintDevices() 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);
}
}
}
// //
// Nested Classes // Nested Classes
@ -957,10 +894,14 @@ public:
int m_joyNum; // Joystick number 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_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_axisDir; // Axis direction (AXIS_FULL, AXIS_INVERTED, AXIS_POSITIVE or AXIS_NEGATIVE)
int m_posDZone; // Dead zone for positive range int m_axisMinVal; // Axis min raw value (default -32768)
int m_negDZone; // Dead zone for negative range int m_axisOffVal; // Axis center/off raw value (default 0)
int m_posSat; // Saturation for positive range int m_axisMaxVal; // Axis max raw value (default 32767)
int m_negSat; // Saturation for negative range 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. * Scales the joystick axis value to the given range.
@ -968,13 +909,14 @@ public:
int ScaleAxisValue(int minVal, int offVal, int maxVal); int ScaleAxisValue(int minVal, int offVal, int maxVal);
public: 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 GetValueAsSwitch(bool &val);
bool GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal); 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"); string key("Input");
key.append(id); key.append(id);
unsigned int autoTrigger; unsigned autoTrigger;
if (ini->Get(section, key, autoTrigger) == OKAY) if (ini->Get(section, key, autoTrigger) == OKAY)
m_autoTrigger = !!autoTrigger; 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 * 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. * reloading the gun easier when playing with just the mouse for example.
*/ */
class CTriggerInput : public CInput class CTriggerInput : public CInput

View file

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

View file

@ -39,10 +39,13 @@ ESourceType CMultiInputSource::GetCombinedType(vector<CInputSource*> &sources)
else return SourceEmpty; 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) : CMultiInputSource::CMultiInputSource(bool isOr, vector<CInputSource*> &sources) :
CInputSource(GetCombinedType(sources)), m_system(system), m_isOr(isOr), m_numSrcs(sources.size()) CInputSource(GetCombinedType(sources)), m_isOr(isOr), m_numSrcs(sources.size())
{ {
m_srcArray = new CInputSource*[m_numSrcs]; m_srcArray = new CInputSource*[m_numSrcs];
copy(sources.begin(), sources.end(), m_srcArray); copy(sources.begin(), sources.end(), m_srcArray);
@ -51,13 +54,33 @@ CMultiInputSource::CMultiInputSource(CInputSystem *system, bool isOr, vector<CIn
CMultiInputSource::~CMultiInputSource() CMultiInputSource::~CMultiInputSource()
{ {
if (m_srcArray != NULL) if (m_srcArray != NULL)
{
for (int i = 0; i < m_numSrcs; i++)
m_system->ReleaseSource(m_srcArray[i]);
delete m_srcArray; 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) bool CMultiInputSource::GetValueAsSwitch(bool &val)
{ {
if (m_isOr) 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; bool result = false;
for (int i = 0; i < m_numSrcs; i++) for (int i = 0; i < m_numSrcs; i++)
@ -121,11 +144,28 @@ bool CMultiInputSource::SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd)
return result; return result;
} }
CNegInputSource::CNegInputSource(CInputSystem *system, CInputSource *source) : CInputSource(source->type), m_system(system), m_source(source) { } CNegInputSource::CNegInputSource(CInputSource *source) : CInputSource(source->type), m_source(source)
CNegInputSource::~CNegInputSource()
{ {
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) 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. * 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. * 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 * Can represent either a combination of multiple assignments that all map to the same input, eg KEY_ALT,JOY1_BUTTON1 or to
* used specify that controls must be activated together, eg KEY_ALT+KEY_P. * specify that controls must be combined together, eg KEY_ALT+KEY_P.
*/ */
class CMultiInputSource : public CInputSource class CMultiInputSource : public CInputSource
{ {
private: private:
// Input system that created this source
CInputSystem *m_system;
// Controls how the inputs sources are combined // Controls how the inputs sources are combined
bool m_isOr; bool m_isOr;
@ -36,7 +33,7 @@ public:
/* /*
* Constructs an 'empty' source (ie one which is always 'off'). * 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. * 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, * 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). * or the first switch input if there are none).
*/ */
CMultiInputSource(CInputSystem *system, bool isOr, vector<CInputSource*> &sources); CMultiInputSource(bool isOr, vector<CInputSource*> &sources);
~CMultiInputSource(); ~CMultiInputSource();
void Acquire();
void Release();
bool GetValueAsSwitch(bool &val); bool GetValueAsSwitch(bool &val);
bool GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal); 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 class CNegInputSource : public CInputSource
{ {
private: private:
// Input system that created this source
CInputSystem *m_system;
// Input source being negated // Input source being negated
CInputSource *m_source; CInputSource *m_source;
public: public:
CNegInputSource(CInputSystem *system, CInputSource *source); CNegInputSource(CInputSource *source);
~CNegInputSource(); void Acquire();
void Release();
bool GetValueAsSwitch(bool &val); bool GetValueAsSwitch(bool &val);

View file

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

View file

@ -181,8 +181,8 @@ void CSDLInputSystem::OpenJoysticks()
// Gather joystick details (name, num POVs & buttons and which axes are available) // Gather joystick details (name, num POVs & buttons and which axes are available)
JoyDetails joyDetails; JoyDetails joyDetails;
const char *pName = SDL_JoystickName(joyNum); const char *pName = SDL_JoystickName(joyNum);
strncpy(joyDetails.name, pName, MAX_NAME_LENGTH - 1); strncpy(joyDetails.name, pName, MAX_NAME_LENGTH);
joyDetails.name[MAX_NAME_LENGTH - 1] = '\0'; joyDetails.name[MAX_NAME_LENGTH] = '\0';
joyDetails.numAxes = SDL_JoystickNumAxes(joystick); joyDetails.numAxes = SDL_JoystickNumAxes(joystick);
for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++) 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); 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 // SDL 1.2 does not support force feedback
return false; return false;

View file

@ -81,7 +81,7 @@ protected:
bool IsJoyButPressed(int joyNum, int butNum); 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); void Wait(int ms);

View file

@ -160,6 +160,7 @@ DIKeyMapStruct CDirectInputSystem::s_keyMap[] =
bool IsXInputDevice(const GUID &devProdGUID) bool IsXInputDevice(const GUID &devProdGUID)
{ {
// Following code taken from MSDN
IWbemLocator* pIWbemLocator = NULL; IWbemLocator* pIWbemLocator = NULL;
IEnumWbemClassObject* pEnumDevices = NULL; IEnumWbemClassObject* pEnumDevices = NULL;
IWbemClassObject* pDevices[20] = {0}; IWbemClassObject* pDevices[20] = {0};
@ -191,7 +192,7 @@ bool IsXInputDevice(const GUID &devProdGUID)
goto exit; goto exit;
// Loop over all devices // Loop over all devices
while (true) for (;;)
{ {
// Get 20 at a time // Get 20 at a time
DWORD uReturned; DWORD uReturned;
@ -259,12 +260,13 @@ exit:
BOOL CALLBACK DI8EnumDevicesCallback(LPCDIDEVICEINSTANCE instance, LPVOID context) BOOL CALLBACK DI8EnumDevicesCallback(LPCDIDEVICEINSTANCE instance, LPVOID context)
{ {
// Keep track of all joystick device GUIDs // Keep track of all joystick device GUIDs
vector<DIJoyInfo> *infos = (vector<DIJoyInfo>*)context; DIEnumDevsContext *diContext = (DIEnumDevsContext*)context;
DIJoyInfo info; DIJoyInfo info;
memset(&info, 0, sizeof(DIJoyInfo)); memset(&info, 0, sizeof(DIJoyInfo));
info.guid = instance->guidInstance; info.guid = instance->guidInstance;
info.isXInput = IsXInputDevice(instance->guidProduct); // If XInput is enabled, see if device is an XInput device
infos->push_back(info); info.isXInput = diContext->useXInput && IsXInputDevice(instance->guidProduct);
diContext->infos->push_back(info);
return DIENUM_CONTINUE; return DIENUM_CONTINUE;
} }
@ -295,7 +297,8 @@ BOOL CALLBACK DI8EnumAxesCallback(LPCDIDEVICEOBJECTINSTANCE instance, LPVOID con
BOOL CALLBACK DI8EnumEffectsCallback(LPCDIEFFECTINFO effectInfo, LPVOID context) BOOL CALLBACK DI8EnumEffectsCallback(LPCDIEFFECTINFO effectInfo, LPVOID context)
{ {
JoyDetails *joyDetails = (JoyDetails*)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; joyDetails->hasFFeedback = true;
return DIENUM_CONTINUE; return DIENUM_CONTINUE;
} }
@ -651,7 +654,7 @@ void CDirectInputSystem::PollKeyboardsAndMice()
if (m_useRawInput) 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 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) if (it->wheelDelta != 0)
{ {
@ -745,7 +748,7 @@ void CDirectInputSystem::CloseKeyboardsAndMice()
} }
// Delete storage for keyboards // 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; delete[] *it;
m_keyDetails.clear(); m_keyDetails.clear();
m_rawKeyboards.clear(); m_rawKeyboards.clear();
@ -785,7 +788,7 @@ void CDirectInputSystem::ResetMice()
m_combRawMseState.x = p.x; m_combRawMseState.x = p.x;
m_combRawMseState.y = p.y; m_combRawMseState.y = p.y;
m_combRawMseState.z = 0; 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->x = p.x;
it->y = p.y; it->y = p.y;
@ -927,7 +930,7 @@ void CDirectInputSystem::ProcessRawInput(HRAWINPUT hInput)
} }
m_combRawMseState.buttons = 0; 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; m_combRawMseState.buttons |= it->buttons;
} }
} }
@ -940,14 +943,17 @@ void CDirectInputSystem::ProcessRawInput(HRAWINPUT hInput)
void CDirectInputSystem::OpenJoysticks() void CDirectInputSystem::OpenJoysticks()
{ {
// Get the info about all attached joystick devices // Get the info about all attached joystick devices
DIEnumDevsContext diContext;
diContext.infos = &m_diJoyInfos;
diContext.useXInput = m_useXInput;
HRESULT hr; 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; return;
// Loop through those found // Loop through those found
int joyNum = 0; int joyNum = 0;
int xNum = 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++; joyNum++;
@ -955,14 +961,14 @@ void CDirectInputSystem::OpenJoysticks()
memset(&joyDetails, 0, sizeof(joyDetails)); memset(&joyDetails, 0, sizeof(joyDetails));
// See if can use XInput for device // 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) // 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)); 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.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.numPOVs = 1; // Digital D-pad
joyDetails.numButtons = 10; joyDetails.numButtons = 10;
joyDetails.hasFFeedback = true; joyDetails.hasFFeedback = m_enableFFeedback;
joyDetails.hasAxis[AXIS_X] = true; joyDetails.hasAxis[AXIS_X] = true;
joyDetails.hasAxis[AXIS_Y] = true; joyDetails.hasAxis[AXIS_Y] = true;
joyDetails.hasAxis[AXIS_Z] = true; joyDetails.hasAxis[AXIS_Z] = true;
@ -982,28 +988,28 @@ void CDirectInputSystem::OpenJoysticks()
else else
{ {
// Otherwise, open joystick with DirectInput for given GUID and set its data format // Otherwise, open joystick with DirectInput for given GUID and set its data format
LPDIRECTINPUTDEVICE8 di8Joystick; LPDIRECTINPUTDEVICE8 joystick;
if (FAILED(hr = m_di8->CreateDevice(it->guid, &di8Joystick, NULL))) 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); ErrorLog("Unable to create DirectInput joystick device %d (error %d) - skipping joystick.\n", joyNum, hr);
continue; 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); ErrorLog("Unable to set data format for DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release(); joystick->Release();
continue; continue;
} }
// Get joystick's capabilities // Get joystick's capabilities
DIDEVCAPS devCaps; DIDEVCAPS devCaps;
devCaps.dwSize = sizeof(DIDEVCAPS); 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); ErrorLog("Unable to query capabilities of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release(); joystick->Release();
continue; continue;
} }
@ -1013,11 +1019,11 @@ void CDirectInputSystem::OpenJoysticks()
didps.diph.dwHeaderSize = sizeof(DIPROPHEADER); didps.diph.dwHeaderSize = sizeof(DIPROPHEADER);
didps.diph.dwHow = DIPH_DEVICE; didps.diph.dwHow = DIPH_DEVICE;
didps.diph.dwObj = 0; 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); ErrorLog("Unable to get name of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release(); joystick->Release();
continue; continue;
} }
// DInput returns name as Unicode, convert to ASCII // DInput returns name as Unicode, convert to ASCII
@ -1028,20 +1034,24 @@ void CDirectInputSystem::OpenJoysticks()
joyDetails.numButtons = devCaps.dwButtons; joyDetails.numButtons = devCaps.dwButtons;
// Enumerate axes // 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); ErrorLog("Unable to enumerate axes of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release(); joystick->Release();
continue; continue;
} }
joyDetails.numAxes = 0; joyDetails.numAxes = 0;
for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++) for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++)
joyDetails.numAxes += joyDetails.hasAxis[axisNum]; joyDetails.numAxes += joyDetails.hasAxis[axisNum];
// See if force feedback available for joystick // See if force feedback enabled and is available for joystick
if (FAILED(hr = di8Joystick->EnumEffects(DI8EnumEffectsCallback, &joyDetails, DIEFT_ALL))) 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); 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 // Configure axes, if any
if (joyDetails.numAxes > 0) if (joyDetails.numAxes > 0)
@ -1054,11 +1064,11 @@ void CDirectInputSystem::OpenJoysticks()
didpr.diph.dwObj = 0; didpr.diph.dwObj = 0;
didpr.lMin = -32768; didpr.lMin = -32768;
didpr.lMax = 32767; 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); ErrorLog("Unable to set axis range of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release(); joystick->Release();
continue; continue;
} }
@ -1069,40 +1079,40 @@ void CDirectInputSystem::OpenJoysticks()
dipdw.diph.dwHow = DIPH_DEVICE; dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.diph.dwObj = 0; dipdw.diph.dwObj = 0;
dipdw.dwData = DIPROPAXISMODE_ABS; 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); ErrorLog("Unable to set axis mode of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release(); joystick->Release();
continue; continue;
} }
// Turn off deadzone as handled by this class // Turn off deadzone as handled by this class
dipdw.dwData = 0; 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); ErrorLog("Unable to set deadzone of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release(); joystick->Release();
continue; continue;
} }
// Turn off saturation as handle by this class // Turn off saturation as handle by this class
dipdw.dwData = 10000; 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); ErrorLog("Unable to set saturation of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr);
di8Joystick->Release(); joystick->Release();
continue; continue;
} }
// If joystick force feedback is enabled and joystick has force feedback capabilities then disable auto-center // If joystick has force feedback capabilities then disable auto-center
if (m_enableFFeedback && joyDetails.hasFFeedback) if (joyDetails.hasFFeedback)
{ {
dipdw.dwData = FALSE; 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); 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 // Keep track of DirectInput device number
it->dInputNum = m_di8Joysticks.size(); it->dInputNum = m_di8Joysticks.size();
m_di8Joysticks.push_back(di8Joystick); m_di8Joysticks.push_back(joystick);
} }
// Create initial blank joystick state // Create initial blank joystick state
@ -1131,20 +1141,31 @@ void CDirectInputSystem::OpenJoysticks()
void CDirectInputSystem::ActivateJoysticks() void CDirectInputSystem::ActivateJoysticks()
{ {
// Set DirectInput cooperative level of joysticks // Set DirectInput cooperative level of joysticks
for (vector<LPDIRECTINPUTDEVICE8>::iterator it = m_di8Joysticks.begin(); it < m_di8Joysticks.end(); it++) unsigned joyNum = 0;
(*it)->SetCooperativeLevel(m_hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); 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() void CDirectInputSystem::PollJoysticks()
{ {
// Get current joystick states from XInput and DirectInput // Get current joystick states from XInput and DirectInput
int i = 0; 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++]; LPDIJOYSTATE2 pJoyState = &m_diJoyStates[i++];
HRESULT hr; HRESULT hr;
if (m_useXInput && it->isXInput) if (it->isXInput)
{ {
// Use XInput to query joystick // Use XInput to query joystick
XINPUT_STATE xState; XINPUT_STATE xState;
@ -1219,8 +1240,24 @@ void CDirectInputSystem::PollJoysticks()
void CDirectInputSystem::CloseJoysticks() 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 // 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)->Unacquire();
(*it)->Release(); (*it)->Release();
@ -1232,47 +1269,102 @@ void CDirectInputSystem::CloseJoysticks()
m_di8Joysticks.clear(); 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; DWORD axisOfs;
switch (axisNum) switch (axisNum)
{ {
case AXIS_X: axisOfs = DIJOFS_X; break; case AXIS_X: axisOfs = DIJOFS_X; break;
case AXIS_Y: axisNum = DIJOFS_Y; break; case AXIS_Y: axisOfs = DIJOFS_Y; break;
case AXIS_Z: axisNum = DIJOFS_Z; break; case AXIS_Z: axisOfs = DIJOFS_Z; break;
case AXIS_RX: axisNum = DIJOFS_RX; break; case AXIS_RX: axisOfs = DIJOFS_RX; break;
case AXIS_RY: axisNum = DIJOFS_RY; break; case AXIS_RY: axisOfs = DIJOFS_RY; break;
case AXIS_RZ: axisNum = DIJOFS_RZ; break; case AXIS_RZ: axisOfs = DIJOFS_RZ; break;
default: return E_FAIL; default: return E_FAIL;
} }
DWORD rgdwAxes[1] = { axisOfs }; DWORD rgdwAxes[1] = { axisOfs };
LONG rglDirection[1] = { 0 }; LONG rglDirection[1] = { 0 };
DICONSTANTFORCE cf = { 0 }; DICONSTANTFORCE dicf;
DICONDITION dic;
DIPERIODIC dip;
DIENVELOPE die;
GUID guid;
// Set common effects parameters
DIEFFECT eff; DIEFFECT eff;
memset(&eff, 0, sizeof(eff)); memset(&eff, 0, sizeof(eff));
eff.dwSize = sizeof(DIEFFECT); eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.dwDuration = INFINITE;
eff.dwSamplePeriod = 0;
eff.dwGain = DI_FFNOMINALMAX;
eff.dwTriggerButton = DIEB_NOTRIGGER; eff.dwTriggerButton = DIEB_NOTRIGGER;
eff.dwTriggerRepeatInterval = 0; eff.dwTriggerRepeatInterval = 0;
eff.dwGain = DI_FFNOMINALMAX;
eff.cAxes = 1; eff.cAxes = 1;
eff.rgdwAxes = rgdwAxes; eff.rgdwAxes = rgdwAxes;
eff.rglDirection = rglDirection; eff.rglDirection = rglDirection;
eff.lpEnvelope = 0; eff.dwDuration = INFINITE;
eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
eff.lpvTypeSpecificParams = &cf;
eff.dwStartDelay = 0; eff.dwStartDelay = 0;
// Create the prepared effect // Set specific effects parameters
HRESULT hr; switch (ffCmd.id)
if (FAILED(hr = di8Joystick->CreateEffect(GUID_ConstantForce, &eff, di8Effect, NULL))) {
return hr; case FFStop:
if (di8Effect == NULL)
return E_FAIL; 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; return S_OK;
} }
@ -1490,18 +1582,15 @@ bool CDirectInputSystem::IsJoyButPressed(int joyNum, int butNum)
return !!m_diJoyStates[joyNum].rgbButtons[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]; DIJoyInfo *pInfo = &m_diJoyInfos[joyNum];
HRESULT hr; HRESULT hr;
if (m_useXInput && pInfo->isXInput) if (pInfo->isXInput)
{ {
XINPUT_VIBRATION vibration; XINPUT_VIBRATION vibration;
switch (ffCmd->id) switch (ffCmd.id)
{ {
case FFStop: case FFStop:
if (axisNum == AXIS_X || axisNum == AXIS_Y) if (axisNum == AXIS_X || axisNum == AXIS_Y)
@ -1510,70 +1599,101 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF
vibration.wRightMotorSpeed = 0; vibration.wRightMotorSpeed = 0;
else else
return false; return false;
if (FAILED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration))) return SUCCEEDED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration));
return false;
break;
case FFConstantForce: case FFConstantForce:
if (axisNum == AXIS_X || axisNum == AXIS_Y) 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) 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 else
return false; 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; return false;
break;
} }
} }
else else
{ {
LPDIRECTINPUTDEVICE8 di8Joystick = m_di8Joysticks[pInfo->dInputNum]; LPDIRECTINPUTDEVICE8 joystick = m_di8Joysticks[pInfo->dInputNum];
switch (ffCmd->id)
{
case FFStop:
if (FAILED(hr = di8Joystick->SendForceFeedbackCommand(DISFFC_STOPALL)))
{
// TODO
return false;
}
break;
case FFConstantForce: // See if command is to stop all force feedback
LPDIRECTINPUTEFFECT di8Effect; if (ffCmd.id == -1)
if (FAILED(hr = CreateJoystickEffect(di8Joystick, axisNum, &di8Effect))) 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; return false;
} }
DICONSTANTFORCE cf;
cf.lMagnitude = ffCmd->data; // TODO - scale data to what?
LONG rglDirection[1] = { 0 }; LONG rglDirection[1] = { 0 };
DICONSTANTFORCE dicf;
DICONDITION dic;
DIPERIODIC dip;
DIENVELOPE die;
// Set common parameters
DIEFFECT eff; DIEFFECT eff;
memset(&eff, 0, sizeof(eff)); memset(&eff, 0, sizeof(eff));
eff.dwSize = sizeof(DIEFFECT); eff.dwSize = sizeof(DIEFFECT);
eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
eff.cAxes = 1; eff.cAxes = 1;
eff.rglDirection = rglDirection; eff.rglDirection = rglDirection;
eff.lpEnvelope = 0;
eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
eff.lpvTypeSpecificParams = &cf;
eff.dwStartDelay = 0; eff.dwStartDelay = 0;
// Now set the new parameters and start the effect immediately. switch (ffCmd.id)
if (FAILED(hr = di8Effect->SetParameters(&eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START)))
{ {
// 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; return false;
} }
//di8Effect->Release(); // Now set the new parameters and start the effect immediately.
break; return SUCCEEDED(hr = (*pEffect)->SetParameters(&eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START));
} }
}
return true;
} }
void CDirectInputSystem::Wait(int ms) void CDirectInputSystem::Wait(int ms)

View file

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