diff --git a/Src/INIFile.cpp b/Src/INIFile.cpp index 550a6f3..2761b93 100644 --- a/Src/INIFile.cpp +++ b/Src/INIFile.cpp @@ -172,7 +172,7 @@ BOOL CINIFile::LookUpSetting(unsigned *idx, unsigned sectionIdx, string SettingN // Assigns a value to the given setting, creating the setting if it does not exist. Nulls out the string (sets it to ""). -void CINIFile::Set(string SectionName, string SettingName, unsigned value) +void CINIFile::Set(string SectionName, string SettingName, int value) { unsigned sectionIdx, settingIdx; @@ -234,7 +234,7 @@ void CINIFile::Set(string SectionName, string SettingName, string String) } // Obtains a numerical setting, if it exists, otherwise does nothing. -BOOL CINIFile::Get(string SectionName, string SettingName, unsigned& value) +BOOL CINIFile::Get(string SectionName, string SettingName, int& value) { unsigned sectionIdx, settingIdx; @@ -248,6 +248,17 @@ BOOL CINIFile::Get(string SectionName, string SettingName, unsigned& value) return OKAY; } +BOOL CINIFile::Get(string SectionName, string SettingName, unsigned& value) +{ + int intVal; + if (Get(SectionName, SettingName, intVal) == FAIL || intVal < 0) + return FAIL; + + value = (unsigned)intVal; + + return OKAY; +} + // Obtains a string setting, if it exists, otherwise does nothing. BOOL CINIFile::Get(string SectionName, string SettingName, string& String) { @@ -323,15 +334,24 @@ CINIFile::CToken CINIFile::GetString(void) return T; } -// Fetch number (decimal or hexadecimal integer). linePtr must point to a character and therefore linePtr[1] is guaranteed to be within bounds. +// Fetch number (decimal or hexadecimal positive/negative integer). +// linePtr must point to a character and therefore linePtr[1] is guaranteed to be within bounds. CINIFile::CToken CINIFile::GetNumber(void) { CToken T; unsigned long long number = 0; + BOOL isNeg = FALSE; int overflow = 0; T.type = TOKEN_NUMBER; + // See if begins with minus sign + if (linePtr[0]=='-') + { + isNeg = TRUE; + linePtr++; + } + // Hexadecimal? if ((linePtr[0]=='0') && ((linePtr[1]=='X') || (linePtr[1]=='x'))) { @@ -360,7 +380,7 @@ CINIFile::CToken CINIFile::GetNumber(void) ++linePtr; // Check for overflows - if (number > 0x00000000FFFFFFFFULL) + if (!isNeg && number > 0x000000007FFFFFFFULL || isNeg && number > 0x0000000080000000ULL) overflow = 1; } else if (IsBlank(linePtr[0])) @@ -387,7 +407,7 @@ CINIFile::CToken CINIFile::GetNumber(void) ++linePtr; // Check for overflows - if (number > 0x00000000FFFFFFFFULL) + if (!isNeg && number > 0x000000007FFFFFFFULL || isNeg && number > 0x0000000080000000ULL) overflow = 1; } else if (IsBlank(linePtr[0])) @@ -404,7 +424,7 @@ CINIFile::CToken CINIFile::GetNumber(void) //if (overflow) // printf("tokenizer: number exceeds 32 bits and has been truncated\n"); - T.number = (unsigned) number; + T.number = (isNeg ? -(int)number : (int)number); return T; } diff --git a/Src/INIFile.h b/Src/INIFile.h index 669a7e1..ea71f4a 100644 --- a/Src/INIFile.h +++ b/Src/INIFile.h @@ -63,6 +63,7 @@ public: * OKAY if the setting was found, FAIL otherwise. The type is not * checked. */ + BOOL Get(string SectionName, string SettingName, int& value); BOOL Get(string SectionName, string SettingName, unsigned& value); BOOL Get(string SectionName, string SettingName, string& String); @@ -82,7 +83,7 @@ public: * value Value to write. String will be set to "". * String String to write. Value will be set to 0. */ - void Set(string SectionName, string SettingName, unsigned value); + void Set(string SectionName, string SettingName, int value); void Set(string SectionName, string SettingName, string String); /* @@ -146,7 +147,7 @@ private: { public: int type; // token type (defined privately in INIFile.cpp) - unsigned number; // numbers and bools + int number; // numbers and bools string String; // strings and identifiers // Constructor (initialize to null token) @@ -177,7 +178,7 @@ private: { string Name; // setting name BOOL isNumber; // internal flag: true if the setting is a number, false if it is a string - unsigned value; // value of number + int value; // value of number string String; // string Setting(void) diff --git a/Src/Inputs/Input.cpp b/Src/Inputs/Input.cpp index 2c9859f..7cd723a 100644 --- a/Src/Inputs/Input.cpp +++ b/Src/Inputs/Input.cpp @@ -7,11 +7,18 @@ CInput::CInput(const char *inputId, const char *inputLabel, unsigned inputFlags, ResetToDefaultMapping(); } +CInput::~CInput() +{ + // Release source, if any + if (m_source != NULL) + m_source->Release(); +} + void CInput::CreateSource() { // If already have a source, then release it now - if (m_system != NULL && m_source != NULL) - m_system->ReleaseSource(m_source); + if (m_source != NULL) + m_source->Release(); // If no system set yet or mapping is empty or NONE, then set source to NULL if (m_system == NULL || m_mapping[0] == '\0' || stricmp(m_mapping, "NONE") == 0) @@ -21,12 +28,18 @@ void CInput::CreateSource() // Otherwise, ask system to parse mapping into appropriate input source m_source = m_system->ParseSource(m_mapping, !!(flags & INPUT_FLAGS_AXIS)); - // Check that mapping was parsed okay and if not then fall back to default mapping - if (m_source == NULL && stricmp(m_mapping, m_defaultMapping) != 0) + // Check that mapping was parsed okay and if so acquire it + if (m_source != NULL) + m_source->Acquire(); + else { - ErrorLog("Unable to map input %s to [%s] - switching to default [%s].\n", id, m_mapping, m_defaultMapping); + // Otherwise, fall back to default mapping + if (stricmp(m_mapping, m_defaultMapping) != 0) + { + ErrorLog("Unable to map input %s to [%s] - switching to default [%s].\n", id, m_mapping, m_defaultMapping); - ResetToDefaultMapping(); + ResetToDefaultMapping(); + } } } } @@ -72,8 +85,8 @@ void CInput::ClearMapping() void CInput::SetMapping(const char *mapping) { - strncpy(m_mapping, mapping, MAX_MAPPING_LENGTH - 1); - m_mapping[MAX_MAPPING_LENGTH - 1] = '\0'; + strncpy(m_mapping, mapping, MAX_MAPPING_LENGTH); + m_mapping[MAX_MAPPING_LENGTH] = '\0'; CreateSource(); } @@ -99,13 +112,23 @@ void CInput::ResetToDefaultMapping() void CInput::ReadFromINIFile(CINIFile *ini, const char *section) { - if (!IsConfigurable()) - return; - string key("Input"); - key.append(id); - string mapping; - if (ini->Get(section, key, mapping) == OKAY) - SetMapping(mapping.c_str()); + // See if input is configurable + if (IsConfigurable()) + { + // If so, check INI file for mapping string + string key("Input"); + key.append(id); + string mapping; + if (ini->Get(section, key, mapping) == OKAY) + { + // If found, then set mapping string + SetMapping(mapping.c_str()); + return; + } + } + + // If input has not been configured, then force recreation of source anyway since input system settings may have changed + CreateSource(); } void CInput::WriteToINIFile(CINIFile *ini, const char *section) @@ -134,7 +157,7 @@ bool CInput::Changed() return value != prevValue; } -bool CInput::SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd) +bool CInput::SendForceFeedbackCmd(ForceFeedbackCmd ffCmd) { if (m_source == NULL) return false; diff --git a/Src/Inputs/Input.h b/Src/Inputs/Input.h index 294a705..46ccb8a 100644 --- a/Src/Inputs/Input.h +++ b/Src/Inputs/Input.h @@ -18,10 +18,14 @@ class CINIFile; #define MAX_MAPPING_LENGTH 255 +#define NUM_FF_EFFECTS 3 + enum EForceFeedback { - FFStop, - FFConstantForce + FFStop = -1, + FFSelfCenter = 0, + FFConstantForce = 1, + FFVibrate = 2 }; struct ForceFeedbackCmd @@ -37,7 +41,7 @@ class CInput { private: // Current mapping(s) for input, eg JOY1_XAXIS_POS - char m_mapping[MAX_MAPPING_LENGTH]; + char m_mapping[MAX_MAPPING_LENGTH + 1]; // Default mapping for input const char *m_defaultMapping; @@ -53,8 +57,16 @@ private: protected: // Current input source CInputSource *m_source; + + /* + * Constructs an input with the given identifier, label, flags, game flags, default mapping and initial value. + */ + CInput(const char *inputId, const char *inputLabel, unsigned inputFlags, unsigned inputGameFlags, + const char *defaultMapping = "NONE", UINT16 initValue = 0); public: + virtual ~CInput(); + // Input identifier const char *id; @@ -72,12 +84,6 @@ public: // Previous input value UINT16 prevValue; - - /* - * Constructs an input with the given identifier, label, flags, game flags, default mapping and initial value. - */ - CInput(const char *inputId, const char *inputLabel, unsigned inputFlags, unsigned inputGameFlags, - const char *defaultMapping = "NONE", UINT16 initValue = 0); /* * Initializes this input with the given input system. Must be called before any other methods are used. @@ -161,7 +167,7 @@ public: /* * Sends a force feedback command to the input source of this input. */ - bool SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd); + bool SendForceFeedbackCmd(ForceFeedbackCmd ffCmd); }; // diff --git a/Src/Inputs/InputSource.cpp b/Src/Inputs/InputSource.cpp index 2eb503f..a5d4495 100644 --- a/Src/Inputs/InputSource.cpp +++ b/Src/Inputs/InputSource.cpp @@ -1,13 +1,32 @@ -#include "InputSource.h" +#include "Supermodel.h" #include using namespace std; -CInputSource::CInputSource(ESourceType sourceType) : type(sourceType) +CInputSource::CInputSource(ESourceType sourceType) : type(sourceType), m_acquired(0) { // } +void CInputSource::Acquire() +{ + m_acquired++; + +#ifdef DEBUG + CInputSystem::totalSrcsAcquired++; +#endif +} + +void CInputSource::Release() +{ + if (--m_acquired == 0) + delete this; + +#ifdef DEBUG + CInputSystem::totalSrcsReleased++; +#endif +} + int CInputSource::Clamp(int val, int minVal, int maxVal) { if (val > maxVal) return maxVal; @@ -22,19 +41,19 @@ int CInputSource::Scale(int val, int fromMinVal, int fromMaxVal, int toMinVal, i int CInputSource::Scale(int val, int fromMinVal, int fromOffVal, int fromMaxVal, int toMinVal, int toOffVal, int toMaxVal) { - int fromRange; + double fromRange; double frac; if (fromMaxVal > fromMinVal) { val = Clamp(val, fromMinVal, fromMaxVal); if (val > fromOffVal) { - fromRange = fromMaxVal - fromOffVal; + fromRange = (double)(fromMaxVal - fromOffVal); frac = (double)(val - fromOffVal) / fromRange; } else if (val < fromOffVal) { - fromRange = fromOffVal - fromMinVal; + fromRange = (double)(fromOffVal - fromMinVal); frac = (double)(val - fromOffVal) / fromRange; } else @@ -45,12 +64,12 @@ int CInputSource::Scale(int val, int fromMinVal, int fromOffVal, int fromMaxVal, val = Clamp(val, fromMaxVal, fromMinVal); if (val > fromOffVal) { - fromRange = fromMinVal - fromOffVal; + fromRange = (double)(fromMinVal - fromOffVal); frac = (double)(fromOffVal - val) / fromRange; } else if (val < fromOffVal) { - fromRange = fromOffVal - fromMaxVal; + fromRange = (double)(fromOffVal - fromMaxVal); frac = (double)(fromOffVal - val) / fromRange; } else @@ -83,7 +102,7 @@ bool CInputSource::IsActive() return GetValueAsSwitch(boolVal); } -bool CInputSource::SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd) +bool CInputSource::SendForceFeedbackCmd(ForceFeedbackCmd ffCmd) { return false; } \ No newline at end of file diff --git a/Src/Inputs/InputSource.h b/Src/Inputs/InputSource.h index ec21947..0f15bea 100644 --- a/Src/Inputs/InputSource.h +++ b/Src/Inputs/InputSource.h @@ -25,6 +25,8 @@ enum ESourceType class CInputSource { protected: + unsigned m_acquired; + CInputSource(ESourceType sourceType); public: @@ -33,6 +35,10 @@ public: */ const ESourceType type; + virtual void Acquire(); + + virtual void Release(); + // // Static helper methods // @@ -73,7 +79,7 @@ public: /* * Sends a force feedback command to the input source. */ - virtual bool SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd); + virtual bool SendForceFeedbackCmd(ForceFeedbackCmd ffCmd); }; #endif // INCLUDED_INPUTSOURCE_H \ No newline at end of file diff --git a/Src/Inputs/InputSystem.cpp b/Src/Inputs/InputSystem.cpp index 0c86f8d..9d65a2f 100644 --- a/Src/Inputs/InputSystem.cpp +++ b/Src/Inputs/InputSystem.cpp @@ -6,6 +6,11 @@ #include using namespace std; +#ifdef DEBUG +unsigned CInputSystem::totalSrcsAcquired = 0; +unsigned CInputSystem::totalSrcsReleased = 0; +#endif + const char *CInputSystem::s_validKeyNames[] = { // General keys @@ -343,17 +348,26 @@ JoyPartsStruct CInputSystem::s_joyParts[] = { NULL, JoyUnknown } }; +const char *CInputSystem::s_axisNames[] = { "X", "Y", "Z", "RX", "RY", "RZ" }; + CInputSystem::CInputSystem(const char *systemName) : name(systemName), m_dispX(0), m_dispY(0), m_dispW(0), m_dispH(0), m_grabMouse(false) { - m_emptySource = new CMultiInputSource(this); + m_emptySource = new CMultiInputSource(); + m_emptySource->Acquire(); } CInputSystem::~CInputSystem() { - DeleteSourceCache(); + m_emptySource->Release(); + ClearSettings(); - delete m_emptySource; + ClearSourceCache(true); + +#ifdef DEBUG + if (totalSrcsAcquired != totalSrcsReleased) + printf("WARNING - number of input source acquisitions (%u) does not equal number of releases (%u)\n", totalSrcsAcquired, totalSrcsReleased); +#endif } void CInputSystem::CreateSourceCache() @@ -398,141 +412,95 @@ void CInputSystem::CreateSourceCache() } } -bool CInputSystem::IsInSourceCache(CInputSource *source) +void CInputSystem::ClearSourceCache(bool deleteCache) { - // Check keyboard source cache + // Clear cache of keyboard sources if (m_anyKeySources != NULL) { for (int keyIndex = 0; keyIndex < NUM_VALID_KEYS; keyIndex++) + ReleaseSource(m_anyKeySources[keyIndex]); + if (deleteCache) { - if (source == m_anyKeySources[keyIndex]) - return true; + delete[] m_anyKeySources; + m_anyKeySources = NULL; } if (m_numKbds != ANY_KEYBOARD) { for (int kbdNum = 0; kbdNum < m_numKbds; kbdNum++) { for (int keyIndex = 0; keyIndex < NUM_VALID_KEYS; keyIndex++) - { - if (source == m_keySources[kbdNum][keyIndex]) - return true; - } + ReleaseSource(m_keySources[kbdNum][keyIndex]); + if (deleteCache) + delete[] m_keySources[kbdNum]; + } + if (deleteCache) + { + delete[] m_keySources; + m_keySources = NULL; } } } - // Check mouse source cache + // Clear cache of mouse sources if (m_anyMseSources != NULL) { for (int mseIndex = 0; mseIndex < NUM_MOUSE_PARTS; mseIndex++) + ReleaseSource(m_anyMseSources[mseIndex]); + if (deleteCache) { - if (source == m_anyMseSources[mseIndex]) - return true; + delete[] m_anyMseSources; + m_anyMseSources = NULL; } if (m_numMice != ANY_MOUSE) { for (int mseNum = 0; mseNum < m_numMice; mseNum++) { for (int mseIndex = 0; mseIndex < NUM_MOUSE_PARTS; mseIndex++) - { - if (source == m_mseSources[mseNum][mseIndex]) - return true; - } + ReleaseSource(m_mseSources[mseNum][mseIndex]); + if (deleteCache) + delete[] m_mseSources[mseNum]; + } + if (deleteCache) + { + delete[] m_mseSources; + m_mseSources = NULL; } } } - // Check joystick source cache + // Clear cache of joystick sources if (m_anyJoySources != NULL) { for (int joyIndex = 0; joyIndex < NUM_JOY_PARTS; joyIndex++) + ReleaseSource(m_anyJoySources[joyIndex]); + if (deleteCache) { - if (source == m_anyJoySources[joyIndex]) - return true; + delete[] m_anyJoySources; + m_anyJoySources = NULL; } if (m_numJoys != ANY_JOYSTICK) { for (int joyNum = 0; joyNum < m_numJoys; joyNum++) { for (int joyIndex = 0; joyIndex < NUM_JOY_PARTS; joyIndex++) - { - if (source == m_joySources[joyNum][joyIndex]) - return true; - } + ReleaseSource(m_joySources[joyNum][joyIndex]); + if (deleteCache) + delete[] m_joySources[joyNum]; } - } - } - - return false; -} - -void CInputSystem::DeleteSourceCache() -{ - // Delete cache for keyboard sources - if (m_anyKeySources != NULL) - { - for (int keyIndex = 0; keyIndex < NUM_VALID_KEYS; keyIndex++) - DeleteSource(m_anyKeySources[keyIndex]); - delete[] m_anyKeySources; - m_anyKeySources = NULL; - if (m_numKbds != ANY_KEYBOARD) - { - for (int kbdNum = 0; kbdNum < m_numKbds; kbdNum++) + if (deleteCache) { - for (int keyIndex = 0; keyIndex < NUM_VALID_KEYS; keyIndex++) - DeleteSource(m_keySources[kbdNum][keyIndex]); - delete[] m_keySources[kbdNum]; + delete[] m_joySources; + m_joySources = NULL; } - delete[] m_keySources; - m_keySources = NULL; - } - } - - // Delete cache for mouse sources - if (m_anyMseSources != NULL) - { - for (int mseIndex = 0; mseIndex < NUM_MOUSE_PARTS; mseIndex++) - DeleteSource(m_anyMseSources[mseIndex]); - delete[] m_anyMseSources; - m_anyMseSources = NULL; - if (m_numMice != ANY_MOUSE) - { - for (int mseNum = 0; mseNum < m_numMice; mseNum++) - { - for (int mseIndex = 0; mseIndex < NUM_MOUSE_PARTS; mseIndex++) - DeleteSource(m_mseSources[mseNum][mseIndex]); - delete[] m_mseSources[mseNum]; - } - delete[] m_mseSources; - m_mseSources = NULL; - } - } - - // Delete cache for joystick sources - if (m_anyJoySources != NULL) - { - for (int joyIndex = 0; joyIndex < NUM_JOY_PARTS; joyIndex++) - DeleteSource(m_anyJoySources[joyIndex]); - delete[] m_anyJoySources; - m_anyJoySources = NULL; - if (m_numJoys != ANY_JOYSTICK) - { - for (int joyNum = 0; joyNum < m_numJoys; joyNum++) - { - for (int joyIndex = 0; joyIndex < NUM_JOY_PARTS; joyIndex++) - DeleteSource(m_joySources[joyNum][joyIndex]); - delete[] m_joySources[joyNum]; - } - delete[] m_joySources; - m_joySources = NULL; } } } -void CInputSystem::DeleteSource(CInputSource *source) +void CInputSystem::ReleaseSource(CInputSource *&source) { - if (source != NULL && source != m_emptySource) - delete source; + if (source != NULL) + source->Release(); + source = NULL; } CInputSource *CInputSystem::GetKeySource(int kbdNum, int keyIndex) @@ -546,6 +514,7 @@ CInputSource *CInputSystem::GetKeySource(int kbdNum, int keyIndex) m_anyKeySources[keyIndex] = CreateKeySource(ANY_KEYBOARD, keyIndex); else m_anyKeySources[keyIndex] = CreateAnyKeySource(keyIndex); + m_anyKeySources[keyIndex]->Acquire(); } return m_anyKeySources[keyIndex]; } @@ -553,7 +522,10 @@ CInputSource *CInputSystem::GetKeySource(int kbdNum, int keyIndex) { // Check keyboard source cache first if (m_keySources[kbdNum][keyIndex] == NULL) + { m_keySources[kbdNum][keyIndex] = CreateKeySource(kbdNum, keyIndex); + m_keySources[kbdNum][keyIndex]->Acquire(); + } return m_keySources[kbdNum][keyIndex]; } else @@ -572,6 +544,7 @@ CInputSource *CInputSystem::GetMouseSource(int mseNum, EMousePart msePart) m_anyMseSources[mseIndex] = CreateMouseSource(ANY_MOUSE, msePart); else m_anyMseSources[mseIndex] = CreateAnyMouseSource(msePart); + m_anyMseSources[mseIndex]->Acquire(); } return m_anyMseSources[mseIndex]; } @@ -579,7 +552,10 @@ CInputSource *CInputSystem::GetMouseSource(int mseNum, EMousePart msePart) { // Check mouse source cache first if (m_mseSources[mseNum][mseIndex] == NULL) + { m_mseSources[mseNum][mseIndex] = CreateMouseSource(mseNum, msePart); + m_mseSources[mseNum][mseIndex]->Acquire(); + } return m_mseSources[mseNum][mseIndex]; } else @@ -598,6 +574,7 @@ CInputSource *CInputSystem::GetJoySource(int joyNum, EJoyPart joyPart) m_anyJoySources[joyIndex] = CreateJoySource(ANY_JOYSTICK, joyPart); else m_anyJoySources[joyIndex] = CreateAnyJoySource(joyPart); + m_anyJoySources[joyIndex]->Acquire(); } return m_anyJoySources[joyIndex]; } @@ -605,14 +582,63 @@ CInputSource *CInputSystem::GetJoySource(int joyNum, EJoyPart joyPart) { // Check joystick source cache first if (m_joySources[joyNum][joyIndex] == NULL) + { m_joySources[joyNum][joyIndex] = CreateJoySource(joyNum, joyPart); + m_joySources[joyNum][joyIndex]->Acquire(); + } return m_joySources[joyNum][joyIndex]; } else return m_emptySource; } -void CInputSystem::CheckKeySources(int kbdNum, bool fullAxisOnly, vector &sources, string &mapping) +void CInputSystem::CheckAllSources(unsigned readFlags, bool fullAxisOnly, bool &mseCentered, vector &sources, string &mapping, vector &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 &sources, string &mapping, vector &badSources) { // Loop through all valid keys for (int i = 0; i < NUM_VALID_KEYS; i++) @@ -621,9 +647,10 @@ void CInputSystem::CheckKeySources(int kbdNum, bool fullAxisOnly, vectorIsActive() && find(sources.begin(), sources.end(), source) == sources.end()) + if (source != NULL && source->IsActive() && find(sources.begin(), sources.end(), source) == sources.end() && + find(badSources.begin(), badSources.end(), source) == badSources.end()) { // Update mapping string and add source to list if (sources.size() == 0) @@ -639,7 +666,7 @@ void CInputSystem::CheckKeySources(int kbdNum, bool fullAxisOnly, vector &sources, string &mapping) +void CInputSystem::CheckMouseSources(int mseNum, bool fullAxisOnly, bool mseCentered, vector &sources, string &mapping, vector &badSources) { // Loop through all mouse parts for (int mseIndex = 0; mseIndex < NUM_MOUSE_PARTS; mseIndex++) @@ -653,9 +680,10 @@ void CInputSystem::CheckMouseSources(int mseNum, bool fullAxisOnly, bool mseCent // Ignore X- & Y-axes if mouse hasn't been centered yet and filter axes according to fullAxisOnly if (isXYAxis && !mseCentered || isAxis && (IsFullAxis(msePart) && !fullAxisOnly || !IsFullAxis(msePart) && fullAxisOnly)) continue; - // Get mouse source for mouse number and part and test it to see if it is active (but was not previously) + // Get mouse source for mouse number and part and test to see if it is active (but was not previously) and that it is not a "bad" source CInputSource *source = GetMouseSource(mseNum, msePart); - if (source != NULL && source->IsActive() && find(sources.begin(), sources.end(), source) == sources.end()) + if (source != NULL && source->IsActive() && find(sources.begin(), sources.end(), source) == sources.end() && + find(badSources.begin(), badSources.end(), source) == badSources.end()) { // Otherwise, update mapping string and add source to list const char *partName = LookupName(msePart); @@ -672,7 +700,7 @@ void CInputSystem::CheckMouseSources(int mseNum, bool fullAxisOnly, bool mseCent } } -void CInputSystem::CheckJoySources(int joyNum, bool fullAxisOnly, vector &sources, string &mapping) +void CInputSystem::CheckJoySources(int joyNum, bool fullAxisOnly, vector &sources, string &mapping, vector &badSources) { // Loop through all joystick parts for (int joyIndex = 0; joyIndex < NUM_JOY_PARTS; joyIndex++) @@ -681,9 +709,10 @@ void CInputSystem::CheckJoySources(int joyNum, bool fullAxisOnly, vectorIsActive() && find(sources.begin(), sources.end(), source) == sources.end()) + if (source != NULL && source->IsActive() && find(sources.begin(), sources.end(), source) == sources.end() && + find(badSources.begin(), badSources.end(), source) == badSources.end()) { // Otherwise, update mapping string and add source to list const char *partName = LookupName(joyPart); @@ -870,7 +899,7 @@ CInputSource* CInputSystem::ParseMultiSource(string str, bool fullAxisOnly, bool while (start < size); // If only parsed a single source, return that, otherwise return a CMultiInputSource combining all the sources - return (isMulti ? new CMultiInputSource(this, isOr, sources) : source); + return (isMulti ? new CMultiInputSource(isOr, sources) : source); } CInputSource *CInputSystem::ParseSingleSource(string str) @@ -886,7 +915,7 @@ CInputSource *CInputSystem::ParseSingleSource(string str) CInputSource *source = ParseSingleSource(str); if (source != NULL && source != m_emptySource) - return new CNegInputSource(this, source); + return new CNegInputSource(source); else return source; } @@ -924,7 +953,7 @@ CInputSource *CInputSystem::ParseSingleSource(string str) sources.push_back(rightSource); } if (sources.size() > 0) - return new CMultiInputSource(this, true, sources); + return new CMultiInputSource(true, sources); } return m_emptySource; } @@ -1037,9 +1066,12 @@ void CInputSystem::PrintMouseSettings(MouseSettings *settings) } // Print all common settings and any settings that are different to common/default settings - if (common == NULL || settings->xDeadZone != common->xDeadZone) printf(" X Dead Zone = %d %%\n", settings->xDeadZone); - if (common == NULL || settings->yDeadZone != common->yDeadZone) printf(" Y Dead Zone = %d %%\n", settings->yDeadZone); - if (common == NULL || settings->zDeadZone != common->zDeadZone) printf(" Z Dead Zone = %d %%\n", settings->zDeadZone); + for (int axisNum = 0; axisNum < NUM_MOUSE_AXES; axisNum++) + { + const char *axisName = s_axisNames[axisNum]; + if (common == NULL || settings->deadZones[axisNum] != common->deadZones[axisNum]) + printf(" %s-Axis Dead Zone = %d %%\n", axisName, settings->deadZones[axisNum]); + } } MouseSettings *CInputSystem::ReadMouseSettings(CINIFile *ini, const char *section, int mseNum) @@ -1054,9 +1086,11 @@ MouseSettings *CInputSystem::ReadMouseSettings(CINIFile *ini, const char *sectio if (mseNum != ANY_MOUSE) baseKey.append(IntToString(mseNum + 1)); bool read = false; - read |= ini->Get(section, baseKey + "XDeadZone", settings->xDeadZone) == OKAY; - read |= ini->Get(section, baseKey + "YDeadZone", settings->yDeadZone) == OKAY; - read |= ini->Get(section, baseKey + "ZDeadZone", settings->zDeadZone) == OKAY; + for (int axisNum = 0; axisNum < NUM_MOUSE_AXES; axisNum++) + { + const char *axisName = s_axisNames[axisNum]; + read |= ini->Get(section, baseKey + axisName + "DeadZone", settings->deadZones[axisNum]) == OKAY; + } if (read) return settings; delete settings; @@ -1072,9 +1106,12 @@ void CInputSystem::WriteMouseSettings(CINIFile *ini, const char *section, MouseS string baseKey("InputMouse"); if (settings->mseNum != ANY_MOUSE) baseKey.append(IntToString(settings->mseNum + 1)); - if (settings->xDeadZone != common->xDeadZone) ini->Set(section, baseKey + "XDeadZone", settings->xDeadZone); - if (settings->yDeadZone != common->yDeadZone) ini->Set(section, baseKey + "YDeadZone", settings->yDeadZone); - if (settings->zDeadZone != common->zDeadZone) ini->Set(section, baseKey + "ZDeadZone", settings->zDeadZone); + for (int axisNum = 0; axisNum < NUM_MOUSE_AXES; axisNum++) + { + const char *axisName = s_axisNames[axisNum]; + if (settings->deadZones[axisNum] != common->deadZones[axisNum]) + ini->Set(section, baseKey + axisName + "DeadZone", settings->deadZones[axisNum]); + } } void CInputSystem::PrintJoySettings(JoySettings *settings) @@ -1093,18 +1130,20 @@ void CInputSystem::PrintJoySettings(JoySettings *settings) } // Print all common settings and any settings that are different to common/default settings - if (common == NULL || settings->xDeadZone != common->xDeadZone) printf(" X-Axis Dead Zone = %d %%\n", settings->xDeadZone); - if (common == NULL || settings->xSaturation != common->xSaturation) printf(" X-Axis Saturation = %d %%\n", settings->xSaturation); - if (common == NULL || settings->yDeadZone != common->yDeadZone) printf(" Y-Axis Dead Zone = %d %%\n", settings->yDeadZone); - if (common == NULL || settings->ySaturation != common->ySaturation) printf(" Y-Axis Saturation = %d %%\n", settings->ySaturation); - if (common == NULL || settings->zDeadZone != common->zDeadZone) printf(" Z-Axis Dead Zone = %d %%\n", settings->zDeadZone); - if (common == NULL || settings->zSaturation != common->zSaturation) printf(" Z-Axis Saturation = %d %%\n", settings->zSaturation); - if (common == NULL || settings->rxDeadZone != common->rxDeadZone) printf(" RX-Axis Dead Zone = %d %%\n", settings->rxDeadZone); - if (common == NULL || settings->rxSaturation != common->rxSaturation) printf(" RX-Axis Saturation = %d %%\n", settings->rxSaturation); - if (common == NULL || settings->ryDeadZone != common->ryDeadZone) printf(" RY-Axis Dead Zone = %d %%\n", settings->ryDeadZone); - if (common == NULL || settings->rySaturation != common->rySaturation) printf(" RY-Axis Saturation = %d %%\n", settings->rySaturation); - if (common == NULL || settings->rzDeadZone != common->rzDeadZone) printf(" RZ-Axis Dead Zone = %d %%\n", settings->rzDeadZone); - if (common == NULL || settings->rzSaturation != common->rzSaturation) printf(" RZ-Axis Saturation = %d %%\n", settings->rzSaturation); + for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++) + { + const char *axisName = s_axisNames[axisNum]; + if (common == NULL || settings->axisMinVals[axisNum] != common->axisMinVals[axisNum]) + printf(" %-2s-Axis Min Value = %d\n", axisName, settings->axisMinVals[axisNum]); + if (common == NULL || settings->axisOffVals[axisNum] != common->axisOffVals[axisNum]) + printf(" %-2s-Axis Center/Off Value = %d\n", axisName, settings->axisOffVals[axisNum]); + if (common == NULL || settings->axisMaxVals[axisNum] != common->axisMaxVals[axisNum]) + printf(" %-2s-Axis Max Value = %d\n", axisName, settings->axisMaxVals[axisNum]); + if (common == NULL || settings->deadZones[axisNum] != common->deadZones[axisNum]) + printf(" %-2s-Axis Dead Zone = %d %%\n", axisName, settings->deadZones[axisNum]); + if (common == NULL || settings->saturations[axisNum] != common->saturations[axisNum]) + printf(" %-2s-Axis Saturation = %d %%\n", axisName, settings->saturations[axisNum]); + } } JoySettings *CInputSystem::ReadJoySettings(CINIFile *ini, const char *section, int joyNum) @@ -1119,18 +1158,15 @@ JoySettings *CInputSystem::ReadJoySettings(CINIFile *ini, const char *section, i if (joyNum != ANY_JOYSTICK) baseKey.append(IntToString(joyNum + 1)); bool read = false; - read |= ini->Get(section, baseKey + "XDeadZone", settings->xDeadZone) == OKAY; - read |= ini->Get(section, baseKey + "XSaturation", settings->xSaturation) == OKAY; - read |= ini->Get(section, baseKey + "YDeadZone", settings->yDeadZone) == OKAY; - read |= ini->Get(section, baseKey + "YSaturation", settings->ySaturation) == OKAY; - read |= ini->Get(section, baseKey + "ZDeadZone", settings->zDeadZone) == OKAY; - read |= ini->Get(section, baseKey + "ZSaturation", settings->zSaturation) == OKAY; - read |= ini->Get(section, baseKey + "RXDeadZone", settings->rxDeadZone) == OKAY; - read |= ini->Get(section, baseKey + "RXSaturation", settings->rxSaturation) == OKAY; - read |= ini->Get(section, baseKey + "RYDeadZone", settings->ryDeadZone) == OKAY; - read |= ini->Get(section, baseKey + "RYSaturation", settings->rySaturation) == OKAY; - read |= ini->Get(section, baseKey + "RZDeadZone", settings->rzDeadZone) == OKAY; - read |= ini->Get(section, baseKey + "RZSaturation", settings->rzSaturation) == OKAY; + for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++) + { + const char *axisName = s_axisNames[axisNum]; + read |= ini->Get(section, baseKey + axisName + "MinVal", settings->axisMinVals[axisNum]) == OKAY; + read |= ini->Get(section, baseKey + axisName + "OffVal", settings->axisOffVals[axisNum]) == OKAY; + read |= ini->Get(section, baseKey + axisName + "MaxVal", settings->axisMaxVals[axisNum]) == OKAY; + read |= ini->Get(section, baseKey + axisName + "DeadZone", settings->deadZones[axisNum]) == OKAY; + read |= ini->Get(section, baseKey + axisName + "Saturation", settings->saturations[axisNum]) == OKAY; + } if (read) return settings; delete settings; @@ -1146,18 +1182,20 @@ void CInputSystem::WriteJoySettings(CINIFile *ini, const char *section, JoySetti string baseKey("InputJoy"); if (settings->joyNum != ANY_JOYSTICK) baseKey.append(IntToString(settings->joyNum + 1)); - if (settings->xDeadZone != common->xDeadZone) ini->Set(section, baseKey + "XDeadZone", settings->xDeadZone); - if (settings->xSaturation != common->xSaturation) ini->Set(section, baseKey + "XSaturation", settings->xSaturation); - if (settings->yDeadZone != common->yDeadZone) ini->Set(section, baseKey + "YDeadZone", settings->yDeadZone); - if (settings->ySaturation != common->ySaturation) ini->Set(section, baseKey + "YSaturation", settings->ySaturation); - if (settings->zDeadZone != common->zDeadZone) ini->Set(section, baseKey + "ZDeadZone", settings->zDeadZone); - if (settings->zSaturation != common->zSaturation) ini->Set(section, baseKey + "ZSaturation", settings->zSaturation); - if (settings->rxDeadZone != common->rxDeadZone) ini->Set(section, baseKey + "RXDeadZone", settings->rxDeadZone); - if (settings->rxSaturation != common->rxSaturation) ini->Set(section, baseKey + "RXSaturation", settings->rxSaturation); - if (settings->ryDeadZone != common->ryDeadZone) ini->Set(section, baseKey + "RYDeadZone", settings->ryDeadZone); - if (settings->rySaturation != common->rySaturation) ini->Set(section, baseKey + "RYSaturation", settings->rySaturation); - if (settings->rzDeadZone != common->rzDeadZone) ini->Set(section, baseKey + "RZDeadZone", settings->rzDeadZone); - if (settings->rzSaturation != common->rzSaturation) ini->Set(section, baseKey + "RZSaturation", settings->rzSaturation); + for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++) + { + const char *axisName = s_axisNames[axisNum]; + if (settings->axisMinVals[axisNum] != common->axisMinVals[axisNum]) + ini->Set(section, baseKey + axisName + "MinVal", settings->axisMinVals[axisNum]); + if (settings->axisOffVals[axisNum] != common->axisOffVals[axisNum]) + ini->Set(section, baseKey + axisName + "OffVal", settings->axisOffVals[axisNum]); + if (settings->axisMaxVals[axisNum] != common->axisMaxVals[axisNum]) + ini->Set(section, baseKey + axisName + "MaxVal", settings->axisMaxVals[axisNum]); + if (settings->deadZones[axisNum] != common->deadZones[axisNum]) + ini->Set(section, baseKey + axisName + "DeadZone", settings->deadZones[axisNum]); + if (settings->saturations[axisNum] != common->saturations[axisNum]) + ini->Set(section, baseKey + axisName + "Saturation", settings->saturations[axisNum]); + } } KeySettings *CInputSystem::GetKeySettings(int kbdNum, bool useDefault) @@ -1338,7 +1376,7 @@ CInputSource *CInputSystem::CreateAnyKeySource(int keyIndex) if (keySrc != NULL) keySrcs.push_back(keySrc); } - return new CMultiInputSource(this, true, keySrcs); + return new CMultiInputSource(true, keySrcs); } CInputSource *CInputSystem::CreateAnyMouseSource(EMousePart msePart) @@ -1351,7 +1389,7 @@ CInputSource *CInputSystem::CreateAnyMouseSource(EMousePart msePart) if (mseSrc != NULL) mseSrcs.push_back(mseSrc); } - return new CMultiInputSource(this, true, mseSrcs); + return new CMultiInputSource(true, mseSrcs); } CInputSource *CInputSystem::CreateAnyJoySource(EJoyPart joyPart) @@ -1364,7 +1402,7 @@ CInputSource *CInputSystem::CreateAnyJoySource(EJoyPart joyPart) if (joySrc != NULL) joySrcs.push_back(joySrc); } - return new CMultiInputSource(this, true, joySrcs); + return new CMultiInputSource(true, joySrcs); } CInputSource *CInputSystem::CreateKeySource(int kbdNum, int keyIndex) @@ -1386,16 +1424,8 @@ CInputSource *CInputSystem::CreateMouseSource(int mseNum, EMousePart msePart) int axisDir; if (GetAxisDetails(msePart, axisNum, axisDir)) { - // Part is mouse axis so get the deadzone setting for it - unsigned deadZone; - switch (axisNum) - { - case AXIS_X: deadZone = settings->xDeadZone; break; - case AXIS_Y: deadZone = settings->yDeadZone; break; - case AXIS_Z: deadZone = settings->zDeadZone; break; - default: return NULL; // Any other axis numbers are invalid - } - return new CMseAxisInputSource(this, mseNum, axisNum, axisDir, deadZone); + // Part is mouse axis so create axis source with appropriate deadzone setting + return new CMseAxisInputSource(this, mseNum, axisNum, axisDir, settings->deadZones[axisNum]); } else if (IsButton(msePart)) { @@ -1423,23 +1453,13 @@ CInputSource *CInputSystem::CreateJoySource(int joyNum, EJoyPart joyPart) int povDir; if (GetAxisDetails(joyPart, axisNum, axisDir)) { - // Part is joystick axis so get the deadzone and saturation settings for it - unsigned axisDZone; - unsigned axisSat; - switch (axisNum) - { - case AXIS_X: axisDZone = settings->xDeadZone; axisSat = settings->xSaturation; break; - case AXIS_Y: axisDZone = settings->yDeadZone; axisSat = settings->ySaturation; break; - case AXIS_Z: axisDZone = settings->zDeadZone; axisSat = settings->zSaturation; break; - case AXIS_RX: axisDZone = settings->rxDeadZone; axisSat = settings->rxSaturation; break; - case AXIS_RY: axisDZone = settings->ryDeadZone; axisSat = settings->rySaturation; break; - case AXIS_RZ: axisDZone = settings->rzDeadZone; axisSat = settings->rzSaturation; break; - default: return NULL; // Any other axis numbers are invalid - } - // See whether joystick has this axis + // Part is joystick axis, so see whether joystick actually has this axis if (!joyDetails->hasAxis[axisNum]) return m_emptySource; // If joystick doesn't have axis, then return empty source rather than NULL as not really an error - return new CJoyAxisInputSource(this, joyNum, axisNum, axisDir, axisDZone, axisSat); + // Otherwise, create axis source with appropriate axis range, deadzone and saturation settings + return new CJoyAxisInputSource(this, joyNum, axisNum, axisDir, + settings->axisMinVals[axisNum], settings->axisOffVals[axisNum], settings->axisMaxVals[axisNum], + settings->deadZones[axisNum], settings->saturations[axisNum]); } else if (GetPOVDetails(joyPart, povNum, povDir)) { @@ -1452,7 +1472,7 @@ CInputSource *CInputSystem::CreateJoySource(int joyNum, EJoyPart joyPart) { // Part is joystick button so map it to a button number int butNum = GetButtonNumber(joyPart); - if (butNum < 0) + if (butNum < 0 || butNum >= NUM_JOY_BUTTONS) return NULL; // Buttons out of range are invalid if (butNum >= joyDetails->numButtons) return m_emptySource; // If joystick doesn't have button, then return empty source rather than NULL as not really an error @@ -1493,13 +1513,6 @@ CInputSource* CInputSystem::ParseSource(const char *mapping, bool fullAxisOnly) return ParseMultiSource(mapping, fullAxisOnly, true); } -void CInputSystem::ReleaseSource(CInputSource *source) -{ - // If source is not being cached then delete it - if (!IsInSourceCache(source)) - DeleteSource(source); -} - void CInputSystem::ClearSettings() { // Delete all key settings @@ -1568,6 +1581,7 @@ void CInputSystem::PrintSettings() void CInputSystem::ReadFromINIFile(CINIFile *ini, const char *section) { ClearSettings(); + ClearSourceCache(); // Read all key settings for attached keyboards KeySettings *keySettings = ReadKeySettings(ini, section, ANY_KEYBOARD); @@ -1622,17 +1636,33 @@ bool CInputSystem::ReadMapping(char *buffer, unsigned bufSize, bool fullAxisOnly { // Map given escape mapping to an input source CInputSource *escape = ParseSource(escapeMapping); + escape->Acquire(); + string badMapping; string mapping; + vector badSources; vector sources; bool mseCentered = false; + // See which sources activated to begin with and from here on ignore these (this stops badly calibrated axes that are constantly "active" + // from preventing the user from exiting read loop) + if (!Poll()) + { + escape->Release(); + return false; + } + + CheckAllSources(readFlags, fullAxisOnly, mseCentered, badSources, badMapping, sources); + // Loop until have received meaningful inputs for (;;) { // Poll inputs if (!Poll()) + { + escape->Release(); return false; + } // Check if escape source was triggered if (escape != NULL && escape->IsActive()) @@ -1641,56 +1671,20 @@ bool CInputSystem::ReadMapping(char *buffer, unsigned bufSize, bool fullAxisOnly while (escape->IsActive()) { if (!Poll()) + { + escape->Release(); return false; + } Wait(1000/60); } + escape->Release(); return false; } - // See if should read keyboards - if (readFlags & READ_KEYBOARD) - { - // Check all keyboard sources for inputs, merging devices if required - if ((readFlags & READ_MERGE_KEYBOARD) || m_numKbds == ANY_KEYBOARD) - CheckKeySources(ANY_KEYBOARD, fullAxisOnly, sources, mapping); - else - { - for (int kbdNum = 0; kbdNum < m_numKbds; kbdNum++) - CheckKeySources(kbdNum, fullAxisOnly, sources, mapping); - } - } + // Check all active sources + CheckAllSources(readFlags, fullAxisOnly, mseCentered, sources, mapping, badSources); - // See if should read mice - if (readFlags & READ_MOUSE) - { - // For mouse input, wait until mouse is in centre of display before parsing X- and Y-axis movements - if (!mseCentered) - mseCentered = ConfigMouseCentered(); - - // Check all mouse sources for input, merging devices if required - if ((readFlags & READ_MERGE_MOUSE) || m_numMice == ANY_MOUSE) - CheckMouseSources(ANY_MOUSE, fullAxisOnly, mseCentered, sources, mapping); - else - { - for (int mseNum = 0; mseNum < m_numMice; mseNum++) - CheckMouseSources(mseNum, fullAxisOnly, mseCentered, sources, mapping); - } - } - - // See if should read joysticks - if (readFlags & READ_JOYSTICK) - { - // Check all joystick sources, merging devices if required - if ((readFlags & READ_MERGE_JOYSTICK) || m_numJoys == ANY_JOYSTICK) - CheckJoySources(ANY_JOYSTICK, fullAxisOnly, sources, mapping); - else - { - for (int joyNum = 0; joyNum < m_numJoys; joyNum++) - CheckJoySources(joyNum, fullAxisOnly, sources, mapping); - } - } - - // When some inputs have been activated, keep lopping until they have all been released again. + // When some inputs have been activated, keep looping until they have all been released again. if (sources.size() > 0) { // Check each source is no longer active @@ -1723,6 +1717,8 @@ bool CInputSystem::ReadMapping(char *buffer, unsigned bufSize, bool fullAxisOnly // Copy mapping to buffer and return strncpy(buffer, mapping.c_str(), bufSize - 1); buffer[bufSize - 1] = '\0'; + + escape->Release(); return true; } @@ -1739,6 +1735,62 @@ void CInputSystem::UngrabMouse() SetMouseVisibility(true); } +bool CInputSystem::SendForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd) +{ + const JoyDetails *joyDetails = GetJoyDetails(joyNum); + if (!joyDetails->hasFFeedback || !joyDetails->axisHasFF[axisNum]) + return false; + return ProcessForceFeedbackCmd(joyNum, axisNum, ffCmd); +} + +void CInputSystem::PrintDevices() +{ + puts("Keyboards:"); + if (m_numKbds == 0) + puts(" None"); + else if (m_numKbds == ANY_KEYBOARD) + puts(" System Keyboard"); + else + { + for (int kbdNum = 0; kbdNum < m_numKbds; kbdNum++) + { + const KeyDetails *keyDetails = GetKeyDetails(kbdNum); + printf(" %d: %s\n", kbdNum + 1, keyDetails->name); + } + } + + puts("Mice:"); + if (m_numMice == 0) + puts(" None"); + else if (m_numMice == ANY_MOUSE) + puts(" System Mouse"); + else + { + for (int mseNum = 0; mseNum < m_numMice; mseNum++) + { + const MouseDetails *mseDetails = GetMouseDetails(mseNum); + printf(" %d: %s\n", mseNum + 1, mseDetails->name); + } + } + + puts("Joysticks:"); + if (m_numJoys == 0) + puts(" None"); + else if (m_numJoys == ANY_JOYSTICK) + puts(" System Joystick"); + else + { + for (int joyNum = 0; joyNum < m_numJoys; joyNum++) + { + const JoyDetails *joyDetails = GetJoyDetails(joyNum); + if (joyDetails->hasFFeedback) + printf(" %d: %s [Force Feedback Available]\n", joyNum + 1, joyDetails->name); + else + printf(" %d: %s\n", joyNum + 1, joyDetails->name); + } + } +} + /* * CInputSystem::CKeyInputSource */ @@ -1888,26 +1940,28 @@ bool CInputSystem::CMseButInputSource::GetValueAsAnalog(int &val, int minVal, in /* * CInputSystem::CJoyAxisInputSource */ -CInputSystem::CJoyAxisInputSource::CJoyAxisInputSource(CInputSystem *system, int joyNum, int axisNum, int axisDir, unsigned deadZone, unsigned saturation) : +CInputSystem::CJoyAxisInputSource::CJoyAxisInputSource(CInputSystem *system, int joyNum, int axisNum, int axisDir, + int axisMinVal, int axisOffVal, int axisMaxVal, unsigned deadZone, unsigned saturation) : CInputSource(axisDir == AXIS_FULL || axisDir == AXIS_INVERTED ? SourceFullAxis : SourceHalfAxis), - m_system(system), m_joyNum(joyNum), m_axisNum(axisNum), m_axisDir(axisDir) + m_system(system), m_joyNum(joyNum), m_axisNum(axisNum), m_axisDir(axisDir), m_axisMinVal(axisMinVal), m_axisOffVal(axisOffVal), m_axisMaxVal(axisMaxVal) { - // Calculate pos/neg deadzone and saturation points (joystick raw values range from -32768 to 32767, deadzone given as - // percentage 0-99 and saturation given as percentage 1-100) + m_axisInverted = m_axisMaxVal < m_axisMinVal; + // Calculate pos/neg deadzone and saturation points (joystick raw values range from axisMinVal to axisMasVal (centered/off at axisOffVal), + // deadzone given as percentage 0-99 and saturation given as percentage 1-100) double dDeadZone = (double)Clamp((int)deadZone, 0, 99) / 100.0; double dSaturation = (double)Clamp((int)saturation, (int)deadZone + 1, 100) / 100.0; - m_posDZone = (int)(dDeadZone * 32767.0); - m_negDZone = (int)(dDeadZone * -32768.0); - m_posSat = (int)(dSaturation * 32767.0); - m_negSat = (int)(dSaturation * -32868.0); + m_posDZone = m_axisOffVal + (int)(dDeadZone * (m_axisMaxVal - m_axisOffVal)); + m_negDZone = m_axisOffVal + (int)(dDeadZone * (m_axisMinVal - m_axisOffVal)); + m_posSat = m_axisOffVal + (int)(dSaturation * (m_axisMaxVal - m_axisOffVal)); + m_negSat = m_axisOffVal + (int)(dSaturation * (m_axisMinVal - m_axisOffVal)); } int CInputSystem::CJoyAxisInputSource::ScaleAxisValue(int minVal, int offVal, int maxVal) { - // Get raw axis value from input system (values range from -32768 to 32767) + // Get raw axis value from input system int joyVal = m_system->GetJoyAxisValue(m_joyNum, m_axisNum); - // Check value is not zero - if (joyVal == 0) + // Check if value is at axis off value + if (joyVal == m_axisOffVal) return offVal; // Scale value between deadzone and saturation points, taking positive or negative values only or using the whole axis range as required if (m_axisDir == AXIS_POS) return Scale(joyVal, m_posDZone, m_posDZone, m_posSat, minVal, offVal, maxVal); @@ -1915,14 +1969,18 @@ int CInputSystem::CJoyAxisInputSource::ScaleAxisValue(int minVal, int offVal, in else if (m_axisDir == AXIS_FULL) { // Full axis range - if (joyVal > 0) return Scale(joyVal, m_posDZone, m_posDZone, m_posSat, minVal, offVal, maxVal); - else return Scale(joyVal, m_negSat, m_negDZone, m_negDZone, minVal, offVal, maxVal); + if (!m_axisInverted && joyVal > m_axisOffVal || m_axisInverted && joyVal < m_axisOffVal) + return Scale(joyVal, m_posDZone, m_posDZone, m_posSat, minVal, offVal, maxVal); + else + return Scale(joyVal, m_negSat, m_negDZone, m_negDZone, minVal, offVal, maxVal); } else { // Full axis range, but inverted - if (joyVal > 0) return Scale(joyVal, m_posDZone, m_posDZone, m_posSat, maxVal, offVal, minVal); - else return Scale(joyVal, m_negSat, m_negDZone, m_negDZone, maxVal, offVal, minVal); + if (!m_axisInverted && joyVal > m_axisOffVal || m_axisInverted && joyVal < m_axisOffVal) + return Scale(joyVal, m_posDZone, m_posDZone, m_posSat, maxVal, offVal, minVal); + else + return Scale(joyVal, m_negSat, m_negDZone, m_negDZone, maxVal, offVal, minVal); } } @@ -1943,7 +2001,7 @@ bool CInputSystem::CJoyAxisInputSource::GetValueAsAnalog(int &val, int minVal, i return true; } -bool CInputSystem::CJoyAxisInputSource::SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd) +bool CInputSystem::CJoyAxisInputSource::SendForceFeedbackCmd(ForceFeedbackCmd ffCmd) { return m_system->SendForceFeedbackCmd(m_joyNum, m_axisNum, ffCmd); } diff --git a/Src/Inputs/InputSystem.h b/Src/Inputs/InputSystem.h index 75439fe..d92ae37 100644 --- a/Src/Inputs/InputSystem.h +++ b/Src/Inputs/InputSystem.h @@ -33,6 +33,9 @@ class CINIFile; #define DEFAULT_KEY_SENSITIVITY 25 #define DEFAULT_KEY_DECAYSPEED 50 #define DEFAULT_MSE_DEADZONE 0 +#define DEFAULT_JOY_AXISMINVAL -32768 +#define DEFAULT_JOY_AXISOFFVAL 0 +#define DEFAULT_JOY_AXISMAXVAL 32767 #define DEFAULT_JOY_DEADZONE 3 #define DEFAULT_JOY_SATURATION 100 @@ -208,10 +211,8 @@ struct KeySettings */ struct MouseSettings { - int mseNum; // Mouse number (or ANY_MOUSE for settings that apply to all mice) - unsigned xDeadZone; // X-Axis dead zone as a percentage 0-99 of display width - unsigned yDeadZone; // Y-Axis dead zone as a percentage 0-99 of display height - unsigned zDeadZone; // Z-Axis dead zone as a percentage 0-99 of axis range + int mseNum; // Mouse number (or ANY_MOUSE for settings that apply to all mice) + unsigned deadZones[NUM_MOUSE_AXES]; // Axis dead zone as a percentage 0-99 of display width (X)/height (Y) or axis range (Z) /* * Creates a MouseSettings with default settings @@ -219,9 +220,9 @@ struct MouseSettings MouseSettings() { mseNum = ANY_MOUSE; - xDeadZone = DEFAULT_MSE_DEADZONE; - yDeadZone = DEFAULT_MSE_DEADZONE; - zDeadZone = 0; + deadZones[AXIS_X] = DEFAULT_MSE_DEADZONE; + deadZones[AXIS_Y] = DEFAULT_MSE_DEADZONE; + deadZones[AXIS_Z] = 0; } }; @@ -230,19 +231,12 @@ struct MouseSettings */ struct JoySettings { - int joyNum; // Joystick number (or ANY_JOYSTICK for settings that apply to all joysticks) - unsigned xDeadZone; // X-Axis dead zone as a percentage 0-99 of axis positive/negative ranges - unsigned xSaturation; // X-Axis saturation as a percentage 1-100 of axis positive/negative ranges - unsigned yDeadZone; // Y-Axis dead zone as a percentage 0-99 of axis positive/negative ranges - unsigned ySaturation; // Y-Axis saturation as a percentage 1-100 of axis positive/negative ranges - unsigned zDeadZone; // Z-Axis dead zone as a percentage 0-99 of axis positive/negative ranges - unsigned zSaturation; // Z-Axis saturation as a percentage 1-100 of axis positive/negative ranges - unsigned rxDeadZone; // RX-Axis dead zone as a percentage 0-99 of axis positive/negative ranges - unsigned rxSaturation; // RX-Axis saturation as a percentage 1-100 of joystick axis positive/negative ranges - unsigned ryDeadZone; // RY-Axis dead zone as a percentage 0-99 of axis positive/negative ranges - unsigned rySaturation; // RY-Axis saturation as a percentage 1-100 of joystick axis positive/negative ranges - unsigned rzDeadZone; // RZ-Axis dead zone as a percentage 0-99 of axis positive/negative ranges - unsigned rzSaturation; // RZ-Axis saturation as a percentage 1-100 of joystick axis positive/negative ranges + int joyNum; // Joystick number (or ANY_JOYSTICK for settings that apply to all joysticks) + int axisMinVals[NUM_JOY_AXES]; // Axis min raw value (default -32768) + int axisOffVals[NUM_JOY_AXES]; // Axis center/off value (default 0) + int axisMaxVals[NUM_JOY_AXES]; // Axis max raw value (default 32767) + unsigned deadZones[NUM_JOY_AXES]; // Axis dead zone as a percentage 0-99 of axis positive/negative ranges + unsigned saturations[NUM_JOY_AXES]; // Axis saturation as a percentage 1-100 of axis positive/negative ranges /* * Creates a JoySettings with default settings @@ -250,41 +244,37 @@ struct JoySettings JoySettings() { joyNum = ANY_JOYSTICK; - xDeadZone = DEFAULT_JOY_DEADZONE; - xSaturation = DEFAULT_JOY_SATURATION; - yDeadZone = DEFAULT_JOY_DEADZONE; - ySaturation = DEFAULT_JOY_SATURATION; - zDeadZone = DEFAULT_JOY_DEADZONE; - zSaturation = DEFAULT_JOY_SATURATION; - rxDeadZone = DEFAULT_JOY_DEADZONE; - rxSaturation = DEFAULT_JOY_SATURATION; - ryDeadZone = DEFAULT_JOY_DEADZONE; - rySaturation = DEFAULT_JOY_SATURATION; - rzDeadZone = DEFAULT_JOY_DEADZONE; - rzSaturation = DEFAULT_JOY_SATURATION; + for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++) + { + axisMinVals[axisNum] = DEFAULT_JOY_AXISMINVAL; + axisOffVals[axisNum] = DEFAULT_JOY_AXISOFFVAL; + axisMaxVals[axisNum] = DEFAULT_JOY_AXISMAXVAL; + deadZones[axisNum] = DEFAULT_JOY_DEADZONE; + saturations[axisNum] = DEFAULT_JOY_SATURATION; + } } }; struct KeyDetails { - char name[MAX_NAME_LENGTH]; // Keyboard name (if available) + char name[MAX_NAME_LENGTH + 1]; // Keyboard name (if available) }; struct MouseDetails { - char name[MAX_NAME_LENGTH]; // Mouse name (if available) - bool isAbsolute; // True if uses absolute positions (ie lightgun) + char name[MAX_NAME_LENGTH + 1]; // Mouse name (if available) + bool isAbsolute; // True if uses absolute positions (ie lightgun) }; struct JoyDetails { - char name[MAX_NAME_LENGTH]; // Joystick name (if available) - int numAxes; // Total number of axes on joystick - int numPOVs; // Total number of POV hat controllers on joystick - int numButtons; // Total number of buttons on joystick - bool hasFFeedback; // True if joystick supports force feedback - bool hasAxis[NUM_JOY_AXES]; // Flags to indicate which axes available on joystick - bool axisHasFF[NUM_JOY_AXES]; // Flags to indicate which axes are force feedback enabled + char name[MAX_NAME_LENGTH + 1]; // Joystick name (if available) + int numAxes; // Total number of axes on joystick + int numPOVs; // Total number of POV hat controllers on joystick + int numButtons; // Total number of buttons on joystick + bool hasFFeedback; // True if joystick supports force feedback + bool hasAxis[NUM_JOY_AXES]; // Flags to indicate which axes available on joystick + bool axisHasFF[NUM_JOY_AXES]; // Flags to indicate which axes are force feedback enabled }; /* @@ -303,6 +293,9 @@ private: // Lookup table for translating joystick mapping strings to their respective joystick parts static JoyPartsStruct s_joyParts[]; + // Names of axes + static const char *s_axisNames[]; + // Number of keyboards, mice and joysticks int m_numKbds; int m_numMice; @@ -334,24 +327,19 @@ private: // /* - * Creates cache for all sources. + * Creates source cache. */ void CreateSourceCache(); /* - * Returns true if the given source is in the source cache. + * Clears cache of all sources and optionally deletes cache itself. */ - bool IsInSourceCache(CInputSource *source); - - /* - * Deletes cache for all sources. - */ - void DeleteSourceCache(); + void ClearSourceCache(bool deleteCache = false); /* - * Deletes an input source. + * Releases a source from the cache. */ - void DeleteSource(CInputSource *source); + void ReleaseSource(CInputSource *&source); /* * Returns a key source for the given keyboard number (or all keyboards if ANY_KEYBOARD supplied) and key index. @@ -371,26 +359,28 @@ private: */ CInputSource *GetJoySource(int joyNum, EJoyPart joyPart); + void CheckAllSources(unsigned readFlags, bool fullAxisOnly, bool &mseCentered, vector &sources, string &mapping, vector &badSources); + /* * Finds any currently activated key sources for the given keyboard number (or all keyboards if ANY_KEYBOARD supplied) * and adds them to the sources vector, aswell as constructing the corresponding mapping(s) in the given string. * If fullAxisOnly is true, then only sources that represent a full axis range (eg MouseXAxis) are considered. */ - void CheckKeySources(int kbdNum, bool fullAxisOnly, vector &sources, string &mapping); + void CheckKeySources(int kbdNum, bool fullAxisOnly, vector &sources, string &mapping, vector &badSources); /* * Finds any currently activated mouse sources for the given mouse number (or all mice if ANY_MOUSE supplied) * and adds them to the sources vector, aswell as constructing the corresponding mapping(s) in the given string. * If fullAxisOnly is true, then only sources that represent a full axis range (eg MouseXAxis) are considered. */ - void CheckMouseSources(int mseNum, bool fullAxisOnly, bool mseCentered, vector &sources, string &mapping); + void CheckMouseSources(int mseNum, bool fullAxisOnly, bool mseCentered, vector &sources, string &mapping, vector &badSources); /* * Finds any currently activated joystick sources for the given joystick number (or all joysticks if ANY_JOYSTICK supplied) * and adds them to the sources vector, aswell as constructing the corresponding mapping(s) in the given string. * If fullAxisOnly is true, then only sources that represent a full axis range (eg MouseXAxis) are considered. */ - void CheckJoySources(int joyNum, bool fullAxisOnly, vector &sources, string &mapping); + void CheckJoySources(int joyNum, bool fullAxisOnly, vector &sources, string &mapping, vector &badSources); bool ParseInt(string str, int &num); @@ -653,7 +643,7 @@ protected: /* * Processes the given force feedback command for the given joystick and axis number. */ - virtual bool ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd *ffCmd) = 0; + virtual bool ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd) = 0; /* * Waits for the given time in milliseconds @@ -700,6 +690,10 @@ protected: virtual CInputSource *CreateJoySource(int joyNum, EJoyPart joyPart); public: +#ifdef DEBUG + static unsigned totalSrcsAcquired; + static unsigned totalSrcsReleased; +#endif // Name of this input system const char *name; @@ -793,11 +787,6 @@ public: */ CInputSource* ParseSource(const char *mapping, bool fullAxisOnly = false); - /* - * Releases the given source when it is no longer in use. - */ - void ReleaseSource(CInputSource *source); - /* * Waits for any input from the user and once received copies a mapping configuration representing the input (eg KEY_A or JOY1_AXIS_POS) * into the given buffer. @@ -809,7 +798,7 @@ public: bool ReadMapping(char *buffer, unsigned bufSize, bool fullAxisOnly = false, unsigned readFlags = READ_ALL, const char *escapeMapping = "KEY_ESCAPE"); /* - * Updates the current state of the input system (called by CInputs::Poll). + * Updates the current state of the input system (called by CInputs.Poll). */ virtual bool Poll() = 0; @@ -822,61 +811,9 @@ public: */ virtual void SetMouseVisibility(bool visible) = 0; - virtual bool SendForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd *ffCmd) - { - const JoyDetails *joyDetails = GetJoyDetails(joyNum); - if (!joyDetails->hasFFeedback || !joyDetails->axisHasFF[axisNum]) - return false; - return ProcessForceFeedbackCmd(joyNum, axisNum, ffCmd); - } + virtual bool SendForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd); - void PrintDevices() - { - puts("Keyboards:"); - if (m_numKbds == 0) - puts(" None"); - else if (m_numKbds == ANY_KEYBOARD) - puts(" System Keyboard"); - else - { - for (int kbdNum = 0; kbdNum < m_numKbds; kbdNum++) - { - const KeyDetails *keyDetails = GetKeyDetails(kbdNum); - printf(" %d: %s\n", kbdNum + 1, keyDetails->name); - } - } - - puts("Mice:"); - if (m_numMice == 0) - puts(" None"); - else if (m_numMice == ANY_MOUSE) - puts(" System Mouse"); - else - { - for (int mseNum = 0; mseNum < m_numMice; mseNum++) - { - const MouseDetails *mseDetails = GetMouseDetails(mseNum); - printf(" %d: %s\n", mseNum + 1, mseDetails->name); - } - } - - puts("Joysticks:"); - if (m_numJoys == 0) - puts(" None"); - else if (m_numJoys == ANY_JOYSTICK) - puts(" System Joystick"); - else - { - for (int joyNum = 0; joyNum < m_numJoys; joyNum++) - { - const JoyDetails *joyDetails = GetJoyDetails(joyNum); - if (joyDetails->hasFFeedback) - printf(" %d: %s [Force Feedback Available]\n", joyNum + 1, joyDetails->name); - else - printf(" %d: %s\n", joyNum + 1, joyDetails->name); - } - } - } + void PrintDevices(); // // Nested Classes @@ -957,10 +894,14 @@ public: int m_joyNum; // Joystick number int m_axisNum; // Axis number (AXIS_X, AXIS_Y, AXIS_Z, AXIS_RX, AXIS_RY or AXIS_RZ) int m_axisDir; // Axis direction (AXIS_FULL, AXIS_INVERTED, AXIS_POSITIVE or AXIS_NEGATIVE) - int m_posDZone; // Dead zone for positive range - int m_negDZone; // Dead zone for negative range - int m_posSat; // Saturation for positive range - int m_negSat; // Saturation for negative range + int m_axisMinVal; // Axis min raw value (default -32768) + int m_axisOffVal; // Axis center/off raw value (default 0) + int m_axisMaxVal; // Axis max raw value (default 32767) + bool m_axisInverted; // True if axis max raw value less than axis min raw value + int m_posDZone; // Dead zone for positive range (0-99%) + int m_negDZone; // Dead zone for negative range (0-99%) + int m_posSat; // Saturation for positive range (1-100%) + int m_negSat; // Saturation for negative range (1-100%) /* * Scales the joystick axis value to the given range. @@ -968,13 +909,14 @@ public: int ScaleAxisValue(int minVal, int offVal, int maxVal); public: - CJoyAxisInputSource(CInputSystem *system, int joyNum, int axisNum, int axisDir, unsigned deadZone, unsigned saturation); + CJoyAxisInputSource(CInputSystem *system, int joyNum, int axisNum, int axisDir, int axisMinVal, int axisOffVal, int axisMaxVal, + unsigned deadZone, unsigned saturation); bool GetValueAsSwitch(bool &val); bool GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal); - bool SendForceFeedbackCmd(ForceFeedbackCmd *fFeedback); + bool SendForceFeedbackCmd(ForceFeedbackCmd ffCmd); }; /* diff --git a/Src/Inputs/InputTypes.cpp b/Src/Inputs/InputTypes.cpp index 8fdf700..594c7d3 100644 --- a/Src/Inputs/InputTypes.cpp +++ b/Src/Inputs/InputTypes.cpp @@ -161,7 +161,7 @@ void CTriggerInput::ReadFromINIFile(CINIFile *ini, const char *section) string key("Input"); key.append(id); - unsigned int autoTrigger; + unsigned autoTrigger; if (ini->Get(section, key, autoTrigger) == OKAY) m_autoTrigger = !!autoTrigger; } diff --git a/Src/Inputs/InputTypes.h b/Src/Inputs/InputTypes.h index 5f77f6e..0225c01 100644 --- a/Src/Inputs/InputTypes.h +++ b/Src/Inputs/InputTypes.h @@ -134,7 +134,7 @@ public: /* * Represents a trigger input, with both a trigger value and an offscreen value. If required, it can simulate pointing offscreen and pulling - * the trigger (in that order, which lightgun games require to reload properly) when just the offscreen input is activated. This makes + * the trigger (in that order, which lightgun games require to reload properly) just when the offscreen input is activated. This makes * reloading the gun easier when playing with just the mouse for example. */ class CTriggerInput : public CInput diff --git a/Src/Inputs/Inputs.cpp b/Src/Inputs/Inputs.cpp index 8fd2127..ca9a892 100644 --- a/Src/Inputs/Inputs.cpp +++ b/Src/Inputs/Inputs.cpp @@ -252,12 +252,16 @@ bool CInputs::Initialize() void CInputs::ReadFromINIFile(CINIFile *ini, const char *section) { + m_system->ReadFromINIFile(ini, section); + for (vector::iterator it = m_inputs.begin(); it != m_inputs.end(); it++) (*it)->ReadFromINIFile(ini, section); } void CInputs::WriteToINIFile(CINIFile *ini, const char *section) { + m_system->WriteToINIFile(ini, section); + for (vector::iterator it = m_inputs.begin(); it != m_inputs.end(); it++) (*it)->WriteToINIFile(ini, section); } diff --git a/Src/Inputs/MultiInputSource.cpp b/Src/Inputs/MultiInputSource.cpp index 39b9636..c2072a9 100644 --- a/Src/Inputs/MultiInputSource.cpp +++ b/Src/Inputs/MultiInputSource.cpp @@ -39,10 +39,13 @@ ESourceType CMultiInputSource::GetCombinedType(vector &sources) else return SourceEmpty; } -CMultiInputSource::CMultiInputSource(CInputSystem *system) : CInputSource(SourceEmpty), m_system(system), m_isOr(true), m_numSrcs(0), m_srcArray(NULL) { } +CMultiInputSource::CMultiInputSource() : CInputSource(SourceEmpty), m_isOr(true), m_numSrcs(0), m_srcArray(NULL) +{ + // +} -CMultiInputSource::CMultiInputSource(CInputSystem *system, bool isOr, vector &sources) : - CInputSource(GetCombinedType(sources)), m_system(system), m_isOr(isOr), m_numSrcs(sources.size()) +CMultiInputSource::CMultiInputSource(bool isOr, vector &sources) : + CInputSource(GetCombinedType(sources)), m_isOr(isOr), m_numSrcs(sources.size()) { m_srcArray = new CInputSource*[m_numSrcs]; copy(sources.begin(), sources.end(), m_srcArray); @@ -51,13 +54,33 @@ CMultiInputSource::CMultiInputSource(CInputSystem *system, bool isOr, vectorReleaseSource(m_srcArray[i]); delete m_srcArray; +} + +void CMultiInputSource::Acquire() +{ + CInputSource::Acquire(); + + if (m_acquired == 1) + { + // Acquire all sources + for (int i = 0; i < m_numSrcs; i++) + m_srcArray[i]->Acquire(); } } +void CMultiInputSource::Release() +{ + if (m_acquired == 1) + { + // Release all sources + for (int i = 0; i < m_numSrcs; i++) + m_srcArray[i]->Release(); + } + + CInputSource::Release(); +} + bool CMultiInputSource::GetValueAsSwitch(bool &val) { if (m_isOr) @@ -113,7 +136,7 @@ bool CMultiInputSource::GetValueAsAnalog(int &val, int minVal, int offVal, int m } } -bool CMultiInputSource::SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd) +bool CMultiInputSource::SendForceFeedbackCmd(ForceFeedbackCmd ffCmd) { bool result = false; for (int i = 0; i < m_numSrcs; i++) @@ -121,11 +144,28 @@ bool CMultiInputSource::SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd) return result; } -CNegInputSource::CNegInputSource(CInputSystem *system, CInputSource *source) : CInputSource(source->type), m_system(system), m_source(source) { } - -CNegInputSource::~CNegInputSource() +CNegInputSource::CNegInputSource(CInputSource *source) : CInputSource(source->type), m_source(source) { - m_system->ReleaseSource(m_source); + // Acquire source + m_source->Acquire(); +} + +void CNegInputSource::Acquire() +{ + CInputSource::Acquire(); + + // Acquire source + if (m_acquired == 1) + m_source->Acquire(); +} + +void CNegInputSource::Release() +{ + // Release source + if (m_acquired == 1) + m_source->Release(); + + CInputSource::Release(); } bool CNegInputSource::GetValueAsSwitch(bool &val) diff --git a/Src/Inputs/MultiInputSource.h b/Src/Inputs/MultiInputSource.h index 34efecf..ce047fe 100644 --- a/Src/Inputs/MultiInputSource.h +++ b/Src/Inputs/MultiInputSource.h @@ -9,15 +9,12 @@ using namespace std; /* * Represents a collection of input sources and combines their values into a single value. * When multiple mappings are assigned to an input, this is the input source that is created. - * Can either represent a combination of multiple assignments that all map to the same input, eg KEY_ALT,JOY1_BUTTON1 or be - * used specify that controls must be activated together, eg KEY_ALT+KEY_P. + * Can represent either a combination of multiple assignments that all map to the same input, eg KEY_ALT,JOY1_BUTTON1 or to + * specify that controls must be combined together, eg KEY_ALT+KEY_P. */ class CMultiInputSource : public CInputSource { private: - // Input system that created this source - CInputSystem *m_system; - // Controls how the inputs sources are combined bool m_isOr; @@ -36,7 +33,7 @@ public: /* * Constructs an 'empty' source (ie one which is always 'off'). */ - CMultiInputSource(CInputSystem *system); + CMultiInputSource(); /* * Constructs a multiple input source from the given vector of sources. @@ -44,15 +41,19 @@ public: * switch inputs must be active for this input to have a value (which will be the value of the first non-switch input in the list, * or the first switch input if there are none). */ - CMultiInputSource(CInputSystem *system, bool isOr, vector &sources); + CMultiInputSource(bool isOr, vector &sources); ~CMultiInputSource(); + + void Acquire(); + + void Release(); bool GetValueAsSwitch(bool &val); bool GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal); - bool SendForceFeedbackCmd(ForceFeedbackCmd *ffCmd); + bool SendForceFeedbackCmd(ForceFeedbackCmd ffCmd); }; /* @@ -63,16 +64,15 @@ public: class CNegInputSource : public CInputSource { private: - // Input system that created this source - CInputSystem *m_system; - // Input source being negated CInputSource *m_source; public: - CNegInputSource(CInputSystem *system, CInputSource *source); - - ~CNegInputSource(); + CNegInputSource(CInputSource *source); + + void Acquire(); + + void Release(); bool GetValueAsSwitch(bool &val); diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index 0bfe3bf..7bb1e54 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -181,7 +181,6 @@ static CInputs *CreateInputs(CInputSystem *InputSystem, BOOL configure) INI.Parse(); Inputs->ReadFromINIFile(&INI, "Global"); - InputSystem->ReadFromINIFile(&INI, "Global"); // If the user wants to configure the inputs, do that now if (configure) @@ -199,14 +198,13 @@ static CInputs *CreateInputs(CInputSystem *InputSystem, BOOL configure) { // Write input configuration and input system settings to config file Inputs->WriteToINIFile(&INI, "Global"); - InputSystem->WriteToINIFile(&INI, "Global"); - if (OKAY != INI.Write(CONFIG_FILE_COMMENT)) - ErrorLog("Unable to save configuration to %s.", CONFIG_FILE_PATH); + if (OKAY != INI.Write(CONFIG_FILE_COMMENT)) + ErrorLog("Unable to save configuration to %s.", CONFIG_FILE_PATH); + else + printf("Configuration successfully saved to %s.\n", CONFIG_FILE_PATH); + } else - printf("Configuration successfully saved to %s.\n", CONFIG_FILE_PATH); - } - else puts("Configuration aborted..."); puts(""); } @@ -1090,10 +1088,10 @@ int main(int argc, char **argv) exitCode = Supermodel(argv[fileIdx],Inputs,ppcFrequency,xRes,yRes,cmdFullScreen,cmdNoThrottle,cmdShowFPS,vsFile,fsFile); Exit: - if (InputSystem != NULL) - delete InputSystem; if (Inputs != NULL) delete Inputs; + if (InputSystem != NULL) + delete InputSystem; SDL_Quit(); return exitCode; } diff --git a/Src/OSD/SDL/SDLInputSystem.cpp b/Src/OSD/SDL/SDLInputSystem.cpp index 2c08da4..9575bfb 100644 --- a/Src/OSD/SDL/SDLInputSystem.cpp +++ b/Src/OSD/SDL/SDLInputSystem.cpp @@ -181,8 +181,8 @@ void CSDLInputSystem::OpenJoysticks() // Gather joystick details (name, num POVs & buttons and which axes are available) JoyDetails joyDetails; const char *pName = SDL_JoystickName(joyNum); - strncpy(joyDetails.name, pName, MAX_NAME_LENGTH - 1); - joyDetails.name[MAX_NAME_LENGTH - 1] = '\0'; + strncpy(joyDetails.name, pName, MAX_NAME_LENGTH); + joyDetails.name[MAX_NAME_LENGTH] = '\0'; joyDetails.numAxes = SDL_JoystickNumAxes(joystick); for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++) { @@ -313,7 +313,7 @@ bool CSDLInputSystem::IsJoyButPressed(int joyNum, int butNum) return !!SDL_JoystickGetButton(joystick, butNum); } -bool CSDLInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd *ffCmd) +bool CSDLInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd) { // SDL 1.2 does not support force feedback return false; diff --git a/Src/OSD/SDL/SDLInputSystem.h b/Src/OSD/SDL/SDLInputSystem.h index 5ba7599..047dfb7 100644 --- a/Src/OSD/SDL/SDLInputSystem.h +++ b/Src/OSD/SDL/SDLInputSystem.h @@ -81,7 +81,7 @@ protected: bool IsJoyButPressed(int joyNum, int butNum); - bool ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd *ffCmd); + bool ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd); void Wait(int ms); diff --git a/Src/OSD/Windows/DirectInputSystem.cpp b/Src/OSD/Windows/DirectInputSystem.cpp index dfdca1b..898bdb3 100644 --- a/Src/OSD/Windows/DirectInputSystem.cpp +++ b/Src/OSD/Windows/DirectInputSystem.cpp @@ -160,6 +160,7 @@ DIKeyMapStruct CDirectInputSystem::s_keyMap[] = bool IsXInputDevice(const GUID &devProdGUID) { + // Following code taken from MSDN IWbemLocator* pIWbemLocator = NULL; IEnumWbemClassObject* pEnumDevices = NULL; IWbemClassObject* pDevices[20] = {0}; @@ -191,7 +192,7 @@ bool IsXInputDevice(const GUID &devProdGUID) goto exit; // Loop over all devices - while (true) + for (;;) { // Get 20 at a time DWORD uReturned; @@ -259,12 +260,13 @@ exit: BOOL CALLBACK DI8EnumDevicesCallback(LPCDIDEVICEINSTANCE instance, LPVOID context) { // Keep track of all joystick device GUIDs - vector *infos = (vector*)context; + DIEnumDevsContext *diContext = (DIEnumDevsContext*)context; DIJoyInfo info; memset(&info, 0, sizeof(DIJoyInfo)); info.guid = instance->guidInstance; - info.isXInput = IsXInputDevice(instance->guidProduct); - infos->push_back(info); + // If XInput is enabled, see if device is an XInput device + info.isXInput = diContext->useXInput && IsXInputDevice(instance->guidProduct); + diContext->infos->push_back(info); return DIENUM_CONTINUE; } @@ -295,7 +297,8 @@ BOOL CALLBACK DI8EnumAxesCallback(LPCDIDEVICEOBJECTINSTANCE instance, LPVOID con BOOL CALLBACK DI8EnumEffectsCallback(LPCDIEFFECTINFO effectInfo, LPVOID context) { JoyDetails *joyDetails = (JoyDetails*)context; - if (effectInfo->dwEffType == DIEFT_CONSTANTFORCE) + // Check joystick has at least one of required types of effects + if (!!(effectInfo->dwEffType & (DIEFT_CONSTANTFORCE | DIEFT_PERIODIC | DIEFT_CONDITION))) joyDetails->hasFFeedback = true; return DIENUM_CONTINUE; } @@ -651,7 +654,7 @@ void CDirectInputSystem::PollKeyboardsAndMice() if (m_useRawInput) { // For RawInput, only thing to do is update wheelDir from wheelData for each mouse state. Everything else is updated via WM events. - for (vector::iterator it = m_rawMseStates.begin(); it < m_rawMseStates.end(); it++) + for (vector::iterator it = m_rawMseStates.begin(); it != m_rawMseStates.end(); it++) { if (it->wheelDelta != 0) { @@ -745,7 +748,7 @@ void CDirectInputSystem::CloseKeyboardsAndMice() } // Delete storage for keyboards - for (vector::iterator it = m_rawKeyStates.begin(); it < m_rawKeyStates.end(); it++) + for (vector::iterator it = m_rawKeyStates.begin(); it != m_rawKeyStates.end(); it++) delete[] *it; m_keyDetails.clear(); m_rawKeyboards.clear(); @@ -785,7 +788,7 @@ void CDirectInputSystem::ResetMice() m_combRawMseState.x = p.x; m_combRawMseState.y = p.y; m_combRawMseState.z = 0; - for (vector::iterator it = m_rawMseStates.begin(); it < m_rawMseStates.end(); it++) + for (vector::iterator it = m_rawMseStates.begin(); it != m_rawMseStates.end(); it++) { it->x = p.x; it->y = p.y; @@ -927,7 +930,7 @@ void CDirectInputSystem::ProcessRawInput(HRAWINPUT hInput) } m_combRawMseState.buttons = 0; - for (vector::iterator it = m_rawMseStates.begin(); it < m_rawMseStates.end(); it++) + for (vector::iterator it = m_rawMseStates.begin(); it != m_rawMseStates.end(); it++) m_combRawMseState.buttons |= it->buttons; } } @@ -940,14 +943,17 @@ void CDirectInputSystem::ProcessRawInput(HRAWINPUT hInput) void CDirectInputSystem::OpenJoysticks() { // Get the info about all attached joystick devices + DIEnumDevsContext diContext; + diContext.infos = &m_diJoyInfos; + diContext.useXInput = m_useXInput; HRESULT hr; - if (FAILED(hr = m_di8->EnumDevices(DI8DEVCLASS_GAMECTRL, DI8EnumDevicesCallback, &m_diJoyInfos, DIEDFL_ATTACHEDONLY))) + if (FAILED(hr = m_di8->EnumDevices(DI8DEVCLASS_GAMECTRL, DI8EnumDevicesCallback, &diContext, DIEDFL_ATTACHEDONLY))) return; // Loop through those found int joyNum = 0; int xNum = 0; - for (vector::iterator it = m_diJoyInfos.begin(); it < m_diJoyInfos.end(); it++) + for (vector::iterator it = m_diJoyInfos.begin(); it != m_diJoyInfos.end(); it++) { joyNum++; @@ -955,14 +961,14 @@ void CDirectInputSystem::OpenJoysticks() memset(&joyDetails, 0, sizeof(joyDetails)); // See if can use XInput for device - if (m_useXInput && it->isXInput) + if (it->isXInput) { // If so, set joystick details (currently XBox controller is only gamepad handled by XInput and so its capabilities are fixed) sprintf(joyDetails.name, "Xbox 360 Controller %d (via XInput)", (xNum + 1)); joyDetails.numAxes = 6; // Left & right triggers are mapped to axes in addition to the two analog sticks, giving a total of 6 axes joyDetails.numPOVs = 1; // Digital D-pad joyDetails.numButtons = 10; - joyDetails.hasFFeedback = true; + joyDetails.hasFFeedback = m_enableFFeedback; joyDetails.hasAxis[AXIS_X] = true; joyDetails.hasAxis[AXIS_Y] = true; joyDetails.hasAxis[AXIS_Z] = true; @@ -982,28 +988,28 @@ void CDirectInputSystem::OpenJoysticks() else { // Otherwise, open joystick with DirectInput for given GUID and set its data format - LPDIRECTINPUTDEVICE8 di8Joystick; - if (FAILED(hr = m_di8->CreateDevice(it->guid, &di8Joystick, NULL))) + LPDIRECTINPUTDEVICE8 joystick; + if (FAILED(hr = m_di8->CreateDevice(it->guid, &joystick, NULL))) { ErrorLog("Unable to create DirectInput joystick device %d (error %d) - skipping joystick.\n", joyNum, hr); continue; } - if (FAILED(hr = di8Joystick->SetDataFormat(&c_dfDIJoystick2))) + if (FAILED(hr = joystick->SetDataFormat(&c_dfDIJoystick2))) { ErrorLog("Unable to set data format for DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr); - di8Joystick->Release(); + joystick->Release(); continue; } // Get joystick's capabilities DIDEVCAPS devCaps; devCaps.dwSize = sizeof(DIDEVCAPS); - if (FAILED(hr = di8Joystick->GetCapabilities(&devCaps))) + if (FAILED(hr = joystick->GetCapabilities(&devCaps))) { ErrorLog("Unable to query capabilities of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr); - di8Joystick->Release(); + joystick->Release(); continue; } @@ -1013,11 +1019,11 @@ void CDirectInputSystem::OpenJoysticks() didps.diph.dwHeaderSize = sizeof(DIPROPHEADER); didps.diph.dwHow = DIPH_DEVICE; didps.diph.dwObj = 0; - if (FAILED(hr = di8Joystick->GetProperty(DIPROP_INSTANCENAME, &didps.diph))) + if (FAILED(hr = joystick->GetProperty(DIPROP_INSTANCENAME, &didps.diph))) { ErrorLog("Unable to get name of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr); - di8Joystick->Release(); + joystick->Release(); continue; } // DInput returns name as Unicode, convert to ASCII @@ -1028,20 +1034,24 @@ void CDirectInputSystem::OpenJoysticks() joyDetails.numButtons = devCaps.dwButtons; // Enumerate axes - if (FAILED(hr = di8Joystick->EnumObjects(DI8EnumAxesCallback, &joyDetails, DIDFT_AXIS))) + if (FAILED(hr = joystick->EnumObjects(DI8EnumAxesCallback, &joyDetails, DIDFT_AXIS))) { ErrorLog("Unable to enumerate axes of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr); - di8Joystick->Release(); + joystick->Release(); continue; } joyDetails.numAxes = 0; for (int axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++) joyDetails.numAxes += joyDetails.hasAxis[axisNum]; - // See if force feedback available for joystick - if (FAILED(hr = di8Joystick->EnumEffects(DI8EnumEffectsCallback, &joyDetails, DIEFT_ALL))) - ErrorLog("Unable to enumerate effects of DirectInput joystick %d (error %d) - force feedback will be unavailable for joystick.\n", joyNum, hr); + // See if force feedback enabled and is available for joystick + if (m_enableFFeedback && (devCaps.dwFlags & DIDC_FORCEFEEDBACK)) + { + // If so, see what types of effects are available and for which axes + if (FAILED(hr = joystick->EnumEffects(DI8EnumEffectsCallback, &joyDetails, DIEFT_ALL))) + ErrorLog("Unable to enumerate effects of DirectInput joystick %d (error %d) - force feedback will be unavailable for joystick.\n", joyNum, hr); + } // Configure axes, if any if (joyDetails.numAxes > 0) @@ -1054,11 +1064,11 @@ void CDirectInputSystem::OpenJoysticks() didpr.diph.dwObj = 0; didpr.lMin = -32768; didpr.lMax = 32767; - if (FAILED(hr = di8Joystick->SetProperty(DIPROP_RANGE, &didpr.diph))) + if (FAILED(hr = joystick->SetProperty(DIPROP_RANGE, &didpr.diph))) { ErrorLog("Unable to set axis range of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr); - di8Joystick->Release(); + joystick->Release(); continue; } @@ -1069,40 +1079,40 @@ void CDirectInputSystem::OpenJoysticks() dipdw.diph.dwHow = DIPH_DEVICE; dipdw.diph.dwObj = 0; dipdw.dwData = DIPROPAXISMODE_ABS; - if (FAILED(hr = di8Joystick->SetProperty(DIPROP_AXISMODE, &dipdw.diph))) + if (FAILED(hr = joystick->SetProperty(DIPROP_AXISMODE, &dipdw.diph))) { ErrorLog("Unable to set axis mode of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr); - di8Joystick->Release(); + joystick->Release(); continue; } // Turn off deadzone as handled by this class dipdw.dwData = 0; - if (FAILED(hr = di8Joystick->SetProperty(DIPROP_DEADZONE, &dipdw.diph))) + if (FAILED(hr = joystick->SetProperty(DIPROP_DEADZONE, &dipdw.diph))) { ErrorLog("Unable to set deadzone of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr); - di8Joystick->Release(); + joystick->Release(); continue; } // Turn off saturation as handle by this class dipdw.dwData = 10000; - if (FAILED(hr = di8Joystick->SetProperty(DIPROP_SATURATION, &dipdw.diph))) + if (FAILED(hr = joystick->SetProperty(DIPROP_SATURATION, &dipdw.diph))) { ErrorLog("Unable to set saturation of DirectInput joystick %d (error %d) - skipping joystick.\n", joyNum, hr); - di8Joystick->Release(); + joystick->Release(); continue; } - // If joystick force feedback is enabled and joystick has force feedback capabilities then disable auto-center - if (m_enableFFeedback && joyDetails.hasFFeedback) + // If joystick has force feedback capabilities then disable auto-center + if (joyDetails.hasFFeedback) { dipdw.dwData = FALSE; - if (FAILED(hr = di8Joystick->SetProperty(DIPROP_AUTOCENTER, &dipdw.diph))) + if (FAILED(hr = joystick->SetProperty(DIPROP_AUTOCENTER, &dipdw.diph))) { ErrorLog("Unable to unset auto-center of DirectInput joystick %d (error %d) - force feedback will be unavailable for joystick.\n", joyNum, hr); @@ -1114,7 +1124,7 @@ void CDirectInputSystem::OpenJoysticks() // Keep track of DirectInput device number it->dInputNum = m_di8Joysticks.size(); - m_di8Joysticks.push_back(di8Joystick); + m_di8Joysticks.push_back(joystick); } // Create initial blank joystick state @@ -1131,20 +1141,31 @@ void CDirectInputSystem::OpenJoysticks() void CDirectInputSystem::ActivateJoysticks() { // Set DirectInput cooperative level of joysticks - for (vector::iterator it = m_di8Joysticks.begin(); it < m_di8Joysticks.end(); it++) - (*it)->SetCooperativeLevel(m_hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + unsigned joyNum = 0; + for (vector::iterator it = m_diJoyInfos.begin(); it != m_diJoyInfos.end(); it++) + { + if (!it->isXInput) + { + LPDIRECTINPUTDEVICE8 joystick = m_di8Joysticks[it->dInputNum]; + if (m_joyDetails[joyNum].hasFFeedback) + joystick->SetCooperativeLevel(m_hwnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND); + else + joystick->SetCooperativeLevel(m_hwnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND); + } + joyNum++; + } } void CDirectInputSystem::PollJoysticks() { // Get current joystick states from XInput and DirectInput int i = 0; - for (vector::iterator it = m_diJoyInfos.begin(); it < m_diJoyInfos.end(); it++) + for (vector::iterator it = m_diJoyInfos.begin(); it != m_diJoyInfos.end(); it++) { LPDIJOYSTATE2 pJoyState = &m_diJoyStates[i++]; HRESULT hr; - if (m_useXInput && it->isXInput) + if (it->isXInput) { // Use XInput to query joystick XINPUT_STATE xState; @@ -1219,8 +1240,24 @@ void CDirectInputSystem::PollJoysticks() void CDirectInputSystem::CloseJoysticks() { + // Release any DirectInput force feedback effects that were created + for (vector::iterator it = m_diJoyInfos.begin(); it != m_diJoyInfos.end(); it++) + { + for (unsigned axisNum = 0; axisNum < NUM_JOY_AXES; axisNum++) + { + for (unsigned effNum = 0; effNum < NUM_FF_EFFECTS; effNum++) + { + if (it->dInputEffects[axisNum][effNum] != NULL) + { + it->dInputEffects[axisNum][effNum]->Release(); + it->dInputEffects[axisNum][effNum] = NULL; + } + } + } + } + // Release each DirectInput joystick - for (vector::iterator it = m_di8Joysticks.begin(); it < m_di8Joysticks.end(); it++) + for (vector::iterator it = m_di8Joysticks.begin(); it != m_di8Joysticks.end(); it++) { (*it)->Unacquire(); (*it)->Release(); @@ -1232,47 +1269,102 @@ void CDirectInputSystem::CloseJoysticks() m_di8Joysticks.clear(); } -HRESULT CDirectInputSystem::CreateJoystickEffect(LPDIRECTINPUTDEVICE8 di8Joystick, int axisNum, LPDIRECTINPUTEFFECT *di8Effect) +HRESULT CDirectInputSystem::CreateJoystickEffect(LPDIRECTINPUTDEVICE8 joystick, int axisNum, ForceFeedbackCmd ffCmd, LPDIRECTINPUTEFFECT *pEffect) { - // TODO - following is not finished + // Map axis number to DI object offset DWORD axisOfs; switch (axisNum) { case AXIS_X: axisOfs = DIJOFS_X; break; - case AXIS_Y: axisNum = DIJOFS_Y; break; - case AXIS_Z: axisNum = DIJOFS_Z; break; - case AXIS_RX: axisNum = DIJOFS_RX; break; - case AXIS_RY: axisNum = DIJOFS_RY; break; - case AXIS_RZ: axisNum = DIJOFS_RZ; break; + case AXIS_Y: axisOfs = DIJOFS_Y; break; + case AXIS_Z: axisOfs = DIJOFS_Z; break; + case AXIS_RX: axisOfs = DIJOFS_RX; break; + case AXIS_RY: axisOfs = DIJOFS_RY; break; + case AXIS_RZ: axisOfs = DIJOFS_RZ; break; default: return E_FAIL; } DWORD rgdwAxes[1] = { axisOfs }; LONG rglDirection[1] = { 0 }; - DICONSTANTFORCE cf = { 0 }; + DICONSTANTFORCE dicf; + DICONDITION dic; + DIPERIODIC dip; + DIENVELOPE die; + GUID guid; + + // Set common effects parameters DIEFFECT eff; memset(&eff, 0, sizeof(eff)); eff.dwSize = sizeof(DIEFFECT); eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; - eff.dwDuration = INFINITE; - eff.dwSamplePeriod = 0; - eff.dwGain = DI_FFNOMINALMAX; eff.dwTriggerButton = DIEB_NOTRIGGER; eff.dwTriggerRepeatInterval = 0; + eff.dwGain = DI_FFNOMINALMAX; eff.cAxes = 1; eff.rgdwAxes = rgdwAxes; eff.rglDirection = rglDirection; - eff.lpEnvelope = 0; - eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); - eff.lpvTypeSpecificParams = &cf; + eff.dwDuration = INFINITE; eff.dwStartDelay = 0; + + // Set specific effects parameters + switch (ffCmd.id) + { + case FFStop: + return E_FAIL; + + case FFSelfCenter: + guid = GUID_Spring; + + dic.lOffset = 0; + dic.lPositiveCoefficient = 0; + dic.lNegativeCoefficient = 0; + dic.dwPositiveSaturation = DI_FFNOMINALMAX; + dic.dwNegativeSaturation = DI_FFNOMINALMAX; + dic.lDeadBand = (LONG)0.05 * DI_FFNOMINALMAX; + + eff.lpEnvelope = NULL; + eff.cbTypeSpecificParams = sizeof(DICONDITION); + eff.lpvTypeSpecificParams = &dic; + break; + + case FFConstantForce: + guid = GUID_ConstantForce; + + dicf.lMagnitude = 0; + + eff.lpEnvelope = NULL; + eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + eff.lpvTypeSpecificParams = &dicf; + break; + + case FFVibrate: + guid = GUID_Sine; + + dip.dwMagnitude = 0; + dip.lOffset = 0; + dip.dwPhase = 0; + dip.dwPeriod = (DWORD)0.1 * DI_SECONDS; // 1/10th second + + die.dwSize = sizeof(die); + die.dwAttackLevel = 0; + die.dwAttackTime = (DWORD)0.5 * DI_SECONDS; + die.dwFadeLevel = 0; + die.dwFadeTime = (DWORD)0.5 * DI_SECONDS; + + eff.lpEnvelope = ¨ + eff.cbTypeSpecificParams = sizeof(DIPERIODIC); + eff.lpvTypeSpecificParams = &dip; + break; + } + + joystick->Acquire(); - // Create the prepared effect HRESULT hr; - if (FAILED(hr = di8Joystick->CreateEffect(GUID_ConstantForce, &eff, di8Effect, NULL))) + if (FAILED(hr = joystick->CreateEffect(guid, &eff, pEffect, NULL))) return hr; - if (di8Effect == NULL) + if (*pEffect == NULL) return E_FAIL; + (*pEffect)->Start(1, 0); return S_OK; } @@ -1490,18 +1582,15 @@ bool CDirectInputSystem::IsJoyButPressed(int joyNum, int butNum) return !!m_diJoyStates[joyNum].rgbButtons[butNum]; } -bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd *ffCmd) +bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd) { - if (!m_enableFFeedback) - return false; - DIJoyInfo *pInfo = &m_diJoyInfos[joyNum]; HRESULT hr; - if (m_useXInput && pInfo->isXInput) + if (pInfo->isXInput) { XINPUT_VIBRATION vibration; - switch (ffCmd->id) + switch (ffCmd.id) { case FFStop: if (axisNum == AXIS_X || axisNum == AXIS_Y) @@ -1510,70 +1599,101 @@ bool CDirectInputSystem::ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceF vibration.wRightMotorSpeed = 0; else return false; - if (FAILED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration))) - return false; - break; + return SUCCEEDED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration)); case FFConstantForce: if (axisNum == AXIS_X || axisNum == AXIS_Y) - vibration.wLeftMotorSpeed = ffCmd->data; // TODO - scale data to 0-65535 + vibration.wLeftMotorSpeed = ffCmd.data; // TODO - scale data to 0-65535 else if (axisNum == AXIS_RX || axisNum == AXIS_RY) - vibration.wRightMotorSpeed = ffCmd->data; // TODO - scale data to 0-65535 + vibration.wRightMotorSpeed = ffCmd.data; // TODO - scale data to 0-65535 else return false; - if (FAILED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration))) - return false; - break; + return SUCCEEDED(hr = m_xiSetStatePtr(pInfo->xInputNum, &vibration)); + + default: + // TODO - other force feedback types + return false; } } else { - LPDIRECTINPUTDEVICE8 di8Joystick = m_di8Joysticks[pInfo->dInputNum]; - switch (ffCmd->id) + LPDIRECTINPUTDEVICE8 joystick = m_di8Joysticks[pInfo->dInputNum]; + + // See if command is to stop all force feedback + if (ffCmd.id == -1) + return SUCCEEDED(hr = joystick->SendForceFeedbackCommand(DISFFC_STOPALL)); + + // Create effect for given axis if has not already been created + int effNum = (int)ffCmd.id; + LPDIRECTINPUTEFFECT *pEffect = &pInfo->dInputEffects[axisNum][effNum]; + if ((*pEffect) == NULL) { - case FFStop: - if (FAILED(hr = di8Joystick->SendForceFeedbackCommand(DISFFC_STOPALL))) - { - // TODO - return false; - } + if (FAILED(hr = CreateJoystickEffect(joystick, axisNum, ffCmd, pEffect))) + return false; + + } + + LONG rglDirection[1] = { 0 }; + DICONSTANTFORCE dicf; + DICONDITION dic; + DIPERIODIC dip; + DIENVELOPE die; + + // Set common parameters + DIEFFECT eff; + memset(&eff, 0, sizeof(eff)); + eff.dwSize = sizeof(DIEFFECT); + eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; + eff.cAxes = 1; + eff.rglDirection = rglDirection; + eff.dwStartDelay = 0; + + switch (ffCmd.id) + { + 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: - LPDIRECTINPUTEFFECT di8Effect; - if (FAILED(hr = CreateJoystickEffect(di8Joystick, axisNum, &di8Effect))) - { - // TODO - return false; - } - - DICONSTANTFORCE cf; - cf.lMagnitude = ffCmd->data; // TODO - scale data to what? - LONG rglDirection[1] = { 0 }; - DIEFFECT eff; - memset(&eff, 0, sizeof(eff)); - eff.dwSize = sizeof(DIEFFECT); - eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; - eff.cAxes = 1; - eff.rglDirection = rglDirection; - eff.lpEnvelope = 0; + dicf.lMagnitude = ffCmd.data; // TODO - scale & cap at +/- DI_FFNOMINALMAX + + eff.lpEnvelope = NULL; eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); - eff.lpvTypeSpecificParams = &cf; - eff.dwStartDelay = 0; - - // Now set the new parameters and start the effect immediately. - if (FAILED(hr = di8Effect->SetParameters(&eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START))) - { - // TODO - return false; - } - - //di8Effect->Release(); + eff.lpvTypeSpecificParams = &dicf; break; - } - } - return true; + 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 = ¨ + eff.cbTypeSpecificParams = sizeof(DIPERIODIC); + eff.lpvTypeSpecificParams = &dip; + break; + + default: + return false; + } + + // Now set the new parameters and start the effect immediately. + return SUCCEEDED(hr = (*pEffect)->SetParameters(&eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START)); + } } void CDirectInputSystem::Wait(int ms) diff --git a/Src/OSD/Windows/DirectInputSystem.h b/Src/OSD/Windows/DirectInputSystem.h index 31b54e8..f94842c 100644 --- a/Src/OSD/Windows/DirectInputSystem.h +++ b/Src/OSD/Windows/DirectInputSystem.h @@ -46,9 +46,16 @@ struct DIJoyInfo GUID guid; bool isXInput; int dInputNum; + LPDIRECTINPUTEFFECT dInputEffects[NUM_JOY_AXES][NUM_FF_EFFECTS]; int xInputNum; }; +struct DIEnumDevsContext +{ + vector *infos; + bool useXInput; +}; + // RawInput API typedef /*WINUSERAPI*/ INT (WINAPI *GetRawInputDeviceListPtr)(OUT PRAWINPUTDEVICELIST pRawInputDeviceList, IN OUT PUINT puiNumDevices, IN UINT cbSize); typedef /*WINUSERAPI*/ INT (WINAPI *GetRawInputDeviceInfoPtr)(IN HANDLE hDevice, IN UINT uiCommand, OUT LPVOID pData, IN OUT PUINT pcbSize); @@ -151,7 +158,7 @@ private: void CloseJoysticks(); - HRESULT CreateJoystickEffect(LPDIRECTINPUTDEVICE8 di8Joystick, int axisNum, LPDIRECTINPUTEFFECT *di8Effect); + HRESULT CreateJoystickEffect(LPDIRECTINPUTDEVICE8 di8Joystick, int axisNum, ForceFeedbackCmd ffCmd, LPDIRECTINPUTEFFECT *di8Effect); protected: /* @@ -177,7 +184,7 @@ protected: bool IsJoyButPressed(int joyNum, int butNum); - bool ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd *ffCmd); + bool ProcessForceFeedbackCmd(int joyNum, int axisNum, ForceFeedbackCmd ffCmd); void Wait(int ms);