From f509ba79f1bd7c0f424d8eb5383c01061fdd7b15 Mon Sep 17 00:00:00 2001 From: karurochari Date: Thu, 12 Dec 2024 19:58:18 +0000 Subject: [PATCH 01/11] Starting dev of features for v0.1.3 --- include/version.hpp | 4 ++-- meson.build | 2 +- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/version.hpp b/include/version.hpp index 554e3e60..ad8d2d43 100644 --- a/include/version.hpp +++ b/include/version.hpp @@ -1,6 +1,6 @@ #pragma once constexpr int vs_version_major=0; constexpr int vs_version_minor=1; -constexpr int vs_version_rev=0; +constexpr int vs_version_rev=2; inline const char* vs_version_tag="alpha"; -inline const char* vs_version(){return "0.1.0-alpha";} +inline const char* vs_version(){return "0.1.2-alpha";} diff --git a/meson.build b/meson.build index f0b9fbd4..db62a434 100644 --- a/meson.build +++ b/meson.build @@ -2,7 +2,7 @@ project( 'vs-fltk', ['c', 'cpp', 'swift'], # - version: '0.1.0-alpha', + version: '0.1.2-alpha', # meson_version: '>= 1.1', default_options: ['c_std=gnu23', 'cpp_std=gnu++23'], diff --git a/package.json b/package.json index d78ca507..35f87b0c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vs-fltk", "description": "Build system for `vs`, a sfc framework for native UI frontends", - "version": "0.1.0-alpha", + "version": "0.1.2-alpha", "license": "UNLICENSED", "scripts": { "codegen": "bun ./scripts/codegen/index.ts", From 8bb69c7fa7e63502ac63106525c5f624a178d363 Mon Sep 17 00:00:00 2001 From: karurochari Date: Thu, 12 Dec 2024 20:16:06 +0000 Subject: [PATCH 02/11] Update docs --- MILESTONES.md | 9 +------ RELEASE.md | 75 +++++++++++++-------------------------------------- 2 files changed, 20 insertions(+), 64 deletions(-) diff --git a/MILESTONES.md b/MILESTONES.md index c7068a57..6ec06267 100644 --- a/MILESTONES.md +++ b/MILESTONES.md @@ -1,15 +1,9 @@ General overview of upcoming features. For past releases please check in [here](./docs/releases/). -## 0.1.1-alpha - -- [x] Complete the build/install process -- [x] Docker image -- [x] Flatpak building pipeline -- [x] Prepare pipeline for github actions - ## 0.1.3 +- [ ] Move all components to the revised codegen format - [ ] New getters and setters design (and updated schemas). - [ ] Implement flex, grid, scroll & pack via codegen. - [ ] Tidy up the XML caching @@ -18,7 +12,6 @@ For past releases please check in [here](./docs/releases/). ## 0.1.5 -- [x] Move all components to the codegen format - [x] Support for script modules - [ ] Full support for getters and setters - [x] Final implementation of the static xml builder (now external dependency) diff --git a/RELEASE.md b/RELEASE.md index 77e453a3..e6197dae 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,64 +1,27 @@ -## v0.1.1-alpha +## v0.1.3-alpha -This first alpha release is not meant to be usable in real-world scenarios, and documentation will not be there yet for the most part. -The main objective is to implement & test most of the pipelines needed to: +This second alpha release is still not usable in real-world scenarios, yet some simple and somewhat useful demos should be achievable. -- build `vs` -- install it -- the generation of its flatpak image +The main objectives for this release are: -Still, `vs` is somewhat usable as a technical preview. -For now, the building pipeline is not well documented, and it can be hard to replicate. -Also, installing it on most distributions might cause issues with your past or future dependencies. -Still, you should be able to try it out via flatpak and the docker image provided by @andy5995. -I am seeking feedback, so you are very welcome to test it along! - -For reference, you can check what is the [intended scope](./README.md) of this project in terms of future developments and expected features. -The [milestone](./docs/milestones.md) page also contains some related information. +- To improve the build system moving it to a separate repo. +- To introduce a wider range of fltk widgets. +- Complete the revised code-generation of widgets +- Better serialization/de-serialization of XML props & revised getters/setters ### What's new? This is what has been implemented so far (and some missing feature for context): -- Basic CLI features for the command `vs`. These are not expected to change for quite a while. -- Code generation infrastructure for fltk components and derived schemas. -- Infrastructure to handle the user sqlite database with proper schemas and migration. -- A small set of components from fltk being exposed, like buttons, labels, windows etc. -- Several features of `vs` exposed as XML elements: - - [x] `mixin` - - [x] embedded `script` - - [ ] embedded `policies` - - [x] `namespace` - - [x] `import` - - [x] `use` - - [ ] caching directives - - [ ] dynamic data -- Basic XML builder (no full caching, no multithreading) -- Templating engine for vs components. Integration is complete, but there is ongoing work tracked in a [separate repo](https://github.com/KaruroChori/vs-templ) -- Embedded scripts: - - Support for `c` via tcc - - [x] Modules - - [x] Callbacks - - [x] Props - - [ ] Computed - - [x] Dispatcher - - [ ] Setters/getters - - [ ] FFI to external libraries - - [x] Debug interface - - Support for `js` via quickjs - - [ ] Modules - - [x] Callbacks - - [x] Props - - [ ] Computed - - [ ] Dispatcher - - [ ] Setters/getters - - [ ] FFI to external libraries - - [x] Debug interface -- Very early and limited c bindings for `vs.fltk`, mostly for testing purposes. -- A simple in-memory caching for files, compiled scripts & parsed XML trees. - The more complex sqlite cache is yet to be implemented in code. -- An early (partial) implementation of policies and coarse-grained flags as a temporary stopgap. -- Install process mostly implemented in meson (the pre-build codegen step will stay in typescript). -- Functional flatpak build -- A docker image for development -- Initial CI integrations +- [ ] Improvements to the build system + + - [ ] migration of distribution-related features to [external repo]() + - [ ] fix linking to follow https://github.com/KaruroChori/vs-fltk/issues/63 + - [ ] debian and arch packages are now supported + - [ ] install script can be now be safely used + +- [ ] New widgets & improvements + +- [ ] Updated code-gen + +- [ ] Updated core features From ba1e569d0d65d07372f8e1f73e5a64c5ae09fa42 Mon Sep 17 00:00:00 2001 From: karurochari Date: Fri, 13 Dec 2024 07:31:38 +0000 Subject: [PATCH 03/11] Moved all widgets to the new schema format, and extended codegen for the new supported properties --- MILESTONES.md | 2 +- commons/schemas/json-component.schema.json | 118 ++++-------- schemas/components/fl:base.json | 86 ++++----- schemas/components/fl:button.json | 8 +- schemas/components/fl:button.toggle.json | 22 +-- schemas/components/fl:flex.json | 92 +++++---- schemas/components/fl:grid.json | 21 +- schemas/components/fl:group.json | 38 ++-- schemas/components/fl:input.json | 21 +- schemas/components/fl:label.json | 22 +-- schemas/components/fl:pack.json | 21 +- schemas/components/fl:scroll.json | 21 +- schemas/components/fl:textbox.json | 13 +- schemas/components/fl:window.json | 11 +- scripts/codegen/components-new.schema.ts | 48 ----- scripts/codegen/components.schema.ts | 50 +++-- scripts/codegen/gen-components-new.tsx | 214 --------------------- scripts/codegen/gen-components.tsx | 41 ++-- 18 files changed, 264 insertions(+), 585 deletions(-) delete mode 100644 scripts/codegen/components-new.schema.ts delete mode 100644 scripts/codegen/gen-components-new.tsx diff --git a/MILESTONES.md b/MILESTONES.md index 6ec06267..aa24537c 100644 --- a/MILESTONES.md +++ b/MILESTONES.md @@ -3,7 +3,7 @@ For past releases please check in [here](./docs/releases/). ## 0.1.3 -- [ ] Move all components to the revised codegen format +- [x] Move all components to the revised codegen format - [ ] New getters and setters design (and updated schemas). - [ ] Implement flex, grid, scroll & pack via codegen. - [ ] Tidy up the XML caching diff --git a/commons/schemas/json-component.schema.json b/commons/schemas/json-component.schema.json index 4fa53dff..7862690f 100644 --- a/commons/schemas/json-component.schema.json +++ b/commons/schemas/json-component.schema.json @@ -6,20 +6,24 @@ "type": "string" }, "ns": { + "description": "Namespace for this component, for example `fl` in case of fltk wrappers.", "type": "string" }, "name": { "type": "string" }, - "usable": { + "exposed": { "default": true, + "description": "If true, this widget will be usable from XML and shown in the documentation, otherwise it is not exported.", "type": "boolean" }, "description": { + "description": "While optional, this field is extremely important for exposed widgets", "type": "string" }, "use_main_header": { "default": null, + "description": "If set, no class definition will be generated. It will instread load one already present. Used to track and document widgets which are not fully autogenerated.", "anyOf": [ { "type": "null" @@ -30,12 +34,21 @@ ] }, "headers": { + "description": "List of header files used by the widget in its public header file.", + "type": "array", + "items": { + "type": "string" + } + }, + "private_headers": { + "description": "List of header files used by the widget in its generated cpp file.", "type": "array", "items": { "type": "string" } }, "type": { + "description": "Type of frame an instance of this widget class will default top.", "anyOf": [ { "const": "leaf", @@ -65,6 +78,7 @@ "properties": { "extends": { "default": null, + "description": "If set, a class to inherit from, usually related to the fltk widget.", "anyOf": [ { "type": "null" @@ -74,7 +88,8 @@ } ] }, - "props_tail": { + "set_tail": { + "description": "If specified, what to do if a getter is not matched.", "anyOf": [ { "type": "null" @@ -84,7 +99,8 @@ } ] }, - "computed_tail": { + "get_tail": { + "description": "If specified, what to do if a setter is not matched.", "anyOf": [ { "type": "null" @@ -101,6 +117,7 @@ }, "extends": { "default": null, + "description": "If set, a vs widget to inherit from (not a c++ class name)", "anyOf": [ { "type": "null" @@ -110,18 +127,19 @@ } ] }, - "skip_props": { + "skip_fields": { "default": [], - "description": "Properties to be matched but ignored", + "description": "Properties to be matched when set, but ignored.", "type": "array", "items": { "type": "string" } }, - "props": { + "fields": { "type": "object", "patternProperties": { "^(.*)$": { + "additionalProperties": false, "type": "object", "properties": { "type": { @@ -141,11 +159,11 @@ "type": "string" }, { - "const": "string", + "const": "path", "type": "string" }, { - "const": "path", + "const": "string", "type": "string" }, { @@ -169,8 +187,8 @@ "subtype": { "type": "string" }, - "code": { - "default": null, + "parse": { + "description": "From string to object. Default based on `type` (and `subtype`)", "anyOf": [ { "type": "string" @@ -180,78 +198,19 @@ } ] }, - "description": { - "type": "string" - }, - "alias": { - "description": "alias names", - "default": [], - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "type", - "code" - ] - } - } - }, - "computed": { - "type": "object", - "patternProperties": { - "^(.*)$": { - "type": "object", - "properties": { - "type": { - "description": "type", - "default": "string", + "setter": { + "description": "Operations on setting. If undefined, this field is only available as a computed value. If null use default", "anyOf": [ { - "const": "flag", - "type": "string" - }, - { - "const": "enum", - "type": "string" - }, - { - "const": "raw", - "type": "string" - }, - { - "const": "string", "type": "string" }, { - "const": "path", - "type": "string" - }, - { - "const": "color", - "type": "string" - }, - { - "const": "scalar-1", - "type": "string" - }, - { - "const": "scalar-2", - "type": "string" - }, - { - "const": "scalar-4", - "type": "string" + "type": "null" } ] }, - "subtype": { - "type": "string" - }, - "code": { - "default": null, + "getter": { + "description": "Implementation on how to extract value from object. If undefined the field is not observable. If null use default", "anyOf": [ { "type": "string" @@ -264,6 +223,11 @@ "description": { "type": "string" }, + "semantic": { + "default": false, + "description": "If true, this field has a strong semantic meaning. Used for semantic serialization of a document.", + "type": "boolean" + }, "alias": { "description": "alias names", "default": [], @@ -274,8 +238,7 @@ } }, "required": [ - "type", - "code" + "type" ] } } @@ -286,7 +249,6 @@ "type", "codegen", "extends", - "props", - "computed" + "fields" ] } \ No newline at end of file diff --git a/schemas/components/fl:base.json b/schemas/components/fl:base.json index ff89c15f..ea95b7e0 100644 --- a/schemas/components/fl:base.json +++ b/schemas/components/fl:base.json @@ -1,170 +1,160 @@ { "$schema": "../../commons/schemas/json-component.schema.json", "description": "Base element to provide basic props/computed for all those which are derived from `fltk_widget`", - "usable": false, + "exposed": false, "type": "node", "headers": ["FL/Fl_Group.H"], "codegen": { "extends": "ui" }, - "skip_props": ["name", "frame.*", "xsml", "xsml:*"], - "props": { + "skip_fields": ["name", "frame.*", "xsml", "xsml:*"], + "fields": { "mixin": { "type": "string", "description": "Apply mixins to this object", - "code": "that->refresh_style(value);" + "setter": "that->refresh_style(value);" }, "label": { "type": "string", "alias": ["label.txt"], - "code": "w.copy_label(value);" + "setter": "w.copy_label(value);" }, "label.align.pos": { "type": "raw", "subtype": "fl_align_pos", - "code": "int t=field_types::fl_align_pos_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_POSITION_MASK))|t);" + "setter": "int t=field_types::fl_align_pos_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_POSITION_MASK))|t);" }, "label.align.img": { "type": "raw", "subtype": "fl_align_image", - "code": "int t=field_types::fl_align_image_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_IMAGE_MASK))|t);" + "setter": "int t=field_types::fl_align_image_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_IMAGE_MASK))|t);" }, "label.align.wrap": { "type": "raw", "subtype": "fl_align_wrap", - "code": "int t=field_types::fl_align_wrap_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_WRAP))|t);" + "setter": "int t=field_types::fl_align_wrap_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_WRAP))|t);" }, "label.align.clip": { "type": "raw", "subtype": "fl_align_clip", - "code": "int t=field_types::fl_align_clip_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_CLIP))|t);" + "setter": "int t=field_types::fl_align_clip_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_CLIP))|t);" }, "label.align.inside": { "type": "raw", "subtype": "fl_align_inside", - "code": "int t=field_types::fl_align_inside_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_INSIDE))|t);" + "setter": "int t=field_types::fl_align_inside_i(value);if((ok=(t!=-1)))w.align((w.align()&(!FL_ALIGN_INSIDE))|t);" }, "label.size": { "type": "raw", "description": "A simple integer value to determine the font size, or 'default' to set the default one.", - "code": "if(strcmp(value,\"default\")==0){w.labelsize(FL_NORMAL_SIZE);}else{size_t size; if((ok=field_types::h_px(1,&size,value,that))) w.labelsize(size);}" + "setter": "if(strcmp(value,\"default\")==0){w.labelsize(FL_NORMAL_SIZE);}else{size_t size; if((ok=field_types::h_px(1,&size,value,that))) w.labelsize(size);}" }, "label.colour": { "type": "color", "alias": ["label.color"], - "code": "w.labelcolor(computed);" + "setter": "w.labelcolor(computed);" }, "label.margin": { "type": "scalar-2", "description": "Horizontal and vertical margins for the label.", - "code": "w.horizontal_label_margin(computed[0]); w.vertical_label_margin(computed[1]);" + "setter": "w.horizontal_label_margin(computed[0]); w.vertical_label_margin(computed[1]);" }, "label.margin.x": { "type": "scalar-1", "description": "Horizontal margin for the label.", - "code": "w.horizontal_label_margin(computed[0]);" + "setter": "w.horizontal_label_margin(computed[0]);" }, "label.margin.y": { "type": "scalar-1", "description": "Vertical margin for the label.", - "code": "w.vertical_label_margin(computed[0]);" + "setter": "w.vertical_label_margin(computed[0]);" }, "tooltip": { "type": "string", "alias": ["tooltip.txt"], - "code": "w.copy_tooltip(value);" + "setter": "w.copy_tooltip(value);" }, "box": { "type": "scalar-4", "description": "Origin offset (0,1) and size (2,3) for the box.", - "code": "w.position(computed[0],computed[1]);w.size(computed[2],computed[3]);" + "setter": "w.position(computed[0],computed[1]);w.size(computed[2],computed[3]);", + "getter": "/*TODO size()*/" }, "box.x": { "type": "scalar-1", - "code": "w.position(computed[0], w.y());" + "setter": "w.position(computed[0], w.y());" }, "box.y": { "type": "scalar-1", - "code": "w.position(w.x(), computed[0]);" + "setter": "w.position(w.x(), computed[0]);" }, "box.w": { "type": "scalar-1", "alias": ["box.width"], - "code": "w.size(computed[0], w.h());" + "setter": "w.size(computed[0], w.h());" }, "box.h": { "type": "scalar-1", "alias": ["box.height"], - "code": "w.size(w.w(), computed[0]);" + "setter": "w.size(w.w(), computed[0]);" }, "box.style": { "type": "raw", "subtype": "fl_boxtype", - "code": "w.box(field_types::fl_boxtype_i(value));" + "setter": "w.box(field_types::fl_boxtype_i(value));" }, "box.fixed": { "type": "flag", "description": "If this widget is child of a container like flex, set its box size as fixed", - "code": "/*TODO*/" + "setter": "/*TODO*/" }, "bg.colour": { "type": "color", "alias": ["bg.color"], "description": "Colour for both relaxed and selected states", - "code": "w.color(computed);w.selection_color(computed);" + "setter": "w.color(computed);w.selection_color(computed);" }, "bg.colour.relaxed": { "type": "color", "alias": ["bg.color.relaxed"], "description": "Colour for the relaxed state", - "code": "w.color(computed);" + "setter": "w.color(computed);" }, "bg.colour.selected": { "type": "color", "alias": ["bg.color.selected"], "description": "Colour for the selected state", - "code": "w.selection_color(computed);" + "setter": "w.selection_color(computed);" }, "active": { "type": "flag", "description": "If true set active (default).", - "code": "if(computed)w.activate();else w.deactivate();" + "setter": "if(computed)w.activate();else w.deactivate();", + "getter": "/*TODO active()*/" + }, + "active.recursive": { + "type": "flag", + "description": "Get back the actual box of the widget", + "getter": "/*TODO active_t()*/" }, "show": { "type": "flag", "description": "If true the widget is shown (default).", - "code": "if(computed)w.show();else w.hide();" + "setter": "if(computed)w.show();else w.hide();" }, "on.callback": { "type": "raw", "subtype": "$symbol", "description": "Empty string to unset.", - "code": "if(strcmp(value,\"\")==0)w.callback((Fl_Callback*)nullptr);else{auto result = that->resolve_symbol(value, true);if(result.symbol.symbol==nullptr){vs_log(severety_t::CONTINUE,that,\"Unable to find symbol `%s` for callback.\",value);}else{ui_callback_t* payload = new ui_callback_t();payload->caller=that;payload->sym=result;w.callback(ui_callback_handler,payload,true);}}" - } - }, - "computed": { - "box": { - "type": "scalar-4", - "description": "Get back the actual box of the widget", - "code": "/*TODO size()*/" + "setter": "if(strcmp(value,\"\")==0)w.callback((Fl_Callback*)nullptr);else{auto result = that->resolve_symbol(value, true);if(result.symbol.symbol==nullptr){vs_log(severety_t::CONTINUE,that,\"Unable to find symbol `%s` for callback.\",value);}else{ui_callback_t* payload = new ui_callback_t();payload->caller=that;payload->sym=result;w.callback(ui_callback_handler,payload,true);}}" }, "visible": { "type": "flag", "description": "Get back the actual box of the widget", - "code": "/*TODO visible()*/" + "getter": "/*TODO visible()*/" }, "visible.recursive": { "type": "flag", "description": "Get back the actual box of the widget", - "code": "/*TODO visible.r()*/" - }, - "active": { - "type": "flag", - "description": "Get back the actual box of the widget", - "code": "/*TODO active()*/" - }, - "active.recursive": { - "type": "flag", - "description": "Get back the actual box of the widget", - "code": "/*TODO active_t()*/" + "getter": "/*TODO visible.r()*/" } } } diff --git a/schemas/components/fl:button.json b/schemas/components/fl:button.json index 381b9668..41e0fee1 100644 --- a/schemas/components/fl:button.json +++ b/schemas/components/fl:button.json @@ -1,14 +1,10 @@ { "$schema": "../../commons/schemas/json-component.schema.json", "description": "Basic button element", - "usable": true, + "exposed": true, "type": "leaf", "headers": ["FL/Fl_Button.H"], "extends": "fl:base", "codegen": {"extends": "ui"}, - "props":{ - }, - "computed":{ - - } + "fields": {} } \ No newline at end of file diff --git a/schemas/components/fl:button.toggle.json b/schemas/components/fl:button.toggle.json index deddf81e..a76fd246 100644 --- a/schemas/components/fl:button.toggle.json +++ b/schemas/components/fl:button.toggle.json @@ -1,14 +1,10 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Toggle button element", - "usable": true, - "type": "leaf", - "headers": ["FL/Fl_Toggle_Button.H"], - "extends": "fl:button", - "codegen": {"extends": "ui"}, - "props":{ - }, - "computed":{ - - } -} \ No newline at end of file + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Toggle button element", + "exposed": true, + "type": "leaf", + "headers": ["FL/Fl_Toggle_Button.H"], + "extends": "fl:button", + "codegen": { "extends": "ui" }, + "fields": {} +} diff --git a/schemas/components/fl:flex.json b/schemas/components/fl:flex.json index 35067c65..6f524629 100644 --- a/schemas/components/fl:flex.json +++ b/schemas/components/fl:flex.json @@ -1,50 +1,48 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Flex container", - "usable": true, - "type": "node", - "headers": ["FL/Fl_Flex.H"], - "extends": "fl:group", - "codegen": {"extends": "ui"}, - "props":{ - "layout.type":{ - "type": "enum", - "subtype": "fl_flex_layout", - "description": "Set to `row` or `column` (default)", - "code": "/*TODO*/" - }, - "layout.gap":{ - "type":"scalar-1", - "description": "Gap between elements in this flex", - "code": "w.gap(computed[0]);" - }, - "margin":{ - "type": "scalar-4", - "description": "Margin left top right bottom", - "code": "w.margin(computed[0],computed[1],computed[2],computed[3]);" - }, - "margin.left":{ - "type": "scalar-1", - "code": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(computed[0],tmp[1],tmp[2],tmp[3]);" - }, - "margin.top":{ - "type": "scalar-1", - "code": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(tmp[0],computed[0],tmp[2],tmp[3]);" - }, - "margin.right":{ - "type": "scalar-1", - "code": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(tmp[0],tmp[1],computed[0],tmp[3]);" - }, - "margin.bottom":{ - "type": "scalar-1", - "code": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(tmp[0],tmp[1],tmp[2],computed[0]);" - }, - "layout.spacing":{ - "type":"scalar-1", - "description": "Gap between elements in this flex", - "code": "w.spacing(computed[0]);" - } + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Flex container", + "exposed": true, + "type": "node", + "headers": ["FL/Fl_Flex.H"], + "extends": "fl:group", + "codegen": { "extends": "ui" }, + "fields": { + "layout.type": { + "type": "enum", + "subtype": "fl_flex_layout", + "description": "Set to `row` or `column` (default)", + "setter": "/*TODO*/" }, - "computed":{ + "layout.gap": { + "type": "scalar-1", + "description": "Gap between elements in this flex", + "setter": "w.gap(computed[0]);" + }, + "margin": { + "type": "scalar-4", + "description": "Margin left top right bottom", + "setter": "w.margin(computed[0],computed[1],computed[2],computed[3]);" + }, + "margin.left": { + "type": "scalar-1", + "setter": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(computed[0],tmp[1],tmp[2],tmp[3]);" + }, + "margin.top": { + "type": "scalar-1", + "setter": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(tmp[0],computed[0],tmp[2],tmp[3]);" + }, + "margin.right": { + "type": "scalar-1", + "setter": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(tmp[0],tmp[1],computed[0],tmp[3]);" + }, + "margin.bottom": { + "type": "scalar-1", + "setter": "int tmp[4];w.margin(tmp,tmp+1,tmp+2,tmp+3);w.margin(tmp[0],tmp[1],tmp[2],computed[0]);" + }, + "layout.spacing": { + "type": "scalar-1", + "description": "Gap between elements in this flex", + "setter": "w.spacing(computed[0]);" } -} \ No newline at end of file + } +} diff --git a/schemas/components/fl:grid.json b/schemas/components/fl:grid.json index 145713e1..6be7201f 100644 --- a/schemas/components/fl:grid.json +++ b/schemas/components/fl:grid.json @@ -1,13 +1,10 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Grid container", - "usable": true, - "type": "node", - "headers": ["FL/Fl_Grid.H"], - "extends": "fl:group", - "codegen": {"extends": "ui"}, - "props":{ - }, - "computed":{ - } -} \ No newline at end of file + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Grid container", + "exposed": true, + "type": "node", + "headers": ["FL/Fl_Grid.H"], + "extends": "fl:group", + "codegen": { "extends": "ui" }, + "fields": {} +} diff --git a/schemas/components/fl:group.json b/schemas/components/fl:group.json index 3035e947..7d6d508f 100644 --- a/schemas/components/fl:group.json +++ b/schemas/components/fl:group.json @@ -1,23 +1,21 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Basic group element", - "usable": true, - "type": "node", - "headers": ["FL/Fl_Group.H"], - "extends": "fl:base", - "codegen": {"extends": "ui"}, - "props":{ - "box.clip":{ - "type":"flag", - "description": "Define the clipping behaviour of this group.", - "code": "w.clip_children(computed);" - }, - "resizable":{ - "type":"raw", - "description": "Set to ``, `self` or the name of a direct child", - "code": "/*TODO*/" - } + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Basic group element", + "exposed": true, + "type": "node", + "headers": ["FL/Fl_Group.H"], + "extends": "fl:base", + "codegen": { "extends": "ui" }, + "fields": { + "box.clip": { + "type": "flag", + "description": "Define the clipping behaviour of this group.", + "setter": "w.clip_children(computed);" }, - "computed":{ + "resizable": { + "type": "raw", + "description": "Set to ``, `self` or the name of a direct child", + "setter": "/*TODO*/" } -} \ No newline at end of file + } +} diff --git a/schemas/components/fl:input.json b/schemas/components/fl:input.json index e14beb2a..741142e8 100644 --- a/schemas/components/fl:input.json +++ b/schemas/components/fl:input.json @@ -1,13 +1,10 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Basic input element", - "usable": true, - "type": "leaf", - "headers": ["FL/Fl_Input.H"], - "extends": "fl:base", - "codegen": {"extends": "ui"}, - "props":{ - }, - "computed":{ - } -} \ No newline at end of file + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Basic input element", + "exposed": true, + "type": "leaf", + "headers": ["FL/Fl_Input.H"], + "extends": "fl:base", + "codegen": { "extends": "ui" }, + "fields": {} +} diff --git a/schemas/components/fl:label.json b/schemas/components/fl:label.json index 1273a160..fee87ad6 100644 --- a/schemas/components/fl:label.json +++ b/schemas/components/fl:label.json @@ -1,14 +1,10 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Label element to a window", - "usable": true, - "type": "leaf", - "headers": ["FL/Fl_Box.H"], - "extends": "fl:base", - "codegen": {"extends": "ui"}, - "props":{ - }, - "computed":{ - - } -} \ No newline at end of file + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Label element to a window", + "exposed": true, + "type": "leaf", + "headers": ["FL/Fl_Box.H"], + "extends": "fl:base", + "codegen": { "extends": "ui" }, + "fields": {} +} diff --git a/schemas/components/fl:pack.json b/schemas/components/fl:pack.json index e03ae137..93069d61 100644 --- a/schemas/components/fl:pack.json +++ b/schemas/components/fl:pack.json @@ -1,13 +1,10 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Pack container", - "usable": true, - "type": "node", - "headers": ["FL/Fl_Pack.H"], - "extends": "fl:group", - "codegen": {"extends": "ui"}, - "props":{ - }, - "computed":{ - } -} \ No newline at end of file + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Pack container", + "exposed": true, + "type": "node", + "headers": ["FL/Fl_Pack.H"], + "extends": "fl:group", + "codegen": { "extends": "ui" }, + "fields": {} +} diff --git a/schemas/components/fl:scroll.json b/schemas/components/fl:scroll.json index 538cc206..79dfbe5e 100644 --- a/schemas/components/fl:scroll.json +++ b/schemas/components/fl:scroll.json @@ -1,13 +1,10 @@ { - "$schema": "../../commons/schemas/json-component.schema.json", - "description": "Scroll container", - "usable": true, - "type": "node", - "headers": ["FL/Fl_Scroll.H"], - "extends": "fl:group", - "codegen": {"extends": "ui"}, - "props":{ - }, - "computed":{ - } -} \ No newline at end of file + "$schema": "../../commons/schemas/json-component.schema.json", + "description": "Scroll container", + "exposed": true, + "type": "node", + "headers": ["FL/Fl_Scroll.H"], + "extends": "fl:group", + "codegen": { "extends": "ui" }, + "fields": {} +} diff --git a/schemas/components/fl:textbox.json b/schemas/components/fl:textbox.json index cdb96b89..7bc56d29 100644 --- a/schemas/components/fl:textbox.json +++ b/schemas/components/fl:textbox.json @@ -2,22 +2,17 @@ "$schema": "../../commons/schemas/json-component.schema.json", "description": "Blabla bla", "type": "leaf", + "exposed": true, "headers": ["FL/Fl_Button.H"], "codegen": { "extends": "ui" }, "extends": "fl:base", - "props": { + "fields": { "somethig": { "type": "string", - "code": "", - "alias": ["alias-a", "alias-b"] - } - }, - "computed": { - "computed-a": { - "type": "string", - "code": "", + "setter": "", + "getter": "", "alias": ["alias-a", "alias-b"] } } diff --git a/schemas/components/fl:window.json b/schemas/components/fl:window.json index 5aad333f..d122a3f9 100644 --- a/schemas/components/fl:window.json +++ b/schemas/components/fl:window.json @@ -1,22 +1,21 @@ { "$schema": "../../commons/schemas/json-component.schema.json", "description": "Base element to a window", - "usable": true, + "exposed": true, "type": "node", "headers": ["FL/Fl_Window.H", "FL/Fl_SVG_Image.H"], "extends": "fl:base", "codegen": { "extends": "ui" }, - "props": { + "fields": { "border": { "type": "flag", "description": "True if using a border around", - "code": "w.border(computed);" + "setter": "w.border(computed);" }, "icon": { "type": "string", "description": "True if using a border around", - "code": "w.icon(new Fl_SVG_Image(value));" + "setter": "w.icon(new Fl_SVG_Image(value));" } - }, - "computed": {} + } } diff --git a/scripts/codegen/components-new.schema.ts b/scripts/codegen/components-new.schema.ts deleted file mode 100644 index 6b17e255..00000000 --- a/scripts/codegen/components-new.schema.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Type as t } from "@sinclair/typebox" - -//New schema to replace the old one. -//Now props, computed, setters and getters are better linked together - -export const type_schema = t.Union([ - t.Literal('flag'), - t.Literal('enum'), - t.Literal('raw'), - t.Literal('path'), - t.Literal('string'), - t.Literal('color'), - t.Literal('scalar-1'), - t.Literal('scalar-2'), - t.Literal('scalar-4') -], { description: 'type', default: 'string' }); - -const entries_schema = t.Record(t.String(), t.Object({ - type: type_schema, - subtype: t.Optional(t.String()), - parse: t.Union([t.String(), t.Null()], { default: null, description: "From string to object. Default based on `type` (and `subtype`)" }), - //`serialize` would be the dual of parse, but it is not needed as types are all defined and serialization methods will not be custom. - setter: t.Union([t.String(), t.Null()], { default: null, description: "Operations on setting. If not set, this field is only available as a computed value." }), - getter: t.Union([t.String(), t.Null()], { default: null, description: "Implementation on how to extract value from object. If not set the field is not observable." }), - description: t.Optional(t.String()), - semantic: t.Optional(t.Boolean({ default: false, description: 'If true, this field has a strong semantic meaning. Used for semantic serialization of a document.' })), - alias: t.Optional(t.Array(t.String(), { description: "alias names", default: [] })) -})) - -export const widget_schema = t.Object({ - $schema: t.Optional(t.String()), - ns: t.Optional(t.String({ description: "Namespace for this component, for example `fl` in case of fltk wrappers." })), - name: t.Optional(t.String()), - exposed: t.Optional(t.Boolean({ default: true, description: "If true, this widget will be usable from XML and shown in the documentation, otherwise it is not exported." })), - description: t.Optional(t.String({ description: "While optional, this field is extremely important for exposed widgets" })), - use_main_header: t.Union([t.Null(), t.String()], { default: null, description: "If set, no class definition will be generated. It will instread load one already present. Used to track and document widgets which are not fully autogenerated." }), - headers: t.Optional(t.Array(t.String(), { description: "List of header files used by the widget in its public header file." })), - private_headers: t.Optional(t.Array(t.String(), { description: "List of header files used by the widget in its generated cpp file." })), - type: t.Union([t.Literal('leaf'), t.Literal('node'), t.Literal('container'), t.Literal('slot'), t.Literal('slot-contaiener')], { description: "Type of frame an instance of this widget class will default top." }), - codegen: t.Object({ - extends: t.Union([t.Null(), t.String()], { default: null, description: "If set, a class to inherit from, usually related to the fltk widget." }), - set_tail: t.Optional(t.Union([t.Null(), t.String()], { description: "If specified, what to do if a getter is not matched." })), - get_tail: t.Optional(t.Union([t.Null(), t.String()], { description: "If specified, what to do if a setter is not matched." })), - }, { additionalProperties: false }), - extends: t.Union([t.Null(), t.String()], { default: null, description: "If set, a vs widget to inherit from (not a c++ class name)" }), - skip_fields: t.Optional(t.Array(t.String(), { default: [], description: "Properties to be matched when set, but ignored." })), - fields: entries_schema, -}, { additionalProperties: false }) \ No newline at end of file diff --git a/scripts/codegen/components.schema.ts b/scripts/codegen/components.schema.ts index 9771fb56..b696cf7c 100644 --- a/scripts/codegen/components.schema.ts +++ b/scripts/codegen/components.schema.ts @@ -1,32 +1,48 @@ import { Type as t } from "@sinclair/typebox" -export const type_schema = t.Union([t.Literal('flag'), t.Literal('enum'), t.Literal('raw'), t.Literal('string'), t.Literal('path'), t.Literal('color'), t.Literal('scalar-1'), t.Literal('scalar-2'), t.Literal('scalar-4')], { description: 'type', default: 'string' }); +//New schema to replace the old one. +//Now props, computed, setters and getters are better linked together + +export const type_schema = t.Union([ + t.Literal('flag'), + t.Literal('enum'), + t.Literal('raw'), + t.Literal('path'), + t.Literal('string'), + t.Literal('color'), + t.Literal('scalar-1'), + t.Literal('scalar-2'), + t.Literal('scalar-4') +], { description: 'type', default: 'string' }); const entries_schema = t.Record(t.String(), t.Object({ type: type_schema, subtype: t.Optional(t.String()), - code: t.Union([t.String(), t.Null()], { default: null }), + parse: t.Optional(t.Union([t.String(), t.Null()], { default: undefined, description: "From string to object. Default based on `type` (and `subtype`)" })), + //`serialize` would be the dual of parse, but it is not needed as types are all defined and serialization methods will not be custom. + setter: t.Optional(t.Union([t.String(), t.Null()], { default: undefined, description: "Operations on setting. If undefined, this field is only available as a computed value. If null use default" })), + getter: t.Optional(t.Union([t.String(), t.Null()], { default: undefined, description: "Implementation on how to extract value from object. If undefined the field is not observable. If null use default" })), description: t.Optional(t.String()), + semantic: t.Optional(t.Boolean({ default: false, description: 'If true, this field has a strong semantic meaning. Used for semantic serialization of a document.' })), alias: t.Optional(t.Array(t.String(), { description: "alias names", default: [] })) -})) +}, { additionalProperties: false })) export const widget_schema = t.Object({ $schema: t.Optional(t.String()), - ns: t.Optional(t.String()), + ns: t.Optional(t.String({ description: "Namespace for this component, for example `fl` in case of fltk wrappers." })), name: t.Optional(t.String()), - usable: t.Optional(t.Boolean({ default: true })), - description: t.Optional(t.String()), - use_main_header: t.Union([t.Null(), t.String()], { default: null }), - headers: t.Optional(t.Array(t.String())), - type: t.Union([t.Literal('leaf'), t.Literal('node'), t.Literal('container'), t.Literal('slot'), t.Literal('slot-contaiener')]), - //public: t.Optional(t.Boolean({ default: true })), //probably replaced by usable + exposed: t.Optional(t.Boolean({ default: true, description: "If true, this widget will be usable from XML and shown in the documentation, otherwise it is not exported." })), + description: t.Optional(t.String({ description: "While optional, this field is extremely important for exposed widgets" })), + use_main_header: t.Union([t.Null(), t.String()], { default: null, description: "If set, no class definition will be generated. It will instread load one already present. Used to track and document widgets which are not fully autogenerated." }), + headers: t.Optional(t.Array(t.String(), { description: "List of header files used by the widget in its public header file." })), + private_headers: t.Optional(t.Array(t.String(), { description: "List of header files used by the widget in its generated cpp file." })), + type: t.Union([t.Literal('leaf'), t.Literal('node'), t.Literal('container'), t.Literal('slot'), t.Literal('slot-contaiener')], { description: "Type of frame an instance of this widget class will default top." }), codegen: t.Object({ - extends: t.Union([t.Null(), t.String()], { default: null }), - props_tail: t.Optional(t.Union([t.Null(), t.String()])), - computed_tail: t.Optional(t.Union([t.Null(), t.String()])), + extends: t.Union([t.Null(), t.String()], { default: null, description: "If set, a class to inherit from, usually related to the fltk widget." }), + set_tail: t.Optional(t.Union([t.Null(), t.String()], { description: "If specified, what to do if a getter is not matched." })), + get_tail: t.Optional(t.Union([t.Null(), t.String()], { description: "If specified, what to do if a setter is not matched." })), }, { additionalProperties: false }), - extends: t.Union([t.Null(), t.String()], { default: null }), - skip_props: t.Optional(t.Array(t.String(), { default: [], description: "Properties to be matched but ignored" })), - props: entries_schema, - computed: entries_schema, + extends: t.Union([t.Null(), t.String()], { default: null, description: "If set, a vs widget to inherit from (not a c++ class name)" }), + skip_fields: t.Optional(t.Array(t.String(), { default: [], description: "Properties to be matched when set, but ignored." })), + fields: entries_schema, }, { additionalProperties: false }) \ No newline at end of file diff --git a/scripts/codegen/gen-components-new.tsx b/scripts/codegen/gen-components-new.tsx deleted file mode 100644 index 822da4cf..00000000 --- a/scripts/codegen/gen-components-new.tsx +++ /dev/null @@ -1,214 +0,0 @@ -/* - Replacement for the old autogen to use the simplified props/computed getter/setter model. - TODO: Implement - - Code generator for code and derived specs from json schemas. - Use `quick` as optional arg to avoid generating the meson file again. - This will speed up compilation considerably and is fine as long as you have not added or removed files in the schema folder. -*/ - -import type { Static } from '@sinclair/typebox'; -import { $ } from 'bun' -import { Glob } from "bun"; -import { parse } from "node:path" -import { widget_schema, type_schema } from './components-new.schema'; -import { Value } from '@sinclair/typebox/value'; - -import { render, JSXXML } from 'jsx-xml' - - -function make_type_code(type: Static, subtype: string, code: string) { - if (type === 'raw') return code; - if (type === 'flag') return `bool computed; if((ok=ui_tree::h_flag(&computed,value,that))){${code}}` - else if (type === 'color') return `uint32_t computed; if((ok=ui_tree::h_colour(&computed,value,that))){${code}}` - else if (type === 'string') return code; - else if (type === 'scalar-1') return `size_t computed[1]; if((ok = ui_tree::h_px(1,computed,value,that))){${code}}` - else if (type === 'scalar-2') return `size_t computed[2]; if((ok = ui_tree::h_px(2,computed,value,that))){${code}}` - else if (type === 'scalar-4') return `size_t computed[4]; if((ok = ui_tree::h_px(4,computed,value,that))){${code}}` - else if (type === 'enum') return `int computed = ui_tree::${subtype}_i(value);if((ok=(computed!=-1))){${code}}` -} - -function gen_cpp(data: Static) { - const cextends = data.extends==null?null:data.extends.split(':'); - const cppname = `${data.ns}_${data.name?.replaceAll('.','_')}`; - - let class_decl = data.use_main_header === null ? ` -#pragma once - -#include -#include -${cextends ? `#include \n`:``} -${data.headers ? data.headers.map(x => `#include <${x}>\n`) : ``} - -namespace vs{ - -class ${cppname} : public ${data.codegen.extends}{ - public: - template - ${cppname}(ui_base* p,Args ...w):${data.codegen.extends}(p,w...){} - virtual frame_type_t default_frame_type() override {return frame_type_t::LEAF;} - virtual const char* class_name() override{return "${data.ns}:${data.name}";} - - virtual ~${cppname}(){} - - virtual int apply_prop(const char* prop, const char* value) override {return ${cppname}::_apply_prop(this,prop,value);} - virtual int get_computed(const char* prop, const char ** value) override {return ${cppname}::_get_computed(this,prop,value);}; - - public: - static int _apply_prop(${cppname}* that, const char* prop, const char* value); - static int _get_computed(${cppname}* that, const char* prop, const char** value); -}; - -} - `: - `#pragma once -#include <${data.use_main_header}> -`; - - let class_impl = ` -#include - -namespace vs{ - -int ${cppname}::_apply_prop(${cppname}* that, const char* prop, const char* value){ - auto& w = that->widget(); - bool ok = true; - if(false){} - ${(data.skip_props??[]).map(x=>{ - if(x[x.length-1]=='*')return `else if (strncmp(prop, "${x}", ${x.length-1})==0){}` - else return `else if(strcmp(prop,"${x}")==0){}` - }).join('\n\t')} - ${Object.entries(data.props).map(x => { - x[1].alias=x[1].alias??[] - x[1].alias.push(x[0]); - return x[1].alias.map(y => ` - ${x[1].description ? `//${x[1].description}` : ``} - else if(strcmp(prop,"${y}")==0){ - ${make_type_code(x[1].type, x[1].subtype ?? "", x[1].code??"")} - }`).join('\n') - } - ).join('\n') - } - ${cextends === null ? `else {return 2;}\nreturn ok?0:1;` : `if(!ok) return 1; else return ${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}::_apply_prop((${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}*)that,prop,value);`} -} - -int ${cppname}:: _get_computed(${cppname} * that, const char* prop, const char** value) { - auto& w = that->widget(); - bool ok = true; - if(false){} - ${Object.entries(data.computed).map(x => { - x[1].alias?.push(x[0]); - return x[1].alias?.map(y => ` - ${x[1].description ? `//${x[1].description}` : ``} - else if(strcmp(prop,"${y}")==0){ - ${x[1].code} - }`).join('\n') - } - ).join('\n') - } - ${cextends === null ? `else {return 2;}\nreturn ok?0:1;` : `if(!ok) return 1; else return ${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}::_get_computed((${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}*)that,prop,value);`} -} - -} -`; - - let parser_selector = - data.usable!=false ? - data.type === 'leaf' ? `mkNSLeafWidget(${data.ns}, ${data.name}, ${cppname})` : - data.type === 'node' ? `mkNSNodeWidget(${data.ns}, ${data.name}, ${cppname}) ` : - data.type === 'container' ? `mkNSContainerWidget(${data.ns}, ${data.name}, ${cppname})` : - data.type === 'slot' ? `mkNSSlotWidget(${data.ns}, ${data.name}, ${cppname})` : - data.type === 'slot-contaiener' ? `mkNSSlotContainerWidget(${data.ns}, ${data.name}, ${cppname})` : - ``:``; - return [class_decl, class_impl, parser_selector] -} - - -function gen_d_ts(data: Static) { } - -function gen_xsd(data: Static) { } - -function gen_xml_editor(data: Static) { - return - {data.description ?? "No description provided"} - {Object.entries(data.props).map(x => {x[1].description ?? 'No description provided'})} - {Object.entries(data.computed).map(x => {x[1].description ?? 'No description provided'})} - -} - -if (process.argv[2] != 'quick') { - await $`rm -rf ./src/components/autogen/` -} -await $`mkdir -p ./src/components/autogen/` - -await $`rm -rf ./include/components/autogen/` -await $`mkdir -p ./include/components/autogen/` - -await $`rm -rf ./commons/schemas/components/` -await $`mkdir -p ./commons/schemas/components/` - -const glob = new Glob("./schemas/components/**/*.json"); - - -const cpp_files = [] -const h_files = [] -const parser_entries = [] - -for await (const file of glob.scan(".")) { - const tmp = Value.Default(widget_schema, (await Bun.file(file).json())) - if (!Value.Check(widget_schema, tmp)) { - console.warn(`[SKIP] ${file} since its syntax is not comformant`); - for (const i of [...Value.Errors(widget_schema, tmp)]) { - console.error(i.path, i.message); - } - } - else { - const namepieces = parse(file).name.split(':'); - tmp.ns ??= namepieces[0] - tmp.name ??= namepieces[1] - - //Cpp code - cpp_files.push(`./src/components/autogen/${tmp.ns}/${tmp.name}.cpp`) - h_files.push(`components/autogen/${tmp.ns}/${tmp.name}.hpp`) - { - const ret = gen_cpp(tmp); - - await $`mkdir -p ./include/components/autogen/${tmp.ns}/` - await Bun.write(`./include/components/autogen/${tmp.ns}/${tmp.name}.hpp`, ret[0]) - - await $`mkdir -p ./src/components/autogen/${tmp.ns}/` - await Bun.write(`./src/components/autogen/${tmp.ns}/${tmp.name}.cpp`, ret[1]) - parser_entries.push(ret[2]) - } - - //XML Model for the Editor - { - const ret = gen_xml_editor(tmp); - await $`mkdir -p ./commons/schemas/components/${tmp.ns}/` - await Bun.write(`./commons/schemas/components/${tmp.ns}/${tmp.name}.xml`, render(ret)) - } - - //XML Schema for aid when writing code - //TODO: - - //Typescript types - //TODO: - - console.log(`[DONE] ${parse(file).base}`) - } -} - -await Bun.write('./include/components/autogen/index.hpp', h_files.map(x => `#include <${x}>`).join('\n')) -await Bun.write('./src/ui.xml-widgets.autogen.cpp', parser_entries.join('\n')) - -//TODO -await Bun.write('./include/cbindings/components.autogen.h', '') -//TODO -await Bun.write('./src/cbindings/components.autogen.cpp', '') - -//Save the schema, so that our json files can all be validated in the editor while writing them. -await Bun.write('./commons/schemas/json-component.schema.json', JSON.stringify(widget_schema, null, 4)) - -if (process.argv[2] != 'quick') { - await Bun.write('./src/components/autogen/meson.build', `autogen_components = [${cpp_files.map(x => `'${x}'`)}]`) -} \ No newline at end of file diff --git a/scripts/codegen/gen-components.tsx b/scripts/codegen/gen-components.tsx index 0e07c0c8..15c45952 100644 --- a/scripts/codegen/gen-components.tsx +++ b/scripts/codegen/gen-components.tsx @@ -15,15 +15,16 @@ import { render, JSXXML } from 'jsx-xml' function make_type_code(type: Static, subtype: string, code: string) { - if (type === 'raw') return code; - if (type === 'flag') return `bool computed; if((ok=field_types::h_flag(&computed,value,that))){${code}}` + if (false) { } + else if (type === 'flag') return `bool computed; if((ok=field_types::h_flag(&computed,value,that))){${code}}` + else if (type === 'enum') return `int computed = field_types::${subtype}_i(value);if((ok=(computed!=-1))){${code}}` + else if (type === 'raw') return code; + else if (type === 'path') return `/*TODO*/` else if (type === 'color') return `uint32_t computed; if((ok=field_types::h_colour(&computed,value,that))){${code}}` else if (type === 'string') return code; else if (type === 'scalar-1') return `size_t computed[1]; if((ok = field_types::h_px(1,computed,value,that))){${code}}` else if (type === 'scalar-2') return `size_t computed[2]; if((ok = field_types::h_px(2,computed,value,that))){${code}}` else if (type === 'scalar-4') return `size_t computed[4]; if((ok = field_types::h_px(4,computed,value,that))){${code}}` - else if (type === 'enum') return `int computed = field_types::${subtype}_i(value);if((ok=(computed!=-1))){${code}}` - else if (type === 'path') return `/*TODO*/` } function gen_cpp(data: Static) { @@ -73,46 +74,46 @@ int ${cppname}::_apply_prop(${cppname}* that, const char* prop, const char* valu auto& w = that->widget(); bool ok = true; if(false){} - ${(data.skip_props ?? []).map(x => { + ${(data.skip_fields ?? []).map(x => { if (x[x.length - 1] == '*') return `else if (strncmp(prop, "${x}", ${x.length - 1})==0){}` else return `else if(strcmp(prop,"${x}")==0){}` }).join('\n\t')} - ${Object.entries(data.props).map(x => { + ${Object.entries(data.fields).filter(x => x[1].setter != undefined).map(x => { x[1].alias = x[1].alias ?? [] x[1].alias.push(x[0]); return x[1].alias.map(y => ` ${x[1].description ? `//${x[1].description}` : ``} else if(strcmp(prop,"${y}")==0){ - ${make_type_code(x[1].type, x[1].subtype ?? "", x[1].code ?? "")} + ${make_type_code(x[1].type, x[1].subtype ?? "", x[1].setter ?? "")} }`).join('\n') } ).join('\n') } - ${cextends === null ? `else {return 2;}\nreturn ok?0:1;` : `if(!ok) return 1; else return ${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}::_apply_prop((${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}*)that,prop,value);`} + ${cextends === null ? `else {return 2;}\nreturn ok?0:1;` : `if(!ok) return 1; else return ${data.codegen.set_tail ?? `${cextends[0]}_${cextends[1]}`}::_apply_prop((${data.codegen.set_tail ?? `${cextends[0]}_${cextends[1]}`}*)that,prop,value);`} } int ${cppname}:: _get_computed(${cppname} * that, const char* prop, const char** value) { auto& w = that->widget(); bool ok = true; if(false){} - ${Object.entries(data.computed).map(x => { + ${Object.entries(data.fields).filter(x => x[1].getter != undefined).map(x => { x[1].alias?.push(x[0]); return x[1].alias?.map(y => ` ${x[1].description ? `//${x[1].description}` : ``} else if(strcmp(prop,"${y}")==0){ - ${x[1].code} + ${x[1].getter} }`).join('\n') } ).join('\n') } - ${cextends === null ? `else {return 2;}\nreturn ok?0:1;` : `if(!ok) return 1; else return ${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}::_get_computed((${data.codegen.props_tail ?? `${cextends[0]}_${cextends[1]}`}*)that,prop,value);`} + ${cextends === null ? `else {return 2;}\nreturn ok?0:1;` : `if(!ok) return 1; else return ${data.codegen.get_tail ?? `${cextends[0]}_${cextends[1]}`}::_get_computed((${data.codegen.get_tail ?? `${cextends[0]}_${cextends[1]}`}*)that,prop,value);`} } } `; let parser_selector = - data.usable != false ? + data.exposed != false ? data.type === 'leaf' ? `mkNSLeafWidget(${data.ns}, ${data.name}, ${cppname})` : data.type === 'node' ? `mkNSNodeWidget(${data.ns}, ${data.name}, ${cppname}) ` : data.type === 'container' ? `mkNSContainerWidget(${data.ns}, ${data.name}, ${cppname})` : @@ -130,8 +131,14 @@ function gen_xsd(data: Static) { } function gen_xml_editor(data: Static) { return {data.description ?? "No description provided"} - {Object.entries(data.props).map(x => {x[1].description ?? 'No description provided'})} - {Object.entries(data.computed).map(x => {x[1].description ?? 'No description provided'})} + {Object.entries(data.fields).map(x => + {x[1].description ?? 'No description provided'})} + } @@ -146,6 +153,9 @@ await $`mkdir -p ./include/components/autogen/` await $`rm -rf ./commons/schemas/components/` await $`mkdir -p ./commons/schemas/components/` +//Save the schema, so that our json files can all be validated in the editor while writing them. +await Bun.write('./commons/schemas/json-component.schema.json', JSON.stringify(widget_schema, null, 4)) + const glob = new Glob("./schemas/components/**/*.json"); @@ -205,9 +215,6 @@ await Bun.write('./include/cbindings/components.autogen.h', '') //TODO await Bun.write('./src/cbindings/components.autogen.cpp', '') -//Save the schema, so that our json files can all be validated in the editor while writing them. -await Bun.write('./commons/schemas/json-component.schema.json', JSON.stringify(widget_schema, null, 4)) - if (process.argv[2] != 'quick') { await Bun.write('./src/components/autogen/meson.build', `autogen_components = [${cpp_files.map(x => `'${x}'`)}]`) } From 7a00484ef703adabbe4b67340450ff0e05548ff7 Mon Sep 17 00:00:00 2001 From: karurochari Date: Fri, 13 Dec 2024 11:23:50 +0000 Subject: [PATCH 04/11] Moved itoa from c script to pipeline --- bindings/native/include/stub.h | 1 - bindings/native/include/vs.h | 5 ++++- bindings/native/src/stub.c | 26 -------------------------- src/pipelines/tcc-c.cpp | 28 ++++++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 28 deletions(-) diff --git a/bindings/native/include/stub.h b/bindings/native/include/stub.h index 6c58afa3..14e2b438 100644 --- a/bindings/native/include/stub.h +++ b/bindings/native/include/stub.h @@ -1,5 +1,4 @@ #pragma once #include "vs.h" -char* itoa(int value, char* result, int base); void* vs_set_env(void* ptr); \ No newline at end of file diff --git a/bindings/native/include/vs.h b/bindings/native/include/vs.h index f5fcddc1..18d500f6 100644 --- a/bindings/native/include/vs.h +++ b/bindings/native/include/vs.h @@ -104,4 +104,7 @@ extern void vs_debug(const char* key, const char* value); #define $cb(x) void* __EXPORT_CB__##x = x; #define $plotter(x) void* __EXPORT_DRW_##x = x; #define $field vs_field_t MAKE_UNIQUE_VARIABLE_NAME(__EXPORT_FIELD_)= -#define $fn(x) void* __EXPORT_UKN_##x = x; \ No newline at end of file +#define $fn(x) void* __EXPORT_UKN_##x = x; + +//Extra functions +char* itoa(int value, char* result, int base); diff --git a/bindings/native/src/stub.c b/bindings/native/src/stub.c index eb8798b1..aee1f313 100644 --- a/bindings/native/src/stub.c +++ b/bindings/native/src/stub.c @@ -1,31 +1,5 @@ #include -char* itoa(int value, char* result, int base) { - // check that the base if valid - if (base < 2 || base > 36) { *result = '\0'; return result; } - - char* ptr = result, *ptr1 = result, tmp_char; - int tmp_value; - - do { - tmp_value = value; - value /= base; - *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; - } while ( value ); - - // Apply negative sign - if (tmp_value < 0) *ptr++ = '-'; - *ptr-- = '\0'; - - // Reverse the string - while(ptr1 < ptr) { - tmp_char = *ptr; - *ptr--= *ptr1; - *ptr1++ = tmp_char; - } - return result; -} - node_t vs_self = 0; void* vs_set_env(void* ptr){ diff --git a/src/pipelines/tcc-c.cpp b/src/pipelines/tcc-c.cpp index 90c3a8f6..a809e10d 100644 --- a/src/pipelines/tcc-c.cpp +++ b/src/pipelines/tcc-c.cpp @@ -22,6 +22,32 @@ void tcc_log_symbol_func_xml(const pugi::xml_node& ctx, const char * msg, const ui_tree_xml::log(severety_t::INFO, ctx , msg, name); } +char* itoa(int value, char* result, int base) { + // check that the base if valid + if (base < 2 || base > 36) { *result = '\0'; return result; } + + char* ptr = result, *ptr1 = result, tmp_char; + int tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; + } while ( value ); + + // Apply negative sign + if (tmp_value < 0) *ptr++ = '-'; + *ptr-- = '\0'; + + // Reverse the string + while(ptr1 < ptr) { + tmp_char = *ptr; + *ptr--= *ptr1; + *ptr1++ = tmp_char; + } + return result; +} + //'/home/checkroom/Documents/projects/vs-fltk/subprojects/libtcc/tcc' test.c -I../../subprojects/libtcc/include/ -L. -L../../subprojects/libtcc -lapp static void vs_debug(const char* k, const char* v){globals::debug(k,v);} @@ -81,6 +107,8 @@ std::shared_ptr tcc_c_pipeline(bool is_runtime, vs::ui_base* obj, const cha script->add_sym("vs_set", (void *)+[](ui_base* w,const char* k, const void* v){if(w==nullptr)return -1;std::string tmp = std::string("#s_")+k;return w->use_setter(w->resolve_symbol_local(tmp.c_str(), false), v);}); script->add_sym("vs_get", (void *)+[](ui_base* w,const char* k, void** v){if(w==nullptr)return -1;std::string tmp = std::string("#g_")+k;return w->use_getter(w->resolve_symbol_local(tmp.c_str(), false), v);}); + //Runtime functions + script->add_sym("itoa", (void *)itoa); // Fragments of stdlib // --- cstring From b2250e9a01b626c45d9fd60ab3370c5c6911324b Mon Sep 17 00:00:00 2001 From: karurochari Date: Fri, 13 Dec 2024 11:42:41 +0000 Subject: [PATCH 05/11] Adding support for feature selection in the build file. --- meson.build | 115 +++++++++++++++++++++++++++++++++++--------------- meson.options | 2 +- 2 files changed, 81 insertions(+), 36 deletions(-) diff --git a/meson.build b/meson.build index db62a434..d92fadaa 100644 --- a/meson.build +++ b/meson.build @@ -11,15 +11,47 @@ cmake = import('cmake') cc = meson.get_compiler('c') cxx = meson.get_compiler('cpp') - -need_pugixml=false -need_fltk=false + +need_pugixml = false +need_fltk = false + +# Full dependency list for vs_fltk + +vs_fltk_deps = [] + +# Process options + +if get_option('use_tcc') + add_global_arguments(['-DVS_USE_TCC'], language: ['cpp', 'c']) +endif + +if get_option('use_quickjs') + add_global_arguments(['-DVS_USE_QJS'], language: ['cpp', 'c']) +endif + +if get_option('use_wamr') + add_global_arguments(['-DVS_USE_WAMR'], language: ['cpp', 'c']) +endif + +if get_option('use_lua') and get_option('use_luajit') + error( + 'Only lua or luajit can be used. At least one must be disabled from the options', + ) +endif + +if get_option('use_lua') + add_global_arguments(['-DVS_USE_LUA'], language: ['cpp', 'c']) +endif + +if get_option('use_luajit') + add_global_arguments(['-DVS_USE_LUAJIT'], language: ['cpp', 'c']) +endif if get_option('use_system_pugixml') - pugixml_dep = dependency('pugixml', version : '>=1.14', required: false) + pugixml_dep = dependency('pugixml', version: '>=1.14', required: false) if pugixml_dep.found() == false need_pugixml = true - endif + endif endif if need_pugixml or get_option('use_system_pugixml') == false #Used by pugi to enable compact mode. I might want to disable execptions too. @@ -29,14 +61,14 @@ if need_pugixml or get_option('use_system_pugixml') == false endif if get_option('use_system_fltk') - libfltk_dep = dependency('fltk', version : '>=1.4', required: false) - libfltk_images_dep = dependency('fltk-images', version : '>=1.4', required: false) - libfltk_forms_dep = dependency('fltk-images', version : '>=1.4', required: false) - if libfltk_dep.found()==false or libfltk_images_dep.found()==false or libfltk_forms_dep.found()==false - need_fltk=true + libfltk_dep = dependency('fltk', version: '>=1.4', required: false) + libfltk_images_dep = dependency('fltk-images', version: '>=1.4', required: false) + libfltk_forms_dep = dependency('fltk-images', version: '>=1.4', required: false) + if libfltk_dep.found() == false or libfltk_images_dep.found() == false or libfltk_forms_dep.found() == false + need_fltk = true endif endif -if need_fltk==true or get_option('use_system_fltk') == false +if need_fltk == true or get_option('use_system_fltk') == false opt_fltk = cmake.subproject_options() opt_fltk.add_cmake_defines( { @@ -60,26 +92,42 @@ if need_fltk==true or get_option('use_system_fltk') == false #libfltk_gl_dep = libfltk_proj.dependency(['fltk-gl']) endif -opt_wamr = cmake.subproject_options() -opt_wamr.add_cmake_defines( - { - 'WAMR_BUILD_INTERP': 1, - 'WAMR_BUILD_PLATFORM': target_machine.system(), - }, -) +if get_option('use_wamr') + opt_wamr = cmake.subproject_options() + opt_wamr.add_cmake_defines( + { + 'WAMR_BUILD_INTERP': 1, + 'WAMR_BUILD_PLATFORM': target_machine.system(), + }, + ) + wamr_proj = cmake.subproject('wamr', options: opt_wamr) + wamr_dep = wamr_proj.dependency('wamr') -wamr_proj = cmake.subproject('wamr', options: opt_wamr) -wamr_dep = wamr_proj.dependency('wamr') + vs_fltk_deps += wamr_dep -opt_quickjs = cmake.subproject_options() -opt_quickjs.add_cmake_defines( - { - 'CMAKE_POSITION_INDEPENDENT_CODE': true, - }, -) +endif + +if get_option('use_quickjs') + opt_quickjs = cmake.subproject_options() + opt_quickjs.add_cmake_defines( + { + 'CMAKE_POSITION_INDEPENDENT_CODE': true, + }, + ) + + quickjs_proj = cmake.subproject('quickjs', options: opt_quickjs) + quickjs_dep = quickjs_proj.dependency('qjs') + + vs_fltk_deps += quickjs_dep + +endif -quickjs_proj = cmake.subproject('quickjs', options: opt_quickjs) -quickjs_dep = quickjs_proj.dependency('qjs') +if get_option('use_tcc') + libtcc_proj = subproject('libtcc') + libtcc_dep = libtcc_proj.get_variable('libtcc_dep') + + vs_fltk_deps += libtcc_dep +endif md4c_proj = cmake.subproject('md4c') md4c_dep = md4c_proj.dependency('md4c') @@ -104,9 +152,6 @@ vs_templ_dep = vs_templ_proj.get_variable('vs_templ_dep') treesitter_proj = subproject('tree-sitter') treesitter_dep = treesitter_proj.get_variable('tree_sitter_dep') -libtcc_proj = subproject('libtcc') -libtcc_dep = libtcc_proj.get_variable('libtcc_dep') - #uv_proj = subproject('libuv') #uv_dep = uv_proj.get_variable('libuv_dep') @@ -133,13 +178,15 @@ sqlite_dep = dependency( ) #TODO handle the case in which libcurl does not exist. Missing is fine, just no https/http prefix for paths. -curl = dependency('libcurl', required: false) +if get_option('use_curl') + curl = dependency('libcurl', required: false) +endif include_dirs = include_directories(['./include']) # Prepare `vs` -vs_fltk_deps = [ +vs_fltk_deps += [ libfltk_dep, libfltk_images_dep, libfltk_forms_dep, @@ -152,8 +199,6 @@ vs_fltk_deps = [ #uv_dep, The one provided with the system if present should be fine for our tasks hashlib_dep, - libtcc_dep, - quickjs_dep, sqlite_dep, sqlitecpp_dep, diff --git a/meson.options b/meson.options index 4d15d92f..6f0e4f53 100644 --- a/meson.options +++ b/meson.options @@ -8,7 +8,7 @@ option('use_wamr', type: 'boolean', value: true) # Only one between lua and luajit should be enabled option('use_lua', type: 'boolean', value: true) -option('use_luajit', type: 'boolean', value: true) +option('use_luajit', type: 'boolean', value: false) # If present, else build it manually option('use_system_fltk', type: 'boolean', value: true) From 24eb6933d63ca8a8e613247f62e0ad5255f18ccf Mon Sep 17 00:00:00 2001 From: karurochari Date: Fri, 13 Dec 2024 11:58:21 +0000 Subject: [PATCH 06/11] Added checks to compile out parts which are not compatible based on use options --- include/pipelines/quickjs-js.hpp | 7 ++++- include/pipelines/tcc-c.hpp | 7 ++++- include/utils/tcc-wrap.hpp | 5 +++- meson.options | 4 +-- src/pipelines/quickjs-js.cpp | 6 +++- src/pipelines/tcc-c.cpp | 6 +++- src/ui-base.cpp | 3 +- src/ui-tree.xml.cpp | 4 +++ src/utils/env.cpp | 49 +++++++++++++++++++++++++------- 9 files changed, 73 insertions(+), 18 deletions(-) diff --git a/include/pipelines/quickjs-js.hpp b/include/pipelines/quickjs-js.hpp index 76b12289..0a1ba86b 100644 --- a/include/pipelines/quickjs-js.hpp +++ b/include/pipelines/quickjs-js.hpp @@ -1,5 +1,7 @@ #pragma once +#if VS_USE_QJS + #include #include #include @@ -48,4 +50,7 @@ inline std::shared_ptr qjs_js_pipeline_xml(bool is_runtime, vs::ui_ba } -} \ No newline at end of file +} + + +#endif \ No newline at end of file diff --git a/include/pipelines/tcc-c.hpp b/include/pipelines/tcc-c.hpp index 9997ec01..be206a34 100644 --- a/include/pipelines/tcc-c.hpp +++ b/include/pipelines/tcc-c.hpp @@ -1,5 +1,8 @@ #pragma once +#if VS_USE_TCC + + #include #include #include @@ -35,4 +38,6 @@ inline std::shared_ptr tcc_c_pipeline_xml(bool is_runtime, vs::ui_base* obj } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/include/utils/tcc-wrap.hpp b/include/utils/tcc-wrap.hpp index b4c87eff..4d7f8f39 100644 --- a/include/utils/tcc-wrap.hpp +++ b/include/utils/tcc-wrap.hpp @@ -1,5 +1,6 @@ #pragma once +#if VS_USE_TCC /* Utility library to simplify the usage of libtcc */ @@ -86,4 +87,6 @@ class tcc{ compileA->relocate(); return std::move(compileA); } -}; \ No newline at end of file +}; + +#endif \ No newline at end of file diff --git a/meson.options b/meson.options index 6f0e4f53..72d97d5f 100644 --- a/meson.options +++ b/meson.options @@ -2,8 +2,8 @@ option('tests', type: 'boolean', value: true) option('use_curl', type: 'boolean', value: true) -option('use_tcc', type: 'boolean', value: true) -option('use_quickjs', type: 'boolean', value: true) +option('use_tcc', type: 'boolean', value: false) +option('use_quickjs', type: 'boolean', value: false) option('use_wamr', type: 'boolean', value: true) # Only one between lua and luajit should be enabled diff --git a/src/pipelines/quickjs-js.cpp b/src/pipelines/quickjs-js.cpp index 1f3bb19a..74fb2df2 100644 --- a/src/pipelines/quickjs-js.cpp +++ b/src/pipelines/quickjs-js.cpp @@ -1,3 +1,5 @@ +#if VS_USE_QJS + #include #include @@ -328,4 +330,6 @@ std::shared_ptr qjs_js_pipeline(bool is_runtime, vs::ui_base* obj, co } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/pipelines/tcc-c.cpp b/src/pipelines/tcc-c.cpp index a809e10d..46281f60 100644 --- a/src/pipelines/tcc-c.cpp +++ b/src/pipelines/tcc-c.cpp @@ -1,3 +1,5 @@ +#if VS_USE_TCC + #include "globals.hpp" #include "ui-frame.hpp" #include "ui.hpp" @@ -248,4 +250,6 @@ std::shared_ptr> tcc_c_pipeline_apply(const std::shared_ptr #undef LIBT } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/ui-base.cpp b/src/ui-base.cpp index 97f8afa9..e3fe1453 100644 --- a/src/ui-base.cpp +++ b/src/ui-base.cpp @@ -6,7 +6,6 @@ #include #include "pipelines/quickjs-js.hpp" -#include "quickjs.h" #include "ui-frame.hpp" #include "utils/strings.hpp" @@ -348,6 +347,7 @@ int ui_base::use_callback(const symbol_ret_t& sym, ui_base * node){ } } else if(sym.found_at->get_mode()==frame_mode_t::QUICKJS){ + #if VS_USE_QJS //TODO: Add support for quickjs script modules pipelines::quickjs_t* script = (pipelines::quickjs_t*)sym.found_at->script.get(); auto globalThis = JS_GetGlobalObject(script->ctx); @@ -355,6 +355,7 @@ int ui_base::use_callback(const symbol_ret_t& sym, ui_base * node){ JS_FreeValue(script->ctx, ret); JS_FreeValue(script->ctx, globalThis); return 0; + #endif } else{ //Callback type not supported yet. diff --git a/src/ui-tree.xml.cpp b/src/ui-tree.xml.cpp index 6c61bb2a..5c34fe66 100755 --- a/src/ui-tree.xml.cpp +++ b/src/ui-tree.xml.cpp @@ -437,6 +437,7 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u if (mode == frame_mode_t::NATIVE || mode == frame_mode_t::AUTO) { const auto &lang = root.attribute("lang").as_string(mode==frame_mode_t::NATIVE?"c":""); if (strcmp(lang, "c") == 0) { + #ifdef VS_USE_TCC auto compiler = pipelines::tcc_c_pipeline_xml(true, is_module?nullptr:current, root, compact, (link_with==nullptr)?nullptr:tmp_link.c_str()); if(compiler!=nullptr){ current->set_mode(frame_mode_t::NATIVE); @@ -452,11 +453,13 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u } } continue; + #endif } } if (mode == frame_mode_t::QUICKJS || mode == frame_mode_t::AUTO) { const auto &lang = root.attribute("lang").as_string(mode==frame_mode_t::QUICKJS?"js":""); if (strcmp(lang, "js") == 0) { + #ifdef VS_USE_QJS auto compiler = pipelines::qjs_js_pipeline_xml(true, is_module?nullptr:current, root, (link_with==nullptr)?nullptr:tmp_link.c_str()); if(compiler!=nullptr){ current->set_mode(frame_mode_t::QUICKJS); @@ -472,6 +475,7 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u } } continue; + #endif } } if (mode == frame_mode_t::LUA || mode == frame_mode_t::AUTO) { diff --git a/src/utils/env.cpp b/src/utils/env.cpp index ae51c3a7..68dab8cb 100644 --- a/src/utils/env.cpp +++ b/src/utils/env.cpp @@ -8,9 +8,18 @@ #include #include + +#ifdef VS_USE_QJS #include +#endif + +#ifdef VS_USE_TCC #include "subprojects/libtcc/config.h" +#endif + +#ifdef VS_USE_WAMR #include "subprojects/wamr/core/version.h" +#endif //#include #ifdef HAS_CURL @@ -71,13 +80,21 @@ path_env_t mk_env(const char* arg0,const char* arg1){ js_rt_t::js_rt_t(){ + #if VS_USE_QJS auto tmp=JS_NewRuntime(); //TODO define limits somewhere //JS_SetMemoryLimit(tmp, 80 * 1024); //JS_SetMaxStackSize(tmp, 10 * 1024); rt=tmp; + #endif } -js_rt_t::~js_rt_t(){JS_FreeRuntime((JSRuntime*)rt);} + +js_rt_t::~js_rt_t(){ + #if VS_USE_QJS + JS_FreeRuntime((JSRuntime*)rt); + #endif +} + void* js_rt_t::operator()(){return rt;} @@ -121,18 +138,30 @@ void prepare_db(){ versions_t get_versions(){ versions_t tmp; + tmp.vs=vs_version(); # ifdef HAS_CURL - tmp.curl=curl_version(); + tmp.curl = curl_version(); # else - tmp.curl="Not installed"; + tmp.curl = "Not installed"; +# endif + tmp.fltk = std::to_string(FL_API_VERSION); + tmp.libuv = "Not installed";//uv_version_string(); + tmp.sqlite = sqlite3_libversion(); +# if VS_USE_TCC + tmp.tcc = TCC_VERSION; +# else + tmp.tcc = "Not installed"; +# endif +# if VS_USE_QJS + tmp.quickjs=JS_GetVersion(); +# else + tmp.quickjs = "Not installed"; +# endif +# if VS_USE_WAMR + tmp.wamr= WAMR_VERSION; +# else + tmp.wamr = "Not installed"; # endif - tmp.fltk=std::to_string(FL_API_VERSION); - tmp.libuv="---";//uv_version_string(); - tmp.sqlite=sqlite3_libversion(); - tmp.tcc= TCC_VERSION; - tmp.quickjs=JS_GetVersion(); - tmp.vs=vs_version(); - tmp.wamr= WAMR_VERSION; return tmp; } From 1ca44cee3fdfd6e129d576883184a5af6c12419b Mon Sep 17 00:00:00 2001 From: karurochari Date: Fri, 13 Dec 2024 11:59:06 +0000 Subject: [PATCH 07/11] Restored sane meson.options --- meson.options | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.options b/meson.options index 72d97d5f..6f0e4f53 100644 --- a/meson.options +++ b/meson.options @@ -2,8 +2,8 @@ option('tests', type: 'boolean', value: true) option('use_curl', type: 'boolean', value: true) -option('use_tcc', type: 'boolean', value: false) -option('use_quickjs', type: 'boolean', value: false) +option('use_tcc', type: 'boolean', value: true) +option('use_quickjs', type: 'boolean', value: true) option('use_wamr', type: 'boolean', value: true) # Only one between lua and luajit should be enabled From 86e0c14e05de65beb258b0057bc8cd0e152be2c1 Mon Sep 17 00:00:00 2001 From: karurochari Date: Fri, 13 Dec 2024 12:19:14 +0000 Subject: [PATCH 08/11] Minor fix in meson cfg --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index d92fadaa..f07db2d0 100644 --- a/meson.build +++ b/meson.build @@ -63,7 +63,7 @@ endif if get_option('use_system_fltk') libfltk_dep = dependency('fltk', version: '>=1.4', required: false) libfltk_images_dep = dependency('fltk-images', version: '>=1.4', required: false) - libfltk_forms_dep = dependency('fltk-images', version: '>=1.4', required: false) + libfltk_forms_dep = dependency('fltk-forms', version: '>=1.4', required: false) if libfltk_dep.found() == false or libfltk_images_dep.found() == false or libfltk_forms_dep.found() == false need_fltk = true endif From 0fc683d31174c2dd84c3d2ea2a9a89c63e3d472a Mon Sep 17 00:00:00 2001 From: karurochari Date: Fri, 13 Dec 2024 18:56:11 +0000 Subject: [PATCH 09/11] Refactored global objects to avoid static global variables in the library --- include/fetcher.hpp | 4 ++-- include/globals.hpp | 30 +++++++++++++++++----------- include/loader.hpp | 5 ++++- include/pipelines/quickjs-js.hpp | 7 ++++--- include/pipelines/tcc-c.hpp | 7 ++++--- include/ui-tree.hpp | 5 +++++ include/ui-tree.xml.hpp | 3 ++- include/utils/env.hpp | 14 ++++++++++++- src/app/main.cpp | 32 ++++++++++++++++-------------- src/cbindings/vs.cpp | 2 +- src/fetcher.cpp | 16 +++++++-------- src/globals.cpp | 13 +----------- src/loader.cpp | 15 +++++++------- src/pipelines/quickjs-js.cpp | 4 ++-- src/pipelines/tcc-c.cpp | 10 +++++----- src/ui-tree.xml.cpp | 34 ++++++++++++++++---------------- src/utils/env.cpp | 14 +++++++++++-- 17 files changed, 124 insertions(+), 91 deletions(-) diff --git a/include/fetcher.hpp b/include/fetcher.hpp index d9fa0f82..71750134 100644 --- a/include/fetcher.hpp +++ b/include/fetcher.hpp @@ -52,7 +52,7 @@ std::tuple the return code, a buffer and the new base path derived from src */ -std::tuple fetcher(resolve_path& base, resolve_path::from_t scope,const char* src, bool promote=false, bool preserve=false); +std::tuple fetcher(global_ctx_t& globals, resolve_path& base, resolve_path::from_t scope,const char* src, bool promote=false, bool preserve=false); /** * @brief Fetch a resource, convert it in an xml tree and store in in cache (or get it if already there) @@ -65,6 +65,6 @@ std::tuple fetcher(re * @param preserve if true, this entry should be preserved after the build process is done * @return std::tuple the return code, a buffer and the new base path derived from src */ -std::tuple fetcher_xml(resolve_path& base, resolve_path::from_t scope,const char* src, bool promote=false, bool preserve=false); +std::tuple fetcher_xml(global_ctx_t& globals, resolve_path& base, resolve_path::from_t scope,const char* src, bool promote=false, bool preserve=false); } \ No newline at end of file diff --git a/include/globals.hpp b/include/globals.hpp index 375cfe21..a51bb270 100644 --- a/include/globals.hpp +++ b/include/globals.hpp @@ -11,12 +11,12 @@ namespace vs{ -namespace globals{ +/* extern app_env_t env; //Computed basic environment based on user config & shell env extern path_env_t path_env; //Computed paths - //extern policies_t policy; +//extern policies_t policy; extern js_rt_t js_rt; extern cache::mem_storage_t mem_storage; @@ -24,17 +24,25 @@ extern cache::mem_storage_t mem_storage; //extern cache::res_storage_t res_storage; //extern cache::secrets_t secrets; -struct vs_test_debug_t{ - FILE* fd = nullptr; - vs_test_debug_t(); - ~vs_test_debug_t(); - - void operator()(const char* field, const char* value); -}; - extern vs_test_debug_t debug; extern field_models_t value_models; +*/ + + +/** + * @brief Global context structure + * + */ +struct global_ctx_t{ + app_env_t env; + path_env_t path_env; + + js_rt_t js_rt; + cache::mem_storage_t mem_storage; + vs_test_debug_t debug; + field_models_t value_models; +}; + -} } \ No newline at end of file diff --git a/include/loader.hpp b/include/loader.hpp index 2c1fce13..982728bd 100644 --- a/include/loader.hpp +++ b/include/loader.hpp @@ -4,12 +4,15 @@ Glue logic exposed to the final application to avoid exposing too many libraries. */ +#include "globals.hpp" + class ui_tree_xml; namespace vs{ struct app_loader{ ui_tree_xml* root; + - app_loader(const char* profile, const char* path); + app_loader(global_ctx_t& globals, const char* profile, const char* path); ~app_loader(); int run(); int test(); diff --git a/include/pipelines/quickjs-js.hpp b/include/pipelines/quickjs-js.hpp index 0a1ba86b..268189b6 100644 --- a/include/pipelines/quickjs-js.hpp +++ b/include/pipelines/quickjs-js.hpp @@ -1,5 +1,6 @@ #pragma once +#include "globals.hpp" #if VS_USE_QJS #include @@ -41,11 +42,11 @@ struct quickjs_t{ } }; -extern std::shared_ptr qjs_js_pipeline(bool is_runtime, vs::ui_base* obj, const char* src, void* ctx, void(*error_fn)(void*,const char*), const char *link_with); +extern std::shared_ptr qjs_js_pipeline(global_ctx_t& globals, bool is_runtime, vs::ui_base* obj, const char* src, void* ctx, void(*error_fn)(void*,const char*), const char *link_with); extern std::shared_ptr> qjs_js_pipeline_apply(const std::shared_ptr& script,vs::ui_base* obj,void* ctx,void(*register_fn)(void*,const char*, const char*)); -inline std::shared_ptr qjs_js_pipeline_xml(bool is_runtime, vs::ui_base* obj, pugi::xml_node& ctx, const char *link_with){ - return qjs_js_pipeline(is_runtime,obj,ctx.text().as_string(),&ctx,(void(*)(void*,const char*))qjs_error_func_xml,link_with); +inline std::shared_ptr qjs_js_pipeline_xml(global_ctx_t& globals, bool is_runtime, vs::ui_base* obj, pugi::xml_node& ctx, const char *link_with){ + return qjs_js_pipeline(globals,is_runtime,obj,ctx.text().as_string(),&ctx,(void(*)(void*,const char*))qjs_error_func_xml,link_with); } diff --git a/include/pipelines/tcc-c.hpp b/include/pipelines/tcc-c.hpp index be206a34..b0ee35e8 100644 --- a/include/pipelines/tcc-c.hpp +++ b/include/pipelines/tcc-c.hpp @@ -1,5 +1,6 @@ #pragma once +#include "globals.hpp" #if VS_USE_TCC @@ -30,11 +31,11 @@ extern void tcc_log_symbol_func_xml(const pugi::xml_node& env, const char * msg, * @param link_with path of a library to be linked against (exposing the standard vs interface) * @return std::shared_ptr */ -extern std::shared_ptr tcc_c_pipeline(bool is_runtime, ui_base* obj, const char* src, void* ctx, void(*error_fn)(void*,const char*), bool compact, const char *link_with); +extern std::shared_ptr tcc_c_pipeline(global_ctx_t& globals, bool is_runtime, ui_base* obj, const char* src, void* ctx, void(*error_fn)(void*,const char*), bool compact, const char *link_with); extern std::shared_ptr> tcc_c_pipeline_apply(const std::shared_ptr& script,vs::ui_base* obj,void* ctx,void(*register_fn)(void*,const char*, const char*)); -inline std::shared_ptr tcc_c_pipeline_xml(bool is_runtime, vs::ui_base* obj, pugi::xml_node& ctx, bool compact, const char *link_with){ - return tcc_c_pipeline(is_runtime,obj,ctx.text().as_string(),&ctx,(void(*)(void*,const char*))tcc_error_func_xml, compact, link_with); +inline std::shared_ptr tcc_c_pipeline_xml(global_ctx_t& globals, bool is_runtime, vs::ui_base* obj, pugi::xml_node& ctx, bool compact, const char *link_with){ + return tcc_c_pipeline(globals,is_runtime,obj,ctx.text().as_string(),&ctx,(void(*)(void*,const char*))tcc_error_func_xml, compact, link_with); } } diff --git a/include/ui-tree.hpp b/include/ui-tree.hpp index 1fd54d78..8338e484 100644 --- a/include/ui-tree.hpp +++ b/include/ui-tree.hpp @@ -1,5 +1,6 @@ #pragma once +#include "globals.hpp" #include "ui-frame.hpp" #include #include @@ -14,6 +15,8 @@ struct ui_tree { //Define the embedded mode supported. frame_mode_t mode = frame_mode_t::AUTO; + global_ctx_t& globals; + ui_tree* parent = nullptr; //Set if there is an explict owner of this root, for example a viewport. @@ -47,6 +50,8 @@ struct ui_tree { virtual void cleanup(); virtual int runtime_testsuite(); + inline ui_tree(global_ctx_t& g):globals(g){} + }; diff --git a/include/ui-tree.xml.hpp b/include/ui-tree.xml.hpp index 19edf448..908a8c9f 100644 --- a/include/ui-tree.xml.hpp +++ b/include/ui-tree.xml.hpp @@ -49,8 +49,9 @@ struct ui_tree_xml : ui_tree { int build(); - int load(const char* file, type_t type, const pugi::xml_node* caller_node=nullptr, ui_base* caller_ui_node=nullptr, const scoped_rpath_t* caller_path=nullptr, const policies_t& base_policies=globals::env.computed_policies); + int load(const char* file, type_t type, const pugi::xml_node* caller_node=nullptr, ui_base* caller_ui_node=nullptr, const scoped_rpath_t* caller_path=nullptr); + inline ui_tree_xml(global_ctx_t& g):ui_tree(g){} virtual ~ui_tree_xml(); virtual void cleanup(); virtual int runtime_testsuite(){if(this->root!=nullptr)return this->root->all_tests();return 0;} diff --git a/include/utils/env.hpp b/include/utils/env.hpp index 8874c6c5..75a1c827 100644 --- a/include/utils/env.hpp +++ b/include/utils/env.hpp @@ -1,5 +1,6 @@ #pragma once #include + #include /** @@ -13,7 +14,10 @@ namespace vs{ -path_env_t mk_env(const char* arg0, const char* arg1); + +struct global_ctx_t; + +path_env_t mk_env(global_ctx_t& globals, const char* arg0, const char* arg1); struct js_rt_t{ void* rt; @@ -22,6 +26,14 @@ struct js_rt_t{ void* operator()(); }; +struct vs_test_debug_t{ + FILE* fd = nullptr; + vs_test_debug_t(); + ~vs_test_debug_t(); + + void operator()(const char* field, const char* value); +}; + /** * @brief In case it is not configured already, it prepares the SQLITE file and all the pre-compiled queries needed. * diff --git a/src/app/main.cpp b/src/app/main.cpp index 4cc4c9ac..e262e5d8 100755 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -20,42 +20,44 @@ #include #include +vs::global_ctx_t globals; + using namespace vs; int run(const char* path, const char *entry, const char* profile, const char* tests=nullptr){ - globals::env.computed_policies.debug(); //To set an initial record in the debug file. - globals::env.computed_policies.inherit(policies_t::from_env()); - globals::path_env = mk_env(path, entry); + globals.env.computed_policies.debug(); //To set an initial record in the debug file. + globals.env.computed_policies.inherit(policies_t::from_env()); + globals.path_env = mk_env(globals, path, entry); std::cout<<"\n--------- paths ---------\n"; - std::cout <<"cwd: "< fetcher(resolve_path& base, resolve_path::from_t from,const char* src, bool promote, bool preserve){ +std::tuple fetcher(global_ctx_t& globals, resolve_path& base, resolve_path::from_t from,const char* src, bool promote, bool preserve){ auto ret = base(from,src); if(ret.first==resolve_path::reason_t::OK){ //If present, use the one cached. { - auto found = globals::mem_storage.get({ret.second.location,0,cache::resource_t::BUFFER}); + auto found = globals.mem_storage.get({ret.second.location,0,cache::resource_t::BUFFER}); if(found!=nullptr){ cache::buffer_t bf = * (cache::buffer_t*)found->ref.get(); return {resolve_path::reason_t::OK,bf,ret.second}; @@ -57,8 +57,8 @@ std::tuple fetcher(re } if(ret.second.type==rpath_type_t::FS){ - auto res = globals::mem_storage.fetch_from_fs({ret.second.location,0,cache::resource_t::BUFFER,promote,preserve}); - if(res==globals::mem_storage.end()){ + auto res = globals.mem_storage.fetch_from_fs({ret.second.location,0,cache::resource_t::BUFFER,promote,preserve}); + if(res==globals.mem_storage.end()){ return {resolve_path::reason_t::NOT_FOUND,{nullptr,0},ret.second}; } else{ @@ -68,8 +68,8 @@ std::tuple fetcher(re } # ifdef HAS_CURL else if(ret.second.type==rpath_type_t::HTTP){ - auto res = globals::mem_storage.fetch_from_http({ret.second.as_string(),0,cache::resource_t::BUFFER,promote,preserve}); - if(res==globals::mem_storage.end()){ + auto res = globals.mem_storage.fetch_from_http({ret.second.as_string(),0,cache::resource_t::BUFFER,promote,preserve}); + if(res==globals.mem_storage.end()){ return {resolve_path::reason_t::NOT_FOUND,{nullptr,0},ret.second}; } else{ @@ -78,8 +78,8 @@ std::tuple fetcher(re } } else if(ret.second.type==rpath_type_t::HTTPS){ - auto res = globals::mem_storage.fetch_from_https({ret.second.as_string(),0,cache::resource_t::BUFFER,promote,preserve}); - if(res==globals::mem_storage.end()){ + auto res = globals.mem_storage.fetch_from_https({ret.second.as_string(),0,cache::resource_t::BUFFER,promote,preserve}); + if(res==globals.mem_storage.end()){ return {resolve_path::reason_t::NOT_FOUND,{nullptr,0},ret.second}; } else{ diff --git a/src/globals.cpp b/src/globals.cpp index 14bd63a4..4023b2ac 100644 --- a/src/globals.cpp +++ b/src/globals.cpp @@ -13,20 +13,9 @@ js_rt_t js_rt; cache::mem_storage_t mem_storage; vs_test_debug_t debug; -vs_test_debug_t::vs_test_debug_t(){auto file=getenv("VS_DEBUG_FILE");if(file!=nullptr)fd=fopen(file,"w+");} -vs_test_debug_t::~vs_test_debug_t(){if(fd!=nullptr)fclose(fd);} - -void vs_test_debug_t::operator()(const char* field, const char* value){ - if(fd==nullptr)return; - else{ - auto now = std::chrono::system_clock::now(); - fprintf(fd,"%s\t%s\t%ld\n",field,value,std::chrono::duration_cast(now.time_since_epoch()).count()); - } -} //TODO: Fill in with the basic types supported. -field_models_t value_models; - +//field_models_t value_models; } } diff --git a/src/loader.cpp b/src/loader.cpp index d802c25c..287ddd56 100644 --- a/src/loader.cpp +++ b/src/loader.cpp @@ -8,11 +8,12 @@ #include #include #include -#include +//TODO:Remove namespace vs { -app_loader::app_loader(const char *profile, const char *path) { +app_loader::app_loader(global_ctx_t& globals, const char *profile, const char *path) { + #ifdef HAS_CURL curl_global_init(CURL_GLOBAL_ALL); #endif @@ -22,8 +23,8 @@ app_loader::app_loader(const char *profile, const char *path) { std::string profile_path = profile != nullptr - ? (globals::path_env.userdata_path.as_string() + profile + ".xml") - : (globals::path_env.userdata_path.as_string() + "default.xml"); + ? (globals.path_env.userdata_path.as_string() + profile + ".xml") + : (globals.path_env.userdata_path.as_string() + "default.xml"); pugi::xml_parse_result result = doc.load_file(profile_path.c_str()); if (!result) { std::cout << "XML [" << profile_path @@ -36,7 +37,7 @@ app_loader::app_loader(const char *profile, const char *path) { doc.load_string(embedded_profile); } - root = new ui_tree_xml(); + root = new ui_tree_xml(globals); if (root->load(path, ui_tree::type_t::APP) != 0) { throw "Unable to process file"; } else { @@ -51,9 +52,9 @@ int app_loader::test() { } int app_loader::run() { - globals::mem_storage.cleanup(); + root->globals.mem_storage.cleanup(); root->cleanup(); - if (!globals::env.computed_policies.headless) { + if (!root->globals.env.computed_policies.headless) { auto t = Fl::run(); delete root; root = nullptr; diff --git a/src/pipelines/quickjs-js.cpp b/src/pipelines/quickjs-js.cpp index 74fb2df2..81f894ab 100644 --- a/src/pipelines/quickjs-js.cpp +++ b/src/pipelines/quickjs-js.cpp @@ -219,9 +219,9 @@ std::shared_ptr> qjs_js_pipeline_apply(const std::shared_ptr qjs_js_pipeline(bool is_runtime, vs::ui_base* obj, const char* src, void* nctx, void(*error_fn)(void*,const char*), const char *link_with){ +std::shared_ptr qjs_js_pipeline(global_ctx_t& globals, bool is_runtime, vs::ui_base* obj, const char* src, void* nctx, void(*error_fn)(void*,const char*), const char *link_with){ //std::shared_ptr _ctx = std::make_shared((JSRuntime*)(globals::js_rt())); - std::shared_ptr _ctx = std::shared_ptr( new quickjs_t((JSRuntime*)(globals::js_rt())), +[](void* o){delete (quickjs_t*)o;}); + std::shared_ptr _ctx = std::shared_ptr( new quickjs_t((JSRuntime*)(globals.js_rt())), +[](void* o){delete (quickjs_t*)o;}); auto& ctx = *_ctx; diff --git a/src/pipelines/tcc-c.cpp b/src/pipelines/tcc-c.cpp index 46281f60..6fda7a26 100644 --- a/src/pipelines/tcc-c.cpp +++ b/src/pipelines/tcc-c.cpp @@ -52,12 +52,12 @@ char* itoa(int value, char* result, int base) { //'/home/checkroom/Documents/projects/vs-fltk/subprojects/libtcc/tcc' test.c -I../../subprojects/libtcc/include/ -L. -L../../subprojects/libtcc -lapp -static void vs_debug(const char* k, const char* v){globals::debug(k,v);} +static void vs_debug(global_ctx_t& global, const char* k, const char* v){/*globals.debug(k,v);*/} #define LIB(x) script->add_sym(#x, (void*) x) #define LIBT(x,t) script->add_sym(#x, (void*) t x) -std::shared_ptr tcc_c_pipeline(bool is_runtime, vs::ui_base* obj, const char* src, void* ctx, void(*error_fn)(void*,const char*), bool compact, const char *link_with){ +std::shared_ptr tcc_c_pipeline(global_ctx_t& globals, bool is_runtime, vs::ui_base* obj, const char* src, void* ctx, void(*error_fn)(void*,const char*), bool compact, const char *link_with){ auto script = std::make_shared(); //This part is a bit of a mess. @@ -78,8 +78,8 @@ std::shared_ptr tcc_c_pipeline(bool is_runtime, vs::ui_base* obj, const cha script->set_out_type(tcc::memory); - script->add_sysinclude_path((globals::path_env.root.location+"./bindings/native/tcc/include").c_str()); - script->add_include_path((globals::path_env.root.location+"./bindings/native/include").c_str()); + script->add_sysinclude_path((globals.path_env.root.location+"./bindings/native/tcc/include").c_str()); + script->add_include_path((globals.path_env.root.location+"./bindings/native/include").c_str()); //script->add_lib("ld"); //script->add_lib("tcc1"); @@ -174,7 +174,7 @@ std::shared_ptr tcc_c_pipeline(bool is_runtime, vs::ui_base* obj, const cha if(on_compiled!=nullptr)on_compiled(); auto on_static_test = (int(*)())script->get_sym("static_test"); - if(on_static_test!=nullptr && globals::env.computed_policies.testing)on_static_test(); + if(on_static_test!=nullptr && globals.env.computed_policies.testing)on_static_test(); if(obj!=nullptr){ //Apply the environment for single use scripts. diff --git a/src/ui-tree.xml.cpp b/src/ui-tree.xml.cpp index 5c34fe66..18391bc5 100755 --- a/src/ui-tree.xml.cpp +++ b/src/ui-tree.xml.cpp @@ -87,14 +87,14 @@ void ui_tree_xml::log(int severety, const void* _ctx, const char* str, ...){ //General XML loader for apps and components. //TODO: The caller node is a design flaw. We need to be given the list of props and slots. Not the full node which might not even exist. -int ui_tree_xml::load(const char* file, type_t type, const pugi::xml_node* caller_node, ui_base* caller_ui_node, const scoped_rpath_t* caller_path, const policies_t& base_policies) +int ui_tree_xml::load(const char* file, type_t type, const pugi::xml_node* caller_node, ui_base* caller_ui_node, const scoped_rpath_t* caller_path) { //TODO: As part of this process, policies should be aligned with what defined in the base config, //and not just one single set of options, so that we can pattern match paths. - policies.inherit(base_policies); + policies.inherit(globals.env.computed_policies); - resolve_path resolver(policies,globals::path_env,(caller_path==nullptr)?globals::path_env.cwd:*caller_path); - auto buffer = fetcher(resolver,resolve_path::from_t::NATIVE_CODE,file); + resolve_path resolver(policies,globals.path_env,(caller_path==nullptr)?globals.path_env.cwd:*caller_path); + auto buffer = fetcher(globals,resolver,resolve_path::from_t::NATIVE_CODE,file); if(std::get<0>(buffer)==resolve_path::reason_t::OK){ this->local=std::get<2>(buffer).base_dir(); @@ -144,9 +144,9 @@ int ui_tree_xml::load(const char* file, type_t type, const pugi::xml_node* calle pugi::xml_document datadoc; auto tplt = root_data.attribute("template"); //TODO: Adapt to use the proper syntax. I cannot remember which one was it - resolve_path resolver(policies,globals::path_env,this->local); + resolve_path resolver(policies,globals.path_env,this->local); - auto buffer = fetcher(resolver,resolve_path::from_t::NATIVE_CODE,tplt.as_string()); + auto buffer = fetcher(globals,resolver,resolve_path::from_t::NATIVE_CODE,tplt.as_string()); if(std::get<0>(buffer)!=resolve_path::reason_t::OK){ //TODO: error handling return 2; @@ -230,8 +230,8 @@ void ui_tree_xml::_build(const pugi::xml_node& root, ui_base* root_ui){ } else{ log(severety_t::INFO,root,"Loading component %s", src.as_string()); //TODO: How is it possible that this shows file:// in place of the actual one? - ui_tree_xml component_tree; - if(component_tree.load(src.as_string(),type_t::COMPONENT,&root,root_ui,&local, policies)!=0){ + ui_tree_xml component_tree(globals); + if(component_tree.load(src.as_string(),type_t::COMPONENT,&root,root_ui,&local)!=0){ log(severety_t::INFO,root,"Loading failed, file cannot be opened %s", src); } else{ @@ -286,7 +286,7 @@ void ui_tree_xml::_build(const pugi::xml_node& root, ui_base* root_ui){ } //DEBUG else if(strcmp(root.name(),strings.DEBUG_TAG)==0){ - globals::debug(root.attribute("key").as_string(""), root.attribute("value").as_string("")); + globals.debug(root.attribute("key").as_string(""), root.attribute("value").as_string("")); return; } //VIEWPORT @@ -342,8 +342,8 @@ void ui_tree_xml::_build(const pugi::xml_node& root, ui_base* root_ui){ else if(imports.contains(root.name())){ auto it = imports.find(root.name()); log(severety_t::INFO,root,"Loading component %s", it->second.c_str()); //TODO: How is it possible that this shows file:// in place of the actual one? - ui_tree_xml component_tree; - if(component_tree.load(it->second.c_str(),type_t::COMPONENT,&root,root_ui,&local, policies)!=0){ + ui_tree_xml component_tree(globals); + if(component_tree.load(it->second.c_str(),type_t::COMPONENT,&root,root_ui,&local)!=0){ log(severety_t::INFO,root,"Loading failed, file cannot be opened %s", it->second.c_str()); } else{ @@ -405,7 +405,7 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u is_module=true; auto filename = this->fullname.as_string(); - auto found = globals::mem_storage.get({filename.c_str(),this->local_unique_counter+1,cache::resource_t::SCRIPT}); + auto found = globals.mem_storage.get({filename.c_str(),this->local_unique_counter+1,cache::resource_t::SCRIPT}); if(found!=nullptr){ current->set_mode(((cache::script_t*)found->ref.get())->mode); current->attach_script(((cache::script_t*)found->ref.get())->script,is_module); @@ -423,7 +423,7 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u auto link_with = doc.first_child().attribute("link-with").as_string(nullptr); std::string tmp_link; if(link_with!=nullptr){ - resolve_path resolver(policies,globals::path_env,local); + resolve_path resolver(policies,globals.path_env,local); auto computed_path = resolver(resolve_path::from_t::NATIVE_CODE,link_with); if(computed_path.first==resolve_path::reason_t::OK){ //TODO: For now I am assuming it is on the fs. I should resolve it to tmp if remote for example @@ -438,7 +438,7 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u const auto &lang = root.attribute("lang").as_string(mode==frame_mode_t::NATIVE?"c":""); if (strcmp(lang, "c") == 0) { #ifdef VS_USE_TCC - auto compiler = pipelines::tcc_c_pipeline_xml(true, is_module?nullptr:current, root, compact, (link_with==nullptr)?nullptr:tmp_link.c_str()); + auto compiler = pipelines::tcc_c_pipeline_xml(globals,true, is_module?nullptr:current, root, compact, (link_with==nullptr)?nullptr:tmp_link.c_str()); if(compiler!=nullptr){ current->set_mode(frame_mode_t::NATIVE); current->attach_script(compiler,is_module); @@ -448,7 +448,7 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u auto tmp = std::make_shared(cache::script_t{ compiler, symbols, frame_mode_t::NATIVE }); - globals::mem_storage.fetch_from_shared({this->fullname.as_string().c_str(),local_unique_counter+1,cache::resource_t::SCRIPT,false,false}, tmp); + globals.mem_storage.fetch_from_shared({this->fullname.as_string().c_str(),local_unique_counter+1,cache::resource_t::SCRIPT,false,false}, tmp); local_unique_counter++; } } @@ -460,7 +460,7 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u const auto &lang = root.attribute("lang").as_string(mode==frame_mode_t::QUICKJS?"js":""); if (strcmp(lang, "js") == 0) { #ifdef VS_USE_QJS - auto compiler = pipelines::qjs_js_pipeline_xml(true, is_module?nullptr:current, root, (link_with==nullptr)?nullptr:tmp_link.c_str()); + auto compiler = pipelines::qjs_js_pipeline_xml(globals,true, is_module?nullptr:current, root, (link_with==nullptr)?nullptr:tmp_link.c_str()); if(compiler!=nullptr){ current->set_mode(frame_mode_t::QUICKJS); current->attach_script(compiler,is_module); @@ -470,7 +470,7 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u auto tmp = std::make_shared(cache::script_t{ compiler, symbols, frame_mode_t::QUICKJS }); - globals::mem_storage.fetch_from_shared({this->fullname.as_string().c_str(),local_unique_counter+1,cache::resource_t::SCRIPT,false,false}, tmp); + globals.mem_storage.fetch_from_shared({this->fullname.as_string().c_str(),local_unique_counter+1,cache::resource_t::SCRIPT,false,false}, tmp); local_unique_counter++; } } diff --git a/src/utils/env.cpp b/src/utils/env.cpp index 68dab8cb..93019e9a 100644 --- a/src/utils/env.cpp +++ b/src/utils/env.cpp @@ -34,7 +34,7 @@ namespace vs{ //TODO: For now this is linux only. I will need to be expanded to support more os -path_env_t mk_env(const char* arg0,const char* arg1){ +path_env_t mk_env(global_ctx_t& globals, const char* arg0,const char* arg1){ path_env_t main_env; static char buffer[1024]; if(getcwd(buffer,1023)==nullptr){throw "Unable to get CWD";} @@ -69,7 +69,7 @@ path_env_t mk_env(const char* arg0,const char* arg1){ main_env.tmp_path={rpath_type_t::FS,"/tmp/"}; //Finally compute path for the requested file - resolve_path resolver(globals::env.computed_policies,main_env,main_env.cwd); + resolve_path resolver(globals.env.computed_policies,main_env,main_env.cwd); { auto t = resolver(resolve_path::from_t::NATIVE_CODE,arg1); if(t.first!=resolve_path::reason_t::OK)exit(2); //TODO: Handle case @@ -98,6 +98,16 @@ js_rt_t::~js_rt_t(){ void* js_rt_t::operator()(){return rt;} +vs_test_debug_t::vs_test_debug_t(){auto file=getenv("VS_DEBUG_FILE");if(file!=nullptr)fd=fopen(file,"w+");} +vs_test_debug_t::~vs_test_debug_t(){if(fd!=nullptr)fclose(fd);} + +void vs_test_debug_t::operator()(const char* field, const char* value){ + if(fd==nullptr)return; + else{ + auto now = std::chrono::system_clock::now(); + fprintf(fd,"%s\t%s\t%ld\n",field,value,std::chrono::duration_cast(now.time_since_epoch()).count()); + } +} From 84c52040e8e41c3d99ffa8f60f54f080809b9c97 Mon Sep 17 00:00:00 2001 From: karurochari Date: Sat, 14 Dec 2024 04:10:42 +0000 Subject: [PATCH 10/11] Simplified interface for ui_xml_tree. Added flags to meson for simple http library --- TODO.md | 3 ++- include/ui-tree.hpp | 14 ++++++-------- include/ui-tree.xml.hpp | 6 +++--- meson.build | 9 +++++++-- meson.options | 3 ++- src/loader.cpp | 2 +- src/ui-tree.xml.cpp | 21 ++++++++++----------- 7 files changed, 31 insertions(+), 27 deletions(-) diff --git a/TODO.md b/TODO.md index 66ede32d..4c07acbf 100644 --- a/TODO.md +++ b/TODO.md @@ -1 +1,2 @@ -This file will now only track outstanding tasks which are needed to reach the next milestone. +- [ ] Restore vs_debug +- [ ] Allow http only withou libcurl via https://github.com/lazy-eggplant/libhttp diff --git a/include/ui-tree.hpp b/include/ui-tree.hpp index 8338e484..9b1995aa 100644 --- a/include/ui-tree.hpp +++ b/include/ui-tree.hpp @@ -15,17 +15,15 @@ struct ui_tree { //Define the embedded mode supported. frame_mode_t mode = frame_mode_t::AUTO; - global_ctx_t& globals; - - ui_tree* parent = nullptr; //Set if there is an explict owner of this root, for example a viewport. - + global_ctx_t& globals; //Just for book-keeping + ui_tree* parent = nullptr; //Set if there is an explict owner of this root, for example a viewport/app. ui_base* caller_ui_node=nullptr; //Element from a parent tree calling me ui_base* root = nullptr; //Base element of this tree - policies_t policies; //Computed policies for this tree - scoped_rpath_t local; //Full path for the location of this component. - scoped_rpath_t fullname; //Full path for the location of this component. + policies_t policies; //Computed policies for this tree + scoped_rpath_t local; //Full path for the location of this component. + scoped_rpath_t fullname; //Full path for the location of this component. size_t local_unique_counter = 0; // Globals @@ -50,7 +48,7 @@ struct ui_tree { virtual void cleanup(); virtual int runtime_testsuite(); - inline ui_tree(global_ctx_t& g):globals(g){} + inline ui_tree(global_ctx_t& g, ui_tree* parent, ui_base* caller_ui_node):globals(g){this->parent=parent;this->caller_ui_node=caller_ui_node;} }; diff --git a/include/ui-tree.xml.hpp b/include/ui-tree.xml.hpp index 908a8c9f..bd769c66 100644 --- a/include/ui-tree.xml.hpp +++ b/include/ui-tree.xml.hpp @@ -25,7 +25,7 @@ struct ui_tree_xml : ui_tree { ns_strings strings; # endif - const pugi::xml_node *caller_node = nullptr; + const pugi::xml_node *caller_xml_node = nullptr; pugi::xml_document doc; //Handle of the xml parser @@ -49,9 +49,9 @@ struct ui_tree_xml : ui_tree { int build(); - int load(const char* file, type_t type, const pugi::xml_node* caller_node=nullptr, ui_base* caller_ui_node=nullptr, const scoped_rpath_t* caller_path=nullptr); + int load(const char* file, type_t type); - inline ui_tree_xml(global_ctx_t& g):ui_tree(g){} + inline ui_tree_xml(global_ctx_t& g, ui_tree* parent, ui_base* caller_ui_node, const pugi::xml_node* caller_xml_node):ui_tree(g,parent,caller_ui_node){this->caller_xml_node=caller_xml_node;} virtual ~ui_tree_xml(); virtual void cleanup(); virtual int runtime_testsuite(){if(this->root!=nullptr)return this->root->all_tests();return 0;} diff --git a/meson.build b/meson.build index f07db2d0..010bebe9 100644 --- a/meson.build +++ b/meson.build @@ -177,11 +177,16 @@ sqlite_dep = dependency( default_options: 'default_library=static', ) -#TODO handle the case in which libcurl does not exist. Missing is fine, just no https/http prefix for paths. -if get_option('use_curl') +if get_option('use_networking_curl') and get_option('use_networking_simple') + error('Only one networking solution can be selected between curl and simple') +endif + +if get_option('use_networking_curl') curl = dependency('libcurl', required: false) endif +#TODO: support networking_simple + include_dirs = include_directories(['./include']) # Prepare `vs` diff --git a/meson.options b/meson.options index 6f0e4f53..bb25f1da 100644 --- a/meson.options +++ b/meson.options @@ -1,6 +1,7 @@ option('tests', type: 'boolean', value: true) -option('use_curl', type: 'boolean', value: true) +option('use_networking_curl', type: 'boolean', value: true) +option('use_networking_simple', type: 'boolean', value: false) option('use_tcc', type: 'boolean', value: true) option('use_quickjs', type: 'boolean', value: true) diff --git a/src/loader.cpp b/src/loader.cpp index 287ddd56..badb7a99 100644 --- a/src/loader.cpp +++ b/src/loader.cpp @@ -37,7 +37,7 @@ app_loader::app_loader(global_ctx_t& globals, const char *profile, const char *p doc.load_string(embedded_profile); } - root = new ui_tree_xml(globals); + root = new ui_tree_xml(globals,nullptr,nullptr,nullptr); if (root->load(path, ui_tree::type_t::APP) != 0) { throw "Unable to process file"; } else { diff --git a/src/ui-tree.xml.cpp b/src/ui-tree.xml.cpp index 18391bc5..62328b6a 100755 --- a/src/ui-tree.xml.cpp +++ b/src/ui-tree.xml.cpp @@ -87,13 +87,14 @@ void ui_tree_xml::log(int severety, const void* _ctx, const char* str, ...){ //General XML loader for apps and components. //TODO: The caller node is a design flaw. We need to be given the list of props and slots. Not the full node which might not even exist. -int ui_tree_xml::load(const char* file, type_t type, const pugi::xml_node* caller_node, ui_base* caller_ui_node, const scoped_rpath_t* caller_path) +int ui_tree_xml::load(const char* file, type_t type) { //TODO: As part of this process, policies should be aligned with what defined in the base config, //and not just one single set of options, so that we can pattern match paths. policies.inherit(globals.env.computed_policies); + if(parent!=nullptr)policies.inherit(parent->policies); - resolve_path resolver(policies,globals.path_env,(caller_path==nullptr)?globals.path_env.cwd:*caller_path); + resolve_path resolver(policies,globals.path_env,(parent==nullptr)?globals.path_env.cwd:parent->local); auto buffer = fetcher(globals,resolver,resolve_path::from_t::NATIVE_CODE,file); if(std::get<0>(buffer)==resolve_path::reason_t::OK){ @@ -102,8 +103,6 @@ int ui_tree_xml::load(const char* file, type_t type, const pugi::xml_node* calle } this->type = type; - this->caller_node=caller_node; - this->caller_ui_node=caller_ui_node; vs_log(severety_t::INFO, nullptr, "Requested loading of file `%s`", std::get<2>(buffer).as_string().data()); //TODO: Move this to cache later on, but for now just dump the result into a buffer @@ -230,8 +229,8 @@ void ui_tree_xml::_build(const pugi::xml_node& root, ui_base* root_ui){ } else{ log(severety_t::INFO,root,"Loading component %s", src.as_string()); //TODO: How is it possible that this shows file:// in place of the actual one? - ui_tree_xml component_tree(globals); - if(component_tree.load(src.as_string(),type_t::COMPONENT,&root,root_ui,&local)!=0){ + ui_tree_xml component_tree(globals,this,root_ui,&root); + if(component_tree.load(src.as_string(),type_t::COMPONENT)!=0){ log(severety_t::INFO,root,"Loading failed, file cannot be opened %s", src); } else{ @@ -267,8 +266,8 @@ void ui_tree_xml::_build(const pugi::xml_node& root, ui_base* root_ui){ //Copy the content provided by the parent in place of the slot, or render its content if the parent has none. const auto& name = root.attribute("tag").as_string("default"); //The default slot. - if(this->caller_node!=nullptr){ - const auto& match = this->caller_node->find_child([&name](const pugi::xml_node& node){ + if(this->caller_xml_node!=nullptr){ + const auto& match = this->caller_xml_node->find_child([&name](const pugi::xml_node& node){ return (strcmp(node.name(),"inject")==0) && (strcmp(node.attribute("tag").as_string("default"),name)==0); }); @@ -342,8 +341,8 @@ void ui_tree_xml::_build(const pugi::xml_node& root, ui_base* root_ui){ else if(imports.contains(root.name())){ auto it = imports.find(root.name()); log(severety_t::INFO,root,"Loading component %s", it->second.c_str()); //TODO: How is it possible that this shows file:// in place of the actual one? - ui_tree_xml component_tree(globals); - if(component_tree.load(it->second.c_str(),type_t::COMPONENT,&root,root_ui,&local)!=0){ + ui_tree_xml component_tree(globals,this,root_ui,&root); + if(component_tree.load(it->second.c_str(),type_t::COMPONENT)!=0){ log(severety_t::INFO,root,"Loading failed, file cannot be opened %s", it->second.c_str()); } else{ @@ -571,7 +570,7 @@ void ui_tree_xml::_build_base_widget_extended_attr(const pugi::xml_node &root, u //For components add to its direct children the attributes coming from above if(strcmp(root.parent().name(),strings.COMPONENT_TAG)==0){ - for (const auto &i : this->caller_node->attributes()) { + for (const auto &i : this->caller_xml_node->attributes()) { //Exclude src as it was consumed in here. if(strcmp(i.name(),"src")!=0)props.insert_or_assign(i.name(), i.value()); } From 0ee164cd1f82454d37aa0c1cf1c5825389581dfe Mon Sep 17 00:00:00 2001 From: karurochari Date: Sat, 14 Dec 2024 05:17:20 +0000 Subject: [PATCH 11/11] Introducing libhttp as a curl alternative --- meson.build | 6 ++++++ subprojects/libhttp.wrap | 5 +++++ 2 files changed, 11 insertions(+) create mode 100644 subprojects/libhttp.wrap diff --git a/meson.build b/meson.build index 010bebe9..762e6606 100644 --- a/meson.build +++ b/meson.build @@ -186,6 +186,12 @@ if get_option('use_networking_curl') endif #TODO: support networking_simple +if get_option('use_networking_simple') + libhttp_proj = subproject('libhttp') + libhttp_dep = libhttp_proj.get_variable('libhttp_dep') + + vs_fltk_deps += libhttp_dep +endif include_dirs = include_directories(['./include']) diff --git a/subprojects/libhttp.wrap b/subprojects/libhttp.wrap new file mode 100644 index 00000000..466aebc5 --- /dev/null +++ b/subprojects/libhttp.wrap @@ -0,0 +1,5 @@ +[wrap-git] +url = https://github.com/lazy-eggplant/libhttp +revision = HEAD +depth = 1 +method = meson \ No newline at end of file