diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/input/touch | |
parent | Initial commit. (diff) | |
download | kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip |
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
18 files changed, 2137 insertions, 0 deletions
diff --git a/xbmc/input/touch/CMakeLists.txt b/xbmc/input/touch/CMakeLists.txt new file mode 100644 index 0000000..12a228b --- /dev/null +++ b/xbmc/input/touch/CMakeLists.txt @@ -0,0 +1,8 @@ +set(SOURCES ITouchInputHandling.cpp) + +set(HEADERS ITouchActionHandler.h + ITouchInputHandler.h + ITouchInputHandling.h + TouchTypes.h) + +core_add_library(input_touch) diff --git a/xbmc/input/touch/ITouchActionHandler.h b/xbmc/input/touch/ITouchActionHandler.h new file mode 100644 index 0000000..28afdd7 --- /dev/null +++ b/xbmc/input/touch/ITouchActionHandler.h @@ -0,0 +1,261 @@ +/* + * 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 <stdint.h> + +/*! + * \ingroup touch + * \brief Directions in which a touch can moved + * + * These values can be combined (bitwise OR) to specify multiple directions. + */ +typedef enum +{ + TouchMoveDirectionNone = 0x0, + TouchMoveDirectionLeft = 0x1, + TouchMoveDirectionRight = 0x2, + TouchMoveDirectionUp = 0x4, + TouchMoveDirectionDown = 0x8 +} TouchMoveDirection; + +/*! + * \ingroup touch + * \brief Interface defining all supported touch action events + */ +class ITouchActionHandler +{ +public: + virtual ~ITouchActionHandler() = default; + + /*! + * \brief A touch action has been aborted + */ + virtual void OnTouchAbort() {} + + /*! + * \brief A single touch has started + * + * \param x The x coordinate (with sub-pixel) of the touch + * \param y The y coordinate (with sub-pixel) of the touch + * + * \return True if the event was handled otherwise false + * + * \sa OnSingleTap + */ + virtual bool OnSingleTouchStart(float x, float y) { return true; } + /*! + * \brief A single touch has been held down for a certain amount of time + * + * \param x The x coordinate (with sub-pixel) of the touch + * \param y The y coordinate (with sub-pixel) of the touch + * + * \return True if the event was handled otherwise false + * + * \sa OnSingleLongPress + */ + virtual bool OnSingleTouchHold(float x, float y) { return true; } + /*! + * \brief A single touch has moved + * + * \param x The x coordinate (with sub-pixel) of the current touch + * \param y The y coordinate (with sub-pixel) of the current touch + * \param offsetX The covered distance on the x axis (with sub-pixel) + * \param offsetX The covered distance on the y axis (with sub-pixel) + * \param velocityX The velocity of the gesture in x direction (pixels/second) + * \param velocityX The velocity of the gesture in y direction (pixels/second) + * + * \return True if the event was handled otherwise false + * + * \sa OnTouchGesturePan + */ + virtual bool OnSingleTouchMove( + float x, float y, float offsetX, float offsetY, float velocityX, float velocityY) + { + return true; + } + /*! + * \brief A single touch has been lifted + * + * \param x The x coordinate (with sub-pixel) of the touch + * \param y The y coordinate (with sub-pixel) of the touch + * + * \return True if the event was handled otherwise false + * + * \sa OnSingleTap + */ + virtual bool OnSingleTouchEnd(float x, float y) { return true; } + + /*! + * \brief An additional touch has been performed + * + * \param x The x coordinate (with sub-pixel) of the touch + * \param y The y coordinate (with sub-pixel) of the touch + * \param pointer The pointer that has performed the touch + * + * \return True if the event was handled otherwise false + */ + virtual bool OnMultiTouchDown(float x, float y, int32_t pointer) { return true; } + /*! + * \brief Multiple simultaneous touches have been held down for a certain amount of time + * + * \param x The x coordinate (with sub-pixel) of the touch + * \param y The y coordinate (with sub-pixel) of the touch + * \param pointers The number of pointers involved (default 2) + * + * \return True if the event was handled otherwise false + */ + virtual bool OnMultiTouchHold(float x, float y, int32_t pointers = 2) { return true; } + /*! + * \brief A touch has moved + * + * \param x The x coordinate (with sub-pixel) of the current touch + * \param y The y coordinate (with sub-pixel) of the current touch + * \param offsetX The covered distance on the x axis (with sub-pixel) + * \param offsetX The covered distance on the y axis (with sub-pixel) + * \param velocityX The velocity of the gesture in x direction (pixels/second) + * \param velocityX The velocity of the gesture in y direction (pixels/second) + * \param pointer The pointer that has performed the touch + * + * \return True if the event was handled otherwise false + */ + virtual bool OnMultiTouchMove(float x, + float y, + float offsetX, + float offsetY, + float velocityX, + float velocityY, + int32_t pointer) + { + return true; + } + /*! + * \brief A touch has been lifted (but there are still active touches) + * + * \param x The x coordinate (with sub-pixel) of the touch + * \param y The y coordinate (with sub-pixel) of the touch + * \param pointer The pointer that has performed the touch + * + * \return True if the event was handled otherwise false + */ + virtual bool OnMultiTouchUp(float x, float y, int32_t pointer) { return true; } + + /*! + * \brief A pan gesture with a single touch has been started + * + * \param x The x coordinate (with sub-pixel) of the initial touch + * \param y The y coordinate (with sub-pixel) of the initial touch + * + * \return True if the event was handled otherwise false + */ + virtual bool OnTouchGestureStart(float x, float y) { return true; } + /*! + * \brief A pan gesture with a single touch is in progress + * + * \param x The x coordinate (with sub-pixel) of the current touch + * \param y The y coordinate (with sub-pixel) of the current touch + * \param offsetX The covered distance on the x axis (with sub-pixel) + * \param offsetX The covered distance on the y axis (with sub-pixel) + * \param velocityX The velocity of the gesture in x direction (pixels/second) + * \param velocityX The velocity of the gesture in y direction (pixels/second) + * + * \return True if the event was handled otherwise false + */ + virtual bool OnTouchGesturePan( + float x, float y, float offsetX, float offsetY, float velocityX, float velocityY) + { + return true; + } + /*! + * \brief A pan gesture with a single touch has ended + * + * \param x The x coordinate (with sub-pixel) of the current touch + * \param y The y coordinate (with sub-pixel) of the current touch + * \param offsetX The covered distance on the x axis (with sub-pixel) + * \param offsetX The covered distance on the y axis (with sub-pixel) + * \param velocityX The velocity of the gesture in x direction (pixels/second) + * \param velocityX The velocity of the gesture in y direction (pixels/second) + * + * \return True if the event was handled otherwise false + */ + virtual bool OnTouchGestureEnd( + float x, float y, float offsetX, float offsetY, float velocityX, float velocityY) + { + return true; + } + + // convenience events + /*! + * \brief A tap with a one or more touches has been performed + * + * \param x The x coordinate (with sub-pixel) of the touch + * \param y The y coordinate (with sub-pixel) of the touch + * \param pointers The number of pointers involved (default 1) + * + * \return True if the event was handled otherwise false + */ + virtual void OnTap(float x, float y, int32_t pointers = 1) {} + /*! + * \brief One or more touches have been held down for a certain amount of time + * + * \param x The x coordinate (with sub-pixel) of the touch + * \param y The y coordinate (with sub-pixel) of the touch + * \param pointers The number of pointers involved (default 1) + * + * \return True if the event was handled otherwise false + * + * \sa OnSingleTouchHold + */ + virtual void OnLongPress(float x, float y, int32_t pointers = 1) {} + /*! + * \brief One or more touches has been moved quickly in a single direction in a short time + * + * \param direction The direction (left, right, up, down) of the swipe gesture + * \param xDown The x coordinate (with sub-pixel) of the first touch + * \param yDown The y coordinate (with sub-pixel) of the first touch + * \param xUp The x coordinate (with sub-pixel) of the last touch + * \param yUp The y coordinate (with sub-pixel) of the last touch + * \param velocityX The velocity of the gesture in x direction (pixels/second) + * \param velocityX The velocity of the gesture in y direction (pixels/second) + * \param pointers The number of pointers involved (default 1) + * + * \return True if the event was handled otherwise false + */ + virtual void OnSwipe(TouchMoveDirection direction, + float xDown, + float yDown, + float xUp, + float yUp, + float velocityX, + float velocityY, + int32_t pointers = 1) + { + } + /*! + * \brief Two simultaneous touches have been held down and moved to perform a zooming/pinching + * gesture + * + * \param centerX The x coordinate (with sub-pixel) of the center of the two touches + * \param centerY The y coordinate (with sub-pixel) of the center of the two touches + * \param zoomFactor The zoom (> 1.0) or pinch (< 1.0) factor of the two touches + * + * \return True if the event was handled otherwise false + */ + virtual void OnZoomPinch(float centerX, float centerY, float zoomFactor) {} + /*! + * \brief Two simultaneous touches have been held down and moved to perform a rotating gesture + * + * \param centerX The x coordinate (with sub-pixel) of the center of the two touches + * \param centerY The y coordinate (with sub-pixel) of the center of the two touches + * \param angle The clockwise angle in degrees of the rotation + * + * \return True if the event was handled otherwise false + */ + virtual void OnRotate(float centerX, float centerY, float angle) {} +}; diff --git a/xbmc/input/touch/ITouchInputHandler.h b/xbmc/input/touch/ITouchInputHandler.h new file mode 100644 index 0000000..1275839 --- /dev/null +++ b/xbmc/input/touch/ITouchInputHandler.h @@ -0,0 +1,104 @@ +/* + * 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 <atomic> +#include <stdint.h> + +/*! + * \ingroup touch + * \brief Touch input event + */ +typedef enum +{ + TouchInputUnchanged = 0, + TouchInputAbort, + TouchInputDown, + TouchInputUp, + TouchInputMove +} TouchInput; + +/*! + * \ingroup touch + * \brief Interface (implements ITouchInputHandling) defining methods to handle + * raw touch input events (down, up, move). + * + * This interface should be implemented on platforms only supporting low level + * (raw) touch input events like touch down/up/move and with no gesture + * recognition logic. + */ +class ITouchInputHandler : public ITouchInputHandling +{ +public: + ITouchInputHandler() : m_dpi(160.0f) {} + ~ITouchInputHandler() override = default; + + /*! + * \brief Handle a touch event + * + * Handles the given touch event at the given location. + * This takes into account all the currently active pointers + * which need to be updated before calling this method to + * actually interpret and handle the changes in touch. + * + * \param event The actual touch event (abort, down, up, move) + * \param x The x coordinate (with sub-pixel) of the touch + * \param y The y coordinate (with sub-pixel) of the touch + * \param time The time (in nanoseconds) when this touch occurred + * \param pointer The number of the touch pointer which caused this event (default 0) + * \param size The size of the touch pointer (with sub-pixel) (default 0.0) + * + * \return True if the event was handled otherwise false. + * + * \sa Update + */ + virtual bool HandleTouchInput( + TouchInput event, float x, float y, int64_t time, int32_t pointer = 0, float size = 0.0f) = 0; + + /*! + * \brief Update the coordinates of a pointer + * + * Depending on how a platform handles touch input and provides the necessary events + * this method needs to be called at different times. If there's an event for every + * touch action this method does not need to be called at all. If there's only a + * touch event for the primary pointer (and no special events for any secondary + * pointers in a multi touch gesture) this method should be called for every active + * secondary pointer before calling Handle. + * + * \param pointer The number of the touch pointer which caused this event (default 0) + * \param x The x coordinate (with sub-pixel) of the touch + * \param y The y coordinate (with sub-pixel) of the touch + * \param time The time (in nanoseconds) when this touch occurred + * \param size The size of the touch pointer (with sub-pixel) (default 0.0) + * + * \return True if the pointer was updated otherwise false. + * + * \sa Handle + */ + virtual bool UpdateTouchPointer( + int32_t pointer, float x, float y, int64_t time, float size = 0.0f) + { + return false; + } + + void SetScreenDPI(float dpi) + { + if (dpi > 0.0f) + m_dpi = dpi; + } + float GetScreenDPI() { return m_dpi; } + +protected: + /*! + * \brief DPI value of the touch screen + */ + std::atomic<float> m_dpi; +}; diff --git a/xbmc/input/touch/ITouchInputHandling.cpp b/xbmc/input/touch/ITouchInputHandling.cpp new file mode 100644 index 0000000..e799f72 --- /dev/null +++ b/xbmc/input/touch/ITouchInputHandling.cpp @@ -0,0 +1,159 @@ +/* + * 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 "ITouchInputHandling.h" + +void ITouchInputHandling::RegisterHandler(ITouchActionHandler* touchHandler) +{ + m_handler = touchHandler; +} + +void ITouchInputHandling::UnregisterHandler() +{ + m_handler = NULL; +} + +void ITouchInputHandling::OnTouchAbort() +{ + if (m_handler) + m_handler->OnTouchAbort(); +} + +bool ITouchInputHandling::OnSingleTouchStart(float x, float y) +{ + if (m_handler) + return m_handler->OnSingleTouchStart(x, y); + + return true; +} + +bool ITouchInputHandling::OnSingleTouchHold(float x, float y) +{ + if (m_handler) + return m_handler->OnSingleTouchHold(x, y); + + return true; +} + +bool ITouchInputHandling::OnSingleTouchMove( + float x, float y, float offsetX, float offsetY, float velocityX, float velocityY) +{ + if (m_handler) + return m_handler->OnSingleTouchMove(x, y, offsetX, offsetY, velocityX, velocityY); + + return true; +} + +bool ITouchInputHandling::OnSingleTouchEnd(float x, float y) +{ + if (m_handler) + return m_handler->OnSingleTouchEnd(x, y); + + return true; +} + +bool ITouchInputHandling::OnMultiTouchDown(float x, float y, int32_t pointer) +{ + if (m_handler) + return m_handler->OnMultiTouchDown(x, y, pointer); + + return true; +} + +bool ITouchInputHandling::OnMultiTouchHold(float x, float y, int32_t pointers /* = 2 */) +{ + if (m_handler) + return m_handler->OnMultiTouchHold(x, y, pointers); + + return true; +} + +bool ITouchInputHandling::OnMultiTouchMove(float x, + float y, + float offsetX, + float offsetY, + float velocityX, + float velocityY, + int32_t pointer) +{ + if (m_handler) + return m_handler->OnMultiTouchMove(x, y, offsetX, offsetY, velocityX, velocityY, pointer); + + return true; +} + +bool ITouchInputHandling::OnMultiTouchUp(float x, float y, int32_t pointer) +{ + if (m_handler) + return m_handler->OnMultiTouchUp(x, y, pointer); + + return true; +} + +bool ITouchInputHandling::OnTouchGestureStart(float x, float y) +{ + if (m_handler) + return m_handler->OnTouchGestureStart(x, y); + + return true; +} + +bool ITouchInputHandling::OnTouchGesturePan( + float x, float y, float offsetX, float offsetY, float velocityX, float velocityY) +{ + if (m_handler) + return m_handler->OnTouchGesturePan(x, y, offsetX, offsetY, velocityX, velocityY); + + return true; +} + +bool ITouchInputHandling::OnTouchGestureEnd( + float x, float y, float offsetX, float offsetY, float velocityX, float velocityY) +{ + if (m_handler) + return m_handler->OnTouchGestureEnd(x, y, offsetX, offsetY, velocityX, velocityY); + + return true; +} + +void ITouchInputHandling::OnTap(float x, float y, int32_t pointers /* = 1 */) +{ + if (m_handler) + m_handler->OnTap(x, y, pointers); +} + +void ITouchInputHandling::OnLongPress(float x, float y, int32_t pointers /* = 1 */) +{ + if (m_handler) + m_handler->OnLongPress(x, y, pointers); +} + +void ITouchInputHandling::OnSwipe(TouchMoveDirection direction, + float xDown, + float yDown, + float xUp, + float yUp, + float velocityX, + float velocityY, + int32_t pointers /* = 1 */) +{ + if (m_handler) + m_handler->OnSwipe(direction, xDown, yDown, xUp, yUp, velocityX, velocityY, pointers); +} + +void ITouchInputHandling::OnZoomPinch(float centerX, float centerY, float zoomFactor) +{ + if (m_handler) + m_handler->OnZoomPinch(centerX, centerY, zoomFactor); +} + +void ITouchInputHandling::OnRotate(float centerX, float centerY, float angle) +{ + if (m_handler) + m_handler->OnRotate(centerX, centerY, angle); +} diff --git a/xbmc/input/touch/ITouchInputHandling.h b/xbmc/input/touch/ITouchInputHandling.h new file mode 100644 index 0000000..cabbf47 --- /dev/null +++ b/xbmc/input/touch/ITouchInputHandling.h @@ -0,0 +1,90 @@ +/* + * 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" + +#include <stdlib.h> + +/*! + * \ingroup touch + * \brief Convenience interface implementing ITouchActionHandler with an + * implementation that forwards any ITouchActionHandler-related calls + * to a previously registered ITouchActionHandler + * + * \sa ITouchActionHandler + */ +class ITouchInputHandling : protected ITouchActionHandler +{ +public: + ITouchInputHandling() : m_handler(NULL) {} + ~ITouchInputHandling() override = default; + + /*! + * \brief Register a touch input handler + * + * There can only be one touch input handler. + * + * \param touchHandler An instance of a touch handler implementing the + * ITouchActionHandler interface + * + * \sa UnregisterHandler + */ + void RegisterHandler(ITouchActionHandler* touchHandler); + /*! + * \brief Unregister the previously registered touch handler + * + * \sa RegisterHandler + */ + void UnregisterHandler(); + +protected: + // 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; + +private: + ITouchActionHandler* m_handler; +}; diff --git a/xbmc/input/touch/TouchTypes.h b/xbmc/input/touch/TouchTypes.h new file mode 100644 index 0000000..b3450c4 --- /dev/null +++ b/xbmc/input/touch/TouchTypes.h @@ -0,0 +1,112 @@ +/* + * 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 "utils/Vector.h" + +/*! + * \ingroup touch + * \brief A class representing a touch consisting of an x and y coordinate and + * a time + */ +class Touch : public CVector +{ +public: + /*! + * \brief Checks if the touch is valid i.e. if the x/y coordinates and the + * time are >= 0 + * + * \return True if the touch is valid otherwise false + */ + bool valid() const { return x >= 0.0f && y >= 0.0f && time >= 0; } + + /*! + * \brief Copies the x/y coordinates and the time from the given touch + * + * \param other Touch to copy x/y coordinates and time from + */ + void copy(const Touch& other) + { + x = other.x; + y = other.y; + time = other.time; + } + + int64_t time = -1; // in nanoseconds +}; + +/*! + * \ingroup touch + * \brief A class representing a touch pointer interaction consisting of an + * down touch, the last touch and the current touch. + */ +class Pointer +{ +public: + Pointer() { reset(); } + + /*! + * \brief Resets the pointer and all its touches + */ + void reset() + { + down = {}; + last = {}; + moving = false; + size = 0.0f; + } + + /*! + * \brief Checks if the "down" touch is valid + * + * \return True if the "down" touch is valid otherwise false + */ + bool valid() const { return down.valid(); } + + /*! + * \brief Calculates the velocity in x/y direction using the "down" and + * either the "last" or "current" touch + * + * \param velocityX Placeholder for velocity in x direction + * \param velocityY Placeholder for velocity in y direction + * \param fromLast Whether to calculate the velocity with the "last" or + * the "current" touch + * + * \return True if the velocity is valid otherwise false + */ + bool velocity(float& velocityX, float& velocityY, bool fromLast = true) const + { + int64_t fromTime = last.time; + float fromX = last.x; + float fromY = last.y; + if (!fromLast) + { + fromTime = down.time; + fromX = down.x; + fromY = down.y; + } + + velocityX = 0.0f; // number of pixels per second + velocityY = 0.0f; // number of pixels per second + + int64_t timeDiff = current.time - fromTime; + if (timeDiff <= 0) + return false; + + velocityX = ((current.x - fromX) * 1000000000) / timeDiff; + velocityY = ((current.y - fromY) * 1000000000) / timeDiff; + return true; + } + + Touch down; + Touch last; + Touch current; + bool moving; + float size; +}; 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; +}; |