summaryrefslogtreecommitdiffstats
path: root/xbmc/peripherals/EventScanner.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/peripherals/EventScanner.cpp')
-rw-r--r--xbmc/peripherals/EventScanner.cpp174
1 files changed, 174 insertions, 0 deletions
diff --git a/xbmc/peripherals/EventScanner.cpp b/xbmc/peripherals/EventScanner.cpp
new file mode 100644
index 0000000..0462a17
--- /dev/null
+++ b/xbmc/peripherals/EventScanner.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2016-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 "EventScanner.h"
+
+#include "IEventScannerCallback.h"
+#include "utils/log.h"
+
+#include <algorithm>
+#include <mutex>
+
+using namespace PERIPHERALS;
+
+// Default event scan rate when no polling handles are held
+#define DEFAULT_SCAN_RATE_HZ 60
+
+// Timeout when a polling handle is held but doesn't trigger scan. This reduces
+// input latency when the game is running at < 1/4 speed.
+#define WATCHDOG_TIMEOUT_MS 80
+
+CEventScanner::CEventScanner(IEventScannerCallback& callback)
+ : CThread("PeripEventScan"), m_callback(callback)
+{
+}
+
+void CEventScanner::Start()
+{
+ Create();
+}
+
+void CEventScanner::Stop()
+{
+ StopThread(false);
+ m_scanEvent.Set();
+ StopThread(true);
+}
+
+EventPollHandlePtr CEventScanner::RegisterPollHandle()
+{
+ EventPollHandlePtr handle(new CEventPollHandle(*this));
+
+ {
+ std::unique_lock<CCriticalSection> lock(m_handleMutex);
+ m_activeHandles.insert(handle.get());
+ }
+
+ CLog::Log(LOGDEBUG, "PERIPHERALS: Event poll handle registered");
+
+ return handle;
+}
+
+void CEventScanner::Activate(CEventPollHandle& handle)
+{
+ {
+ std::unique_lock<CCriticalSection> lock(m_handleMutex);
+ m_activeHandles.insert(&handle);
+ }
+
+ CLog::Log(LOGDEBUG, "PERIPHERALS: Event poll handle activated");
+}
+
+void CEventScanner::Deactivate(CEventPollHandle& handle)
+{
+ {
+ std::unique_lock<CCriticalSection> lock(m_handleMutex);
+ m_activeHandles.erase(&handle);
+ }
+
+ CLog::Log(LOGDEBUG, "PERIPHERALS: Event poll handle deactivated");
+}
+
+void CEventScanner::HandleEvents(bool bWait)
+{
+ if (bWait)
+ {
+ std::unique_lock<CCriticalSection> lock(m_pollMutex);
+
+ m_scanFinishedEvent.Reset();
+ m_scanEvent.Set();
+ m_scanFinishedEvent.Wait();
+ }
+ else
+ {
+ m_scanEvent.Set();
+ }
+}
+
+void CEventScanner::Release(CEventPollHandle& handle)
+{
+ {
+ std::unique_lock<CCriticalSection> lock(m_handleMutex);
+ m_activeHandles.erase(&handle);
+ }
+
+ CLog::Log(LOGDEBUG, "PERIPHERALS: Event poll handle released");
+}
+
+EventLockHandlePtr CEventScanner::RegisterLock()
+{
+ EventLockHandlePtr handle(new CEventLockHandle(*this));
+
+ {
+ std::unique_lock<CCriticalSection> lock(m_lockMutex);
+ m_activeLocks.insert(handle.get());
+ }
+
+ CLog::Log(LOGDEBUG, "PERIPHERALS: Event lock handle registered");
+
+ return handle;
+}
+
+void CEventScanner::ReleaseLock(CEventLockHandle& handle)
+{
+ {
+ std::unique_lock<CCriticalSection> lock(m_lockMutex);
+ m_activeLocks.erase(&handle);
+ }
+
+ CLog::Log(LOGDEBUG, "PERIPHERALS: Event lock handle released");
+}
+
+void CEventScanner::Process()
+{
+ auto nextScan = std::chrono::steady_clock::now();
+
+ while (!m_bStop)
+ {
+ {
+ std::unique_lock<CCriticalSection> lock(m_lockMutex);
+ if (m_activeLocks.empty())
+ m_callback.ProcessEvents();
+ }
+
+ m_scanFinishedEvent.Set();
+
+ auto now = std::chrono::steady_clock::now();
+ auto scanIntervalMs = GetScanIntervalMs();
+
+ // Handle wrap-around
+ if (now < nextScan)
+ nextScan = now;
+
+ while (nextScan <= now)
+ nextScan += scanIntervalMs;
+
+ auto waitTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(nextScan - now);
+
+ if (!m_bStop && waitTimeMs.count() > 0)
+ m_scanEvent.Wait(waitTimeMs);
+ }
+}
+
+std::chrono::milliseconds CEventScanner::GetScanIntervalMs() const
+{
+ bool bHasActiveHandle;
+
+ {
+ std::unique_lock<CCriticalSection> lock(m_handleMutex);
+ bHasActiveHandle = !m_activeHandles.empty();
+ }
+
+ if (!bHasActiveHandle)
+ {
+ // this truncates to 16 (from 16.666) should it round up to 17 using std::nearbyint or should we use nanoseconds?
+ return std::chrono::milliseconds(static_cast<uint32_t>(1000.0 / DEFAULT_SCAN_RATE_HZ));
+ }
+ else
+ return std::chrono::milliseconds(WATCHDOG_TIMEOUT_MS);
+}