/* * Copyright (C) 2015-2018 Team Kodi * This file is part of Kodi - https://kodi.tv * * SPDX-License-Identifier: GPL-2.0-or-later * See LICENSES/README.md for more information. */ #include "Controller.h" #include "ControllerDefinitions.h" #include "ControllerLayout.h" #include "URL.h" #include "addons/addoninfo/AddonType.h" #include "games/controllers/input/PhysicalTopology.h" #include "utils/XBMCTinyXML.h" #include "utils/XMLUtils.h" #include "utils/log.h" #include using namespace KODI; using namespace GAME; // --- FeatureTypeEqual -------------------------------------------------------- struct FeatureTypeEqual { FeatureTypeEqual(FEATURE_TYPE type, JOYSTICK::INPUT_TYPE inputType) : type(type), inputType(inputType) { } bool operator()(const CPhysicalFeature& feature) const { if (type == FEATURE_TYPE::UNKNOWN) return true; // Match all feature types if (type == FEATURE_TYPE::SCALAR && feature.Type() == FEATURE_TYPE::SCALAR) { if (inputType == JOYSTICK::INPUT_TYPE::UNKNOWN) return true; // Match all input types return inputType == feature.InputType(); } return type == feature.Type(); } const FEATURE_TYPE type; const JOYSTICK::INPUT_TYPE inputType; }; // --- CController ------------------------------------------------------------- const ControllerPtr CController::EmptyPtr; CController::CController(const ADDON::AddonInfoPtr& addonInfo) : CAddon(addonInfo, ADDON::AddonType::GAME_CONTROLLER), m_layout(new CControllerLayout) { } CController::~CController() = default; const CPhysicalFeature& CController::GetFeature(const std::string& name) const { auto it = std::find_if(m_features.begin(), m_features.end(), [&name](const CPhysicalFeature& feature) { return name == feature.Name(); }); if (it != m_features.end()) return *it; static const CPhysicalFeature invalid{}; return invalid; } unsigned int CController::FeatureCount( FEATURE_TYPE type /* = FEATURE_TYPE::UNKNOWN */, JOYSTICK::INPUT_TYPE inputType /* = JOYSTICK::INPUT_TYPE::UNKNOWN */) const { auto featureCount = std::count_if(m_features.begin(), m_features.end(), FeatureTypeEqual(type, inputType)); return static_cast(featureCount); } void CController::GetFeatures(std::vector& features, FEATURE_TYPE type /* = FEATURE_TYPE::UNKNOWN */) const { for (const CPhysicalFeature& feature : m_features) { if (type == FEATURE_TYPE::UNKNOWN || type == feature.Type()) features.push_back(feature.Name()); } } JOYSTICK::FEATURE_TYPE CController::FeatureType(const std::string& feature) const { for (auto it = m_features.begin(); it != m_features.end(); ++it) { if (feature == it->Name()) return it->Type(); } return JOYSTICK::FEATURE_TYPE::UNKNOWN; } JOYSTICK::INPUT_TYPE CController::GetInputType(const std::string& feature) const { for (auto it = m_features.begin(); it != m_features.end(); ++it) { if (feature == it->Name()) return it->InputType(); } return JOYSTICK::INPUT_TYPE::UNKNOWN; } bool CController::LoadLayout(void) { if (!m_bLoaded) { std::string strLayoutXmlPath = LibPath(); CLog::Log(LOGINFO, "Loading controller layout: {}", CURL::GetRedacted(strLayoutXmlPath)); CXBMCTinyXML xmlDoc; if (!xmlDoc.LoadFile(strLayoutXmlPath)) { CLog::Log(LOGDEBUG, "Unable to load file: {} at line {}", xmlDoc.ErrorDesc(), xmlDoc.ErrorRow()); return false; } TiXmlElement* pRootElement = xmlDoc.RootElement(); if (!pRootElement || pRootElement->NoChildren() || pRootElement->ValueStr() != LAYOUT_XML_ROOT) { CLog::Log(LOGERROR, "Can't find root <{}> tag", LAYOUT_XML_ROOT); return false; } m_layout->Deserialize(pRootElement, this, m_features); if (m_layout->IsValid(true)) { m_bLoaded = true; } else { m_layout->Reset(); } } return m_bLoaded; } const CPhysicalTopology& CController::Topology() const { return m_layout->Topology(); }