diff options
Diffstat (limited to 'xbmc/addons/gui/skin/SkinTimerManager.cpp')
-rw-r--r-- | xbmc/addons/gui/skin/SkinTimerManager.cpp | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/xbmc/addons/gui/skin/SkinTimerManager.cpp b/xbmc/addons/gui/skin/SkinTimerManager.cpp new file mode 100644 index 0000000..663f5aa --- /dev/null +++ b/xbmc/addons/gui/skin/SkinTimerManager.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2022 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 "SkinTimerManager.h" + +#include "GUIInfoManager.h" +#include "ServiceBroker.h" +#include "guilib/GUIAction.h" +#include "guilib/GUIComponent.h" +#include "utils/StringUtils.h" +#include "utils/XBMCTinyXML.h" +#include "utils/log.h" + +#include <chrono> +#include <mutex> + +using namespace std::chrono_literals; + +void CSkinTimerManager::LoadTimers(const std::string& path) +{ + CXBMCTinyXML doc; + if (!doc.LoadFile(path)) + { + CLog::LogF(LOGWARNING, "Could not load timers file {}: {} (row: {}, col: {})", path, + doc.ErrorDesc(), doc.ErrorRow(), doc.ErrorCol()); + return; + } + + TiXmlElement* root = doc.RootElement(); + if (!root || !StringUtils::EqualsNoCase(root->Value(), "timers")) + { + CLog::LogF(LOGERROR, "Error loading timers file {}: Root element <timers> required.", path); + return; + } + + const TiXmlElement* timerNode = root->FirstChildElement("timer"); + while (timerNode) + { + LoadTimerInternal(timerNode); + timerNode = timerNode->NextSiblingElement("timer"); + } +} + +void CSkinTimerManager::LoadTimerInternal(const TiXmlElement* node) +{ + if ((!node->FirstChild("name") || !node->FirstChild("name")->FirstChild() || + node->FirstChild("name")->FirstChild()->ValueStr().empty())) + { + CLog::LogF(LOGERROR, "Missing required field name for valid skin. Ignoring timer."); + return; + } + + std::string timerName = node->FirstChild("name")->FirstChild()->Value(); + if (m_timers.count(timerName) > 0) + { + CLog::LogF(LOGWARNING, + "Ignoring timer with name {} - another timer with the same name already exists", + timerName); + return; + } + + // timer start + INFO::InfoPtr startInfo{nullptr}; + bool resetOnStart{false}; + if (node->FirstChild("start") && node->FirstChild("start")->FirstChild() && + !node->FirstChild("start")->FirstChild()->ValueStr().empty()) + { + startInfo = CServiceBroker::GetGUI()->GetInfoManager().Register( + node->FirstChild("start")->FirstChild()->ValueStr()); + // check if timer needs to be reset after start + if (node->FirstChildElement("start")->Attribute("reset") && + StringUtils::EqualsNoCase(node->FirstChildElement("start")->Attribute("reset"), "true")) + { + resetOnStart = true; + } + } + + // timer reset + INFO::InfoPtr resetInfo{nullptr}; + if (node->FirstChild("reset") && node->FirstChild("reset")->FirstChild() && + !node->FirstChild("reset")->FirstChild()->ValueStr().empty()) + { + resetInfo = CServiceBroker::GetGUI()->GetInfoManager().Register( + node->FirstChild("reset")->FirstChild()->ValueStr()); + } + // timer stop + INFO::InfoPtr stopInfo{nullptr}; + if (node->FirstChild("stop") && node->FirstChild("stop")->FirstChild() && + !node->FirstChild("stop")->FirstChild()->ValueStr().empty()) + { + stopInfo = CServiceBroker::GetGUI()->GetInfoManager().Register( + node->FirstChild("stop")->FirstChild()->ValueStr()); + } + + // process onstart actions + CGUIAction startActions; + startActions.EnableSendThreadMessageMode(); + const TiXmlElement* onStartElement = node->FirstChildElement("onstart"); + while (onStartElement) + { + if (onStartElement->FirstChild()) + { + const std::string conditionalActionAttribute = + onStartElement->Attribute("condition") != nullptr ? onStartElement->Attribute("condition") + : ""; + startActions.Append(CGUIAction::CExecutableAction{conditionalActionAttribute, + onStartElement->FirstChild()->Value()}); + } + onStartElement = onStartElement->NextSiblingElement("onstart"); + } + + // process onstop actions + CGUIAction stopActions; + stopActions.EnableSendThreadMessageMode(); + const TiXmlElement* onStopElement = node->FirstChildElement("onstop"); + while (onStopElement) + { + if (onStopElement->FirstChild()) + { + const std::string conditionalActionAttribute = + onStopElement->Attribute("condition") != nullptr ? onStopElement->Attribute("condition") + : ""; + stopActions.Append(CGUIAction::CExecutableAction{conditionalActionAttribute, + onStopElement->FirstChild()->Value()}); + } + onStopElement = onStopElement->NextSiblingElement("onstop"); + } + + m_timers[timerName] = std::make_unique<CSkinTimer>(CSkinTimer( + timerName, startInfo, resetInfo, stopInfo, startActions, stopActions, resetOnStart)); +} + +bool CSkinTimerManager::TimerIsRunning(const std::string& timer) const +{ + if (m_timers.count(timer) == 0) + { + CLog::LogF(LOGERROR, "Couldn't find Skin Timer with name: {}", timer); + return false; + } + return m_timers.at(timer)->IsRunning(); +} + +float CSkinTimerManager::GetTimerElapsedSeconds(const std::string& timer) const +{ + if (m_timers.count(timer) == 0) + { + CLog::LogF(LOGERROR, "Couldn't find Skin Timer with name: {}", timer); + return 0; + } + return m_timers.at(timer)->GetElapsedSeconds(); +} + +void CSkinTimerManager::TimerStart(const std::string& timer) const +{ + if (m_timers.count(timer) == 0) + { + CLog::LogF(LOGERROR, "Couldn't find Skin Timer with name: {}", timer); + return; + } + m_timers.at(timer)->Start(); +} + +void CSkinTimerManager::TimerStop(const std::string& timer) const +{ + if (m_timers.count(timer) == 0) + { + CLog::LogF(LOGERROR, "Couldn't find Skin Timer with name: {}", timer); + return; + } + m_timers.at(timer)->Stop(); +} + +void CSkinTimerManager::Stop() +{ + // skintimers, as infomanager clients register info conditions/expressions in the infomanager. + // The infomanager is linked to skins, being initialized or cleared when + // skins are loaded (or unloaded). All the registered boolean conditions from + // skin timers will end up being removed when the skin is unloaded. However, to + // self-contain this component unregister them all here. + for (auto const& [key, val] : m_timers) + { + const std::unique_ptr<CSkinTimer>::pointer timer = val.get(); + if (timer->GetStartCondition()) + { + CServiceBroker::GetGUI()->GetInfoManager().UnRegister(timer->GetStartCondition()); + } + if (timer->GetStopCondition()) + { + CServiceBroker::GetGUI()->GetInfoManager().UnRegister(timer->GetStopCondition()); + } + if (timer->GetResetCondition()) + { + CServiceBroker::GetGUI()->GetInfoManager().UnRegister(timer->GetResetCondition()); + } + } + m_timers.clear(); +} + +void CSkinTimerManager::Process() +{ + for (const auto& [key, val] : m_timers) + { + const std::unique_ptr<CSkinTimer>::pointer timer = val.get(); + if (!timer->IsRunning() && timer->VerifyStartCondition()) + { + timer->Start(); + } + else if (timer->IsRunning() && timer->VerifyStopCondition()) + { + timer->Stop(); + } + if (timer->GetElapsedSeconds() > 0 && timer->VerifyResetCondition()) + { + timer->Reset(); + } + } +} |