Scripting: Add callback API

This commit is contained in:
Vicki Pfau 2022-05-11 23:27:00 -07:00
parent a59349af8a
commit c14fb54a74
6 changed files with 161 additions and 4 deletions

View File

@ -24,6 +24,7 @@ struct mScriptContext {
struct mScriptList refPool;
struct Table weakrefs;
uint32_t nextWeakref;
struct Table callbacks;
};
struct mScriptEngine2 {
@ -66,6 +67,11 @@ struct mScriptValue* mScriptContextMakeWeakref(struct mScriptContext*, struct mS
struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct mScriptValue* value);
void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref);
void mScriptContextAttachStdlib(struct mScriptContext* context);
void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback);
void mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value);
struct VFile;
bool mScriptContextLoadVF(struct mScriptContext*, const char* name, struct VFile* vf);
bool mScriptContextLoadFile(struct mScriptContext*, const char* path);

View File

@ -102,6 +102,7 @@ CXX_GUARD_START
#define mSCRIPT_TYPE_CMP_STR(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_STR, TYPE)
#define mSCRIPT_TYPE_CMP_CHARP(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE)
#define mSCRIPT_TYPE_CMP_PTR(TYPE) ((TYPE)->base >= mSCRIPT_TYPE_OPAQUE)
#define mSCRIPT_TYPE_CMP_WRAPPER(TYPE) (true)
#define mSCRIPT_TYPE_CMP_S(STRUCT) mSCRIPT_TYPE_MS_S(STRUCT)->name == _mSCRIPT_FIELD_NAME
#define mSCRIPT_TYPE_CMP_CS(STRUCT) mSCRIPT_TYPE_MS_CS(STRUCT)->name == _mSCRIPT_FIELD_NAME
#define mSCRIPT_TYPE_CMP_S_METHOD(STRUCT, NAME) mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME)->name == _mSCRIPT_FIELD_NAME

View File

