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