diff --git a/src/gba/gba-input.c b/src/gba/gba-input.c index 6b9df8ae7..3712d21d7 100644 --- a/src/gba/gba-input.c +++ b/src/gba/gba-input.c @@ -6,6 +6,7 @@ #include "gba-input.h" #include "util/configuration.h" +#include "util/table.h" #include @@ -16,8 +17,86 @@ struct GBAInputMapImpl { int* map; uint32_t type; + + struct Table axes; }; +static bool _getIntValue(const struct Configuration* config, const char* section, const char* key, int* value) { + const char* strValue = ConfigurationGetValue(config, section, key); + if (!strValue) { + return false; + } + char* end; + long intValue = strtol(strValue, &end, 10); + if (*end) { + return false; + } + *value = intValue; + return true; +} + +static struct GBAInputMapImpl* _lookupMap(struct GBAInputMap* map, uint32_t type) { + size_t m; + struct GBAInputMapImpl* impl = 0; + for (m = 0; m < map->numMaps; ++m) { + if (map->maps[m].type == type) { + impl = &map->maps[m]; + break; + } + } + return impl; +} + +static const struct GBAInputMapImpl* _lookupMapConst(const struct GBAInputMap* map, uint32_t type) { + size_t m; + const struct GBAInputMapImpl* impl = 0; + for (m = 0; m < map->numMaps; ++m) { + if (map->maps[m].type == type) { + impl = &map->maps[m]; + break; + } + } + return impl; +} + +static struct GBAInputMapImpl* _guaranteeMap(struct GBAInputMap* map, uint32_t type) { + struct GBAInputMapImpl* impl = 0; + if (map->numMaps == 0) { + map->maps = malloc(sizeof(*map->maps)); + map->numMaps = 1; + impl = &map->maps[0]; + impl->type = type; + impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey)); + } else { + impl = _lookupMap(map, type); + } + if (!impl) { + size_t m; + for (m = 0; m < map->numMaps; ++m) { + if (!map->maps[m].type) { + impl = &map->maps[m]; + break; + } + } + if (impl) { + impl->type = type; + impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey)); + } else { + map->maps = realloc(map->maps, sizeof(*map->maps) * map->numMaps * 2); + for (m = map->numMaps * 2 - 1; m > map->numMaps; --m) { + map->maps[m].type = 0; + map->maps[m].map = 0; + } + map->numMaps *= 2; + impl = &map->maps[m]; + impl->type = type; + impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey)); + } + } + TableInit(&impl->axes, 2, free); + return impl; +} + static void _loadKey(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, enum GBAKey key, const char* keyName) { char sectionName[SECTION_NAME_MAX]; snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type); @@ -27,16 +106,48 @@ static void _loadKey(struct GBAInputMap* map, uint32_t type, const struct Config snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName); keyKey[KEY_NAME_MAX - 1] = '\0'; - const char* value = ConfigurationGetValue(config, sectionName, keyKey); - if (!value) { + int value; + if (!_getIntValue(config, sectionName, keyKey, &value)) { return; } - char* end; - long intValue = strtol(value, &end, 10); - if (*end) { + GBAInputBindKey(map, type, value, key); +} + +static void _loadAxis(struct GBAInputMap* map, uint32_t type, const struct Configuration* config, enum GBAKey direction, const char* axisName) { + char sectionName[SECTION_NAME_MAX]; + snprintf(sectionName, SECTION_NAME_MAX, "input.%c%c%c%c", type >> 24, type >> 16, type >> 8, type); + sectionName[SECTION_NAME_MAX - 1] = '\0'; + + char axisKey[KEY_NAME_MAX]; + snprintf(axisKey, KEY_NAME_MAX, "axis%sValue", axisName); + axisKey[KEY_NAME_MAX - 1] = '\0'; + int value; + if (!_getIntValue(config, sectionName, axisKey, &value)) { return; } - GBAInputBindKey(map, type, intValue, key); + + snprintf(axisKey, KEY_NAME_MAX, "axis%sAxis", axisName); + axisKey[KEY_NAME_MAX - 1] = '\0'; + int axis; + if (!_getIntValue(config, sectionName, axisKey, &axis)) { + return; + } + + const struct GBAAxis* description = GBAInputQueryAxis(map, type, axis); + struct GBAAxis realDescription; + if (!description) { + realDescription = (struct GBAAxis) { direction, GBA_KEY_NONE, value, 0 }; + } else { + realDescription = *description; + if (value >= realDescription.lowDirection) { + realDescription.deadHigh = value; + realDescription.highDirection = direction; + } else if (value <= realDescription.highDirection) { + realDescription.deadLow = value; + realDescription.lowDirection = direction; + } + } + GBAInputBindAxis(map, type, axis, &realDescription); } static void _saveKey(const struct GBAInputMap* map, uint32_t type, struct Configuration* config, enum GBAKey key, const char* keyName) { @@ -63,7 +174,10 @@ void GBAInputMapInit(struct GBAInputMap* map) { void GBAInputMapDeinit(struct GBAInputMap* map) { size_t m; for (m = 0; m < map->numMaps; ++m) { - free(map->maps[m].map); + if (map->maps[m].type) { + free(map->maps[m].map); + TableDeinit(&map->maps[m].axes); + } } free(map->maps); map->maps = 0; @@ -72,13 +186,7 @@ void GBAInputMapDeinit(struct GBAInputMap* map) { enum GBAKey GBAInputMapKey(const struct GBAInputMap* map, uint32_t type, int key) { size_t m; - const struct GBAInputMapImpl* impl = 0; - for (m = 0; m < map->numMaps; ++m) { - if (map->maps[m].type == type) { - impl = &map->maps[m]; - break; - } - } + const struct GBAInputMapImpl* impl = _lookupMapConst(map, type); if (!impl || !impl->map) { return GBA_KEY_NONE; } @@ -92,61 +200,23 @@ enum GBAKey GBAInputMapKey(const struct GBAInputMap* map, uint32_t type, int key } void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKey input) { - struct GBAInputMapImpl* impl = 0; - if (map->numMaps == 0) { - map->maps = malloc(sizeof(*map->maps)); - map->numMaps = 1; - impl = &map->maps[0]; - impl->type = type; - impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey)); - } else { - size_t m; - for (m = 0; m < map->numMaps; ++m) { - if (map->maps[m].type == type) { - impl = &map->maps[m]; - break; - } - } - } - if (!impl) { - size_t m; - for (m = 0; m < map->numMaps; ++m) { - if (!map->maps[m].type) { - impl = &map->maps[m]; - break; - } - } - if (impl) { - impl->type = type; - impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey)); - } else { - map->maps = realloc(map->maps, sizeof(*map->maps) * map->numMaps * 2); - for (m = map->numMaps * 2 - 1; m > map->numMaps; --m) { - map->maps[m].type = 0; - map->maps[m].map = 0; - } - map->numMaps *= 2; - impl = &map->maps[m]; - impl->type = type; - impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey)); - } - } + struct GBAInputMapImpl* impl = _guaranteeMap(map, type); impl->map[input] = key; } +void GBAInputUnbindKey(struct GBAInputMap* map, uint32_t type, enum GBAKey input) { + struct GBAInputMapImpl* impl = _lookupMap(map, type); + if (impl) { + impl->map[input] = -1; + } +} + int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAKey input) { if (input >= GBA_KEY_MAX) { return 0; } - size_t m; - const struct GBAInputMapImpl* impl = 0; - for (m = 0; m < map->numMaps; ++m) { - if (map->maps[m].type == type) { - impl = &map->maps[m]; - break; - } - } + const struct GBAInputMapImpl* impl = _lookupMapConst(map, type); if (!impl || !impl->map) { return 0; } @@ -154,6 +224,71 @@ int GBAInputQueryBinding(const struct GBAInputMap* map, uint32_t type, enum GBAK return impl->map[input]; } +enum GBAKey GBAInputMapAxis(const struct GBAInputMap* map, uint32_t type, int axis, int value) { + const struct GBAInputMapImpl* impl = _lookupMapConst(map, type); + if (!impl) { + return GBA_KEY_NONE; + } + struct GBAAxis* description = TableLookup(&impl->axes, axis); + if (!description) { + return GBA_KEY_NONE; + } + int state = 0; + if (value < description->deadLow) { + state = -1; + } else if (value > description->deadHigh) { + state = 1; + } + if (state > 0) { + return description->highDirection; + } + if (state < 0) { + return description->lowDirection; + } + return GBA_KEY_NONE; +} + +int GBAInputClearAxis(const struct GBAInputMap* map, uint32_t type, int axis, int keys) { + const struct GBAInputMapImpl* impl = _lookupMapConst(map, type); + if (!impl) { + return keys; + } + struct GBAAxis* description = TableLookup(&impl->axes, axis); + if (!description) { + return keys; + } + return keys &= ~((1 << description->highDirection) | (1 << description->lowDirection)); +} + +void GBAInputBindAxis(struct GBAInputMap* map, uint32_t type, int axis, const struct GBAAxis* description) { + struct GBAInputMapImpl* impl = _guaranteeMap(map, type); + struct GBAAxis* dup = malloc(sizeof(struct GBAAxis)); + *dup = *description; + TableInsert(&impl->axes, axis, dup); +} + +void GBAInputUnbindAxis(struct GBAInputMap* map, uint32_t type, int axis) { + struct GBAInputMapImpl* impl = _lookupMap(map, type); + if (impl) { + TableRemove(&impl->axes, axis); + } +} + +void GBAInputUnbindAllAxes(struct GBAInputMap* map, uint32_t type) { + struct GBAInputMapImpl* impl = _lookupMap(map, type); + if (impl) { + TableClear(&impl->axes); + } +} + +const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap* map, uint32_t type, int axis) { + const struct GBAInputMapImpl* impl = _lookupMapConst(map, type); + if (!impl) { + return 0; + } + return TableLookup(&impl->axes, axis); +} + void GBAInputMapLoad(struct GBAInputMap* map, uint32_t type, const struct Configuration* config) { _loadKey(map, type, config, GBA_KEY_A, "A"); _loadKey(map, type, config, GBA_KEY_B, "B"); diff --git a/src/gba/gba-input.h b/src/gba/gba-input.h index cc513aa8f..3c8ca4eab 100644 --- a/src/gba/gba-input.h +++ b/src/gba/gba-input.h @@ -15,13 +15,28 @@ struct GBAInputMap { size_t numMaps; }; +struct GBAAxis { + enum GBAKey highDirection; + enum GBAKey lowDirection; + int32_t deadHigh; + int32_t deadLow; +}; + void GBAInputMapInit(struct GBAInputMap*); void GBAInputMapDeinit(struct GBAInputMap*); enum GBAKey GBAInputMapKey(const struct GBAInputMap*, uint32_t type, int key); void GBAInputBindKey(struct GBAInputMap*, uint32_t type, int key, enum GBAKey input); +void GBAInputUnbindKey(struct GBAInputMap*, uint32_t type, int key); int GBAInputQueryBinding(const struct GBAInputMap*, uint32_t type, enum GBAKey input); +enum GBAKey GBAInputMapAxis(const struct GBAInputMap*, uint32_t type, int axis, int value); +int GBAInputClearAxis(const struct GBAInputMap*, uint32_t type, int axis, int keys); +void GBAInputBindAxis(struct GBAInputMap*, uint32_t type, int axis, const struct GBAAxis* description); +void GBAInputUnbindAxis(struct GBAInputMap*, uint32_t type, int axis); +void GBAInputUnbindAllAxes(struct GBAInputMap*, uint32_t type); +const struct GBAAxis* GBAInputQueryAxis(const struct GBAInputMap*, uint32_t type, int axis); + void GBAInputMapLoad(struct GBAInputMap*, uint32_t type, const struct Configuration*); void GBAInputMapSave(const struct GBAInputMap*, uint32_t type, struct Configuration*); diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index fe6e76fa6..ccc789761 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -100,6 +100,17 @@ int InputController::testSDLEvents() { activeButtons |= 1 << GBA_KEY_RIGHT; } } + + int numAxes = SDL_JoystickNumAxes(joystick); + for (i = 0; i < numAxes; ++i) { + int value = SDL_JoystickGetAxis(joystick, i); + + activeButtons = GBAInputClearAxis(&m_inputMap, SDL_BINDING_BUTTON, i, activeButtons); + enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value); + if (key != GBA_KEY_NONE) { + activeButtons |= 1 << key; + } + } return activeButtons; } diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 859cde7ab..7f3eb8bff 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -69,6 +69,11 @@ void GBASDLInitBindings(struct GBAInputMap* inputMap) { GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 6, GBA_KEY_DOWN); GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 7, GBA_KEY_LEFT); GBAInputBindKey(inputMap, SDL_BINDING_BUTTON, 5, GBA_KEY_RIGHT); + + struct GBAAxis description = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x4000, -0x4000 }; + GBAInputBindAxis(inputMap, SDL_BINDING_BUTTON, 0, &description); + description = (struct GBAAxis) { GBA_KEY_DOWN, GBA_KEY_UP, 0x4000, -0x4000 }; + GBAInputBindAxis(inputMap, SDL_BINDING_BUTTON, 1, &description); } void GBASDLEventsLoadConfig(struct GBASDLEvents* context, const struct Configuration* config) { @@ -272,6 +277,18 @@ static void _GBASDLHandleJoyHat(struct GBAThread* context, const struct SDL_JoyH context->activeKeys |= key; } +static void _GBASDLHandleJoyAxis(struct GBAThread* context, struct GBASDLEvents* sdlContext, const struct SDL_JoyAxisEvent* event) { + int keys = context->activeKeys; + + keys = GBAInputClearAxis(sdlContext->bindings, SDL_BINDING_BUTTON, event->axis, keys); + enum GBAKey key = GBAInputMapAxis(sdlContext->bindings, SDL_BINDING_BUTTON, event->axis, event->value); + if (key != GBA_KEY_NONE) { + keys |= 1 << key; + } + + context->activeKeys = keys; +} + #if SDL_VERSION_ATLEAST(2, 0, 0) static void _GBASDLHandleWindowEvent(struct GBAThread* context, struct GBASDLEvents* sdlContext, const struct SDL_WindowEvent* event) { UNUSED(context); @@ -303,5 +320,9 @@ void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContex break; case SDL_JOYHATMOTION: _GBASDLHandleJoyHat(context, &event->jhat); + break; + case SDL_JOYAXISMOTION: + _GBASDLHandleJoyAxis(context, sdlContext, &event->jaxis); + break; } }