From 681b1cd0699e2c9b8d64b08845a780e6365cc233 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 26 Jun 2024 02:53:39 +0200 Subject: [PATCH] JS: Refactor widget with ViewHolder, fix crash Based on textbox refactor by nminaylov --- .../system/js_app/modules/js_widget.c | 172 +++++++----------- 1 file changed, 69 insertions(+), 103 deletions(-) diff --git a/applications/system/js_app/modules/js_widget.c b/applications/system/js_app/modules/js_widget.c index 69279bb7c..0d6aeb1db 100644 --- a/applications/system/js_app/modules/js_widget.c +++ b/applications/system/js_app/modules/js_widget.c @@ -1,6 +1,5 @@ #include -#include -#include +#include #include #include #include @@ -113,8 +112,8 @@ typedef struct { typedef struct { View* view; - ViewDispatcher* view_dispatcher; - FuriThread* thread; + ViewHolder* view_holder; + bool is_shown; } JsWidgetInst; static JsWidgetInst* get_this_ctx(struct mjs* mjs) { @@ -754,60 +753,34 @@ static void js_widget_is_open(struct mjs* mjs) { JsWidgetInst* widget = get_this_ctx(mjs); if(!check_arg_count(mjs, 0)) return; - mjs_return(mjs, mjs_mk_boolean(mjs, !!widget->thread)); -} - -static void widget_deinit(void* context) { - JsWidgetInst* widget = context; - if(widget->thread) { - furi_thread_join(widget->thread); - furi_thread_free(widget->thread); - widget->thread = NULL; - - furi_assert(widget->view_dispatcher); - view_dispatcher_remove_view(widget->view_dispatcher, 0); - view_dispatcher_free(widget->view_dispatcher); - widget->view_dispatcher = NULL; - - furi_record_close(RECORD_GUI); - } + mjs_return(mjs, mjs_mk_boolean(mjs, widget->is_shown)); } static void widget_callback(void* context, uint32_t arg) { UNUSED(arg); - widget_deinit(context); -} - -static bool widget_exit(void* context) { JsWidgetInst* widget = context; - view_dispatcher_stop(widget->view_dispatcher); - furi_timer_pending_callback(widget_callback, widget, 0); - return true; + view_holder_stop(widget->view_holder); + widget->is_shown = false; } -static int32_t widget_thread(void* context) { - ViewDispatcher* view_dispatcher = context; - view_dispatcher_run(view_dispatcher); - return 0; +static void widget_exit(void* context) { + JsWidgetInst* widget = context; + // Using timer to schedule view_holder stop, will not work under high CPU load + furi_timer_pending_callback(widget_callback, widget, 0); } static void js_widget_show(struct mjs* mjs) { JsWidgetInst* widget = get_this_ctx(mjs); if(!check_arg_count(mjs, 0)) return; - Gui* gui = furi_record_open(RECORD_GUI); + if(widget->is_shown) { + mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Widget is already shown"); + mjs_return(mjs, MJS_UNDEFINED); + return; + } - widget->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(widget->view_dispatcher); - view_dispatcher_add_view(widget->view_dispatcher, 0, widget->view); - view_dispatcher_set_event_callback_context(widget->view_dispatcher, widget); - view_dispatcher_set_navigation_event_callback(widget->view_dispatcher, widget_exit); - view_dispatcher_attach_to_gui(widget->view_dispatcher, gui, ViewDispatcherTypeFullscreen); - view_dispatcher_switch_to_view(widget->view_dispatcher, 0); - - widget->thread = - furi_thread_alloc_ex("JsWidget", 1024, widget_thread, widget->view_dispatcher); - furi_thread_start(widget->thread); + view_holder_start(widget->view_holder); + widget->is_shown = true; mjs_return(mjs, MJS_UNDEFINED); } @@ -816,10 +789,8 @@ static void js_widget_close(struct mjs* mjs) { JsWidgetInst* widget = get_this_ctx(mjs); if(!check_arg_count(mjs, 0)) return; - if(widget->thread) { - view_dispatcher_stop(widget->view_dispatcher); - widget_deinit(widget); - } + view_holder_stop(widget->view_holder); + widget->is_shown = false; mjs_return(mjs, MJS_UNDEFINED); } @@ -839,57 +810,9 @@ static void widget_draw_callback(Canvas* canvas, void* model) { } } -static void widget_remove_view(void* context) { - JsWidgetInst* widget = context; - - if(widget->view) { - with_view_model( - widget->view, - WidgetModel * model, - { - ComponentArray_it_t it; - ComponentArray_it(it, model->component); - while(!ComponentArray_end_p(it)) { - WidgetComponent* component = *ComponentArray_ref(it); - if(component && component->free) { - component->free(component); - } - ComponentArray_next(it); - } - ComponentArray_reset(model->component); - ComponentArray_clear(model->component); - }, - false); - with_view_model( - widget->view, WidgetModel * model, { XbmImageList_clear(model->image); }, false); - view_free(widget->view); - widget->view = NULL; - } -} - -static JsWidgetInst* widget_alloc(void) { - JsWidgetInst* widget = malloc(sizeof(JsWidgetInst)); - widget->thread = NULL; - widget->view_dispatcher = NULL; - - widget->view = view_alloc(); - view_allocate_model(widget->view, ViewModelTypeLockFree, sizeof(WidgetModel)); - view_set_draw_callback(widget->view, widget_draw_callback); - with_view_model( - widget->view, - WidgetModel * model, - { - ComponentArray_init(model->component); - XbmImageList_init(model->image); - model->max_assigned_id = 0; - }, - true); - - return widget; -} - static void* js_widget_create(struct mjs* mjs, mjs_val_t* object) { - JsWidgetInst* widget = widget_alloc(); + JsWidgetInst* widget = malloc(sizeof(JsWidgetInst)); + mjs_val_t widget_obj = mjs_mk_object(mjs); mjs_set(mjs, widget_obj, INST_PROP_NAME, ~0, mjs_mk_foreign(mjs, widget)); // addBox(x: number, y: number, w: number, h: number): number (returns id of the added component) @@ -924,17 +847,60 @@ static void* js_widget_create(struct mjs* mjs, mjs_val_t* object) { mjs_set(mjs, widget_obj, "show", ~0, MJS_MK_FN(js_widget_show)); // close(): void (closes the widget) mjs_set(mjs, widget_obj, "close", ~0, MJS_MK_FN(js_widget_close)); + + widget->view = view_alloc(); + view_allocate_model(widget->view, ViewModelTypeLockFree, sizeof(WidgetModel)); + view_set_draw_callback(widget->view, widget_draw_callback); + with_view_model( + widget->view, + WidgetModel * model, + { + ComponentArray_init(model->component); + XbmImageList_init(model->image); + model->max_assigned_id = 0; + }, + true); + + Gui* gui = furi_record_open(RECORD_GUI); + widget->view_holder = view_holder_alloc(); + view_holder_attach_to_gui(widget->view_holder, gui); + view_holder_set_back_callback(widget->view_holder, widget_exit, widget); + view_holder_set_view(widget->view_holder, widget->view); + *object = widget_obj; return widget; } static void js_widget_destroy(void* inst) { JsWidgetInst* widget = inst; - if(widget->thread) { - view_dispatcher_stop(widget->view_dispatcher); - widget_deinit(widget); - } - widget_remove_view(widget); + + view_holder_stop(widget->view_holder); + view_holder_free(widget->view_holder); + widget->view_holder = NULL; + + furi_record_close(RECORD_GUI); + + with_view_model( + widget->view, + WidgetModel * model, + { + ComponentArray_it_t it; + ComponentArray_it(it, model->component); + while(!ComponentArray_end_p(it)) { + WidgetComponent* component = *ComponentArray_ref(it); + if(component && component->free) { + component->free(component); + } + ComponentArray_next(it); + } + ComponentArray_reset(model->component); + ComponentArray_clear(model->component); + XbmImageList_clear(model->image); + }, + false); + view_free(widget->view); + widget->view = NULL; + free(widget); }