diff --git a/include/mgba/script/base.h b/include/mgba/script/base.h index 4e8bbf168..dc0c2ed6f 100644 --- a/include/mgba/script/base.h +++ b/include/mgba/script/base.h @@ -11,9 +11,12 @@ CXX_GUARD_START #include +#include #include mSCRIPT_DECLARE_STRUCT(mImage) +mSCRIPT_DECLARE_STRUCT(mRectangle); +mSCRIPT_DECLARE_STRUCT(mSize); struct mScriptContext; void mScriptContextAttachImage(struct mScriptContext* context); diff --git a/src/script/stdlib.c b/src/script/stdlib.c index caa1e8a31..aab36a9a1 100644 --- a/src/script/stdlib.c +++ b/src/script/stdlib.c @@ -101,6 +101,100 @@ mSCRIPT_DEFINE_STRUCT(mScriptCallbackManager) mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, remove) mSCRIPT_DEFINE_END; +static struct mScriptValue* _mRectangleNew(int32_t x, int32_t y, int32_t width, int32_t height) { + struct mRectangle* rect = malloc(sizeof(*rect)); + rect->x = x; + rect->y = y; + rect->width = width; + rect->height = height; + struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mRectangle)); + result->value.opaque = rect; + result->flags = mSCRIPT_VALUE_FLAG_DEINIT; + return result; +} + +static struct mScriptValue* _mRectangleCopy(const struct mRectangle* old) { + struct mRectangle* rect = malloc(sizeof(*rect)); + memcpy(rect, old, sizeof(*rect)); + struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mRectangle)); + result->value.opaque = rect; + result->flags = mSCRIPT_VALUE_FLAG_DEINIT; + return result; +} + +static struct mScriptValue* _mRectangleSize(const struct mRectangle* rect) { + struct mSize* size = malloc(sizeof(*size)); + size->width = rect->width; + size->height = rect->height; + struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mSize)); + result->value.opaque = size; + result->flags = mSCRIPT_VALUE_FLAG_DEINIT; + return result; +} + +mSCRIPT_DECLARE_STRUCT_C_METHOD(mRectangle, W(mRectangle), copy, _mRectangleCopy, 0); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mRectangle, union, mRectangleUnion, 1, CS(mRectangle), other); +mSCRIPT_DECLARE_STRUCT_METHOD(mRectangle, BOOL, intersection, mRectangleIntersection, 1, CS(mRectangle), other); +mSCRIPT_DECLARE_STRUCT_VOID_C_METHOD(mRectangle, center, mRectangleCenter, 1, S(mRectangle), other); +mSCRIPT_DECLARE_STRUCT_METHOD(mRectangle, W(mSize), size, _mRectangleSize, 0); + +mSCRIPT_DEFINE_STRUCT(mRectangle) + mSCRIPT_DEFINE_CLASS_DOCSTRING("A basic axis-aligned rectangle object") + mSCRIPT_DEFINE_DOCSTRING("Create a copy of this struct::mRectangle") + mSCRIPT_DEFINE_STRUCT_METHOD(mRectangle, copy) + mSCRIPT_DEFINE_DOCSTRING("The x coordinate of the top-left corner") + mSCRIPT_DEFINE_STRUCT_MEMBER(mRectangle, S32, x) + mSCRIPT_DEFINE_DOCSTRING("The y coordinate of the top-left corner") + mSCRIPT_DEFINE_STRUCT_MEMBER(mRectangle, S32, y) + mSCRIPT_DEFINE_DOCSTRING("The width of the rectangle") + mSCRIPT_DEFINE_STRUCT_MEMBER(mRectangle, S32, width) + mSCRIPT_DEFINE_DOCSTRING("The height of the rectangle") + mSCRIPT_DEFINE_STRUCT_MEMBER(mRectangle, S32, height) + mSCRIPT_DEFINE_DOCSTRING("Find the bounding box of the union of this and another rectangle") + mSCRIPT_DEFINE_STRUCT_METHOD(mRectangle, union) + mSCRIPT_DEFINE_DOCSTRING("Find the intersection of this and another rectangle. Returns false if the rectangles don't intersect") + mSCRIPT_DEFINE_STRUCT_METHOD(mRectangle, intersection) + mSCRIPT_DEFINE_DOCSTRING("Center another rectangle inside this one") + mSCRIPT_DEFINE_STRUCT_METHOD(mRectangle, center) + mSCRIPT_DEFINE_DOCSTRING("Return the size of this struct::mRectangle as a struct::mSize object") + mSCRIPT_DEFINE_STRUCT_METHOD(mRectangle, size) +mSCRIPT_DEFINE_END; + +mSCRIPT_BIND_FUNCTION(mRectangleNew_Binding, W(mRectangle), _mRectangleNew, 4, S32, x, S32, y, S32, width, S32, height); + +static struct mScriptValue* _mSizeNew(int32_t width, int32_t height) { + struct mSize* size = malloc(sizeof(*size)); + size->width = width; + size->height = height; + struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mSize)); + result->value.opaque = size; + result->flags = mSCRIPT_VALUE_FLAG_DEINIT; + return result; +} + +static struct mScriptValue* _mSizeCopy(const struct mSize* old) { + struct mSize* size = malloc(sizeof(*size)); + memcpy(size, old, sizeof(*size)); + struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mSize)); + result->value.opaque = size; + result->flags = mSCRIPT_VALUE_FLAG_DEINIT; + return result; +} + +mSCRIPT_DECLARE_STRUCT_C_METHOD(mSize, W(mSize), copy, _mSizeCopy, 0); + +mSCRIPT_DEFINE_STRUCT(mSize) + mSCRIPT_DEFINE_CLASS_DOCSTRING("A basic size (width/height) object") + mSCRIPT_DEFINE_DOCSTRING("Create a copy of this struct::mSize") + mSCRIPT_DEFINE_STRUCT_METHOD(mSize, copy) + mSCRIPT_DEFINE_DOCSTRING("The width") + mSCRIPT_DEFINE_STRUCT_MEMBER(mSize, S32, width) + mSCRIPT_DEFINE_DOCSTRING("The height") + mSCRIPT_DEFINE_STRUCT_MEMBER(mSize, S32, height) +mSCRIPT_DEFINE_END; + +mSCRIPT_BIND_FUNCTION(mSizeNew_Binding, W(mSize), _mSizeNew, 2, S32, width, S32, height); + void mScriptContextAttachStdlib(struct mScriptContext* context) { struct mScriptValue* lib; @@ -177,11 +271,15 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) { mScriptContextExportNamespace(context, "util", (struct mScriptKVPair[]) { mSCRIPT_KV_PAIR(makeBitmask, &mScriptMakeBitmask_Binding), mSCRIPT_KV_PAIR(expandBitmask, &mScriptExpandBitmask_Binding), + mSCRIPT_KV_PAIR(newRectangle, &mRectangleNew_Binding), + mSCRIPT_KV_PAIR(newSize, &mSizeNew_Binding), mSCRIPT_KV_SENTINEL }); mScriptContextSetDocstring(context, "util", "Basic utility library"); mScriptContextSetDocstring(context, "util.makeBitmask", "Compile a list of bit indices into a bitmask"); mScriptContextSetDocstring(context, "util.expandBitmask", "Expand a bitmask into a list of bit indices"); + mScriptContextSetDocstring(context, "util.newRectangle", "Create a new mRectangle"); + mScriptContextSetDocstring(context, "util.newSize", "Create a new mSize"); struct mScriptValue* systemVersion = mScriptStringCreateFromUTF8(projectVersion); struct mScriptValue* systemProgram = mScriptStringCreateFromUTF8(projectName); diff --git a/src/script/test/stdlib.c b/src/script/test/stdlib.c index eda6deb7b..9b1056db5 100644 --- a/src/script/test/stdlib.c +++ b/src/script/test/stdlib.c @@ -169,10 +169,100 @@ M_TEST_DEFINE(callbackWeakref) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(rectangle) { + SETUP_LUA; + + struct mRectangle rect = { + .x = 1, + .y = 2, + .width = 3, + .height = 4, + }; + struct mScriptValue rval = mSCRIPT_MAKE_S(mRectangle, &rect); + mScriptContextSetGlobal(&context, "testRect", &rval); + TEST_PROGRAM("assert(testRect)"); + TEST_PROGRAM("assert(testRect.x == 1)"); + TEST_PROGRAM("assert(testRect.y == 2)"); + TEST_PROGRAM("assert(testRect.width == 3)"); + TEST_PROGRAM("assert(testRect.height == 4)"); + TEST_PROGRAM( + "testRect.x = 10\n" + "testRect.y = 11\n" + "testRect.width = 20\n" + "testRect.height = 21\n" + ); + assert_int_equal(rect.x, 10); + assert_int_equal(rect.y, 11); + assert_int_equal(rect.width, 20); + assert_int_equal(rect.height, 21); + + TEST_PROGRAM("newRect = util.newRectangle(5, 6, 10, 11)"); + TEST_PROGRAM("assert(newRect.x == 5)"); + TEST_PROGRAM("assert(newRect.y == 6)"); + TEST_PROGRAM("assert(newRect.width == 10)"); + TEST_PROGRAM("assert(newRect.height == 11)"); + + TEST_PROGRAM("testRect2 = testRect:copy()"); + TEST_PROGRAM("assert(testRect2.x == 10)"); + TEST_PROGRAM("assert(testRect2.y == 11)"); + TEST_PROGRAM("assert(testRect2.width == 20)"); + TEST_PROGRAM("assert(testRect2.height == 21)"); + + TEST_PROGRAM("assert(testRect2:intersection(newRect))"); + TEST_PROGRAM("assert(testRect2.x == 10)"); + TEST_PROGRAM("assert(testRect2.y == 11)"); + TEST_PROGRAM("assert(testRect2.width == 5)"); + TEST_PROGRAM("assert(testRect2.height == 6)"); + + TEST_PROGRAM("testRect2:union(testRect)"); + TEST_PROGRAM("assert(testRect2.x == 10)"); + TEST_PROGRAM("assert(testRect2.y == 11)"); + TEST_PROGRAM("assert(testRect2.width == 20)"); + TEST_PROGRAM("assert(testRect2.height == 21)"); + + TEST_PROGRAM("testSize = testRect:size()"); + TEST_PROGRAM("assert(testRect.width == testSize.width)"); + TEST_PROGRAM("assert(testRect.height == testSize.height)"); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(size) { + SETUP_LUA; + + struct mSize size = { + .width = 1, + .height = 2, + }; + struct mScriptValue sval = mSCRIPT_MAKE_S(mSize, &size); + mScriptContextSetGlobal(&context, "testSize", &sval); + TEST_PROGRAM("assert(testSize)"); + TEST_PROGRAM("assert(testSize.width == 1)"); + TEST_PROGRAM("assert(testSize.height == 2)"); + TEST_PROGRAM( + "testSize.width = 3\n" + "testSize.height = 4\n" + ); + assert_int_equal(size.width, 3); + assert_int_equal(size.height, 4); + + TEST_PROGRAM("newSize = util.newSize(5, 6)"); + TEST_PROGRAM("assert(newSize.width == 5)"); + TEST_PROGRAM("assert(newSize.height == 6)"); + + TEST_PROGRAM("testSize2 = testSize:copy()"); + TEST_PROGRAM("assert(testSize2.width == 3)"); + TEST_PROGRAM("assert(testSize2.height == 4)"); + + mScriptContextDeinit(&context); +} + M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptStdlib, cmocka_unit_test(bitMask), cmocka_unit_test(bitUnmask), cmocka_unit_test(callbacks), cmocka_unit_test(oneshot), cmocka_unit_test(callbackWeakref), + cmocka_unit_test(rectangle), + cmocka_unit_test(size), )