mirror of https://github.com/mgba-emu/mgba.git
Scripting: Add callback removing
This commit is contained in:
parent
6289c1a4d1
commit
3687863cbc
|
@ -29,6 +29,8 @@ struct mScriptContext {
|
||||||
struct Table weakrefs;
|
struct Table weakrefs;
|
||||||
uint32_t nextWeakref;
|
uint32_t nextWeakref;
|
||||||
struct Table callbacks;
|
struct Table callbacks;
|
||||||
|
struct Table callbackId;
|
||||||
|
uint32_t nextCallbackId;
|
||||||
struct mScriptValue* constants;
|
struct mScriptValue* constants;
|
||||||
struct Table docstrings;
|
struct Table docstrings;
|
||||||
};
|
};
|
||||||
|
@ -85,7 +87,8 @@ void mScriptContextExportConstants(struct mScriptContext* context, const char* n
|
||||||
void mScriptContextExportNamespace(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* value);
|
void mScriptContextExportNamespace(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* value);
|
||||||
|
|
||||||
void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback);
|
void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback);
|
||||||
void mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value);
|
uint32_t mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value);
|
||||||
|
void mScriptContextRemoveCallback(struct mScriptContext*, uint32_t cbid);
|
||||||
|
|
||||||
void mScriptContextSetDocstring(struct mScriptContext*, const char* key, const char* docstring);
|
void mScriptContextSetDocstring(struct mScriptContext*, const char* key, const char* docstring);
|
||||||
const char* mScriptContextGetDocstring(struct mScriptContext*, const char* key);
|
const char* mScriptContextGetDocstring(struct mScriptContext*, const char* key);
|
||||||
|
|
|
@ -14,6 +14,11 @@ struct mScriptFileInfo {
|
||||||
struct mScriptEngineContext* context;
|
struct mScriptEngineContext* context;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct mScriptCallbackInfo {
|
||||||
|
const char* callback;
|
||||||
|
size_t id;
|
||||||
|
};
|
||||||
|
|
||||||
static void _engineContextDestroy(void* ctx) {
|
static void _engineContextDestroy(void* ctx) {
|
||||||
struct mScriptEngineContext* context = ctx;
|
struct mScriptEngineContext* context = ctx;
|
||||||
context->destroy(context);
|
context->destroy(context);
|
||||||
|
@ -56,6 +61,8 @@ void mScriptContextInit(struct mScriptContext* context) {
|
||||||
TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref);
|
TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref);
|
||||||
context->nextWeakref = 1;
|
context->nextWeakref = 1;
|
||||||
HashTableInit(&context->callbacks, 0, (void (*)(void*)) mScriptValueDeref);
|
HashTableInit(&context->callbacks, 0, (void (*)(void*)) mScriptValueDeref);
|
||||||
|
TableInit(&context->callbackId, 0, free);
|
||||||
|
context->nextCallbackId = 1;
|
||||||
context->constants = NULL;
|
context->constants = NULL;
|
||||||
HashTableInit(&context->docstrings, 0, NULL);
|
HashTableInit(&context->docstrings, 0, NULL);
|
||||||
}
|
}
|
||||||
|
@ -66,6 +73,7 @@ void mScriptContextDeinit(struct mScriptContext* context) {
|
||||||
mScriptContextDrainPool(context);
|
mScriptContextDrainPool(context);
|
||||||
mScriptListDeinit(&context->refPool);
|
mScriptListDeinit(&context->refPool);
|
||||||
HashTableDeinit(&context->callbacks);
|
HashTableDeinit(&context->callbacks);
|
||||||
|
TableDeinit(&context->callbackId);
|
||||||
HashTableDeinit(&context->engines);
|
HashTableDeinit(&context->engines);
|
||||||
HashTableDeinit(&context->docstrings);
|
HashTableDeinit(&context->docstrings);
|
||||||
}
|
}
|
||||||
|
@ -199,8 +207,11 @@ void mScriptContextTriggerCallback(struct mScriptContext* context, const char* c
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < mScriptListSize(list->value.list); ++i) {
|
for (i = 0; i < mScriptListSize(list->value.list); ++i) {
|
||||||
struct mScriptFrame frame;
|
struct mScriptFrame frame;
|
||||||
mScriptFrameInit(&frame);
|
|
||||||
struct mScriptValue* fn = mScriptListGetPointer(list->value.list, i);
|
struct mScriptValue* fn = mScriptListGetPointer(list->value.list, i);
|
||||||
|
if (!fn->type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
mScriptFrameInit(&frame);
|
||||||
if (fn->type->base == mSCRIPT_TYPE_WRAPPER) {
|
if (fn->type->base == mSCRIPT_TYPE_WRAPPER) {
|
||||||
fn = mScriptValueUnwrap(fn);
|
fn = mScriptValueUnwrap(fn);
|
||||||
}
|
}
|
||||||
|
@ -209,16 +220,48 @@ void mScriptContextTriggerCallback(struct mScriptContext* context, const char* c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mScriptContextAddCallback(struct mScriptContext* context, const char* callback, struct mScriptValue* fn) {
|
uint32_t mScriptContextAddCallback(struct mScriptContext* context, const char* callback, struct mScriptValue* fn) {
|
||||||
if (fn->type->base != mSCRIPT_TYPE_FUNCTION) {
|
if (fn->type->base != mSCRIPT_TYPE_FUNCTION) {
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
struct mScriptValue* list = HashTableLookup(&context->callbacks, callback);
|
struct mScriptValue* list = HashTableLookup(&context->callbacks, callback);
|
||||||
if (!list) {
|
if (!list) {
|
||||||
list = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST);
|
list = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST);
|
||||||
HashTableInsert(&context->callbacks, callback, list);
|
HashTableInsert(&context->callbacks, callback, list);
|
||||||
}
|
}
|
||||||
|
struct mScriptCallbackInfo* info = malloc(sizeof(*info));
|
||||||
|
// Steal the string from the table key, since it's guaranteed to outlive this struct
|
||||||
|
struct TableIterator iter;
|
||||||
|
HashTableIteratorLookup(&context->callbacks, &iter, callback);
|
||||||
|
info->callback = HashTableIteratorGetKey(&context->callbacks, &iter);
|
||||||
|
info->id = mScriptListSize(list->value.list);
|
||||||
mScriptValueWrap(fn, mScriptListAppend(list->value.list));
|
mScriptValueWrap(fn, mScriptListAppend(list->value.list));
|
||||||
|
while (true) {
|
||||||
|
uint32_t id = context->nextCallbackId;
|
||||||
|
++context->nextCallbackId;
|
||||||
|
if (TableLookup(&context->callbackId, id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
TableInsert(&context->callbackId, id, info);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mScriptContextRemoveCallback(struct mScriptContext* context, uint32_t cbid) {
|
||||||
|
struct mScriptCallbackInfo* info = TableLookup(&context->callbackId, cbid);
|
||||||
|
if (!info) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct mScriptValue* list = HashTableLookup(&context->callbacks, info->callback);
|
||||||
|
if (!list) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (info->id >= mScriptListSize(list->value.list)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct mScriptValue* fn = mScriptValueUnwrap(mScriptListGetPointer(list->value.list, info->id));
|
||||||
|
mScriptValueDeref(fn);
|
||||||
|
mScriptListGetPointer(list->value.list, info->id)->type = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants) {
|
void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants) {
|
||||||
|
|
|
@ -19,16 +19,22 @@ struct mScriptCallbackManager {
|
||||||
struct mScriptContext* context;
|
struct mScriptContext* context;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void _mScriptCallbackAdd(struct mScriptCallbackManager* adapter, struct mScriptString* name, struct mScriptValue* fn) {
|
static uint32_t _mScriptCallbackAdd(struct mScriptCallbackManager* adapter, struct mScriptString* name, struct mScriptValue* fn) {
|
||||||
if (fn->type->base == mSCRIPT_TYPE_WRAPPER) {
|
if (fn->type->base == mSCRIPT_TYPE_WRAPPER) {
|
||||||
fn = mScriptValueUnwrap(fn);
|
fn = mScriptValueUnwrap(fn);
|
||||||
}
|
}
|
||||||
mScriptContextAddCallback(adapter->context, name->buffer, fn);
|
uint32_t id = mScriptContextAddCallback(adapter->context, name->buffer, fn);
|
||||||
mScriptValueDeref(fn);
|
mScriptValueDeref(fn);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _mScriptCallbackRemove(struct mScriptCallbackManager* adapter, uint32_t id) {
|
||||||
|
mScriptContextRemoveCallback(adapter->context, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
mSCRIPT_DECLARE_STRUCT(mScriptCallbackManager);
|
mSCRIPT_DECLARE_STRUCT(mScriptCallbackManager);
|
||||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackManager, add, _mScriptCallbackAdd, 2, STR, callback, WRAPPER, function);
|
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCallbackManager, U32, add, _mScriptCallbackAdd, 2, STR, callback, WRAPPER, function);
|
||||||
|
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackManager, remove, _mScriptCallbackRemove, 1, U32, cbid);
|
||||||
|
|
||||||
static uint64_t mScriptMakeBitmask(struct mScriptList* list) {
|
static uint64_t mScriptMakeBitmask(struct mScriptList* list) {
|
||||||
size_t i;
|
size_t i;
|
||||||
|
@ -76,8 +82,10 @@ mSCRIPT_DEFINE_STRUCT(mScriptCallbackManager)
|
||||||
"- **start**: The emulation has started\n"
|
"- **start**: The emulation has started\n"
|
||||||
"- **stop**: The emulation has voluntarily shut down\n"
|
"- **stop**: The emulation has voluntarily shut down\n"
|
||||||
)
|
)
|
||||||
mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type")
|
mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type. The returned id can be used to remove it later")
|
||||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, add)
|
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, add)
|
||||||
|
mSCRIPT_DEFINE_DOCSTRING("Remove a callback with the previously retuned id")
|
||||||
|
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackManager, remove)
|
||||||
mSCRIPT_DEFINE_END;
|
mSCRIPT_DEFINE_END;
|
||||||
|
|
||||||
void mScriptContextAttachStdlib(struct mScriptContext* context) {
|
void mScriptContextAttachStdlib(struct mScriptContext* context) {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <mgba/internal/script/lua.h>
|
#include <mgba/internal/script/lua.h>
|
||||||
#include <mgba/script/context.h>
|
#include <mgba/script/context.h>
|
||||||
|
#include <mgba/script/macros.h>
|
||||||
#include <mgba/script/types.h>
|
#include <mgba/script/types.h>
|
||||||
|
|
||||||
#define SETUP_LUA \
|
#define SETUP_LUA \
|
||||||
|
@ -85,7 +86,36 @@ M_TEST_DEFINE(bitUnmask) {
|
||||||
mScriptContextDeinit(&context);
|
mScriptContextDeinit(&context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
M_TEST_DEFINE(callbacks) {
|
||||||
|
SETUP_LUA;
|
||||||
|
|
||||||
|
TEST_PROGRAM(
|
||||||
|
"val = 0\n"
|
||||||
|
"function cb()\n"
|
||||||
|
" val = val + 1\n"
|
||||||
|
"end\n"
|
||||||
|
"id = callbacks:add('test', cb)\n"
|
||||||
|
"assert(id)"
|
||||||
|
);
|
||||||
|
|
||||||
|
TEST_VALUE(S32, "val", 0);
|
||||||
|
|
||||||
|
mScriptContextTriggerCallback(&context, "test");
|
||||||
|
TEST_VALUE(S32, "val", 1);
|
||||||
|
|
||||||
|
mScriptContextTriggerCallback(&context, "test");
|
||||||
|
TEST_VALUE(S32, "val", 2);
|
||||||
|
|
||||||
|
TEST_PROGRAM("callbacks:remove(id)");
|
||||||
|
|
||||||
|
mScriptContextTriggerCallback(&context, "test");
|
||||||
|
TEST_VALUE(S32, "val", 2);
|
||||||
|
|
||||||
|
mScriptContextDeinit(&context);
|
||||||
|
}
|
||||||
|
|
||||||
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptStdlib,
|
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptStdlib,
|
||||||
cmocka_unit_test(bitMask),
|
cmocka_unit_test(bitMask),
|
||||||
cmocka_unit_test(bitUnmask),
|
cmocka_unit_test(bitUnmask),
|
||||||
|
cmocka_unit_test(callbacks),
|
||||||
)
|
)
|
||||||
|
|
|
@ -249,6 +249,9 @@ void _allocList(struct mScriptValue* val) {
|
||||||
void _freeList(struct mScriptValue* val) {
|
void _freeList(struct mScriptValue* val) {
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < mScriptListSize(val->value.list); ++i) {
|
for (i = 0; i < mScriptListSize(val->value.list); ++i) {
|
||||||
|
if (val->type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
struct mScriptValue* unwrapped = mScriptValueUnwrap(mScriptListGetPointer(val->value.list, i));
|
struct mScriptValue* unwrapped = mScriptValueUnwrap(mScriptListGetPointer(val->value.list, i));
|
||||||
if (unwrapped) {
|
if (unwrapped) {
|
||||||
mScriptValueDeref(unwrapped);
|
mScriptValueDeref(unwrapped);
|
||||||
|
|
Loading…
Reference in New Issue