The open source OpenXR runtime
1// Copyright 2019-2022, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief OpenGloves Alpha Encoding Decoding implementation.
6 * @author Daniel Willmott <web@dan-w.com>
7 * @ingroup drv_opengloves
8 */
9
10#include <string>
11#include <stdexcept>
12
13#include <map>
14#include "util/u_logging.h"
15
16#include "alpha_encoding.h"
17#include "encoding.h"
18
19enum opengloves_alpha_encoding_key
20{
21 OPENGLOVES_ALPHA_ENCODING_FinThumb,
22 OPENGLOVES_ALPHA_ENCODING_FinSplayThumb,
23
24 OPENGLOVES_ALPHA_ENCODING_FinIndex,
25 OPENGLOVES_ALPHA_ENCODING_FinSplayIndex,
26
27 OPENGLOVES_ALPHA_ENCODING_FinMiddle,
28 OPENGLOVES_ALPHA_ENCODING_FinSplayMiddle,
29
30 OPENGLOVES_ALPHA_ENCODING_FinRing,
31 OPENGLOVES_ALPHA_ENCODING_FinSplayRing,
32
33 OPENGLOVES_ALPHA_ENCODING_FinPinky,
34 OPENGLOVES_ALPHA_ENCODING_FinSplayPinky,
35
36 OPENGLOVES_ALPHA_ENCODING_FinJointThumb0,
37 OPENGLOVES_ALPHA_ENCODING_FinJointThumb1,
38 OPENGLOVES_ALPHA_ENCODING_FinJointThumb2,
39 OPENGLOVES_ALPHA_ENCODING_FinJointThumb3, // unused in input but used for parity to other fingers in the array
40
41
42 OPENGLOVES_ALPHA_ENCODING_FinJointIndex0,
43 OPENGLOVES_ALPHA_ENCODING_FinJointIndex1,
44 OPENGLOVES_ALPHA_ENCODING_FinJointIndex2,
45 OPENGLOVES_ALPHA_ENCODING_FinJointIndex3,
46
47
48 OPENGLOVES_ALPHA_ENCODING_FinJointMiddle0,
49 OPENGLOVES_ALPHA_ENCODING_FinJointMiddle1,
50 OPENGLOVES_ALPHA_ENCODING_FinJointMiddle2,
51 OPENGLOVES_ALPHA_ENCODING_FinJointMiddle3,
52
53
54 OPENGLOVES_ALPHA_ENCODING_FinJointRing0,
55 OPENGLOVES_ALPHA_ENCODING_FinJointRing1,
56 OPENGLOVES_ALPHA_ENCODING_FinJointRing2,
57 OPENGLOVES_ALPHA_ENCODING_FinJointRing3,
58
59
60 OPENGLOVES_ALPHA_ENCODING_FinJointPinky0,
61 OPENGLOVES_ALPHA_ENCODING_FinJointPinky1,
62 OPENGLOVES_ALPHA_ENCODING_FinJointPinky2,
63 OPENGLOVES_ALPHA_ENCODING_FinJointPinky3,
64
65 OPENGLOVES_ALPHA_ENCODING_JoyX,
66 OPENGLOVES_ALPHA_ENCODING_JoyY,
67 OPENGLOVES_ALPHA_ENCODING_JoyBtn,
68
69 OPENGLOVES_ALPHA_ENCODING_TrgValue,
70 OPENGLOVES_ALPHA_ENCODING_BtnTrg,
71 OPENGLOVES_ALPHA_ENCODING_BtnA,
72 OPENGLOVES_ALPHA_ENCODING_BtnB,
73
74 OPENGLOVES_ALPHA_ENCODING_GesGrab,
75 OPENGLOVES_ALPHA_ENCODING_GesPinch,
76
77 OPENGLOVES_ALPHA_ENCODING_BtnMenu,
78 OPENGLOVES_ALPHA_ENCODING_BtnCalib,
79
80 OPENGLOVES_ALPHA_ENCODING_MAX
81};
82
83#define OPENGLOVES_ALPHA_ENCODING_VAL_IN_MAP_E_0
84
85static const std::string opengloves_alpha_encoding_key_characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ()";
86
87static bool
88opengloves_alpha_encoding_is_key_character(const char character)
89{
90 return opengloves_alpha_encoding_key_characters.find(character) != std::string::npos;
91}
92
93static const std::map<std::string, int> opengloves_alpha_encoding_input_key_string{
94 {"A", OPENGLOVES_ALPHA_ENCODING_FinThumb}, // whole thumb curl (default curl value for thumb joints)
95 {"(AB)", OPENGLOVES_ALPHA_ENCODING_FinSplayThumb}, // whole thumb splay thumb joint 3 (doesn't exist, but keeps
96 // consistency with the other fingers
97 {"B", OPENGLOVES_ALPHA_ENCODING_FinIndex}, // whole index curl (default curl value for index joints)
98 {"(BB)", OPENGLOVES_ALPHA_ENCODING_FinSplayIndex}, // whole index splay
99
100 {"C", OPENGLOVES_ALPHA_ENCODING_FinMiddle}, // whole middle curl (default curl value for middle joints)
101 {"(CB)", OPENGLOVES_ALPHA_ENCODING_FinSplayMiddle}, // whole middle splay
102
103 {"D", OPENGLOVES_ALPHA_ENCODING_FinRing}, // whole ring curl (default curl value for
104 {"(DB)", OPENGLOVES_ALPHA_ENCODING_FinSplayRing}, // whole ring splay
105 // ring joints)
106 {"E", OPENGLOVES_ALPHA_ENCODING_FinPinky}, // whole pinky curl (default curl value
107 {"(EB)", OPENGLOVES_ALPHA_ENCODING_FinSplayPinky}, // whole pinky splay
108 // for pinky joints
109 {"(AAA)", OPENGLOVES_ALPHA_ENCODING_FinJointThumb0}, // thumb joint 0
110 {"(AAB)", OPENGLOVES_ALPHA_ENCODING_FinJointThumb1}, // thumb joint 1
111 {"(AAC)", OPENGLOVES_ALPHA_ENCODING_FinJointThumb2}, // thumb joint 2
112 {"(AAD)", OPENGLOVES_ALPHA_ENCODING_FinJointThumb3},
113 {"(BAA)", OPENGLOVES_ALPHA_ENCODING_FinJointIndex0}, // index joint 0
114 {"(BAB)", OPENGLOVES_ALPHA_ENCODING_FinJointIndex1}, // index joint 1
115 {"(BAC)", OPENGLOVES_ALPHA_ENCODING_FinJointIndex2}, // index joint 2
116 {"(BAD)", OPENGLOVES_ALPHA_ENCODING_FinJointIndex3}, // index joint 3
117 {"(CAA)", OPENGLOVES_ALPHA_ENCODING_FinJointMiddle0}, // middle joint 0
118 {"(CAB)", OPENGLOVES_ALPHA_ENCODING_FinJointMiddle1}, // middle joint 1
119 {"(CAC)", OPENGLOVES_ALPHA_ENCODING_FinJointMiddle2}, // middle joint 2
120 {"(CAD)", OPENGLOVES_ALPHA_ENCODING_FinJointMiddle3}, // middle joint 3
121 {"(DAA)", OPENGLOVES_ALPHA_ENCODING_FinJointRing0}, // ring joint 0
122 {"(DAB)", OPENGLOVES_ALPHA_ENCODING_FinJointRing1}, // ring joint 1
123 {"(DAC)", OPENGLOVES_ALPHA_ENCODING_FinJointRing2}, // ring joint 2
124 {"(DAD)", OPENGLOVES_ALPHA_ENCODING_FinJointRing3}, // ring joint 3
125 {"(EAA)", OPENGLOVES_ALPHA_ENCODING_FinJointPinky0}, // pinky joint 0
126 {"(EAB)", OPENGLOVES_ALPHA_ENCODING_FinJointPinky1}, // pinky joint 1
127 {"(EAC)", OPENGLOVES_ALPHA_ENCODING_FinJointPinky2}, // pinky joint 2
128 {"(EAD)", OPENGLOVES_ALPHA_ENCODING_FinJointPinky3}, // pinky joint 3
129 {"F", OPENGLOVES_ALPHA_ENCODING_JoyX}, // joystick x component
130 {"G", OPENGLOVES_ALPHA_ENCODING_JoyY}, // joystick y component
131 {"H", OPENGLOVES_ALPHA_ENCODING_JoyBtn}, // joystick button
132 {"I", OPENGLOVES_ALPHA_ENCODING_BtnTrg}, // trigger button
133 {"J", OPENGLOVES_ALPHA_ENCODING_BtnA}, // A button
134 {"K", OPENGLOVES_ALPHA_ENCODING_BtnB}, // B button
135 {"L", OPENGLOVES_ALPHA_ENCODING_GesGrab}, // grab gesture (boolean)
136 {"M", OPENGLOVES_ALPHA_ENCODING_GesPinch}, // pinch gesture (boolean)
137 {"N", OPENGLOVES_ALPHA_ENCODING_BtnMenu}, // system button pressed (opens SteamVR menu)
138 {"O", OPENGLOVES_ALPHA_ENCODING_BtnCalib}, // calibration button
139 {"P", OPENGLOVES_ALPHA_ENCODING_TrgValue}, // analog trigger value
140 {"", OPENGLOVES_ALPHA_ENCODING_MAX} // Junk key
141};
142
143static const std::map<int, std::string> opengloves_alpha_encoding_output_key_string{
144 {OPENGLOVES_ALPHA_ENCODING_FinThumb, "A"}, // thumb force feedback
145 {OPENGLOVES_ALPHA_ENCODING_FinIndex, "B"}, // index force feedback
146 {OPENGLOVES_ALPHA_ENCODING_FinMiddle, "C"}, // middle force feedback
147 {OPENGLOVES_ALPHA_ENCODING_FinRing, "D"}, // ring force feedback
148 {OPENGLOVES_ALPHA_ENCODING_FinPinky, "E"}, // pinky force feedback
149};
150
151static std::map<int, std::string>
152opengloves_alpha_encoding_parse_to_map(const std::string &str)
153{
154 std::map<int, std::string> result;
155
156 size_t i = 0;
157 while (i < str.length()) {
158 // Advance until we get an alphabetic character (no point in looking at values that don't have a key
159 // associated with them)
160
161 if (str[i] >= 0 && opengloves_alpha_encoding_is_key_character(str[i])) {
162 std::string key = {str[i]};
163 i++;
164
165 // we're going to be parsing a "long key", i.e. (AB) for thumb finger splay. Long keys must
166 // always be enclosed in brackets
167 if (key[0] == '(') {
168 while (str[i] >= 0 && opengloves_alpha_encoding_is_key_character(str[i]) &&
169 i < str.length()) {
170 key += str[i];
171 i++;
172 }
173 }
174
175 std::string value;
176 while (str[i] >= 0 && isdigit(str[i]) && i < str.length()) {
177 value += str[i];
178 i++;
179 }
180
181 // Even if the value is empty we still want to use the key, it means that we have a button that
182 // is pressed (it only appears in the packet if it is)
183 if (opengloves_alpha_encoding_input_key_string.find(key) !=
184 opengloves_alpha_encoding_input_key_string.end())
185 result.insert_or_assign(opengloves_alpha_encoding_input_key_string.at(key), value);
186 else
187 U_LOG_W("Unable to insert key: %s into input map as it was not found", key.c_str());
188 } else
189 i++;
190 }
191
192 return result;
193}
194
195
196void
197opengloves_alpha_encoding_decode(const char *data, struct opengloves_input *out)
198{
199 std::map<int, std::string> input_map = opengloves_alpha_encoding_parse_to_map(data);
200
201 try {
202 // five fingers, 2 (curl + splay)
203 for (int i = 0; i < 5; i++) {
204 int enum_position = i * 2;
205 // curls
206 if (input_map.find(enum_position) != input_map.end()) {
207 float fin_curl_value = std::stof(input_map.at(enum_position));
208 std::fill(std::begin(out->flexion[i]), std::begin(out->flexion[i]) + 4,
209 fin_curl_value / OPENGLOVES_ENCODING_MAX_ANALOG_VALUE);
210 }
211
212 // splay
213 if (input_map.find(enum_position + 1) != input_map.end())
214 out->splay[i] =
215 (std::stof(input_map.at(enum_position + 1)) / OPENGLOVES_ENCODING_MAX_ANALOG_VALUE -
216 0.5f) *
217 2.0f;
218 }
219
220 int current_finger_joint = OPENGLOVES_ALPHA_ENCODING_FinJointThumb0;
221 for (int i = 0; i < 5; i++) {
222 for (int j = 0; j < 4; j++) {
223 // individual joint curls
224 out->flexion[i][j] = input_map.find(current_finger_joint) != input_map.end()
225 ? (std::stof(input_map.at(current_finger_joint)) /
226 OPENGLOVES_ENCODING_MAX_ANALOG_VALUE)
227 // use the curl of the previous joint
228 : out->flexion[i][j > 0 ? j - 1 : 0];
229 current_finger_joint++;
230 }
231 }
232
233 // joysticks
234 if (input_map.find(OPENGLOVES_ALPHA_ENCODING_JoyX) != input_map.end())
235 out->joysticks.main.x = 2 * std::stof(input_map.at(OPENGLOVES_ALPHA_ENCODING_JoyX)) /
236 OPENGLOVES_ENCODING_MAX_ANALOG_VALUE -
237 1;
238 if (input_map.find(OPENGLOVES_ALPHA_ENCODING_JoyY) != input_map.end())
239 out->joysticks.main.y = 2 * std::stof(input_map.at(OPENGLOVES_ALPHA_ENCODING_JoyY)) /
240 OPENGLOVES_ENCODING_MAX_ANALOG_VALUE -
241 1;
242 out->joysticks.main.pressed = input_map.find(OPENGLOVES_ALPHA_ENCODING_JoyBtn) != input_map.end();
243
244 } catch (std::invalid_argument &e) {
245 U_LOG_E("Error parsing input string: %s", e.what());
246 }
247
248 if (input_map.find(OPENGLOVES_ALPHA_ENCODING_TrgValue) != input_map.end())
249 out->buttons.trigger.value =
250 std::stof(input_map.at(OPENGLOVES_ALPHA_ENCODING_TrgValue)) / OPENGLOVES_ENCODING_MAX_ANALOG_VALUE;
251 out->buttons.trigger.pressed = input_map.find(OPENGLOVES_ALPHA_ENCODING_BtnTrg) != input_map.end();
252
253 out->buttons.A.pressed = input_map.find(OPENGLOVES_ALPHA_ENCODING_BtnA) != input_map.end();
254 out->buttons.B.pressed = input_map.find(OPENGLOVES_ALPHA_ENCODING_BtnB) != input_map.end();
255 out->gestures.grab.activated = input_map.find(OPENGLOVES_ALPHA_ENCODING_GesGrab) != input_map.end();
256 out->gestures.pinch.activated = input_map.find(OPENGLOVES_ALPHA_ENCODING_GesPinch) != input_map.end();
257 out->buttons.menu.pressed = input_map.find(OPENGLOVES_ALPHA_ENCODING_BtnMenu) != input_map.end();
258}
259
260void
261opengloves_alpha_encoding_encode(const struct opengloves_output *output, char *out_buff)
262{
263 sprintf(out_buff, "%s%d%s%d%s%d%s%d%s%d\n",
264 opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinThumb).c_str(),
265 (int)(output->force_feedback.thumb * 1000),
266 opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinIndex).c_str(),
267 (int)(output->force_feedback.index * 1000),
268 opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinMiddle).c_str(),
269 (int)(output->force_feedback.middle * 1000),
270 opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinRing).c_str(),
271 (int)(output->force_feedback.ring * 1000),
272 opengloves_alpha_encoding_output_key_string.at(OPENGLOVES_ALPHA_ENCODING_FinPinky).c_str(),
273 (int)(output->force_feedback.little * 1000));
274}