SDL: Add support for configuring hats
@@ -12,10 +12,26 @@ CXX_GUARD_START
struct Configuration; +enum mInputHat { + M_INPUT_HAT_NEUTRAL = 0, + M_INPUT_HAT_UP = 1, + M_INPUT_HAT_RIGHT = 2, + M_INPUT_HAT_DOWN = 4, + M_INPUT_HAT_LEFT = 8 +}; + +struct mInputHatBindings { + int up; + int right; + int down; + int left; +}; + struct mInputPlatformInfo { const char* platformName; const char** keyId; size_t nKeys; + struct mInputHatBindings hat; }; struct mInputMap {@@ -47,6 +63,11 @@ void mInputUnbindAxis(struct mInputMap*, uint32_t type, int axis);
void mInputUnbindAllAxes(struct mInputMap*, uint32_t type); const struct mInputAxis* mInputQueryAxis(const struct mInputMap*, uint32_t type, int axis); void mInputEnumerateAxes(const struct mInputMap*, uint32_t type, void (handler(int axis, const struct mInputAxis* description, void* user)), void* user); + +int mInputMapHat(const struct mInputMap*, uint32_t type, int id, int direction); +void mInputBindHat(struct mInputMap*, uint32_t type, int id, const struct mInputHatBindings* bindings); +void mInputUnbindHat(struct mInputMap*, uint32_t type, int id); +void mInputUnbindAllHats(struct mInputMap*, uint32_t type); void mInputMapLoad(struct mInputMap*, uint32_t type, const struct Configuration*); void mInputMapSave(const struct mInputMap*, uint32_t type, struct Configuration*);
@@ -7,6 +7,7 @@ #include <mgba/core/input.h>
#include <mgba-util/configuration.h> #include <mgba-util/table.h> +#include <mgba-util/vector.h> #include <inttypes.h>@@ -15,11 +16,15 @@ #define KEY_NAME_MAX 32
#define KEY_VALUE_MAX 16 #define AXIS_INFO_MAX 12 +DECLARE_VECTOR(mInputHatList, struct mInputHatBindings); +DEFINE_VECTOR(mInputHatList, struct mInputHatBindings); + struct mInputMapImpl { int* map; uint32_t type; struct Table axes; + struct mInputHatList hats; }; struct mInputAxisSave {@@ -89,6 +94,7 @@ for (i = 0; i < map->info->nKeys; ++i) {
impl->map[i] = -1; } TableInit(&impl->axes, 2, free); + mInputHatListInit(&impl->hats, 1); } else { impl = _lookupMap(map, type); }@@ -123,6 +129,7 @@ impl->map[i] = -1;
} } TableInit(&impl->axes, 2, free); + mInputHatListInit(&impl->hats, 1); } return impl; }@@ -176,6 +183,28 @@ }
mInputBindAxis(map, type, axis, &realDescription); } +static bool _loadHat(struct mInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config, int hatId) { + char hatKey[KEY_NAME_MAX]; + + struct mInputHatBindings hatBindings = { -1, -1, -1, -1 }; + + bool found = false; + snprintf(hatKey, KEY_NAME_MAX, "hat%iUp", hatId); + found = _getIntValue(config, sectionName, hatKey, &hatBindings.up) || found; + snprintf(hatKey, KEY_NAME_MAX, "hat%iRight", hatId); + found = _getIntValue(config, sectionName, hatKey, &hatBindings.right) || found; + snprintf(hatKey, KEY_NAME_MAX, "hat%iDown", hatId); + found = _getIntValue(config, sectionName, hatKey, &hatBindings.down) || found; + snprintf(hatKey, KEY_NAME_MAX, "hat%iLeft", hatId); + found = _getIntValue(config, sectionName, hatKey, &hatBindings.left) || found; + + if (!found) { + return false; + } + mInputBindHat(map, type, hatId, &hatBindings); + return true; +} + static void _saveKey(const struct mInputMap* map, uint32_t type, const char* sectionName, struct Configuration* config, int key, const char* keyName) { char keyKey[KEY_NAME_MAX]; snprintf(keyKey, KEY_NAME_MAX, "key%s", keyName);@@ -239,6 +268,24 @@ ConfigurationSetValue(user->config, sectionName, axisKey, axisInfo);
} } +static void _saveHat(const char* sectionName, struct Configuration* config, int hatId, const struct mInputHatBindings* hat) { + char hatKey[KEY_NAME_MAX]; + char hatValue[KEY_VALUE_MAX]; + + snprintf(hatKey, KEY_NAME_MAX, "hat%iUp", hatId); + snprintf(hatValue, KEY_VALUE_MAX, "%i", hat->up); + ConfigurationSetValue(config, sectionName, hatKey, hatValue); + snprintf(hatKey, KEY_NAME_MAX, "hat%iRight", hatId); + snprintf(hatValue, KEY_VALUE_MAX, "%i", hat->right); + ConfigurationSetValue(config, sectionName, hatKey, hatValue); + snprintf(hatKey, KEY_NAME_MAX, "hat%iDown", hatId); + snprintf(hatValue, KEY_VALUE_MAX, "%i", hat->down); + ConfigurationSetValue(config, sectionName, hatKey, hatValue); + snprintf(hatKey, KEY_NAME_MAX, "hat%iLeft", hatId); + snprintf(hatValue, KEY_VALUE_MAX, "%i", hat->left); + ConfigurationSetValue(config, sectionName, hatKey, hatValue); +} + void _enumerateAxis(uint32_t axis, void* dp, void* ep) { struct mInputAxisEnumerate* enumUser = ep; const struct mInputAxis* description = dp;@@ -266,6 +313,10 @@ for (i = 0; i < map->info->nKeys; ++i) {
_loadKey(map, type, sectionName, config, i, map->info->keyId[i]); _loadAxis(map, type, sectionName, config, i, map->info->keyId[i]); } + i = 0; + while (_loadHat(map, type, sectionName, config, i)) { + ++i; + } return true; }@@ -289,6 +340,11 @@ sectionName,
map->info }; TableEnumerate(&impl->axes, _saveAxis, &save); + + for (i = 0; i < mInputHatListSize(&impl->hats); ++i) { + const struct mInputHatBindings* hat = mInputHatListGetConstPointer(&impl->hats, i); + _saveHat(sectionName, config, i, hat); + } } void mInputMapInit(struct mInputMap* map, const struct mInputPlatformInfo* info) {@@ -303,6 +359,7 @@ for (m = 0; m < map->numMaps; ++m) {
if (map->maps[m].type) { free(map->maps[m].map); TableDeinit(&map->maps[m].axes); + mInputHatListDeinit(&map->maps[m].hats); } } free(map->maps);@@ -449,6 +506,59 @@ handler,
user }; TableEnumerate(&impl->axes, _enumerateAxis, &enumUser); +} + +int mInputMapHat(const struct mInputMap* map, uint32_t type, int id, int direction) { + const struct mInputMapImpl* impl = _lookupMapConst(map, type); + if (!impl) { + return 0; + } + if (id >= (ssize_t) mInputHatListSize(&impl->hats)) { + return 0; + } + const struct mInputHatBindings* description = mInputHatListGetConstPointer(&impl->hats, id); + int mapping = 0; + if (direction & M_INPUT_HAT_UP && description->up >= 0) { + mapping |= 1 << description->up; + } + if (direction & M_INPUT_HAT_RIGHT && description->right >= 0) { + mapping |= 1 << description->right; + } + if (direction & M_INPUT_HAT_DOWN && description->down >= 0) { + mapping |= 1 << description->down; + } + if (direction & M_INPUT_HAT_LEFT && description->left >= 0) { + mapping |= 1 << description->left; + } + return mapping; +} + +void mInputBindHat(struct mInputMap* map, uint32_t type, int id, const struct mInputHatBindings* bindings) { + struct mInputMapImpl* impl = _guaranteeMap(map, type); + while (id >= (ssize_t) mInputHatListSize(&impl->hats)) { + *mInputHatListAppend(&impl->hats) = (struct mInputHatBindings) { -1, -1, -1, -1 }; + } + *mInputHatListGetPointer(&impl->hats, id) = *bindings; +} + +void mInputUnbindHat(struct mInputMap* map, uint32_t type, int id) { + struct mInputMapImpl* impl = _lookupMap(map, type); + if (!impl) { + return; + } + if (mInputHatListSize(&impl->hats) && id + 1 == (ssize_t) mInputHatListSize(&impl->hats)) { + mInputHatListResize(&impl->hats, -1); + } else { + struct mInputHatBindings* description = mInputHatListGetPointer(&impl->hats, id); + memset(description, -1, sizeof(&description)); + } +} + +void mInputUnbindAllHats(struct mInputMap* map, uint32_t type) { + struct mInputMapImpl* impl = _lookupMap(map, type); + if (impl) { + mInputHatListClear(&impl->hats); + } } void mInputMapLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config) {
@@ -21,5 +21,11 @@ "Down",
"R", "L" }, - .nKeys = GBA_KEY_MAX + .nKeys = GBA_KEY_MAX, + .hat = { + .up = GBA_KEY_UP, + .left = GBA_KEY_LEFT, + .down = GBA_KEY_DOWN, + .right = GBA_KEY_RIGHT + } };
@@ -311,18 +311,7 @@ }
int numHats = SDL_JoystickNumHats(joystick); for (i = 0; i < numHats; ++i) { int hat = SDL_JoystickGetHat(joystick, i); - if (hat & SDL_HAT_UP) { - activeButtons |= 1 << GBA_KEY_UP; - } - if (hat & SDL_HAT_LEFT) { - activeButtons |= 1 << GBA_KEY_LEFT; - } - if (hat & SDL_HAT_DOWN) { - activeButtons |= 1 << GBA_KEY_DOWN; - } - if (hat & SDL_HAT_RIGHT) { - activeButtons |= 1 << GBA_KEY_RIGHT; - } + activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat); } int numAxes = SDL_JoystickNumAxes(joystick);
@@ -141,6 +141,8 @@ struct mInputAxis description = { GBA_KEY_RIGHT, GBA_KEY_LEFT, 0x4000, -0x4000 };
mInputBindAxis(inputMap, SDL_BINDING_BUTTON, 0, &description); description = (struct mInputAxis) { GBA_KEY_DOWN, GBA_KEY_UP, 0x4000, -0x4000 }; mInputBindAxis(inputMap, SDL_BINDING_BUTTON, 1, &description); + + mInputBindHat(inputMap, SDL_BINDING_BUTTON, 0, &GBAInputInfo.hat); } bool mSDLAttachPlayer(struct mSDLEvents* events, struct mSDLPlayer* player) {@@ -498,6 +500,18 @@ core->clearKeys(core, 1 << key);
} } +static void _mSDLHandleJoyHat(struct mCore* core, struct mSDLPlayer* sdlContext, const struct SDL_JoyHatEvent* event) { + int allKeys = mInputMapHat(sdlContext->bindings, SDL_BINDING_BUTTON, event->hat, -1); + if (allKeys == 0) { + return; + } + + int keys = mInputMapHat(sdlContext->bindings, SDL_BINDING_BUTTON, event->hat, event->value); + + core->clearKeys(core, allKeys ^ keys); + core->addKeys(core, keys); +} + static void _mSDLHandleJoyAxis(struct mCore* core, struct mSDLPlayer* sdlContext, const struct SDL_JoyAxisEvent* event) { int clearKeys = ~mInputClearAxis(sdlContext->bindings, SDL_BINDING_BUTTON, event->axis, -1); int newKeys = 0;@@ -540,7 +554,7 @@ case SDL_JOYBUTTONUP:
_mSDLHandleJoyButton(context->core, sdlContext, &event->jbutton); break; case SDL_JOYHATMOTION: - // TODO + _mSDLHandleJoyHat(context->core, sdlContext, &event->jhat); break; case SDL_JOYAXISMOTION: _mSDLHandleJoyAxis(context->core, sdlContext, &event->jaxis);