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 mScriptList refPool;
|
||||||
struct Table weakrefs;
|
struct Table weakrefs;
|
||||||
uint32_t nextWeakref;
|
uint32_t nextWeakref;
|
||||||
|
struct Table callbacks;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mScriptEngine2 {
|
struct mScriptEngine2 {
|
||||||
|
@ -66,6 +67,11 @@ struct mScriptValue* mScriptContextMakeWeakref(struct mScriptContext*, struct mS
|
||||||
struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct mScriptValue* value);
|
struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct mScriptValue* value);
|
||||||
void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref);
|
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;
|
struct VFile;
|
||||||
bool mScriptContextLoadVF(struct mScriptContext*, const char* name, struct VFile* vf);
|
bool mScriptContextLoadVF(struct mScriptContext*, const char* name, struct VFile* vf);
|
||||||
bool mScriptContextLoadFile(struct mScriptContext*, const char* path);
|
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_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_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_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_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_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
|
#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/blip_buf.h>
|
||||||
#include <mgba/core/core.h>
|
#include <mgba/core/core.h>
|
||||||
|
#ifdef ENABLE_SCRIPTING
|
||||||
|
#include <mgba/core/scripting.h>
|
||||||
|
#endif
|
||||||
#include <mgba/core/serialize.h>
|
#include <mgba/core/serialize.h>
|
||||||
#include <mgba-util/patch.h>
|
#include <mgba-util/patch.h>
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
@ -183,6 +186,42 @@ void _coreShutdown(void* context) {
|
||||||
_changeState(thread->impl, mTHREAD_EXITING, true);
|
_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) {
|
static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||||
struct mCoreThread* threadContext = context;
|
struct mCoreThread* threadContext = context;
|
||||||
#ifdef USE_PTHREADS
|
#ifdef USE_PTHREADS
|
||||||
|
@ -220,8 +259,10 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_SCRIPTING
|
#ifdef ENABLE_SCRIPTING
|
||||||
if (threadContext->scriptContext) {
|
struct mScriptContext* scriptContext = threadContext->scriptContext;
|
||||||
mScriptContextAttachCore(threadContext->scriptContext, core);
|
if (scriptContext) {
|
||||||
|
mScriptContextAttachCore(scriptContext, core);
|
||||||
|
_mCoreThreadAddCallbacks(threadContext);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -229,6 +270,18 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||||
if (threadContext->startCallback) {
|
if (threadContext->startCallback) {
|
||||||
threadContext->startCallback(threadContext);
|
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);
|
core->reset(core);
|
||||||
threadContext->impl->core = core;
|
threadContext->impl->core = core;
|
||||||
|
@ -238,6 +291,19 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||||
threadContext->resetCallback(threadContext);
|
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;
|
struct mCoreThreadInternal* impl = threadContext->impl;
|
||||||
bool wasPaused = false;
|
bool wasPaused = false;
|
||||||
int pendingRequests = 0;
|
int pendingRequests = 0;
|
||||||
|
@ -283,6 +349,14 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||||
MutexLock(&impl->stateMutex);
|
MutexLock(&impl->stateMutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef ENABLE_SCRIPTING
|
||||||
|
if (!scriptContext) {
|
||||||
|
scriptContext = threadContext->scriptContext;
|
||||||
|
if (scriptContext) {
|
||||||
|
_mCoreThreadAddCallbacks(threadContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (wasPaused && !(impl->requested & mTHREAD_REQ_PAUSE)) {
|
if (wasPaused && !(impl->requested & mTHREAD_REQ_PAUSE)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -324,6 +398,11 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||||
if (threadContext->resetCallback) {
|
if (threadContext->resetCallback) {
|
||||||
threadContext->resetCallback(threadContext);
|
threadContext->resetCallback(threadContext);
|
||||||
}
|
}
|
||||||
|
#ifdef ENABLE_SCRIPTING
|
||||||
|
if (scriptContext) {
|
||||||
|
mScriptContextTriggerCallback(scriptContext, "reset");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
if (pendingRequests & mTHREAD_REQ_RUN_ON) {
|
if (pendingRequests & mTHREAD_REQ_RUN_ON) {
|
||||||
if (threadContext->run) {
|
if (threadContext->run) {
|
||||||
|
@ -344,8 +423,9 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||||
threadContext->cleanCallback(threadContext);
|
threadContext->cleanCallback(threadContext);
|
||||||
}
|
}
|
||||||
#ifdef ENABLE_SCRIPTING
|
#ifdef ENABLE_SCRIPTING
|
||||||
if (threadContext->scriptContext) {
|
if (scriptContext) {
|
||||||
mScriptContextDetachCore(threadContext->scriptContext);
|
mScriptContextTriggerCallback(scriptContext, "shutdown");
|
||||||
|
mScriptContextDetachCore(scriptContext);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
core->clearCoreCallbacks(core);
|
core->clearCoreCallbacks(core);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
include(ExportDirectory)
|
include(ExportDirectory)
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
context.c
|
context.c
|
||||||
|
stdlib.c
|
||||||
types.c)
|
types.c)
|
||||||
|
|
||||||
set(TEST_FILES
|
set(TEST_FILES
|
||||||
|
|
|
@ -60,6 +60,7 @@ void mScriptContextInit(struct mScriptContext* context) {
|
||||||
mScriptListInit(&context->refPool, 0);
|
mScriptListInit(&context->refPool, 0);
|
||||||
TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref);
|
TableInit(&context->weakrefs, 0, (void (*)(void*)) mScriptValueDeref);
|
||||||
context->nextWeakref = 0;
|
context->nextWeakref = 0;
|
||||||
|
HashTableInit(&context->callbacks, 0, (void (*)(void*)) mScriptValueDeref);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mScriptContextDeinit(struct mScriptContext* context) {
|
void mScriptContextDeinit(struct mScriptContext* context) {
|
||||||
|
@ -68,6 +69,7 @@ void mScriptContextDeinit(struct mScriptContext* context) {
|
||||||
HashTableDeinit(&context->weakrefs);
|
HashTableDeinit(&context->weakrefs);
|
||||||
mScriptContextDrainPool(context);
|
mScriptContextDrainPool(context);
|
||||||
mScriptListDeinit(&context->refPool);
|
mScriptListDeinit(&context->refPool);
|
||||||
|
HashTableDeinit(&context->callbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mScriptContextFillPool(struct mScriptContext* context, struct mScriptValue* value) {
|
void mScriptContextFillPool(struct mScriptContext* context, struct mScriptValue* value) {
|
||||||
|
@ -177,6 +179,36 @@ void mScriptContextClearWeakref(struct mScriptContext* context, uint32_t weakref
|
||||||
TableRemove(&context->weakrefs, 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) {
|
bool mScriptContextLoadVF(struct mScriptContext* context, const char* name, struct VFile* vf) {
|
||||||
struct mScriptFileInfo info = {
|
struct mScriptFileInfo info = {
|
||||||
.name = name,
|
.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