all repos — mgba @ 6a188289c209a3c905dc5209640904a91165c879

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 <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}