From 45d9e0a97d2c56a48ea558523e588d999a7577a1 Mon Sep 17 00:00:00 2001 From: Gabor de Mooij Date: Fri, 7 Feb 2025 00:39:41 +0100 Subject: [PATCH] Add Gui use: to include code from files or datapaks in the GUI plugin. --- plugins/gui/gui.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/plugins/gui/gui.c b/plugins/gui/gui.c index f4b6e3b7..d77c7a21 100644 --- a/plugins/gui/gui.c +++ b/plugins/gui/gui.c @@ -4,6 +4,9 @@ #include #include +#include + + #include "lvgl/lvgl.h" #include "../../citrine.h" #include @@ -12,6 +15,58 @@ uint16_t CtrGUIWidth = 800; uint16_t CtrGUIHeight = 400; ctr_object* guiObject; +ctr_object* packageObject; +ctr_object* CtrGUIAssetPackage; + + +SDL_RWops* ctr_internal_gui_load_asset(char* asset_name, char asset_type) { + SDL_RWops* res = NULL; + // If we have no asset package, load from file instead + if (CtrGUIAssetPackage == NULL) { + res = SDL_RWFromFile(asset_name, "rb"); + return res; + } + char* path = ctr_heap_allocate_cstring(ctr_internal_object_property(CtrGUIAssetPackage, "path", NULL)); + SDL_RWops* asset_file = SDL_RWFromFile(path, "rb"); + ctr_heap_free(path); + if (!asset_file) { + unsigned int blob_len; + void* blob; + //No asset file? then maybe embedded data blob? + blob = ctr_data_blob(&blob_len); + if (blob == NULL) { + return NULL; + } + asset_file = SDL_RWFromMem(blob, blob_len); + } + SDL_RWseek(asset_file, 0, RW_SEEK_SET); + char* buffer = ctr_heap_allocate(500); + while(1) { + uint64_t read_start = SDL_RWtell(asset_file); + int bytes_read = SDL_RWread(asset_file, buffer, 1, 500); + if (strncmp(asset_name, buffer, bytes_read) == 0) { + SDL_RWseek(asset_file, read_start + strlen(asset_name) + 1, RW_SEEK_SET); + uint64_t next_entry = 0; + SDL_RWread(asset_file, &next_entry, 8, 1); + uint64_t curpos = SDL_RWtell(asset_file); + uint64_t read_size = next_entry - curpos; + char* read_buffer = malloc(read_size); + SDL_RWread(asset_file, read_buffer, 1, read_size); + res = SDL_RWFromMem(read_buffer, read_size); + break; + } else { + char* boundary = strchr(buffer, 0); + uint64_t jmp_address = *((uint64_t*) (boundary+1)); + if (jmp_address == 0) { + break; + } + SDL_RWseek(asset_file, jmp_address, SEEK_SET); + } + } + SDL_RWclose(asset_file); + ctr_heap_free(buffer); + return res; +} void ctr_gui_internal_event_handler(lv_event_t* e) { ctr_argument* arguments; @@ -120,14 +175,113 @@ ctr_object* ctr_gui_screen(ctr_object* myself, ctr_argument* argumentList) { return myself; } +/** + * @internal + * + * 'DataStart' + * Exports can use this message to bootstrap from a data package. + * The package is often part of the executable or the distribution. + * This will connect to a data package called 'data' and include + * program '__1__' for execution. + */ +ctr_object* ctr_gui_datastart(ctr_object* myself, ctr_argument* none) { + ctr_argument* argumentList; + ctr_object* data_package; + argumentList = (ctr_argument*) ctr_heap_allocate(sizeof(ctr_argument)); + argumentList->object = ctr_build_string_from_cstring("data"); + argumentList->next = NULL; + // Create an asset package for 'data' + data_package = ctr_send_message( packageObject, CTR_DICT_NEW_SET, strlen(CTR_DICT_NEW_SET), argumentList ); + argumentList->object = data_package; + // Connect the assets to the program + ctr_send_message( guiObject, CTR_DICT_LINK_SET, strlen(CTR_DICT_LINK_SET), argumentList ); + argumentList->object = ctr_build_string_from_cstring("__1__"); + // Load the __1__ program from the data package + ctr_send_message( guiObject, "use:", strlen("use:"), argumentList ); + ctr_heap_free(argumentList); + return myself; +} + + + +ctr_object* ctr_gui_include(ctr_object* myself, ctr_argument* argumentList) { + ctr_object* pathStrObj = ctr_internal_cast2string(argumentList->object); + char* pathString = ctr_heap_allocate_tracked(pathStrObj->value.svalue->vlen + 1); + strncpy(pathString, pathStrObj->value.svalue->value, pathStrObj->value.svalue->vlen); + pathString[pathStrObj->value.svalue->vlen] = '\0'; + SDL_RWops* asset_reader = ctr_internal_gui_load_asset(pathString, 1); + if (!asset_reader) { + ctr_error("Unable to open code asset.", 0); + return CtrStdNil; + } + char* prg; + int prg_id; + int chunk = 512; + size_t bytes_read; + size_t offset = 0; + ctr_tnode* parsedCode; + prg = ctr_heap_allocate_tracked(chunk); + prg_id = ctr_heap_get_latest_tracking_id(); + bytes_read = SDL_RWread(asset_reader, prg, 1, chunk); + offset += bytes_read; + while (bytes_read > 0) { + prg = ctr_heap_reallocate_tracked(prg_id, offset + chunk + 1); + bytes_read = SDL_RWread(asset_reader, prg + offset, 1, chunk); + offset += bytes_read; + } + SDL_RWclose(asset_reader); + prg[offset + 1] = '\0'; + ctr_program_length = offset; + parsedCode = ctr_cparse_parse(prg, pathString); + ctr_cwlk_subprogram++; + ctr_cwlk_run(parsedCode); + ctr_cwlk_subprogram--; + return myself; +} + +ctr_object* ctr_package_new(ctr_object* myself, ctr_argument* argumentList) { + ctr_object* instance = ctr_internal_create_object(CTR_OBJECT_TYPE_OTOBJECT); + instance->link = myself; + return instance; +} + +ctr_object* ctr_package_new_set(ctr_object* myself, ctr_argument* argumentList) { + ctr_object* instance = ctr_package_new(myself, argumentList); + ctr_internal_object_property(instance, "path", ctr_internal_copy2string(argumentList->object)); + return instance; +} + +ctr_object* ctr_gui_link_package(ctr_object* myself, ctr_argument* argumentList) { + if (argumentList->object->link != packageObject) { + ctr_error("Not an asset package.\n", 0); + } + CtrGUIAssetPackage = argumentList->object; + return myself; +} + void begin() { + CtrGUIAssetPackage = NULL; + packageObject = ctr_package_new(CtrStdObject, NULL); + packageObject->link = CtrStdObject; + ctr_internal_create_func(packageObject, ctr_build_string_from_cstring( CTR_DICT_NEW ), &ctr_package_new ); + ctr_internal_create_func(packageObject, ctr_build_string_from_cstring( CTR_DICT_NEW_SET ), &ctr_package_new_set ); guiObject = NULL; guiObject = ctr_gui_new(CtrStdObject, NULL); guiObject->link = CtrStdObject; ctr_internal_create_func(guiObject, ctr_build_string_from_cstring( CTR_DICT_NEW ), &ctr_gui_new ); ctr_internal_create_func(guiObject, ctr_build_string_from_cstring( CTR_DICT_XML_NAME_AT_SET ), &ctr_gui_xml_at_set ); ctr_internal_create_func(guiObject, ctr_build_string_from_cstring( CTR_DICT_WIDTH_HEIGHT_SET ), &ctr_gui_width_height_set ); + ctr_internal_create_func(guiObject, ctr_build_string_from_cstring( CTR_DICT_LINK_SET ), &ctr_gui_link_package ); ctr_internal_create_func(guiObject, ctr_build_string_from_cstring( CTR_DICT_SCREEN ), &ctr_gui_screen ); + ctr_internal_create_func(guiObject, ctr_build_string_from_cstring( "use:" ), &ctr_gui_include ); + if (strcmp(CTR_DICT_USE_SET,"use:")!=0) { + ctr_internal_create_func(guiObject, ctr_build_string_from_cstring( CTR_DICT_USE_SET ), &ctr_gui_include ); + } + ctr_internal_create_func(guiObject, ctr_build_string_from_cstring( "_datastart" ), &ctr_gui_datastart ); ctr_internal_object_add_property(CtrStdWorld, ctr_build_string_from_cstring( CTR_DICT_GUI_PLUGIN_ID ), guiObject, CTR_CATEGORY_PUBLIC_PROPERTY); ctr_internal_object_add_property(CtrStdWorld, ctr_build_string_from_cstring( "Gui" ), guiObject, CTR_CATEGORY_PUBLIC_PROPERTY); } + +void init_embedded_gui_plugin() { + begin(); +} \ No newline at end of file