@ -7,6 +7,9 @@
#include <mgba/core/blip_buf.h>
#include <mgba/core/core.h>
#ifdef ENABLE_SCRIPTING
#include <mgba/core/scripting.h>
#endif
#include <mgba/core/serialize.h>
#include <mgba-util/patch.h>
#include <mgba-util/vfs.h>
@ -183,6 +186,42 @@ void _coreShutdown(void* context) {
_changeState(thread->impl, mTHREAD_EXITING, true);
}
#ifdef ENABLE_SCRIPTING
#define ADD_CALLBACK(NAME) \
void _script_ ## NAME(void* context) { \
struct mCoreThread* threadContext = context; \
if (!threadContext->scriptContext) { \
return; \
} \
mScriptContextTriggerCallback(threadContext->scriptContext, #NAME); \
}
ADD_CALLBACK(frame)
ADD_CALLBACK(crashed)
ADD_CALLBACK(sleep)
ADD_CALLBACK(stop)
ADD_CALLBACK(keysRead)
ADD_CALLBACK(savedataUpdated)
ADD_CALLBACK(alarm)
#undef ADD_CALLBACK
#define CALLBACK(NAME) _script_ ## NAME
static void _mCoreThreadAddCallbacks(struct mCoreThread* threadContext) {
struct mCoreCallbacks callbacks = {
.videoFrameEnded = CALLBACK(frame),
.coreCrashed = CALLBACK(crashed),
.sleep = CALLBACK(sleep),
.shutdown = CALLBACK(stop),
.keysRead = CALLBACK(keysRead),
.savedataUpdated = CALLBACK(savedataUpdated),
.alarm = CALLBACK(alarm),
.context = threadContext
};
threadContext->core->addCoreCallbacks(threadContext->core, &callbacks);
}
#endif
static THREAD_ENTRY _mCoreThreadRun(void* context) {
struct mCoreThread* threadContext = context;
#ifdef USE_PTHREADS
@ -220,8 +259,10 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
}
#ifdef ENABLE_SCRIPTING
if (threadContext->scriptContext) {
mScriptContextAttachCore(threadContext->scriptContext, core);
struct mScriptContext* scriptContext = threadContext->scriptContext;
if (scriptContext) {
mScriptContextAttachCore(scriptContext, core);
_mCoreThreadAddCallbacks(threadContext);
}
#endif
@ -229,6 +270,18 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
if (threadContext->startCallback) {
threadContext->startCallback(threadContext);
}
#ifdef ENABLE_SCRIPTING
// startCallback could add a script context
if (!scriptContext) {
scriptContext = threadContext->scriptContext;
if (scriptContext) {
_mCoreThreadAddCallbacks(threadContext);
}
}
if (scriptContext) {
mScriptContextTriggerCallback(scriptContext, "start");
}
#endif
core->reset(core);
threadContext->impl->core = core;
@ -238,6 +291,19 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
threadContext->resetCallback(threadContext);
}
#ifdef ENABLE_SCRIPTING
// resetCallback could add a script context
if (!scriptContext) {
scriptContext = threadContext->scriptContext;
if (scriptContext) {
_mCoreThreadAddCallbacks(threadContext);
}
}
if (scriptContext) {
mScriptContextTriggerCallback(scriptContext, "reset");
}
#endif
struct mCoreThreadInternal* impl = threadContext->impl;
bool wasPaused = false;
int pendingRequests = 0;
@ -283,6 +349,14 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
MutexLock(&impl->stateMutex);
}
}
#ifdef ENABLE_SCRIPTING
if (!scriptContext) {
scriptContext = threadContext->scriptContext;
if (scriptContext) {
_mCoreThreadAddCallbacks(threadContext);
}
}
#endif
if (wasPaused && !(impl->requested & mTHREAD_REQ_PAUSE)) {
break;
}
@ -324,6 +398,11 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
if (threadContext->resetCallback) {
threadContext->resetCallback(threadContext);
}
#ifdef ENABLE_SCRIPTING
if (scriptContext) {
mScriptContextTriggerCallback(scriptContext, "reset");
}
#endif
}
if (pendingRequests & mTHREAD_REQ_RUN_ON) {
if (threadContext->run) {
@ -344,8 +423,9 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
threadContext->cleanCallback(threadContext);
}
#ifdef ENABLE_SCRIPTING
if (threadContext->scriptContext) {
mScriptContextDetachCore(threadContext->scriptContext);
if (scriptContext) {
mScriptContextTriggerCallback(scriptContext, "shutdown");
mScriptContextDetachCore(scriptContext);
}
#endif
core->clearCoreCallbacks(core);

View File

@ -1,6 +1,7 @@
include(ExportDirectory)
set(SOURCE_FILES
context.c
stdlib.c
types.c)
set(TEST_FILES

View File

@ -60,6 +60,7 @@ void mScriptContextInit(struct mScriptContext* context) {
mScriptListInit(&context->refPool, 0);
TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref);
context->nextWeakref = 0;
HashTableInit(&context->callbacks, 0, (void (*)(void*)) mScriptValueDeref);
}
void mScriptContextDeinit(struct mScriptContext* context) {
@ -68,6 +69,7 @@ void mScriptContextDeinit(struct mScriptContext* context) {
HashTableDeinit(&context->weakrefs);
mScriptContextDrainPool(context);
mScriptListDeinit(&context->refPool);
HashTableDeinit(&context->callbacks);
}
void mScriptContextFillPool(struct mScriptContext* context, struct mScriptValue* value) {
@ -177,6 +179,36 @@ void mScriptContextClearWeakref(struct mScriptContext* context, uint32_t weakref
TableRemove(&context->weakrefs, weakref);
}
void mScriptContextTriggerCallback(struct mScriptContext* context, const char* callback) {
struct mScriptValue* list = HashTableLookup(&context->callbacks, callback);
if (!list) {
return;
}
size_t i;
for (i = 0; i < mScriptListSize(list->value.opaque); ++i) {
struct mScriptFrame frame;
mScriptFrameInit(&frame);
struct mScriptValue* fn = mScriptListGetPointer(list->value.opaque, i);
if (fn->type->base == mSCRIPT_TYPE_WRAPPER) {
fn = mScriptValueUnwrap(fn);
}
mScriptInvoke(fn, &frame);
mScriptFrameDeinit(&frame);
}
}
void mScriptContextAddCallback(struct mScriptContext* context, const char* callback, struct mScriptValue* fn) {
if (fn->type->base != mSCRIPT_TYPE_FUNCTION) {
return;
}
struct mScriptValue* list = HashTableLookup(&context->callbacks, callback);
if (!list) {
list = mScriptValueAlloc(mSCRIPT_TYPE_MS_LIST);
HashTableInsert(&context->callbacks, callback, list);
}
mScriptValueWrap(fn, mScriptListAppend(list->value.opaque));
}
bool mScriptContextLoadVF(struct mScriptContext* context, const char* name, struct VFile* vf) {
struct mScriptFileInfo info = {
.name = name,

37
src/script/stdlib.c Normal file
View File

@ -0,0 +1,37 @@
/* Copyright (c) 2013-2022 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/script/context.h>
struct mScriptCallbackAdapter {
struct mScriptContext* context;
};
static void _mScriptCallbackAdd(struct mScriptCallbackAdapter* adapter, struct mScriptString* name, struct mScriptValue* fn) {
if (fn->type->base == mSCRIPT_TYPE_WRAPPER) {
fn = mScriptValueUnwrap(fn);
}
mScriptContextAddCallback(adapter->context, name->buffer, fn);
}
mSCRIPT_DECLARE_STRUCT(mScriptCallbackAdapter);
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCallbackAdapter, add, _mScriptCallbackAdd, 2, STR, callback, WRAPPER, function);
mSCRIPT_DEFINE_STRUCT(mScriptCallbackAdapter)
mSCRIPT_DEFINE_DOCSTRING("Add a callback of the named type")
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCallbackAdapter, add)
mSCRIPT_DEFINE_END;
void mScriptContextAttachStdlib(struct mScriptContext* context) {
struct mScriptValue* lib;
lib = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptCallbackAdapter));
lib->value.opaque = calloc(1, sizeof(struct mScriptCallbackAdapter));
*(struct mScriptCallbackAdapter*) lib->value.opaque = (struct mScriptCallbackAdapter) {
.context = context
};
lib->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER;
mScriptContextSetGlobal(context, "callbacks", lib);
}