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 <mgba/core/input.h>
7
8#include <mgba-util/configuration.h>
9#include <mgba-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->highDirection];
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 if (!map->info->keyId[i]) {
276 continue;
277 }
278 _saveKey(map, type, sectionName, config, i, map->info->keyId[i]);
279 _clearAxis(sectionName, config, map->info->keyId[i]);
280 }
281
282 const struct mInputMapImpl* impl = _lookupMapConst(map, type);
283 if (!impl) {
284 return;
285 }
286 struct mInputAxisSave save = {
287 config,
288 sectionName,
289 map->info
290 };
291 TableEnumerate(&impl->axes, _saveAxis, &save);
292}
293
294void mInputMapInit(struct mInputMap* map, const struct mInputPlatformInfo* info) {
295 map->maps = 0;
296 map->numMaps = 0;
297 map->info = info;
298}
299
300void mInputMapDeinit(struct mInputMap* map) {
301 size_t m;
302 for (m = 0; m < map->numMaps; ++m) {
303 if (map->maps[m].type) {
304 free(map->maps[m].map);
305 TableDeinit(&map->maps[m].axes);
306 }
307 }
308 free(map->maps);
309 map->maps = 0;
310 map->numMaps = 0;
311}
312
313int mInputMapKey(const struct mInputMap* map, uint32_t type, int key) {
314 size_t m;
315 const struct mInputMapImpl* impl = _lookupMapConst(map, type);
316 if (!impl || !impl->map) {
317 return -1;
318 }
319
320 for (m = 0; m < map->info->nKeys; ++m) {
321 if (impl->map[m] == key) {
322 return m;
323 }
324 }
325 return -1;
326}
327
328int mInputMapKeyBits(const struct mInputMap* map, uint32_t type, uint32_t bits, unsigned offset) {
329 int keys = 0;
330 for (; bits; bits >>= 1, ++offset) {
331 if (bits & 1) {
332 int key = mInputMapKey(map, type, offset);
333 if (key == -1) {
334 continue;
335 }
336 keys |= 1 << key;
337 }
338 }
339 return keys;
340}
341
342void mInputBindKey(struct mInputMap* map, uint32_t type, int key, int input) {
343 struct mInputMapImpl* impl = _guaranteeMap(map, type);
344 if (input < 0 || (size_t) input >= map->info->nKeys) {
345 return;
346 }
347 mInputUnbindKey(map, type, input);
348 impl->map[input] = key;
349}
350
351void mInputUnbindKey(struct mInputMap* map, uint32_t type, int input) {
352 struct mInputMapImpl* impl = _lookupMap(map, type);
353 if (input < 0 || (size_t) input >= map->info->nKeys) {
354 return;
355 }
356 if (impl) {
357 impl->map[input] = -1;
358 }
359}
360
361int mInputQueryBinding(const struct mInputMap* map, uint32_t type, int input) {
362 if (input < 0 || (size_t) input >= map->info->nKeys) {
363 return -1;
364 }
365
366 const struct mInputMapImpl* impl = _lookupMapConst(map, type);
367 if (!impl || !impl->map) {
368 return -1;
369 }
370
371 return impl->map[input];
372}
373
374int mInputMapAxis(const struct mInputMap* map, uint32_t type, int axis, int value) {
375 const struct mInputMapImpl* impl = _lookupMapConst(map, type);
376 if (!impl) {
377 return -1;
378 }
379 struct mInputAxis* description = TableLookup(&impl->axes, axis);
380 if (!description) {
381 return -1;
382 }
383 int state = 0;
384 if (value < description->deadLow) {
385 state = -1;
386 } else if (value > description->deadHigh) {
387 state = 1;
388 }
389 if (state > 0) {
390 return description->highDirection;
391 }
392 if (state < 0) {
393 return description->lowDirection;
394 }
395 return -1;
396}
397
398int mInputClearAxis(const struct mInputMap* map, uint32_t type, int axis, int keys) {
399 const struct mInputMapImpl* impl = _lookupMapConst(map, type);
400 if (!impl) {
401 return keys;
402 }
403 struct mInputAxis* description = TableLookup(&impl->axes, axis);
404 if (!description) {
405 return keys;
406 }
407 return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection));
408}
409
410void mInputBindAxis(struct mInputMap* map, uint32_t type, int axis, const struct mInputAxis* description) {
411 struct mInputMapImpl* impl = _guaranteeMap(map, type);
412 struct mInputAxis d2 = *description;
413 TableEnumerate(&impl->axes, _unbindAxis, &d2.highDirection);
414 TableEnumerate(&impl->axes, _unbindAxis, &d2.lowDirection);
415 struct mInputAxis* dup = malloc(sizeof(struct mInputAxis));
416 *dup = *description;
417 TableInsert(&impl->axes, axis, dup);
418}
419
420void mInputUnbindAxis(struct mInputMap* map, uint32_t type, int axis) {
421 struct mInputMapImpl* impl = _lookupMap(map, type);
422 if (impl) {
423 TableRemove(&impl->axes, axis);
424 }
425}
426
427void mInputUnbindAllAxes(struct mInputMap* map, uint32_t type) {
428 struct mInputMapImpl* impl = _lookupMap(map, type);
429 if (impl) {
430 TableClear(&impl->axes);
431 }
432}
433
434const struct mInputAxis* mInputQueryAxis(const struct mInputMap* map, uint32_t type, int axis) {
435 const struct mInputMapImpl* impl = _lookupMapConst(map, type);
436 if (!impl) {
437 return 0;
438 }
439 return TableLookup(&impl->axes, axis);
440}
441
442void mInputEnumerateAxes(const struct mInputMap* map, uint32_t type, void (handler(int axis, const struct mInputAxis* description, void* user)), void* user) {
443 const struct mInputMapImpl* impl = _lookupMapConst(map, type);
444 if (!impl) {
445 return;
446 }
447 struct mInputAxisEnumerate enumUser = {
448 handler,
449 user
450 };
451 TableEnumerate(&impl->axes, _enumerateAxis, &enumUser);
452}
453
454void mInputMapLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config) {
455 char sectionName[SECTION_NAME_MAX];
456 _makeSectionName(map->info->platformName, sectionName, SECTION_NAME_MAX, type);
457 _loadAll(map, type, sectionName, config);
458}
459
460void mInputMapSave(const struct mInputMap* map, uint32_t type, struct Configuration* config) {
461 char sectionName[SECTION_NAME_MAX];
462 _makeSectionName(map->info->platformName, sectionName, SECTION_NAME_MAX, type);
463 _saveAll(map, type, sectionName, config);
464}
465
466bool mInputProfileLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config, const char* profile) {
467 char sectionName[SECTION_NAME_MAX];
468 snprintf(sectionName, SECTION_NAME_MAX, "%s.input-profile.%s", map->info->platformName, profile);
469 sectionName[SECTION_NAME_MAX - 1] = '\0';
470 return _loadAll(map, type, sectionName, config);
471}
472
473void mInputProfileSave(const struct mInputMap* map, uint32_t type, struct Configuration* config, const char* profile) {
474 char sectionName[SECTION_NAME_MAX];
475 snprintf(sectionName, SECTION_NAME_MAX, "%s.input-profile.%s", map->info->platformName, profile);
476 sectionName[SECTION_NAME_MAX - 1] = '\0';
477 _saveAll(map, type, sectionName, config);
478}
479
480const char* mInputGetPreferredDevice(const struct Configuration* config, const char* platformName, uint32_t type, int playerId) {
481 char sectionName[SECTION_NAME_MAX];
482 _makeSectionName(platformName, sectionName, SECTION_NAME_MAX, type);
483
484 char deviceId[KEY_NAME_MAX];
485 snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
486 return ConfigurationGetValue(config, sectionName, deviceId);
487}
488
489void mInputSetPreferredDevice(struct Configuration* config, const char* platformName, uint32_t type, int playerId, const char* deviceName) {
490 char sectionName[SECTION_NAME_MAX];
491 _makeSectionName(platformName, sectionName, SECTION_NAME_MAX, type);
492
493 char deviceId[KEY_NAME_MAX];
494 snprintf(deviceId, sizeof(deviceId), "device%i", playerId);
495 return ConfigurationSetValue(config, sectionName, deviceId, deviceName);
496}
497
498const char* mInputGetCustomValue(const struct Configuration* config, const char* platformName, uint32_t type, const char* key, const char* profile) {
499 char sectionName[SECTION_NAME_MAX];
500 if (profile) {
501 snprintf(sectionName, SECTION_NAME_MAX, "%s.input-profile.%s", platformName, profile);
502 const char* value = ConfigurationGetValue(config, sectionName, key);
503 if (value) {
504 return value;
505 }
506 }
507 _makeSectionName(platformName, sectionName, SECTION_NAME_MAX, type);
508 return ConfigurationGetValue(config, sectionName, key);
509}
510
511void mInputSetCustomValue(struct Configuration* config, const char* platformName, uint32_t type, const char* key, const char* value, const char* profile) {
512 char sectionName[SECTION_NAME_MAX];
513 if (profile) {
514 snprintf(sectionName, SECTION_NAME_MAX, "%s.input-profile.%s", platformName, profile);
515 ConfigurationSetValue(config, sectionName, key, value);
516 }
517 _makeSectionName(platformName, sectionName, SECTION_NAME_MAX, type);
518 ConfigurationSetValue(config, sectionName, key, value);
519}