diff --git a/app/app.pro b/app/app.pro index 52d151e5..c84117d1 100644 --- a/app/app.pro +++ b/app/app.pro @@ -107,7 +107,8 @@ SOURCES += \ gui/appmodel.cpp \ streaming/streamutils.cpp \ backend/autoupdatechecker.cpp \ - path.cpp + path.cpp \ + settings/mappingmanager.cpp HEADERS += \ utils.h \ @@ -128,7 +129,8 @@ HEADERS += \ streaming/video/decoder.h \ streaming/streamutils.h \ backend/autoupdatechecker.h \ - path.h + path.h \ + settings/mappingmanager.h # Platform-specific renderers and decoders ffmpeg { diff --git a/app/settings/mappingmanager.cpp b/app/settings/mappingmanager.cpp new file mode 100644 index 00000000..a0bc31cb --- /dev/null +++ b/app/settings/mappingmanager.cpp @@ -0,0 +1,108 @@ +#include "mappingmanager.h" +#include "path.h" + +#include + +#include + +#define SER_GAMEPADMAPPING "gcmapping" + +#define SER_GUID "guid" +#define SER_MAPPING "mapping" + +MappingManager::MappingManager() +{ + QSettings settings; + + // First load existing saved mappings. This ensures the user's + // hints can always override the old data. + int mappingCount = settings.beginReadArray(SER_GAMEPADMAPPING); + for (int i = 0; i < mappingCount; i++) { + settings.setArrayIndex(i); + + SdlGamepadMapping mapping(settings.value(SER_GUID).toString(), settings.value(SER_MAPPING).toString()); + addMapping(mapping); + } + settings.endArray(); + + // Finally load mappings from SDL_HINT_GAMECONTROLLERCONFIG + QStringList sdlMappings = QString::fromLocal8Bit(SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG)).split('\n', QString::SkipEmptyParts); + for (QString sdlMapping : sdlMappings) { + SdlGamepadMapping mapping(sdlMapping); + addMapping(mapping); + } + + // Save the updated mappings to settings + save(); +} + +void MappingManager::save() +{ + QSettings settings; + + settings.remove(SER_GAMEPADMAPPING); + settings.beginWriteArray(SER_GAMEPADMAPPING); + QList mappings = m_Mappings.values(); + for (int i = 0; i < mappings.count(); i++) { + settings.setArrayIndex(i); + + settings.setValue(SER_GUID, mappings[i].getGuid()); + settings.setValue(SER_MAPPING, mappings[i].getMapping()); + } + settings.endArray(); +} + +void MappingManager::applyMappings() +{ + QString mappingFile = Path::getGamepadMappingFile(); + if (!mappingFile.isEmpty()) { + std::string mappingFileNative = QDir::toNativeSeparators(mappingFile).toStdString(); + + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Loading gamepad mappings from: %s", + mappingFileNative.c_str()); + + int newMappings = SDL_GameControllerAddMappingsFromFile(mappingFileNative.c_str()); + if (newMappings < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "Error loading gamepad mappings: %s", + SDL_GetError()); + } + else { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Loaded %d new gamepad mappings", + newMappings); + } + } + else { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "No gamepad mapping file found"); + } + + QList mappings = m_Mappings.values(); + for (const SdlGamepadMapping& mapping : mappings) { + QString sdlMappingString = mapping.getSdlMappingString(); + int ret = SDL_GameControllerAddMapping(qPrintable(sdlMappingString)); + if (ret < 0) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "Unable to add mapping: %s", + qPrintable(sdlMappingString)); + } + else if (ret == 1) { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Loaded saved user mapping: %s", + qPrintable(sdlMappingString)); + } + } +} + +void MappingManager::addMapping(QString mappingString) +{ + SdlGamepadMapping mapping(mappingString); + addMapping(mapping); +} + +void MappingManager::addMapping(SdlGamepadMapping& mapping) +{ + m_Mappings[mapping.getGuid()] = mapping; +} diff --git a/app/settings/mappingmanager.h b/app/settings/mappingmanager.h new file mode 100644 index 00000000..bdbb2d26 --- /dev/null +++ b/app/settings/mappingmanager.h @@ -0,0 +1,74 @@ +#pragma once + +#include + +class SdlGamepadMapping +{ +public: + SdlGamepadMapping() {} + + SdlGamepadMapping(QString string) + { + QStringList mapping = string.split(","); + if (!mapping.isEmpty()) { + m_Guid = mapping[0]; + + string.remove(0, m_Guid.length() + 1); + m_Mapping = string; + } + } + + SdlGamepadMapping(QString guid, QString mapping) + : m_Guid(guid), + m_Mapping(mapping) + { + + } + + bool operator==(const SdlGamepadMapping& other) const + { + return m_Guid == other.m_Guid && m_Mapping == other.m_Mapping; + } + + QString getGuid() const + { + return m_Guid; + } + + QString getMapping() const + { + return m_Mapping; + } + + QString getSdlMappingString() const + { + if (m_Guid.isEmpty() || m_Mapping.isEmpty()) { + return ""; + } + else { + return m_Guid + "," + m_Mapping; + } + } + +private: + QString m_Guid; + QString m_Mapping; +}; + +class MappingManager +{ +public: + MappingManager(); + + void addMapping(QString gamepadString); + + void addMapping(SdlGamepadMapping& gamepadMapping); + + void applyMappings(); + + void save(); + +private: + QMap m_Mappings; +}; + diff --git a/app/streaming/input.cpp b/app/streaming/input.cpp index abb66035..570bbadd 100644 --- a/app/streaming/input.cpp +++ b/app/streaming/input.cpp @@ -1,6 +1,7 @@ #include #include #include "streaming/session.h" +#include "settings/mappingmanager.h" #include "path.h" #include @@ -48,30 +49,8 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer* comput SDL_GetError()); } - QString mappingFile = Path::getGamepadMappingFile(); - if (!mappingFile.isEmpty()) { - std::string mappingFileNative = QDir::toNativeSeparators(mappingFile).toStdString(); - - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Loading gamepad mappings from: %s", - mappingFileNative.c_str()); - - int newMappings = SDL_GameControllerAddMappingsFromFile(mappingFileNative.c_str()); - if (newMappings < 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Error loading gamepad mappings: %s", - SDL_GetError()); - } - else { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Loaded %d new gamepad mappings", - newMappings); - } - } - else { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "No gamepad mapping file found"); - } + MappingManager mappingManager; + mappingManager.applyMappings(); if (!m_MultiController) { // Player 1 is always present in non-MC mode @@ -727,6 +706,9 @@ QString SdlInputHandler::getUnmappedGamepads() SDL_GetError()); } + MappingManager mappingManager; + mappingManager.applyMappings(); + for (int i = 0; i < SDL_NumJoysticks(); i++) { if (!SDL_IsGameController(i)) { char guidStr[33];