src/core/input.c (view raw)
1/* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "input.h"
7
8#include "util/configuration.h"
9#include "util/table.h"
10
11#include <inttypes.h>
12
13#define SECTION_NAME_MAX 128
14#define KEY_NAME_MAX 32
15#define KEY_VALUE_MAX 16
16#define AXIS_INFO_MAX 12
17
18struct mInputMapImpl {
19 int* map;
20 uint32_t type;
21
22 struct Table axes;
23};
24
25struct mInputAxisSave {
26 struct Configuration* config;
27 const char* sectionName;
28 const struct mInputPlatformInfo* info;
29};
30
31struct mInputAxisEnumerate {
32 void (*handler)(int axis, const struct mInputAxis* description, void* user);
33 void* user;
34};
35
36static void _makeSectionName(const char* platform, char* sectionName, size_t len, uint32_t type) {
37 snprintf(sectionName, len, "%s.input.%c%c%c%c", platform, type >> 24, type >> 16, type >> 8, type);
38 sectionName[len - 1] = '\0';
39}
40
41static bool _getIntValue(const struct Configuration* config, const char* section, const char* key, int* value) {
42 const char* strValue = ConfigurationGetValue(config, section, key);
43 if (!strValue) {
44 return false;
45 }
46 char* end;
47 long intValue = strtol(strValue, &end, 10);
48 if (*end) {
49 return false;
50 }
51 *value = intValue;
52 return true;
53}
54
55static struct mInputMapImpl* _lookupMap(struct mInputMap* map, uint32_t type) {
56 size_t m;
57 struct mInputMapImpl* impl = 0;
58 for (m = 0; m < map->numMaps; ++m) {
59 if (map->maps[m].type == type) {
60 impl = &map->maps[m];
61 break;
62 }
63 }
64 return impl;
65}
66
67static const struct mInputMapImpl* _lookupMapConst(const struct mInputMap* map, uint32_t type) {
68 size_t m;
69 const struct mInputMapImpl* impl = 0;
70 for (m = 0; m < map->numMaps; ++m) {
71 if (map->maps[m].type == type) {
72 impl = &map->maps[m];
73 break;
74 }
75 }
76 return impl;
77}
78
79static struct mInputMapImpl* _guaranteeMap(struct mInputMap* map, uint32_t type) {
80 struct mInputMapImpl* impl = 0;
81 if (map->numMaps == 0) {
82 map->maps = malloc(sizeof(*map->maps));
83 map->numMaps = 1;
84 impl = &map->maps[0];
85 impl->type = type;
86 impl->map = malloc(map->info->nKeys * sizeof(int));
87 size_t i;
88 for (i = 0; i < map->info->nKeys; ++i) {
89 impl->map[i] = -1;
90 }
91 TableInit(&impl->axes, 2, free);
92 } else {
93 impl = _lookupMap(map, type);
94 }
95 if (!impl) {
96 size_t m;
97 for (m = 0; m < map->numMaps; ++m) {
98 if (!map->maps[m].type) {
99 impl = &map->maps[m];
100 break;
101 }
102 }
103 if (impl) {
104 impl->type = type;
105 impl->map = malloc(map->info->nKeys * sizeof(int));
106 size_t i;
107 for (i = 0; i < map->info->nKeys; ++i) {
108 impl->map[i] = -1;
109 }
110 } else {
111 map->maps = realloc(map->maps, sizeof(*map->maps) * map->numMaps * 2);
112 for (m = map->numMaps * 2 - 1; m > map->numMaps; --m) {
113 map->maps[m].type = 0;
114 map->maps[m].map = 0;
115 }
116 map->numMaps *= 2;
117 impl = &map->maps[m];
118 impl->type = type;
119 impl->map = malloc(map->info->nKeys * sizeof(int));
120 size_t i;
121 for (i = 0; i < map->info->nKeys; ++i) {
122 impl->map[i] = -1;
123 }
124 }
125 TableInit(&impl->axes, 2, free);
126 }
127 return impl;
128}
129
130static void _loadKey(struct mInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, int key, const char* keyName) {
131 char keyKey[KEY_NAME_MAX];
132 snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
133 keyKey[KEY_NAME_MAX - 1] = '\0';
134
135 int value;
136 if (!_getIntValue(config, sectionName, keyKey, &value)) {
137 return;
138 }
139 mInputBindKey(map, type, value, key);
140}
141
142static void _loadAxis(struct mInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, int direction, const char* axisName) {
143 char axisKey[KEY_NAME_MAX];
144 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
145 axisKey[KEY_NAME_MAX - 1] = '\0';
146 int value;
147 if (!_getIntValue(config, sectionName, axisKey, &value)) {
148 return;
149 }
150
151 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName);
152 axisKey[KEY_NAME_MAX - 1] = '\0';
153 int axis;
154 const char* strValue = ConfigurationGetValue(config, sectionName, axisKey);
155 if (!strValue || !strValue[0]) {
156 return;
157 }
158 char* end;
159 axis = strtoul(&strValue[1], &end, 10);
160 if (*end) {
161 return;
162 }
163
164 const struct mInputAxis* description = mInputQueryAxis(map, type, axis);
165 struct mInputAxis realDescription = { -1, -1, 0, 0 };
166 if (description) {
167 realDescription = *description;
168 }
169 if (strValue[0] == '+') {
170 realDescription.deadHigh = value;
171 realDescription.highDirection = direction;
172 } else if (strValue[0] == '-') {
173 realDescription.deadLow = value;
174 realDescription.lowDirection = direction;
175 }
176 mInputBindAxis(map, type, axis, &realDescription);
177}
178
179static void _saveKey(const struct mInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config, int key, const char* keyName) {
180 char keyKey[KEY_NAME_MAX];
181 snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);
182 keyKey[KEY_NAME_MAX - 1] = '\0';
183
184 int value = mInputQueryBinding(map, type, key);
185 char keyValue[KEY_VALUE_MAX];
186 snprintf(keyValue, KEY_VALUE_MAX, "%" PRIi32, value);
187
188 ConfigurationSetValue(config, sectionName, keyKey, keyValue);
189}
190
191static void _clearAxis(const char* sectionName, struct Configuration* config, const char* axisName) {
192 char axisKey[KEY_NAME_MAX];
193 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName);
194 axisKey[KEY_NAME_MAX - 1] = '\0';
195 ConfigurationClearValue(config, sectionName, axisKey);
196
197 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName);
198 axisKey[KEY_NAME_MAX - 1] = '\0';
199 ConfigurationClearValue(config, sectionName, axisKey);
200}
201
202static void _saveAxis(uint32_t axis, void* dp, void* up) {
203 struct mInputAxisSave* user = up;
204 const struct mInputAxis* description = dp;
205
206 const char* sectionName = user->sectionName;
207
208 if (description->lowDirection != -1) {
209 const char* keyName = user->info->keyId[description->lowDirection];
210
211 char axisKey[KEY_NAME_MAX];
212 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
213 axisKey[KEY_NAME_MAX - 1] = '\0';
214 ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadLow);
215
216 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
217 axisKey[KEY_NAME_MAX - 1] = '\0';
218
219 char axisInfo[AXIS_INFO_MAX];
220 snprintf(axisInfo, AXIS_INFO_MAX, "-%u", axis);
221 axisInfo[AXIS_INFO_MAX - 1] = '\0';
222 ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
223 }
224 if (description->highDirection != -1) {
225 const char* keyName = user->info->keyId[description->lowDirection];
226
227 char axisKey[KEY_NAME_MAX];
228 snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", keyName);
229 axisKey[KEY_NAME_MAX - 1] = '\0';
230 ConfigurationSetIntValue(user->config, sectionName, axisKey, description->deadHigh);
231
232 snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", keyName);
233 axisKey[KEY_NAME_MAX - 1] = '\0';
234
235 char axisInfo[AXIS_INFO_MAX];
236 snprintf(axisInfo, AXIS_INFO_MAX, "+%u", axis);
237 axisInfo[AXIS_INFO_MAX - 1] = '\0';
238 ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
239 }
240}
241
242void _enumerateAxis(uint32_t axis, void* dp, void* ep) {
243 struct mInputAxisEnumerate* enumUser = ep;
244 const struct mInputAxis* description = dp;
245 enumUser->handler(axis, description, enumUser->user);
246}
247
248void _unbindAxis(uint32_t axis, void* dp, void* user) {
249 UNUSED(axis);
250 int* key = user;
251 struct mInputAxis* description = dp;
252 if (description->highDirection == *key) {
253 description->highDirection = -1;
254 }
255 if (description->lowDirection == *key) {
256 description->lowDirection = -1;
257 }
258}
259
260static bool _loadAll(struct mInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) {
261 if (!ConfigurationHasSection(config, sectionName)) {
262 return false;
263 }
264 size_t i;
265 for (i = 0; i < map->info->nKeys; ++i) {
266 _loadKey(map, type, sectionName, config, i, map->info->keyId[i]);
267 _loadAxis(map, type, sectionName, config, i, map->info->keyId[i]);
268 }
269 return true;
270}
271
272static void _saveAll(const struct mInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config) {
273 size_t i;
274 for (i = 0; i < map->info->nKeys; ++i) {
275 _saveKey(map, type, sectionName, config, i, map->info->keyId[i]);
276 _clearAxis(sectionName, config, map->info->keyId[i]);
277 }
278
279 const struct mInputMapImpl* impl = _lookupMapConst(map, type);
280 if (!impl) {
281 return;
282 }
283 struct mInputAxisSave save = {
284 config,
285 sectionName,
286 map->info
287 };
288 TableEnumerate(&impl->axes, _saveAxis, &save);
289}
290
291void mInputMapInit(struct mInputMap* map, const struct mInputPlatformInfo* info) {
292 map->maps = 0;
293 map->numMaps = 0;
294 map->info = info;
295}
296
297void mInputMapDeinit(struct mInputMap* map) {
298 size_t m;
299 for (m = 0; m < map->numMaps; ++m) {
300 if (map->maps[m].type) {
301 free(map->maps[m].map);
302 TableDeinit(&map->maps[m].axes);
303 }
304 }
305 free(map->maps);
306 map->maps = 0;
307 map->numMaps = 0;
308}
309
310int mInputMapKey(const struct mInputMap* map, uint32_t type, int key) {
311 size_t m;
312 const struct mInputMapImpl* impl = _lookupMapConst(map, type);
313 if (!impl || !impl->map) {
314 return -1;
315 }
316
317 for (m = 0; m < map->info->nKeys; ++m) {
318 if (impl->map[m] == key) {
319 return m;
320 }
321 }
322 return -1;
323}
324
325int mInputMapKeyBits(const struct mInputMap* map, uint32_t type, uint32_t bits, unsigned offset) {
326 int keys = 0;
327 for (; bits; bits >>= 1, ++offset) {
328 if (bits & 1) {
329 int key = mInputMapKey(map, type, offset);
330 if (key == -1) {
331 continue;
332 }
333 keys |= 1 << key;
334 }
335 }
336 return keys;
337}
338
339void mInputBindKey(struct mInputMap* map, uint32_t type, int key, int input) {
340 struct mInputMapImpl* impl = _guaranteeMap(map, type);
341 mInputUnbindKey(map, type, input);
342 impl->map[input] = key;
343}
344
345void mInputUnbindKey(struct mInputMap* map, uint32_t type, int input) {
346 struct mInputMapImpl* impl = _lookupMap(map, type);
347 if (input < 0 || (size_t) input >= map->info->nKeys) {
348 return;
349 }
350 if (impl) {
351 impl->map[input] = -1;
352 }
353}
354
355int mInputQueryBinding(const struct mInputMap* map, uint32_t type, int input) {
356 if (input < 0 || (size_t) input >= map->info->nKeys) {
357 return -1;
358 }
359
360 const struct mInputMapImpl* impl = _lookupMapConst(map, type);
361 if (!impl || !impl->map) {
362 return -1;
363 }
364
365 return impl->map[input];
366}
367
368int mInputMapAxis(const struct mInputMap* map, uint32_t type, int axis, int value) {
369 const struct mInputMapImpl* impl = _lookupMapConst(map, type);
370 if (!impl) {
371 return -1;
372 }
373 struct mInputAxis* description = TableLookup(&impl->axes, axis);
374 if (!description) {
375 return -1;
376 }
377 int state = 0;
378 if (value < description->deadLow) {
379 state = -1;
380 } else if (value > description->deadHigh) {
381 state = 1;
382 }
383 if (state > 0) {
384 return description->highDirection;
385 }
386 if (state < 0) {
387 return description->lowDirection;
388 }
389 return -1;
390}
391
392int mInputClearAxis(const struct mInputMap* map, uint32_t type, int axis, int keys) {
393 const struct mInputMapImpl* impl = _lookupMapConst(map, type);
394 if (!impl) {
395 return keys;
396 }
397 struct mInputAxis* description = TableLookup(&impl->axes, axis);
398 if (!description) {
399 return keys;
400 }
401 return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
402}
403
404void mInputBindAxis(struct mInputMap* map, uint32_t type, int axis, const struct mInputAxis* description) {
405 struct mInputMapImpl* impl = _guaranteeMap(map, type);
406 struct mInputAxis d2 = *description;
407 TableEnumerate(&impl->axes, _unbindAxis, &d2.highDirection);
408 TableEnumerate(&impl->axes, _unbindAxis, &d2.lowDirection);
409 struct mInputAxis* dup = malloc(sizeof(struct mInputAxis));
410 *dup = *description;
411 TableInsert(&impl->axes, axis, dup);
412}
413
414void mInputUnbindAxis(struct mInputMap* map, uint32_t type, int axis) {
415 struct mInputMapImpl* impl = _lookupMap(map, type);
416 if (impl) {
417 TableRemove(&impl->axes, axis);
418 }
419}
420
421void mInputUnbindAllAxes(struct mInputMap* map, uint32_t type) {
422 struct mInputMapImpl* impl = _lookupMap(map, type);
423 if (impl) {
424 TableClear(&impl->axes);
425 }
426}
427
428const struct mInputAxis* mInputQueryAxis(const struct mInputMap* map, uint32_t type, int axis) {
429 const struct mInputMapImpl* impl = _lookupMapConst(map, type);
430 if (!impl) {
431 return 0;
432 }
433 return TableLookup(&impl->axes, axis);
434}
435
436void mInputEnumerateAxes(const struct mInputMap* map, uint32_t type, void (handler(int axis, const struct mInputAxis* description, void* user)), void* user) {
437 const struct mInputMapImpl* impl = _lookupMapConst(map, type);
438 if (!impl) {
439 return;
440 }
441 struct mInputAxisEnumerate enumUser = {
442 handler,
443 user
444 };
445 TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
446}
447
448void mInputMapLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config) {
449 char sectionName[SECTION_NAME_MAX];
450 _makeSectionName(map->info->platformName, sectionName, SECTION_NAME_MAX, type);
451 _loadAll(map, type, sectionName, config);
452}
453
454void mInputMapSave(const struct mInputMap* map, uint32_t type, struct Configuration* config) {
455 char sectionName[SECTION_NAME_MAX];
456 _makeSectionName(map->info->platformName, sectionName, SECTION_NAME_MAX, type);
457 _saveAll(map, type, sectionName, config);
458}
459
460bool mInputProfileLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) {
461 char sectionName[SECTION_NAME_MAX];
462 snprintf(sectionName, SECTION_NAME_MAX, "%s.input-profile.%s", map->info->platformName, profile);
463 sectionName[SECTION_NAME_MAX - 1] = '\0';
464 return _loadAll(map, type, sectionName, config);
465}
466
467void mInputProfileSave(const struct mInputMap* map, uint32_t type, struct Configuration* config, const char* profile) {
468 char sectionName[SECTION_NAME_MAX];
469 snprintf(sectionName, SECTION_NAME_MAX, "%s.input-profile.%s", map->info->platformName, profile);
470 sectionName[SECTION_NAME_MAX - 1] = '\0';
471 _saveAll(map, type, sectionName, config);
472}
473
474const char* mInputGetPreferredDevice(const struct Configuration* config, const char* platformName, uint32_t type, int playerId) {
475 char sectionName[SECTION_NAME_MAX];
476 _makeSectionName(platformName, sectionName, SECTION_NAME_MAX, type);
477
478 char deviceId[KEY_NAME_MAX];
479 snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
480 return ConfigurationGetValue(config, sectionName, deviceId);
481}
482
483void mInputSetPreferredDevice(struct Configuration* config, const char* platformName, uint32_t type, int playerId, const char* deviceName) {
484 char sectionName[SECTION_NAME_MAX];
485 _makeSectionName(platformName, sectionName, SECTION_NAME_MAX, type);
486
487 char deviceId[KEY_NAME_MAX];
488 snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
489 return ConfigurationSetValue(config, sectionName, deviceId, deviceName);
490}
491
492const char* mInputGetCustomValue(const struct Configuration* config, const char* platformName, uint32_t type, const char* key, const char* profile) {
493 char sectionName[SECTION_NAME_MAX];
494 if (profile) {
495 snprintf(sectionName, SECTION_NAME_MAX, "%s.input-profile.%s", platformName, profile);
496 const char* value = ConfigurationGetValue(config, sectionName, key);
497 if (value) {
498 return value;
499 }
500 }
501 _makeSectionName(platformName, sectionName, SECTION_NAME_MAX, type);
502 return ConfigurationGetValue(config, sectionName, key);
503}
504
505void mInputSetCustomValue(struct Configuration* config, const char* platformName, uint32_t type, const char* key, const char* value, const char* profile) {
506 char sectionName[SECTION_NAME_MAX];
507 if (profile) {
508 snprintf(sectionName, SECTION_NAME_MAX, "%s.input-profile.%s", platformName, profile);
509 ConfigurationSetValue(config, sectionName, key, value);
510 }
511 _makeSectionName(platformName, sectionName, SECTION_NAME_MAX, type);
512 ConfigurationSetValue(config, sectionName, key, value);
513}