mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-25 03:29:58 -07:00
[FL-3925, FL-3942, FL-3944] JS features & bugfixes (SDK 0.2) (#4075)
* feat: JS GPIO PWM, JS GUI Widget view; fix: JS EvtLoop stop on request, JS EvtLoop stop on error * fix: f18 build * docs: widget * fix: js unit test * change feature naming Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
* @brief Context passed to the generic event callback
|
||||
*/
|
||||
typedef struct {
|
||||
FuriEventLoop* event_loop;
|
||||
JsEventLoopObjectType object_type;
|
||||
|
||||
struct mjs* mjs;
|
||||
@@ -36,11 +37,6 @@ typedef struct {
|
||||
void* subscriptions; // SubscriptionArray_t, which we can't reference in this definition
|
||||
} JsEventLoopSubscription;
|
||||
|
||||
typedef struct {
|
||||
FuriEventLoop* loop;
|
||||
struct mjs* mjs;
|
||||
} JsEventLoopTickContext;
|
||||
|
||||
ARRAY_DEF(SubscriptionArray, JsEventLoopSubscription*, M_PTR_OPLIST); //-V575
|
||||
ARRAY_DEF(ContractArray, JsEventLoopContract*, M_PTR_OPLIST); //-V575
|
||||
|
||||
@@ -51,7 +47,6 @@ struct JsEventLoop {
|
||||
FuriEventLoop* loop;
|
||||
SubscriptionArray_t subscriptions;
|
||||
ContractArray_t owned_contracts; //<! Contracts that were produced by this module
|
||||
JsEventLoopTickContext* tick_context;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -60,7 +55,7 @@ struct JsEventLoop {
|
||||
static void js_event_loop_callback_generic(void* param) {
|
||||
JsEventLoopCallbackContext* context = param;
|
||||
mjs_val_t result;
|
||||
mjs_apply(
|
||||
mjs_err_t error = mjs_apply(
|
||||
context->mjs,
|
||||
&result,
|
||||
context->callback,
|
||||
@@ -68,6 +63,12 @@ static void js_event_loop_callback_generic(void* param) {
|
||||
context->arity,
|
||||
context->arguments);
|
||||
|
||||
bool is_error = strcmp(mjs_strerror(context->mjs, error), "NO_ERROR") != 0;
|
||||
bool asked_to_stop = js_flags_wait(context->mjs, ThreadEventStop, 0) & ThreadEventStop;
|
||||
if(is_error || asked_to_stop) {
|
||||
furi_event_loop_stop(context->event_loop);
|
||||
}
|
||||
|
||||
// save returned args for next call
|
||||
if(mjs_array_length(context->mjs, result) != context->arity - SYSTEM_ARGS) return;
|
||||
for(size_t i = 0; i < context->arity - SYSTEM_ARGS; i++) {
|
||||
@@ -111,11 +112,14 @@ static void js_event_loop_subscription_cancel(struct mjs* mjs) {
|
||||
JsEventLoopSubscription* subscription = JS_GET_CONTEXT(mjs);
|
||||
|
||||
if(subscription->object_type == JsEventLoopObjectTypeTimer) {
|
||||
// timer operations are deferred, which creates lifetime issues
|
||||
// just stop the timer and let the cleanup routine free everything when the script is done
|
||||
furi_event_loop_timer_stop(subscription->object);
|
||||
} else {
|
||||
furi_event_loop_unsubscribe(subscription->loop, subscription->object);
|
||||
return;
|
||||
}
|
||||
|
||||
furi_event_loop_unsubscribe(subscription->loop, subscription->object);
|
||||
|
||||
free(subscription->context->arguments);
|
||||
free(subscription->context);
|
||||
|
||||
@@ -158,6 +162,7 @@ static void js_event_loop_subscribe(struct mjs* mjs) {
|
||||
mjs_set(mjs, subscription_obj, "cancel", ~0, MJS_MK_FN(js_event_loop_subscription_cancel));
|
||||
|
||||
// create callback context
|
||||
context->event_loop = module->loop;
|
||||
context->object_type = contract->object_type;
|
||||
context->arity = mjs_nargs(mjs) - SYSTEM_ARGS + 2;
|
||||
context->arguments = calloc(context->arity, sizeof(mjs_val_t));
|
||||
@@ -333,37 +338,22 @@ static void js_event_loop_queue(struct mjs* mjs) {
|
||||
mjs_return(mjs, queue);
|
||||
}
|
||||
|
||||
static void js_event_loop_tick(void* param) {
|
||||
JsEventLoopTickContext* context = param;
|
||||
uint32_t flags = furi_thread_flags_wait(ThreadEventStop, FuriFlagWaitAny | FuriFlagNoClear, 0);
|
||||
if(flags & FuriFlagError) {
|
||||
return;
|
||||
}
|
||||
if(flags & ThreadEventStop) {
|
||||
furi_event_loop_stop(context->loop);
|
||||
mjs_exit(context->mjs);
|
||||
}
|
||||
}
|
||||
|
||||
static void* js_event_loop_create(struct mjs* mjs, mjs_val_t* object, JsModules* modules) {
|
||||
UNUSED(modules);
|
||||
mjs_val_t event_loop_obj = mjs_mk_object(mjs);
|
||||
JsEventLoop* module = malloc(sizeof(JsEventLoop));
|
||||
JsEventLoopTickContext* tick_ctx = malloc(sizeof(JsEventLoopTickContext));
|
||||
module->loop = furi_event_loop_alloc();
|
||||
tick_ctx->loop = module->loop;
|
||||
tick_ctx->mjs = mjs;
|
||||
module->tick_context = tick_ctx;
|
||||
furi_event_loop_tick_set(module->loop, 10, js_event_loop_tick, tick_ctx);
|
||||
SubscriptionArray_init(module->subscriptions);
|
||||
ContractArray_init(module->owned_contracts);
|
||||
|
||||
mjs_set(mjs, event_loop_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, module));
|
||||
mjs_set(mjs, event_loop_obj, "subscribe", ~0, MJS_MK_FN(js_event_loop_subscribe));
|
||||
mjs_set(mjs, event_loop_obj, "run", ~0, MJS_MK_FN(js_event_loop_run));
|
||||
mjs_set(mjs, event_loop_obj, "stop", ~0, MJS_MK_FN(js_event_loop_stop));
|
||||
mjs_set(mjs, event_loop_obj, "timer", ~0, MJS_MK_FN(js_event_loop_timer));
|
||||
mjs_set(mjs, event_loop_obj, "queue", ~0, MJS_MK_FN(js_event_loop_queue));
|
||||
JS_ASSIGN_MULTI(mjs, event_loop_obj) {
|
||||
JS_FIELD(INST_PROP_NAME, mjs_mk_foreign(mjs, module));
|
||||
JS_FIELD("subscribe", MJS_MK_FN(js_event_loop_subscribe));
|
||||
JS_FIELD("run", MJS_MK_FN(js_event_loop_run));
|
||||
JS_FIELD("stop", MJS_MK_FN(js_event_loop_stop));
|
||||
JS_FIELD("timer", MJS_MK_FN(js_event_loop_timer));
|
||||
JS_FIELD("queue", MJS_MK_FN(js_event_loop_queue));
|
||||
}
|
||||
|
||||
*object = event_loop_obj;
|
||||
return module;
|
||||
@@ -418,7 +408,6 @@ static void js_event_loop_destroy(void* inst) {
|
||||
ContractArray_clear(module->owned_contracts);
|
||||
|
||||
furi_event_loop_free(module->loop);
|
||||
free(module->tick_context);
|
||||
free(module);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user