From dfe2f62f162fd8258e725f3f9fcff7669aea320d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 04:04:43 -0800 Subject: [PATCH] Scripting: Basic gamepad support --- include/mgba/script/input.h | 26 ++++++ src/script/input.c | 152 ++++++++++++++++++++++++++++++++++++ src/script/test/input.c | 41 ++++++++++ 3 files changed, 219 insertions(+) diff --git a/include/mgba/script/input.h b/include/mgba/script/input.h index 3815433f0..53ef15c30 100644 --- a/include/mgba/script/input.h +++ b/include/mgba/script/input.h @@ -217,6 +217,14 @@ struct mScriptTriggerEvent { bool state; }; +struct mScriptGamepad { + unsigned pad; + + struct mScriptList axes; + struct mScriptList buttons; + struct mScriptList hats; +}; + mSCRIPT_DECLARE_STRUCT(mScriptEvent); mSCRIPT_DECLARE_STRUCT(mScriptKeyEvent); mSCRIPT_DECLARE_STRUCT(mScriptMouseButtonEvent); @@ -227,10 +235,28 @@ mSCRIPT_DECLARE_STRUCT(mScriptGamepadHatEvent); mSCRIPT_DECLARE_STRUCT(mScriptSensorEvent); mSCRIPT_DECLARE_STRUCT(mScriptTriggerEvent); +mSCRIPT_DECLARE_STRUCT(mScriptGamepad); + void mScriptContextAttachInput(struct mScriptContext* context); void mScriptContextFireEvent(struct mScriptContext*, struct mScriptEvent*); +int mScriptContextGamepadAttach(struct mScriptContext*, struct mScriptGamepad*); +bool mScriptContextGamepadDetach(struct mScriptContext*, int pad); +struct mScriptGamepad* mScriptContextGamepadLookup(struct mScriptContext*, int pad); + +void mScriptGamepadInit(struct mScriptGamepad*); +void mScriptGamepadDeinit(struct mScriptGamepad*); +void mScriptGamepadSetAxisCount(struct mScriptGamepad*, unsigned); +void mScriptGamepadSetButtonCount(struct mScriptGamepad*, unsigned); +void mScriptGamepadSetHatCount(struct mScriptGamepad*, unsigned); +void mScriptGamepadSetAxis(struct mScriptGamepad*, unsigned, int16_t value); +void mScriptGamepadSetButton(struct mScriptGamepad*, unsigned, bool down); +void mScriptGamepadSetHat(struct mScriptGamepad*, unsigned, int direction); +int16_t mScriptGamepadGetAxis(struct mScriptGamepad*, unsigned); +bool mScriptGamepadGetButton(struct mScriptGamepad*, unsigned); +int mScriptGamepadGetHat(struct mScriptGamepad*, unsigned); + CXX_GUARD_END #endif diff --git a/src/script/input.c b/src/script/input.c index 3803b4ba6..5f0901353 100644 --- a/src/script/input.c +++ b/src/script/input.c @@ -30,6 +30,7 @@ static const struct mScriptType* eventTypes[mSCRIPT_EV_TYPE_MAX] = { struct mScriptInputContext { uint64_t seq; struct Table activeKeys; + struct mScriptGamepad* activeGamepad; }; static void _mScriptInputDeinit(struct mScriptInputContext*); @@ -43,6 +44,7 @@ mSCRIPT_DEFINE_STRUCT(mScriptInputContext) mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptInputContext) mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptInputContext, U64, seq) mSCRIPT_DEFINE_STRUCT_METHOD(mScriptInputContext, isKeyActive) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptInputContext, PCS(mScriptGamepad), activeGamepad) mSCRIPT_DEFINE_END; mSCRIPT_DEFINE_STRUCT(mScriptEvent) @@ -92,6 +94,12 @@ mSCRIPT_DEFINE_STRUCT(mScriptGamepadHatEvent) mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadHatEvent, U8, direction) mSCRIPT_DEFINE_END; +mSCRIPT_DEFINE_STRUCT(mScriptGamepad) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, axes) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, buttons) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, hats) +mSCRIPT_DEFINE_END; + void mScriptContextAttachInput(struct mScriptContext* context) { struct mScriptInputContext* inputContext = calloc(1, sizeof(*inputContext)); struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptInputContext)); @@ -258,3 +266,147 @@ void mScriptContextFireEvent(struct mScriptContext* context, struct mScriptEvent mScriptContextTriggerCallback(context, eventNames[event->type], &args); mScriptListDeinit(&args); } + + +int mScriptContextGamepadAttach(struct mScriptContext* context, struct mScriptGamepad* pad) { + struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); + if (!input) { + return false; + } + struct mScriptInputContext* inputContext = input->value.opaque; + if (inputContext->activeGamepad) { + return -1; + } + inputContext->activeGamepad = pad; + + return 0; +} + +bool mScriptContextGamepadDetach(struct mScriptContext* context, int pad) { + if (pad != 0) { + return false; + } + struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); + if (!input) { + return false; + } + struct mScriptInputContext* inputContext = input->value.opaque; + if (!inputContext->activeGamepad) { + return false; + } + inputContext->activeGamepad = NULL; + return true; +} + +struct mScriptGamepad* mScriptContextGamepadLookup(struct mScriptContext* context, int pad) { + if (pad != 0) { + return NULL; + } + struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); + if (!input) { + return false; + } + struct mScriptInputContext* inputContext = input->value.opaque; + return inputContext->activeGamepad; +} + +void mScriptGamepadInit(struct mScriptGamepad* gamepad) { + memset(gamepad, 0, sizeof(*gamepad)); + + mScriptListInit(&gamepad->axes, 8); + mScriptListInit(&gamepad->buttons, 1); + mScriptListInit(&gamepad->hats, 1); +} + +void mScriptGamepadDeinit(struct mScriptGamepad* gamepad) { + mScriptListDeinit(&gamepad->axes); + mScriptListDeinit(&gamepad->buttons); + mScriptListDeinit(&gamepad->hats); +} + +void mScriptGamepadSetAxisCount(struct mScriptGamepad* gamepad, unsigned count) { + if (count > UINT8_MAX) { + count = UINT8_MAX; + } + + unsigned oldSize = mScriptListSize(&gamepad->axes); + mScriptListResize(&gamepad->axes, (ssize_t) count - oldSize); + unsigned i; + for (i = oldSize; i < count; ++i) { + *mScriptListGetPointer(&gamepad->axes, i) = mSCRIPT_MAKE_S16(0); + } +} + +void mScriptGamepadSetButtonCount(struct mScriptGamepad* gamepad, unsigned count) { + if (count > UINT16_MAX) { + count = UINT16_MAX; + } + + unsigned oldSize = mScriptListSize(&gamepad->buttons); + mScriptListResize(&gamepad->buttons, (ssize_t) count - oldSize); + unsigned i; + for (i = oldSize; i < count; ++i) { + *mScriptListGetPointer(&gamepad->buttons, i) = mSCRIPT_MAKE_BOOL(false); + } +} + +void mScriptGamepadSetHatCount(struct mScriptGamepad* gamepad, unsigned count) { + if (count > UINT8_MAX) { + count = UINT8_MAX; + } + + unsigned oldSize = mScriptListSize(&gamepad->hats); + mScriptListResize(&gamepad->hats, (ssize_t) count - oldSize); + unsigned i; + for (i = oldSize; i < count; ++i) { + *mScriptListGetPointer(&gamepad->hats, i) = mSCRIPT_MAKE_U8(0); + } +} + +void mScriptGamepadSetAxis(struct mScriptGamepad* gamepad, unsigned id, int16_t value) { + if (id >= mScriptListSize(&gamepad->axes)) { + return; + } + + mScriptListGetPointer(&gamepad->axes, id)->value.s32 = value; +} + +void mScriptGamepadSetButton(struct mScriptGamepad* gamepad, unsigned id, bool down) { + if (id >= mScriptListSize(&gamepad->buttons)) { + return; + } + + mScriptListGetPointer(&gamepad->buttons, id)->value.u32 = down; +} + +void mScriptGamepadSetHat(struct mScriptGamepad* gamepad, unsigned id, int direction) { + if (id >= mScriptListSize(&gamepad->hats)) { + return; + } + + mScriptListGetPointer(&gamepad->hats, id)->value.u32 = direction; +} + +int16_t mScriptGamepadGetAxis(struct mScriptGamepad* gamepad, unsigned id) { + if (id >= mScriptListSize(&gamepad->axes)) { + return 0; + } + + return mScriptListGetPointer(&gamepad->axes, id)->value.s32; +} + +bool mScriptGamepadGetButton(struct mScriptGamepad* gamepad, unsigned id) { + if (id >= mScriptListSize(&gamepad->buttons)) { + return false; + } + + return mScriptListGetPointer(&gamepad->buttons, id)->value.u32; +} + +int mScriptGamepadGetHat(struct mScriptGamepad* gamepad, unsigned id) { + if (id >= mScriptListSize(&gamepad->hats)) { + return mSCRIPT_INPUT_DIR_NONE; + } + + return mScriptListGetPointer(&gamepad->hats, id)->value.u32; +} diff --git a/src/script/test/input.c b/src/script/test/input.c index b98c73e72..de3ae0e0c 100644 --- a/src/script/test/input.c +++ b/src/script/test/input.c @@ -109,8 +109,49 @@ M_TEST_DEFINE(fireKey) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(gamepadExport) { + SETUP_LUA; + + struct mScriptGamepad m_gamepad; + mScriptGamepadInit(&m_gamepad); + + TEST_PROGRAM("assert(not input.activeGamepad)"); + assert_int_equal(mScriptContextGamepadAttach(&context, &m_gamepad), 0); + TEST_PROGRAM("assert(input.activeGamepad)"); + + TEST_PROGRAM("assert(#input.activeGamepad.axes == 0)"); + TEST_PROGRAM("assert(#input.activeGamepad.buttons == 0)"); + TEST_PROGRAM("assert(#input.activeGamepad.hats == 0)"); + + mScriptGamepadSetAxisCount(&m_gamepad, 1); + TEST_PROGRAM("assert(#input.activeGamepad.axes == 1)"); + TEST_PROGRAM("assert(input.activeGamepad.axes[1] == 0)"); + mScriptGamepadSetAxis(&m_gamepad, 0, 123); + TEST_PROGRAM("assert(input.activeGamepad.axes[1] == 123)"); + + mScriptGamepadSetButtonCount(&m_gamepad, 1); + TEST_PROGRAM("assert(#input.activeGamepad.buttons == 1)"); + TEST_PROGRAM("assert(input.activeGamepad.buttons[1] == false)"); + mScriptGamepadSetButton(&m_gamepad, 0, true); + TEST_PROGRAM("assert(input.activeGamepad.buttons[1] == true)"); + + mScriptGamepadSetHatCount(&m_gamepad, 1); + TEST_PROGRAM("assert(#input.activeGamepad.hats == 1)"); + TEST_PROGRAM("assert(input.activeGamepad.hats[1] == C.INPUT_DIR.NONE)"); + mScriptGamepadSetHat(&m_gamepad, 0, mSCRIPT_INPUT_DIR_NORTHWEST); + TEST_PROGRAM("assert(input.activeGamepad.hats[1] == C.INPUT_DIR.NORTHWEST)"); + + mScriptContextGamepadDetach(&context, 0); + TEST_PROGRAM("assert(not input.activeGamepad)"); + + mScriptGamepadDeinit(&m_gamepad); + + mScriptContextDeinit(&context); +} + M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptInput, cmocka_unit_test(members), cmocka_unit_test(seq), cmocka_unit_test(fireKey), + cmocka_unit_test(gamepadExport), )