summaryrefslogtreecommitdiffstats
path: root/xbmc/input/touch/generic
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/input/touch/generic')
-rw-r--r--xbmc/input/touch/generic/CMakeLists.txt14
-rw-r--r--xbmc/input/touch/generic/GenericTouchActionHandler.cpp212
-rw-r--r--xbmc/input/touch/generic/GenericTouchActionHandler.h94
-rw-r--r--xbmc/input/touch/generic/GenericTouchInputHandler.cpp397
-rw-r--r--xbmc/input/touch/generic/GenericTouchInputHandler.h98
-rw-r--r--xbmc/input/touch/generic/GenericTouchPinchDetector.cpp87
-rw-r--r--xbmc/input/touch/generic/GenericTouchPinchDetector.h32
-rw-r--r--xbmc/input/touch/generic/GenericTouchRotateDetector.cpp117
-rw-r--r--xbmc/input/touch/generic/GenericTouchRotateDetector.h36
-rw-r--r--xbmc/input/touch/generic/GenericTouchSwipeDetector.cpp176
-rw-r--r--xbmc/input/touch/generic/GenericTouchSwipeDetector.h49
-rw-r--r--xbmc/input/touch/generic/IGenericTouchGestureDetector.h91
12 files changed, 1403 insertions, 0 deletions
diff --git a/xbmc/input/touch/generic/CMakeLists.txt b/xbmc/input/touch/generic/CMakeLists.txt
new file mode 100644
index 0000000..d5e8adc
--- /dev/null
+++ b/xbmc/input/touch/generic/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(SOURCES GenericTouchActionHandler.cpp
+ GenericTouchInputHandler.cpp
+ GenericTouchPinchDetector.cpp
+ GenericTouchRotateDetector.cpp
+ GenericTouchSwipeDetector.cpp)
+
+set(HEADERS GenericTouchActionHandler.h
+ GenericTouchInputHandler.h
+ GenericTouchPinchDetector.h
+ GenericTouchRotateDetector.h
+ GenericTouchSwipeDetector.h
+ IGenericTouchGestureDetector.h)
+
+core_add_library(input_touch_generic)
diff --git a/xbmc/input/touch/generic/GenericTouchActionHandler.cpp b/xbmc/input/touch/generic/GenericTouchActionHandler.cpp
new file mode 100644
index 0000000..d601074
--- /dev/null
+++ b/xbmc/input/touch/generic/GenericTouchActionHandler.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2013-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 "GenericTouchActionHandler.h"
+
+#include "ServiceBroker.h"
+#include "application/AppInboundProtocol.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "input/Key.h"
+
+#include <cmath>
+
+using namespace KODI::MESSAGING;
+
+CGenericTouchActionHandler& CGenericTouchActionHandler::GetInstance()
+{
+ static CGenericTouchActionHandler sTouchAction;
+ return sTouchAction;
+}
+
+void CGenericTouchActionHandler::OnTouchAbort()
+{
+ sendEvent(ACTION_GESTURE_ABORT, 0.0f, 0.0f);
+}
+
+bool CGenericTouchActionHandler::OnSingleTouchStart(float x, float y)
+{
+ focusControl(x, y);
+
+ return true;
+}
+
+bool CGenericTouchActionHandler::OnSingleTouchHold(float x, float y)
+{
+ return true;
+}
+
+bool CGenericTouchActionHandler::OnSingleTouchMove(
+ float x, float y, float offsetX, float offsetY, float velocityX, float velocityY)
+{
+ return true;
+}
+
+bool CGenericTouchActionHandler::OnSingleTouchEnd(float x, float y)
+{
+ return true;
+}
+
+bool CGenericTouchActionHandler::OnMultiTouchDown(float x, float y, int32_t pointer)
+{
+ return true;
+}
+
+bool CGenericTouchActionHandler::OnMultiTouchHold(float x, float y, int32_t pointers /* = 2 */)
+{
+ return true;
+}
+
+bool CGenericTouchActionHandler::OnMultiTouchMove(float x,
+ float y,
+ float offsetX,
+ float offsetY,
+ float velocityX,
+ float velocityY,
+ int32_t pointer)
+{
+ return true;
+}
+
+bool CGenericTouchActionHandler::OnMultiTouchUp(float x, float y, int32_t pointer)
+{
+ return true;
+}
+
+bool CGenericTouchActionHandler::OnTouchGestureStart(float x, float y)
+{
+ sendEvent(ACTION_GESTURE_BEGIN, x, y, 0.0f, 0.0f);
+
+ return true;
+}
+
+bool CGenericTouchActionHandler::OnTouchGesturePan(
+ float x, float y, float offsetX, float offsetY, float velocityX, float velocityY)
+{
+ sendEvent(ACTION_GESTURE_PAN, x, y, offsetX, offsetY, velocityX, velocityY);
+
+ return true;
+}
+
+bool CGenericTouchActionHandler::OnTouchGestureEnd(
+ float x, float y, float offsetX, float offsetY, float velocityX, float velocityY)
+{
+ sendEvent(ACTION_GESTURE_END, velocityX, velocityY, x, y, offsetX, offsetY);
+
+ return true;
+}
+
+void CGenericTouchActionHandler::OnTap(float x, float y, int32_t pointers /* = 1 */)
+{
+ if (pointers <= 0 || pointers > 10)
+ return;
+
+ sendEvent(ACTION_TOUCH_TAP, x, y, 0.0f, 0.0f, 0.0f, 0.0f, pointers);
+}
+
+void CGenericTouchActionHandler::OnLongPress(float x, float y, int32_t pointers /* = 1 */)
+{
+ if (pointers <= 0 || pointers > 10)
+ return;
+
+ sendEvent(ACTION_TOUCH_LONGPRESS, x, y, 0.0f, 0.0f, 0.0f, 0.0f, pointers);
+}
+
+void CGenericTouchActionHandler::OnSwipe(TouchMoveDirection direction,
+ float xDown,
+ float yDown,
+ float xUp,
+ float yUp,
+ float velocityX,
+ float velocityY,
+ int32_t pointers /* = 1 */)
+{
+ if (pointers <= 0 || pointers > 10)
+ return;
+
+ int actionId = 0;
+ if (direction == TouchMoveDirectionLeft)
+ actionId = ACTION_GESTURE_SWIPE_LEFT;
+ else if (direction == TouchMoveDirectionRight)
+ actionId = ACTION_GESTURE_SWIPE_RIGHT;
+ else if (direction == TouchMoveDirectionUp)
+ actionId = ACTION_GESTURE_SWIPE_UP;
+ else if (direction == TouchMoveDirectionDown)
+ actionId = ACTION_GESTURE_SWIPE_DOWN;
+ else
+ return;
+
+ sendEvent(actionId, xUp, yUp, velocityX, velocityY, xDown, yDown, pointers);
+}
+
+void CGenericTouchActionHandler::OnZoomPinch(float centerX, float centerY, float zoomFactor)
+{
+ sendEvent(ACTION_GESTURE_ZOOM, centerX, centerY, zoomFactor, 0.0f);
+}
+
+void CGenericTouchActionHandler::OnRotate(float centerX, float centerY, float angle)
+{
+ sendEvent(ACTION_GESTURE_ROTATE, centerX, centerY, angle, 0.0f);
+}
+
+int CGenericTouchActionHandler::QuerySupportedGestures(float x, float y)
+{
+ CGUIMessage msg(GUI_MSG_GESTURE_NOTIFY, 0, 0, static_cast<int>(std::round(x)),
+ static_cast<int>(std::round(y)));
+ if (!CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg))
+ return 0;
+
+ int result = 0;
+ if (msg.GetPointer())
+ {
+ int* p = static_cast<int*>(msg.GetPointer());
+ msg.SetPointer(nullptr);
+ result = *p;
+ delete p;
+ }
+ return result;
+}
+
+void CGenericTouchActionHandler::sendEvent(int actionId,
+ float x,
+ float y,
+ float x2 /* = 0.0f */,
+ float y2 /* = 0.0f */,
+ float x3,
+ float y3,
+ int pointers /* = 1 */)
+{
+ XBMC_Event newEvent{};
+ newEvent.type = XBMC_TOUCH;
+
+ newEvent.touch.action = actionId;
+ newEvent.touch.x = x;
+ newEvent.touch.y = y;
+ newEvent.touch.x2 = x2;
+ newEvent.touch.y2 = y2;
+ newEvent.touch.x3 = x3;
+ newEvent.touch.y3 = y3;
+ newEvent.touch.pointers = pointers;
+
+ std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
+ if (appPort)
+ appPort->OnEvent(newEvent);
+}
+
+void CGenericTouchActionHandler::focusControl(float x, float y)
+{
+ XBMC_Event newEvent{};
+ newEvent.type = XBMC_SETFOCUS;
+
+ newEvent.focus.x = static_cast<int>(std::round(x));
+ newEvent.focus.y = static_cast<int>(std::round(y));
+
+ std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort();
+ if (appPort)
+ appPort->OnEvent(newEvent);
+}
diff --git a/xbmc/input/touch/generic/GenericTouchActionHandler.h b/xbmc/input/touch/generic/GenericTouchActionHandler.h
new file mode 100644
index 0000000..e8697ce
--- /dev/null
+++ b/xbmc/input/touch/generic/GenericTouchActionHandler.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "input/touch/ITouchActionHandler.h"
+
+/*!
+ * \ingroup touch_generic
+ * \brief Generic implementation of ITouchActionHandler to translate
+ * touch actions into XBMC specific and mappable actions.
+ *
+ * \sa ITouchActionHandler
+ */
+class CGenericTouchActionHandler : public ITouchActionHandler
+{
+public:
+ /*!
+ \brief Get an instance of the touch input manager
+ */
+ static CGenericTouchActionHandler& GetInstance();
+
+ // implementation of ITouchActionHandler
+ void OnTouchAbort() override;
+
+ bool OnSingleTouchStart(float x, float y) override;
+ bool OnSingleTouchHold(float x, float y) override;
+ bool OnSingleTouchMove(
+ float x, float y, float offsetX, float offsetY, float velocityX, float velocityY) override;
+ bool OnSingleTouchEnd(float x, float y) override;
+
+ bool OnMultiTouchDown(float x, float y, int32_t pointer) override;
+ bool OnMultiTouchHold(float x, float y, int32_t pointers = 2) override;
+ bool OnMultiTouchMove(float x,
+ float y,
+ float offsetX,
+ float offsetY,
+ float velocityX,
+ float velocityY,
+ int32_t pointer) override;
+ bool OnMultiTouchUp(float x, float y, int32_t pointer) override;
+
+ bool OnTouchGestureStart(float x, float y) override;
+ bool OnTouchGesturePan(
+ float x, float y, float offsetX, float offsetY, float velocityX, float velocityY) override;
+ bool OnTouchGestureEnd(
+ float x, float y, float offsetX, float offsetY, float velocityX, float velocityY) override;
+
+ // convenience events
+ void OnTap(float x, float y, int32_t pointers = 1) override;
+ void OnLongPress(float x, float y, int32_t pointers = 1) override;
+ void OnSwipe(TouchMoveDirection direction,
+ float xDown,
+ float yDown,
+ float xUp,
+ float yUp,
+ float velocityX,
+ float velocityY,
+ int32_t pointers = 1) override;
+ void OnZoomPinch(float centerX, float centerY, float zoomFactor) override;
+ void OnRotate(float centerX, float centerY, float angle) override;
+
+ /*!
+ \brief Asks the control at the given coordinates for a list of the supported gestures.
+
+ \param x The x coordinate (with sub-pixel) of the touch
+ \param y The y coordinate (with sub-pixel) of the touch
+
+ \return EVENT_RESULT value of bitwise ORed gestures.
+ */
+ int QuerySupportedGestures(float x, float y);
+
+private:
+ // private construction, and no assignments; use the provided singleton methods
+ CGenericTouchActionHandler() = default;
+ CGenericTouchActionHandler(const CGenericTouchActionHandler&) = delete;
+ CGenericTouchActionHandler const& operator=(CGenericTouchActionHandler const&) = delete;
+ ~CGenericTouchActionHandler() override = default;
+
+ void sendEvent(int actionId,
+ float x,
+ float y,
+ float x2 = 0.0f,
+ float y2 = 0.0f,
+ float x3 = 0.0f,
+ float y3 = 0.0f,
+ int pointers = 1);
+ void focusControl(float x, float y);
+};
diff --git a/xbmc/input/touch/generic/GenericTouchInputHandler.cpp b/xbmc/input/touch/generic/GenericTouchInputHandler.cpp
new file mode 100644
index 0000000..6290860
--- /dev/null
+++ b/xbmc/input/touch/generic/GenericTouchInputHandler.cpp
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2012-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 "GenericTouchInputHandler.h"
+
+#include "input/touch/generic/GenericTouchPinchDetector.h"
+#include "input/touch/generic/GenericTouchRotateDetector.h"
+#include "input/touch/generic/GenericTouchSwipeDetector.h"
+#include "utils/log.h"
+
+#include <algorithm>
+#include <cmath>
+#include <mutex>
+
+using namespace std::chrono_literals;
+
+namespace
+{
+constexpr auto TOUCH_HOLD_TIMEOUT = 500ms;
+}
+
+CGenericTouchInputHandler::CGenericTouchInputHandler() : m_holdTimer(new CTimer(this))
+{
+}
+
+CGenericTouchInputHandler::~CGenericTouchInputHandler() = default;
+
+CGenericTouchInputHandler& CGenericTouchInputHandler::GetInstance()
+{
+ static CGenericTouchInputHandler sTouchInput;
+ return sTouchInput;
+}
+
+float CGenericTouchInputHandler::AdjustPointerSize(float size)
+{
+ if (size > 0.0f)
+ return size;
+ else
+ // Set a default size if touch input layer does not have anything useful,
+ // approx. 3.2mm
+ return m_dpi / 8.0f;
+}
+
+bool CGenericTouchInputHandler::HandleTouchInput(TouchInput event,
+ float x,
+ float y,
+ int64_t time,
+ int32_t pointer /* = 0 */,
+ float size /* = 0.0f */)
+{
+ if (time < 0 || pointer < 0 || pointer >= MAX_POINTERS)
+ return false;
+
+ std::unique_lock<CCriticalSection> lock(m_critical);
+
+ bool result = true;
+
+ m_pointers[pointer].current.x = x;
+ m_pointers[pointer].current.y = y;
+ m_pointers[pointer].current.time = time;
+
+ switch (event)
+ {
+ case TouchInputAbort:
+ {
+ triggerDetectors(event, pointer);
+
+ setGestureState(TouchGestureUnknown);
+ for (auto& pointer : m_pointers)
+ pointer.reset();
+
+ OnTouchAbort();
+ break;
+ }
+
+ case TouchInputDown:
+ {
+ m_pointers[pointer].down.x = x;
+ m_pointers[pointer].down.y = y;
+ m_pointers[pointer].down.time = time;
+ m_pointers[pointer].moving = false;
+ m_pointers[pointer].size = AdjustPointerSize(size);
+
+ // If this is the down event of the primary pointer
+ // we start by assuming that it's a single touch
+ if (pointer == 0)
+ {
+ // create new gesture detectors
+ m_detectors.emplace(new CGenericTouchSwipeDetector(this, m_dpi));
+ m_detectors.emplace(new CGenericTouchPinchDetector(this, m_dpi));
+ m_detectors.emplace(new CGenericTouchRotateDetector(this, m_dpi));
+ triggerDetectors(event, pointer);
+
+ setGestureState(TouchGestureSingleTouch);
+ result = OnSingleTouchStart(x, y);
+
+ m_holdTimer->Start(TOUCH_HOLD_TIMEOUT);
+ }
+ // Otherwise it's the down event of another pointer
+ else
+ {
+ triggerDetectors(event, pointer);
+
+ // If we so far assumed single touch or still have the primary
+ // pointer of a previous multi touch pressed down, we can update to multi touch
+ if (m_gestureState == TouchGestureSingleTouch ||
+ m_gestureState == TouchGestureSingleTouchHold ||
+ m_gestureState == TouchGestureMultiTouchDone)
+ {
+ result = OnMultiTouchDown(x, y, pointer);
+ m_holdTimer->Stop(true);
+
+ if (m_gestureState == TouchGestureSingleTouch ||
+ m_gestureState == TouchGestureSingleTouchHold)
+ m_holdTimer->Start(TOUCH_HOLD_TIMEOUT);
+
+ setGestureState(TouchGestureMultiTouchStart);
+ }
+ // Otherwise we should ignore this pointer
+ else
+ {
+ m_pointers[pointer].reset();
+ break;
+ }
+ }
+ return result;
+ }
+
+ case TouchInputUp:
+ {
+ // unexpected event => abort
+ if (!m_pointers[pointer].valid() || m_gestureState == TouchGestureUnknown)
+ break;
+
+ triggerDetectors(event, pointer);
+
+ m_holdTimer->Stop(false);
+
+ // Just a single tap with a pointer
+ if (m_gestureState == TouchGestureSingleTouch ||
+ m_gestureState == TouchGestureSingleTouchHold)
+ {
+ result = OnSingleTouchEnd(x, y);
+
+ if (m_gestureState == TouchGestureSingleTouch)
+ OnTap(x, y, 1);
+ }
+ // A pan gesture started with a single pointer (ignoring any other pointers)
+ else if (m_gestureState == TouchGesturePan)
+ {
+ float velocityX = 0.0f; // number of pixels per second
+ float velocityY = 0.0f; // number of pixels per second
+ m_pointers[pointer].velocity(velocityX, velocityY, false);
+
+ result = OnTouchGestureEnd(x, y, x - m_pointers[pointer].down.x,
+ y - m_pointers[pointer].down.y, velocityX, velocityY);
+ }
+ // we are in multi-touch
+ else
+ result = OnMultiTouchUp(x, y, pointer);
+
+ // If we were in multi touch mode and lifted one pointer
+ // we can go into the TouchGestureMultiTouchDone state which will allow
+ // the user to go back into multi touch mode without lifting the primary pointer
+ if (m_gestureState == TouchGestureMultiTouchStart ||
+ m_gestureState == TouchGestureMultiTouchHold || m_gestureState == TouchGestureMultiTouch)
+ {
+ setGestureState(TouchGestureMultiTouchDone);
+
+ // after lifting the primary pointer, the secondary pointer will
+ // become the primary pointer in the next event
+ if (pointer == 0)
+ {
+ m_pointers[0] = m_pointers[1];
+ pointer = 1;
+ }
+ }
+ // Otherwise abort
+ else
+ {
+ if (m_gestureState == TouchGestureMultiTouchDone)
+ {
+ float velocityX = 0.0f; // number of pixels per second
+ float velocityY = 0.0f; // number of pixels per second
+ m_pointers[pointer].velocity(velocityX, velocityY, false);
+
+ result = OnTouchGestureEnd(x, y, x - m_pointers[pointer].down.x,
+ y - m_pointers[pointer].down.y, velocityX, velocityY);
+
+ // if neither of the two pointers moved we have a single tap with multiple pointers
+ if (m_gestureStateOld != TouchGestureMultiTouchHold &&
+ m_gestureStateOld != TouchGestureMultiTouch)
+ OnTap(std::abs((m_pointers[0].down.x + m_pointers[1].down.x) / 2),
+ std::abs((m_pointers[0].down.y + m_pointers[1].down.y) / 2), 2);
+ }
+
+ setGestureState(TouchGestureUnknown);
+ }
+ m_pointers[pointer].reset();
+
+ return result;
+ }
+
+ case TouchInputMove:
+ {
+ // unexpected event => abort
+ if (!m_pointers[pointer].valid() || m_gestureState == TouchGestureUnknown ||
+ m_gestureState == TouchGestureMultiTouchDone)
+ break;
+
+ bool moving = std::any_of(m_pointers.cbegin(), m_pointers.cend(),
+ [](Pointer const& p) { return p.valid() && p.moving; });
+
+ if (moving)
+ {
+ m_holdTimer->Stop();
+
+ // the touch is moving so we start a gesture
+ if (m_gestureState == TouchGestureSingleTouch ||
+ m_gestureState == TouchGestureMultiTouchStart)
+ result = OnTouchGestureStart(m_pointers[pointer].down.x, m_pointers[pointer].down.y);
+ }
+
+ triggerDetectors(event, pointer);
+
+ // Check if the touch has moved far enough to count as movement
+ if ((m_gestureState == TouchGestureSingleTouch ||
+ m_gestureState == TouchGestureMultiTouchStart) &&
+ !m_pointers[pointer].moving)
+ break;
+
+ if (m_gestureState == TouchGestureSingleTouch)
+ {
+ m_pointers[pointer].last.copy(m_pointers[pointer].down);
+ setGestureState(TouchGesturePan);
+ }
+ else if (m_gestureState == TouchGestureMultiTouchStart)
+ {
+ setGestureState(TouchGestureMultiTouch);
+
+ // set the starting point
+ saveLastTouch();
+ }
+
+ float offsetX = x - m_pointers[pointer].last.x;
+ float offsetY = y - m_pointers[pointer].last.y;
+ float velocityX = 0.0f; // number of pixels per second
+ float velocityY = 0.0f; // number of pixels per second
+ m_pointers[pointer].velocity(velocityX, velocityY);
+
+ if (m_pointers[pointer].moving &&
+ (m_gestureState == TouchGestureSingleTouch ||
+ m_gestureState == TouchGestureSingleTouchHold || m_gestureState == TouchGesturePan))
+ result = OnSingleTouchMove(x, y, offsetX, offsetY, velocityX, velocityY);
+
+ // Let's see if we have a pan gesture (i.e. the primary and only pointer moving)
+ if (m_gestureState == TouchGesturePan)
+ {
+ result = OnTouchGesturePan(x, y, offsetX, offsetY, velocityX, velocityY);
+
+ m_pointers[pointer].last.x = x;
+ m_pointers[pointer].last.y = y;
+ }
+ else if (m_gestureState == TouchGestureMultiTouch)
+ {
+ if (moving)
+ result = OnMultiTouchMove(x, y, offsetX, offsetY, velocityX, velocityY, pointer);
+ }
+ else
+ break;
+
+ return result;
+ }
+
+ default:
+ CLog::Log(LOGDEBUG, "CGenericTouchInputHandler: unknown TouchInput");
+ break;
+ }
+
+ return false;
+}
+
+bool CGenericTouchInputHandler::UpdateTouchPointer(
+ int32_t pointer, float x, float y, int64_t time, float size /* = 0.0f */)
+{
+ if (pointer < 0 || pointer >= MAX_POINTERS)
+ return false;
+
+ std::unique_lock<CCriticalSection> lock(m_critical);
+
+ m_pointers[pointer].last.copy(m_pointers[pointer].current);
+
+ m_pointers[pointer].current.x = x;
+ m_pointers[pointer].current.y = y;
+ m_pointers[pointer].current.time = time;
+ m_pointers[pointer].size = AdjustPointerSize(size);
+
+ // calculate whether the pointer has moved at all
+ if (!m_pointers[pointer].moving)
+ {
+ CVector down = m_pointers[pointer].down;
+ CVector current = m_pointers[pointer].current;
+ CVector distance = down - current;
+
+ if (distance.length() > m_pointers[pointer].size)
+ m_pointers[pointer].moving = true;
+ }
+
+ for (auto const& detector : m_detectors)
+ detector->OnTouchUpdate(pointer, m_pointers[pointer]);
+
+ return true;
+}
+
+void CGenericTouchInputHandler::saveLastTouch()
+{
+ for (auto& pointer : m_pointers)
+ pointer.last.copy(pointer.current);
+}
+
+void CGenericTouchInputHandler::OnTimeout()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+
+ switch (m_gestureState)
+ {
+ case TouchGestureSingleTouch:
+ setGestureState(TouchGestureSingleTouchHold);
+
+ OnSingleTouchHold(m_pointers[0].down.x, m_pointers[0].down.y);
+ OnLongPress(m_pointers[0].down.x, m_pointers[0].down.y, 1);
+ break;
+
+ case TouchGestureMultiTouchStart:
+ if (!m_pointers[0].moving && !m_pointers[1].moving)
+ {
+ setGestureState(TouchGestureMultiTouchHold);
+
+ OnMultiTouchHold(m_pointers[0].down.x, m_pointers[0].down.y);
+ OnLongPress(std::abs((m_pointers[0].down.x + m_pointers[1].down.x) / 2),
+ std::abs((m_pointers[0].down.y + m_pointers[1].down.y) / 2), 2);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void CGenericTouchInputHandler::triggerDetectors(TouchInput event, int32_t pointer)
+{
+ switch (event)
+ {
+ case TouchInputAbort:
+ {
+ m_detectors.clear();
+ break;
+ }
+
+ case TouchInputDown:
+ {
+ for (auto const& detector : m_detectors)
+ detector->OnTouchDown(pointer, m_pointers[pointer]);
+ break;
+ }
+
+ case TouchInputUp:
+ {
+ for (auto const& detector : m_detectors)
+ detector->OnTouchUp(pointer, m_pointers[pointer]);
+ break;
+ }
+
+ case TouchInputMove:
+ {
+ for (auto const& detector : m_detectors)
+ detector->OnTouchMove(pointer, m_pointers[pointer]);
+ break;
+ }
+
+ default:
+ return;
+ }
+
+ for (auto it = m_detectors.begin(); it != m_detectors.end();)
+ {
+ if ((*it)->IsDone())
+ it = m_detectors.erase(it);
+ else
+ it++;
+ }
+}
diff --git a/xbmc/input/touch/generic/GenericTouchInputHandler.h b/xbmc/input/touch/generic/GenericTouchInputHandler.h
new file mode 100644
index 0000000..154f5f2
--- /dev/null
+++ b/xbmc/input/touch/generic/GenericTouchInputHandler.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2012-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.
+ */
+
+#pragma once
+
+#include "input/touch/ITouchInputHandler.h"
+#include "input/touch/TouchTypes.h"
+#include "threads/CriticalSection.h"
+#include "threads/Timer.h"
+
+#include <array>
+#include <memory>
+#include <set>
+
+class IGenericTouchGestureDetector;
+
+/*!
+ * \ingroup touch_generic
+ * \brief Generic implementation of ITouchInputHandler to handle low level (raw)
+ * touch events and translate them into touch actions which are passed
+ * on to the registered ITouchActionHandler implementation.
+ *
+ * The generic implementation supports single a double touch and hold
+ * actions and basic gesture recognition for panning, swiping, pinching/zooming
+ * and rotating.
+ *
+ * \sa ITouchInputHandler
+ */
+class CGenericTouchInputHandler : public ITouchInputHandler, private ITimerCallback
+{
+public:
+ /*!
+ \brief Get an instance of the touch input manager
+ */
+ static CGenericTouchInputHandler& GetInstance();
+ static constexpr int MAX_POINTERS = 2;
+
+ // implementation of ITouchInputHandler
+ bool HandleTouchInput(TouchInput event,
+ float x,
+ float y,
+ int64_t time,
+ int32_t pointer = 0,
+ float size = 0.0f) override;
+ bool UpdateTouchPointer(
+ int32_t pointer, float x, float y, int64_t time, float size = 0.0f) override;
+
+private:
+ // private construction, and no assignments; use the provided singleton methods
+ CGenericTouchInputHandler();
+ ~CGenericTouchInputHandler() override;
+ CGenericTouchInputHandler(const CGenericTouchInputHandler&) = delete;
+ CGenericTouchInputHandler const& operator=(CGenericTouchInputHandler const&) = delete;
+
+ typedef enum
+ {
+ TouchGestureUnknown = 0,
+ // only primary pointer active but stationary so far
+ TouchGestureSingleTouch,
+ // primary pointer active but stationary for a certain time
+ TouchGestureSingleTouchHold,
+ // primary pointer moving
+ TouchGesturePan,
+ // at least two pointers active but stationary so far
+ TouchGestureMultiTouchStart,
+ // at least two pointers active but stationary for a certain time
+ TouchGestureMultiTouchHold,
+ // at least two pointers active and moving
+ TouchGestureMultiTouch,
+ // all but primary pointer have been lifted
+ TouchGestureMultiTouchDone
+ } TouchGestureState;
+
+ // implementation of ITimerCallback
+ void OnTimeout() override;
+
+ void saveLastTouch();
+ void setGestureState(TouchGestureState gestureState)
+ {
+ m_gestureStateOld = m_gestureState;
+ m_gestureState = gestureState;
+ }
+ void triggerDetectors(TouchInput event, int32_t pointer);
+ float AdjustPointerSize(float size);
+
+ CCriticalSection m_critical;
+ std::unique_ptr<CTimer> m_holdTimer;
+ std::array<Pointer, MAX_POINTERS> m_pointers;
+ std::set<std::unique_ptr<IGenericTouchGestureDetector>> m_detectors;
+
+ TouchGestureState m_gestureState = TouchGestureUnknown;
+ TouchGestureState m_gestureStateOld = TouchGestureUnknown;
+};
diff --git a/xbmc/input/touch/generic/GenericTouchPinchDetector.cpp b/xbmc/input/touch/generic/GenericTouchPinchDetector.cpp
new file mode 100644
index 0000000..3cff00d
--- /dev/null
+++ b/xbmc/input/touch/generic/GenericTouchPinchDetector.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2013-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 "GenericTouchPinchDetector.h"
+
+bool CGenericTouchPinchDetector::OnTouchDown(unsigned int index, const Pointer& pointer)
+{
+ if (index >= MAX_POINTERS)
+ return false;
+
+ if (m_done)
+ return true;
+
+ m_pointers[index] = pointer;
+ return true;
+}
+
+bool CGenericTouchPinchDetector::OnTouchUp(unsigned int index, const Pointer& pointer)
+{
+ if (index >= MAX_POINTERS)
+ return false;
+
+ if (m_done)
+ return true;
+
+ // after lifting the primary pointer, the secondary pointer will
+ // become the primary pointer in the next event
+ if (index == 0)
+ {
+ m_pointers[0] = m_pointers[1];
+ index = 1;
+ }
+
+ m_pointers[index].reset();
+
+ if (!m_pointers[0].valid() && !m_pointers[1].valid())
+ m_done = true;
+
+ return true;
+}
+
+bool CGenericTouchPinchDetector::OnTouchMove(unsigned int index, const Pointer& pointer)
+{
+ if (index >= MAX_POINTERS)
+ return false;
+
+ if (m_done)
+ return true;
+
+ // update the internal pointers
+ m_pointers[index] = pointer;
+
+ const Pointer& primaryPointer = m_pointers[0];
+ const Pointer& secondaryPointer = m_pointers[1];
+
+ if (!primaryPointer.valid() || !secondaryPointer.valid() ||
+ (!primaryPointer.moving && !secondaryPointer.moving))
+ return false;
+
+ // calculate zoom/pinch
+ CVector primary = primaryPointer.down;
+ CVector secondary = secondaryPointer.down;
+ CVector diagonal = primary - secondary;
+
+ float baseDiffLength = diagonal.length();
+ if (baseDiffLength != 0.0f)
+ {
+ CVector primaryNow = primaryPointer.current;
+ CVector secondaryNow = secondaryPointer.current;
+ CVector diagonalNow = primaryNow - secondaryNow;
+ float curDiffLength = diagonalNow.length();
+
+ float centerX = (primary.x + secondary.x) / 2;
+ float centerY = (primary.y + secondary.y) / 2;
+
+ float zoom = curDiffLength / baseDiffLength;
+
+ OnZoomPinch(centerX, centerY, zoom);
+ }
+
+ return true;
+}
diff --git a/xbmc/input/touch/generic/GenericTouchPinchDetector.h b/xbmc/input/touch/generic/GenericTouchPinchDetector.h
new file mode 100644
index 0000000..191e950
--- /dev/null
+++ b/xbmc/input/touch/generic/GenericTouchPinchDetector.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "input/touch/generic/IGenericTouchGestureDetector.h"
+
+/*!
+ * \ingroup touch_generic
+ * \brief Implementation of IGenericTouchGestureDetector to detect pinch/zoom
+ * gestures with at least two active touch pointers.
+ *
+ * \sa IGenericTouchGestureDetector
+ */
+class CGenericTouchPinchDetector : public IGenericTouchGestureDetector
+{
+public:
+ CGenericTouchPinchDetector(ITouchActionHandler* handler, float dpi)
+ : IGenericTouchGestureDetector(handler, dpi)
+ {
+ }
+ ~CGenericTouchPinchDetector() override = default;
+
+ bool OnTouchDown(unsigned int index, const Pointer& pointer) override;
+ bool OnTouchUp(unsigned int index, const Pointer& pointer) override;
+ bool OnTouchMove(unsigned int index, const Pointer& pointer) override;
+};
diff --git a/xbmc/input/touch/generic/GenericTouchRotateDetector.cpp b/xbmc/input/touch/generic/GenericTouchRotateDetector.cpp
new file mode 100644
index 0000000..79db638
--- /dev/null
+++ b/xbmc/input/touch/generic/GenericTouchRotateDetector.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013-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 "GenericTouchRotateDetector.h"
+
+#include <math.h>
+
+#ifndef M_PI
+#define M_PI 3.1415926535897932384626433832795028842
+#endif
+
+CGenericTouchRotateDetector::CGenericTouchRotateDetector(ITouchActionHandler* handler, float dpi)
+ : IGenericTouchGestureDetector(handler, dpi), m_angle(0.0f)
+{
+}
+
+bool CGenericTouchRotateDetector::OnTouchDown(unsigned int index, const Pointer& pointer)
+{
+ if (index >= MAX_POINTERS)
+ return false;
+
+ if (m_done)
+ return true;
+
+ m_pointers[index] = pointer;
+ m_angle = 0.0f;
+ return true;
+}
+
+bool CGenericTouchRotateDetector::OnTouchUp(unsigned int index, const Pointer& pointer)
+{
+ if (index >= MAX_POINTERS)
+ return false;
+
+ if (m_done)
+ return true;
+
+ // after lifting the primary pointer, the secondary pointer will
+ // become the primary pointer in the next event
+ if (index == 0)
+ {
+ m_pointers[0] = m_pointers[1];
+ index = 1;
+ }
+
+ m_pointers[index].reset();
+
+ if (!m_pointers[0].valid() && !m_pointers[1].valid())
+ m_done = true;
+
+ return true;
+}
+
+bool CGenericTouchRotateDetector::OnTouchMove(unsigned int index, const Pointer& pointer)
+{
+ if (index >= MAX_POINTERS)
+ return false;
+
+ if (m_done)
+ return true;
+
+ // update the internal pointers
+ m_pointers[index] = pointer;
+
+ Pointer& primaryPointer = m_pointers[0];
+ Pointer& secondaryPointer = m_pointers[1];
+
+ if (!primaryPointer.valid() || !secondaryPointer.valid() ||
+ (!primaryPointer.moving && !secondaryPointer.moving))
+ return false;
+
+ CVector last = primaryPointer.last - secondaryPointer.last;
+ CVector current = primaryPointer.current - secondaryPointer.current;
+
+ float length = last.length() * current.length();
+ if (length != 0.0f)
+ {
+ float centerX = (primaryPointer.current.x + secondaryPointer.current.x) / 2;
+ float centerY = (primaryPointer.current.y + secondaryPointer.current.y) / 2;
+ float scalar = last.scalar(current);
+ float angle = acos(scalar / length) * 180.0f / static_cast<float>(M_PI);
+
+ // make sure the result of acos is a valid number
+ if (angle == angle)
+ {
+ // calculate the direction of the rotation using the
+ // z-component of the cross-product of last and current
+ float direction = last.x * current.y - current.x * last.y;
+ if (direction < 0.0f)
+ m_angle -= angle;
+ else
+ m_angle += angle;
+
+ OnRotate(centerX, centerY, m_angle);
+ }
+ }
+
+ return true;
+}
+
+bool CGenericTouchRotateDetector::OnTouchUpdate(unsigned int index, const Pointer& pointer)
+{
+ if (index >= MAX_POINTERS)
+ return false;
+
+ if (m_done)
+ return true;
+
+ // update the internal pointers
+ m_pointers[index] = pointer;
+ return true;
+}
diff --git a/xbmc/input/touch/generic/GenericTouchRotateDetector.h b/xbmc/input/touch/generic/GenericTouchRotateDetector.h
new file mode 100644
index 0000000..695879a
--- /dev/null
+++ b/xbmc/input/touch/generic/GenericTouchRotateDetector.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "input/touch/generic/IGenericTouchGestureDetector.h"
+
+/*!
+ * \ingroup touch_generic
+ * \brief Implementation of IGenericTouchGestureDetector to detect rotation
+ * gestures with at least two active touch pointers.
+ *
+ * \sa IGenericTouchGestureDetector
+ */
+class CGenericTouchRotateDetector : public IGenericTouchGestureDetector
+{
+public:
+ CGenericTouchRotateDetector(ITouchActionHandler* handler, float dpi);
+ ~CGenericTouchRotateDetector() override = default;
+
+ bool OnTouchDown(unsigned int index, const Pointer& pointer) override;
+ bool OnTouchUp(unsigned int index, const Pointer& pointer) override;
+ bool OnTouchMove(unsigned int index, const Pointer& pointer) override;
+ bool OnTouchUpdate(unsigned int index, const Pointer& pointer) override;
+
+private:
+ /*!
+ * \brief Angle of the detected rotation
+ */
+ float m_angle;
+};
diff --git a/xbmc/input/touch/generic/GenericTouchSwipeDetector.cpp b/xbmc/input/touch/generic/GenericTouchSwipeDetector.cpp
new file mode 100644
index 0000000..7d772b5
--- /dev/null
+++ b/xbmc/input/touch/generic/GenericTouchSwipeDetector.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#ifndef _USE_MATH_DEFINES
+#define _USE_MATH_DEFINES
+#endif
+#include "GenericTouchSwipeDetector.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+// maximum time between touch down and up (in nanoseconds)
+#define SWIPE_MAX_TIME 500000000
+// maximum swipe distance between touch down and up (in multiples of screen DPI)
+#define SWIPE_MIN_DISTANCE 0.5f
+// original maximum variance of the touch movement
+#define SWIPE_MAX_VARIANCE 0.2f
+// tangents of the maximum angle (20 degrees) the touch movement may vary in a
+// direction perpendicular to the swipe direction (in radians)
+// => tan(20 deg) = tan(20 * M_PI / 180)
+#define SWIPE_MAX_VARIANCE_ANGLE 0.36397023f
+
+CGenericTouchSwipeDetector::CGenericTouchSwipeDetector(ITouchActionHandler* handler, float dpi)
+ : IGenericTouchGestureDetector(handler, dpi),
+ m_directions(TouchMoveDirectionLeft | TouchMoveDirectionRight | TouchMoveDirectionUp |
+ TouchMoveDirectionDown),
+ m_swipeDetected(false),
+ m_size(0)
+{
+}
+
+bool CGenericTouchSwipeDetector::OnTouchDown(unsigned int index, const Pointer& pointer)
+{
+ if (index >= MAX_POINTERS)
+ return false;
+
+ m_size += 1;
+ if (m_size > 1)
+ return true;
+
+ // reset all values
+ m_done = false;
+ m_swipeDetected = false;
+ m_directions = TouchMoveDirectionLeft | TouchMoveDirectionRight | TouchMoveDirectionUp |
+ TouchMoveDirectionDown;
+
+ return true;
+}
+
+bool CGenericTouchSwipeDetector::OnTouchUp(unsigned int index, const Pointer& pointer)
+{
+ if (index >= MAX_POINTERS)
+ return false;
+
+ m_size -= 1;
+ if (m_done)
+ return false;
+
+ m_done = true;
+
+ // check if a swipe has been detected and if it has a valid direction
+ if (!m_swipeDetected || m_directions == TouchMoveDirectionNone)
+ return false;
+
+ // check if the swipe has been performed in the proper time span
+ if ((pointer.current.time - pointer.down.time) > SWIPE_MAX_TIME)
+ return false;
+
+ // calculate the velocity of the swipe
+ float velocityX = 0.0f; // number of pixels per second
+ float velocityY = 0.0f; // number of pixels per second
+ pointer.velocity(velocityX, velocityY, false);
+
+ // call the OnSwipe() callback
+ OnSwipe((TouchMoveDirection)m_directions, pointer.down.x, pointer.down.y, pointer.current.x,
+ pointer.current.y, velocityX, velocityY, m_size + 1);
+ return true;
+}
+
+bool CGenericTouchSwipeDetector::OnTouchMove(unsigned int index, const Pointer& pointer)
+{
+ if (index >= MAX_POINTERS)
+ return false;
+
+ // only handle swipes of moved pointers
+ if (index >= m_size || m_done || !pointer.moving)
+ return false;
+
+ float deltaXmovement = pointer.current.x - pointer.last.x;
+ float deltaYmovement = pointer.current.y - pointer.last.y;
+
+ if (deltaXmovement > 0.0f)
+ m_directions &= ~TouchMoveDirectionLeft;
+ else if (deltaXmovement < 0.0f)
+ m_directions &= ~TouchMoveDirectionRight;
+
+ if (deltaYmovement > 0.0f)
+ m_directions &= ~TouchMoveDirectionUp;
+ else if (deltaYmovement < 0.0f)
+ m_directions &= ~TouchMoveDirectionDown;
+
+ if (m_directions == TouchMoveDirectionNone)
+ {
+ m_done = true;
+ return false;
+ }
+
+ float deltaXabs = fabs(pointer.current.x - pointer.down.x);
+ float deltaYabs = fabs(pointer.current.y - pointer.down.y);
+ float varXabs = deltaYabs * SWIPE_MAX_VARIANCE_ANGLE + (m_dpi * SWIPE_MAX_VARIANCE) / 2;
+ float varYabs = deltaXabs * SWIPE_MAX_VARIANCE_ANGLE + (m_dpi * SWIPE_MAX_VARIANCE) / 2;
+
+ if (m_directions & TouchMoveDirectionLeft)
+ {
+ // check if the movement went too much in Y direction
+ if (deltaYabs > varYabs)
+ m_directions &= ~TouchMoveDirectionLeft;
+ // check if the movement went far enough in the X direction
+ else if (deltaXabs > m_dpi * SWIPE_MIN_DISTANCE)
+ m_swipeDetected = true;
+ }
+
+ if (m_directions & TouchMoveDirectionRight)
+ {
+ // check if the movement went too much in Y direction
+ if (deltaYabs > varYabs)
+ m_directions &= ~TouchMoveDirectionRight;
+ // check if the movement went far enough in the X direction
+ else if (deltaXabs > m_dpi * SWIPE_MIN_DISTANCE)
+ m_swipeDetected = true;
+ }
+
+ if (m_directions & TouchMoveDirectionUp)
+ {
+ // check if the movement went too much in X direction
+ if (deltaXabs > varXabs)
+ m_directions &= ~TouchMoveDirectionUp;
+ // check if the movement went far enough in the Y direction
+ else if (deltaYabs > m_dpi * SWIPE_MIN_DISTANCE)
+ m_swipeDetected = true;
+ }
+
+ if (m_directions & TouchMoveDirectionDown)
+ {
+ // check if the movement went too much in X direction
+ if (deltaXabs > varXabs)
+ m_directions &= ~TouchMoveDirectionDown;
+ // check if the movement went far enough in the Y direction
+ else if (deltaYabs > m_dpi * SWIPE_MIN_DISTANCE)
+ m_swipeDetected = true;
+ }
+
+ if (m_directions == TouchMoveDirectionNone)
+ {
+ m_done = true;
+ return false;
+ }
+
+ return true;
+}
+
+bool CGenericTouchSwipeDetector::OnTouchUpdate(unsigned int index, const Pointer& pointer)
+{
+ if (index >= MAX_POINTERS)
+ return false;
+
+ if (m_done)
+ return true;
+
+ return OnTouchMove(index, pointer);
+}
diff --git a/xbmc/input/touch/generic/GenericTouchSwipeDetector.h b/xbmc/input/touch/generic/GenericTouchSwipeDetector.h
new file mode 100644
index 0000000..7fe88b1
--- /dev/null
+++ b/xbmc/input/touch/generic/GenericTouchSwipeDetector.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "input/touch/generic/IGenericTouchGestureDetector.h"
+
+/*!
+ * \ingroup touch_generic
+ * \brief Implementation of IGenericTouchGestureDetector to detect swipe
+ * gestures in any direction.
+ *
+ * \sa IGenericTouchGestureDetector
+ */
+class CGenericTouchSwipeDetector : public IGenericTouchGestureDetector
+{
+public:
+ CGenericTouchSwipeDetector(ITouchActionHandler* handler, float dpi);
+ ~CGenericTouchSwipeDetector() override = default;
+
+ bool OnTouchDown(unsigned int index, const Pointer& pointer) override;
+ bool OnTouchUp(unsigned int index, const Pointer& pointer) override;
+ bool OnTouchMove(unsigned int index, const Pointer& pointer) override;
+ bool OnTouchUpdate(unsigned int index, const Pointer& pointer) override;
+
+private:
+ /*!
+ * \brief Swipe directions that are still possible to detect
+ *
+ * The directions are stored as a combination (bitwise OR) of
+ * TouchMoveDirection enum values
+ *
+ * \sa TouchMoveDirection
+ */
+ unsigned int m_directions;
+ /*!
+ * \brief Whether a swipe gesture has been detected or not
+ */
+ bool m_swipeDetected;
+ /*!
+ * \brief Number of active pointers
+ */
+ unsigned int m_size;
+};
diff --git a/xbmc/input/touch/generic/IGenericTouchGestureDetector.h b/xbmc/input/touch/generic/IGenericTouchGestureDetector.h
new file mode 100644
index 0000000..7e2cb88
--- /dev/null
+++ b/xbmc/input/touch/generic/IGenericTouchGestureDetector.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "input/touch/ITouchInputHandling.h"
+#include "input/touch/TouchTypes.h"
+
+#include <array>
+
+/*!
+ * \ingroup touch_generic
+ * \brief Interface defining methods to perform gesture recognition
+ */
+class IGenericTouchGestureDetector : public ITouchInputHandling
+{
+public:
+ IGenericTouchGestureDetector(ITouchActionHandler* handler, float dpi) : m_done(false), m_dpi(dpi)
+ {
+ RegisterHandler(handler);
+ }
+ ~IGenericTouchGestureDetector() override = default;
+ static constexpr int MAX_POINTERS = 2;
+
+ /*!
+ * \brief Check whether the gesture recognition is finished or not
+ *
+ * \return True if the gesture recognition is finished otherwise false
+ */
+ bool IsDone() { return m_done; }
+
+ /*!
+ * \brief A new touch pointer has been recognised.
+ *
+ * \param index Index of the given touch pointer
+ * \param pointer Touch pointer that has changed
+ *
+ * \return True if the event was handled otherwise false
+ */
+ virtual bool OnTouchDown(unsigned int index, const Pointer& pointer) = 0;
+ /*!
+ * \brief An active touch pointer has vanished.
+ *
+ * If the first touch pointer is lifted and there are more active touch
+ * pointers, the remaining pointers change their index.
+ *
+ * \param index Index of the given touch pointer
+ * \param pointer Touch pointer that has changed
+ *
+ * \return True if the event was handled otherwise false
+ */
+ virtual bool OnTouchUp(unsigned int index, const Pointer& pointer) { return false; }
+ /*!
+ * \brief An active touch pointer has moved.
+ *
+ * \param index Index of the given touch pointer
+ * \param pointer Touch pointer that has changed
+ *
+ * \return True if the event was handled otherwise false
+ */
+ virtual bool OnTouchMove(unsigned int index, const Pointer& pointer) { return false; }
+ /*!
+ * \brief An active touch pointer's values have been updated but no event has
+ * occurred.
+ *
+ * \param index Index of the given touch pointer
+ * \param pointer Touch pointer that has changed
+ *
+ * \return True if the event was handled otherwise false
+ */
+ virtual bool OnTouchUpdate(unsigned int index, const Pointer& pointer) { return false; }
+
+protected:
+ /*!
+ * \brief Whether the gesture recognition is finished or not
+ */
+ bool m_done;
+ /*!
+ * \brief DPI value of the touch screen
+ */
+ float m_dpi;
+ /*!
+ * \brief Local list of all known touch pointers
+ */
+ std::array<Pointer, MAX_POINTERS> m_pointers;
+};