mirror of https://github.com/mgba-emu/mgba.git
Scripting: Add callback API
This commit is contained in:
parent
a59349af8a
commit
c14fb54a74
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
include(ExportDirectory)
|
||||
set(SOURCE_FILES
|
||||
context.c
|
||||
stdlib.c
|
||||
types.c)
|
||||
|
||||
set(TEST_FILES
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue