summaryrefslogtreecommitdiffstats
path: root/xbmc/addons/gui/skin/SkinTimerManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/addons/gui/skin/SkinTimerManager.cpp')
-rw-r--r--xbmc/addons/gui/skin/SkinTimerManager.cpp222
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();
+ }
+ }
+}