1 /*
2  *  Copyright (C) 2017-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "TouchTranslator.h"
10 
11 #include "WindowTranslator.h" //! @todo
12 #include "input/actions/ActionIDs.h"
13 #include "input/actions/ActionTranslator.h"
14 #include "utils/StringUtils.h"
15 #include "utils/XBMCTinyXML.h"
16 #include "utils/log.h"
17 
18 #include <map>
19 
20 using ActionName = std::string;
21 using TouchCommandID = unsigned int;
22 
23 #define TOUCH_COMMAND_NONE 0
24 
25 static const std::map<ActionName, TouchCommandID> TouchCommands = {
26     {"tap", ACTION_TOUCH_TAP},
27     {"longpress", ACTION_TOUCH_LONGPRESS},
28     {"pan", ACTION_GESTURE_PAN},
29     {"zoom", ACTION_GESTURE_ZOOM},
30     {"rotate", ACTION_GESTURE_ROTATE},
31     {"swipeleft", ACTION_GESTURE_SWIPE_LEFT},
32     {"swiperight", ACTION_GESTURE_SWIPE_RIGHT},
33     {"swipeup", ACTION_GESTURE_SWIPE_UP},
34     {"swipedown", ACTION_GESTURE_SWIPE_DOWN}};
35 
MapActions(int windowID,const TiXmlNode * pTouch)36 void CTouchTranslator::MapActions(int windowID, const TiXmlNode* pTouch)
37 {
38   if (pTouch == nullptr)
39     return;
40 
41   TouchActionMap map;
42 
43   // Check if there already is a touch map for the window ID
44   auto it = m_touchMap.find(windowID);
45   if (it != m_touchMap.end())
46   {
47     // Get the existing touch map and remove it from the window mapping as it
48     // will be inserted later on
49     map = std::move(it->second);
50     m_touchMap.erase(it);
51   }
52 
53   const TiXmlElement* pTouchElem = pTouch->ToElement();
54   if (pTouchElem == nullptr)
55     return;
56 
57   const TiXmlElement* pButton = pTouchElem->FirstChildElement();
58   while (pButton != nullptr)
59   {
60     CTouchAction action;
61     unsigned int touchActionKey = TranslateTouchCommand(pButton, action);
62     if (touchActionKey != ACTION_NONE)
63     {
64       // check if there already is a mapping for the parsed action
65       // and remove it if necessary
66       TouchActionMap::iterator actionIt = map.find(touchActionKey);
67       if (actionIt != map.end())
68         map.erase(actionIt);
69 
70       map.insert(std::make_pair(touchActionKey, std::move(action)));
71     }
72 
73     pButton = pButton->NextSiblingElement();
74   }
75 
76   // add the modified touch map with the window ID
77   if (!map.empty())
78     m_touchMap.insert(std::make_pair(windowID, std::move(map)));
79 }
80 
Clear()81 void CTouchTranslator::Clear()
82 {
83   m_touchMap.clear();
84 }
85 
TranslateTouchAction(int window,int touchAction,int touchPointers,int & action,std::string & actionString)86 bool CTouchTranslator::TranslateTouchAction(
87     int window, int touchAction, int touchPointers, int& action, std::string& actionString)
88 {
89   if (touchAction < 0)
90     return false;
91 
92   unsigned int actionId = ACTION_NONE;
93 
94   // handle virtual windows
95   window = CWindowTranslator::GetVirtualWindow(window);
96 
97   if (!TranslateAction(window, touchAction, touchPointers, actionId, actionString))
98   {
99     // if it's invalid, try to get it from fallback windows or the global map (window == -1)
100     while (actionId == ACTION_NONE && window > -1)
101     {
102       window = CWindowTranslator::GetFallbackWindow(window);
103       TranslateAction(window, touchAction, touchPointers, actionId, actionString);
104     }
105   }
106 
107   action = actionId;
108   return actionId != ACTION_NONE;
109 }
110 
TranslateAction(int window,unsigned int touchCommand,int touchPointers,unsigned int & actionId,std::string & actionString)111 bool CTouchTranslator::TranslateAction(int window,
112                                        unsigned int touchCommand,
113                                        int touchPointers,
114                                        unsigned int& actionId,
115                                        std::string& actionString)
116 {
117   unsigned int touchActionKey = GetTouchActionKey(touchCommand, touchPointers);
118 
119   actionId = GetActionID(window, touchActionKey, actionString);
120 
121   return actionId != ACTION_NONE;
122 }
123 
GetActionID(WindowID window,TouchActionKey touchActionKey,std::string & actionString)124 unsigned int CTouchTranslator::GetActionID(WindowID window,
125                                            TouchActionKey touchActionKey,
126                                            std::string& actionString)
127 {
128   auto windowIt = m_touchMap.find(window);
129   if (windowIt == m_touchMap.end())
130     return ACTION_NONE;
131 
132   auto touchIt = windowIt->second.find(touchActionKey);
133   if (touchIt == windowIt->second.end())
134     return ACTION_NONE;
135 
136   actionString = touchIt->second.strAction;
137   return touchIt->second.actionId;
138 }
139 
TranslateTouchCommand(const TiXmlElement * pButton,CTouchAction & action)140 unsigned int CTouchTranslator::TranslateTouchCommand(const TiXmlElement* pButton,
141                                                      CTouchAction& action)
142 {
143   const char* szButton = pButton->Value();
144   if (szButton == nullptr || pButton->FirstChild() == nullptr)
145     return ACTION_NONE;
146 
147   const char* szAction = pButton->FirstChild()->Value();
148   if (szAction == nullptr)
149     return ACTION_NONE;
150 
151   std::string strTouchCommand = szButton;
152   StringUtils::ToLower(strTouchCommand);
153 
154   // Handle direction
155   const char* attrVal = pButton->Attribute("direction");
156   if (attrVal != nullptr)
157     strTouchCommand += attrVal;
158 
159   // Lookup command
160   unsigned int touchCommandId = TOUCH_COMMAND_NONE;
161   auto it = TouchCommands.find(strTouchCommand);
162   if (it != TouchCommands.end())
163     touchCommandId = it->second;
164 
165   if (touchCommandId == TOUCH_COMMAND_NONE)
166   {
167     CLog::Log(LOGERROR, "%s: Can't find touch command %s", __FUNCTION__, szButton);
168     return ACTION_NONE;
169   }
170 
171   // Handle pointers
172   int pointers = 1;
173   attrVal = pButton->Attribute("pointers");
174   if (attrVal != nullptr)
175     pointers = (int)strtol(attrVal, nullptr, 0);
176 
177   unsigned int touchActionKey = GetTouchActionKey(touchCommandId, pointers);
178 
179   action.strAction = szAction;
180   if (!CActionTranslator::TranslateString(action.strAction, action.actionId) ||
181       action.actionId == ACTION_NONE)
182     return ACTION_NONE;
183 
184   return touchActionKey;
185 }
186 
GetTouchActionKey(unsigned int touchCommandId,int touchPointers)187 unsigned int CTouchTranslator::GetTouchActionKey(unsigned int touchCommandId, int touchPointers)
188 {
189   if (touchPointers <= 0)
190     touchPointers = 1;
191 
192   return touchCommandId + touchPointers - 1;
193 }
194