all repos — mgba @ 6acc14d31b5faf681b8d67593fea42932f701bfa

mGBA Game Boy Advance Emulator

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}