@typecad/cuttlefish 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/api/board-types.d.ts +1 -0
- package/dist/api/board-types.js +4 -0
- package/dist/api/capabilities.d.ts +12 -0
- package/dist/api/capabilities.js +1 -0
- package/dist/api/config.d.ts +120 -0
- package/dist/api/config.js +7 -0
- package/dist/api/index.d.ts +5 -0
- package/dist/api/index.js +2 -0
- package/dist/api/schema/builder.d.ts +14 -0
- package/dist/api/schema/builder.js +15 -0
- package/dist/api/schema/index.d.ts +2 -0
- package/dist/api/schema/index.js +1 -0
- package/dist/api/schema/types.d.ts +257 -0
- package/dist/api/schema/types.js +4 -0
- package/dist/api/shared/async-runtime-static.d.ts +8 -0
- package/dist/api/shared/async-runtime-static.js +201 -0
- package/dist/api/shared/async-types.d.ts +42 -0
- package/dist/api/shared/async-types.js +8 -0
- package/dist/api/shared/board-resolver.d.ts +12 -0
- package/dist/api/shared/board-resolver.js +7 -0
- package/dist/api/shared/cpp-type-ir.d.ts +229 -0
- package/dist/api/shared/cpp-type-ir.js +672 -0
- package/dist/api/shared/display-adapter.d.ts +12 -0
- package/dist/api/shared/display-adapter.js +145 -0
- package/dist/api/shared/display-adapters/eink-mono.d.ts +2 -0
- package/dist/api/shared/display-adapters/eink-mono.js +53 -0
- package/dist/api/shared/display-adapters/sdl.d.ts +2 -0
- package/dist/api/shared/display-adapters/sdl.js +452 -0
- package/dist/api/shared/display-adapters/ssd1309.d.ts +2 -0
- package/dist/api/shared/display-adapters/ssd1309.js +136 -0
- package/dist/api/shared/display-adapters/st7796.d.ts +2 -0
- package/dist/api/shared/display-adapters/st7796.js +132 -0
- package/dist/api/shared/display-capabilities.d.ts +45 -0
- package/dist/api/shared/display-capabilities.js +54 -0
- package/dist/api/shared/display-op-ir.d.ts +62 -0
- package/dist/api/shared/display-op-ir.js +19 -0
- package/dist/api/shared/display-profile.d.ts +265 -0
- package/dist/api/shared/display-profile.js +338 -0
- package/dist/api/shared/graphics-strategy.d.ts +29 -0
- package/dist/api/shared/graphics-strategy.js +11 -0
- package/dist/api/shared/hal-op-ir.d.ts +393 -0
- package/dist/api/shared/hal-op-ir.js +18 -0
- package/dist/api/shared/index.d.ts +26 -0
- package/dist/api/shared/index.js +21 -0
- package/dist/api/shared/ir-core.d.ts +363 -0
- package/dist/api/shared/ir-core.js +8 -0
- package/dist/api/shared/ir-declarations.d.ts +201 -0
- package/dist/api/shared/ir-declarations.js +26 -0
- package/dist/api/shared/ir.d.ts +85 -0
- package/dist/api/shared/ir.js +13 -0
- package/dist/api/shared/platform-strategy.d.ts +402 -0
- package/dist/api/shared/platform-strategy.js +14 -0
- package/dist/api/shared/polyfill-helper-registry.d.ts +11 -0
- package/dist/api/shared/polyfill-helper-registry.js +86 -0
- package/dist/api/shared/polyfill-types.d.ts +33 -0
- package/dist/api/shared/polyfill-types.js +76 -0
- package/dist/api/shared/promise-runtime.d.ts +10 -0
- package/dist/api/shared/promise-runtime.js +244 -0
- package/dist/api/shared/snprintf-types.d.ts +29 -0
- package/dist/api/shared/snprintf-types.js +8 -0
- package/dist/api/shared/string-method-registry.d.ts +53 -0
- package/dist/api/shared/string-method-registry.js +168 -0
- package/dist/api/shared/toolchain-types.d.ts +63 -0
- package/dist/api/shared/toolchain-types.js +71 -0
- package/dist/api/shared/types.d.ts +50 -0
- package/dist/api/shared/types.js +6 -0
- package/dist/ast/parse.d.ts +2 -0
- package/dist/ast/parse.js +4 -0
- package/dist/cache.d.ts +26 -0
- package/dist/cache.js +95 -0
- package/dist/cli-utils.d.ts +37 -0
- package/dist/cli-utils.js +250 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +635 -0
- package/dist/config-loader.d.ts +78 -0
- package/dist/config-loader.js +508 -0
- package/dist/config-schema.d.ts +233 -0
- package/dist/config-schema.js +83 -0
- package/dist/create/board-checklist.d.ts +2 -0
- package/dist/create/board-checklist.js +52 -0
- package/dist/create/board-codegen.d.ts +9 -0
- package/dist/create/board-codegen.js +238 -0
- package/dist/create/board-generators.d.ts +12 -0
- package/dist/create/board-generators.js +652 -0
- package/dist/create/board-spec.d.ts +640 -0
- package/dist/create/board-spec.js +215 -0
- package/dist/create/eslint-rules-template.d.ts +2 -0
- package/dist/create/eslint-rules-template.js +672 -0
- package/dist/create/index.d.ts +11 -0
- package/dist/create/index.js +8 -0
- package/dist/create/init-scaffold.d.ts +27 -0
- package/dist/create/init-scaffold.js +150 -0
- package/dist/create/init-templates.d.ts +24 -0
- package/dist/create/init-templates.js +379 -0
- package/dist/create/init-wizard.d.ts +8 -0
- package/dist/create/init-wizard.js +168 -0
- package/dist/debug/breakpoint-loader.d.ts +24 -0
- package/dist/debug/breakpoint-loader.js +137 -0
- package/dist/debug/index.d.ts +3 -0
- package/dist/debug/index.js +7 -0
- package/dist/debug/preprocessor.d.ts +9 -0
- package/dist/debug/preprocessor.js +214 -0
- package/dist/debug/types.d.ts +32 -0
- package/dist/debug/types.js +6 -0
- package/dist/diagnostics/diagnostics-report.d.ts +51 -0
- package/dist/diagnostics/diagnostics-report.js +333 -0
- package/dist/diagnostics/json-schema.d.ts +138 -0
- package/dist/diagnostics/json-schema.js +27 -0
- package/dist/diagnostics/md-writer.d.ts +10 -0
- package/dist/diagnostics/md-writer.js +336 -0
- package/dist/diagnostics/mermaid-builder.d.ts +65 -0
- package/dist/diagnostics/mermaid-builder.js +681 -0
- package/dist/emit/cpp-emitter.d.ts +6 -0
- package/dist/emit/cpp-emitter.js +48 -0
- package/dist/emit/emitters/class-emitter.d.ts +2 -0
- package/dist/emit/emitters/class-emitter.js +523 -0
- package/dist/emit/emitters/emitter-context.d.ts +174 -0
- package/dist/emit/emitters/emitter-context.js +1 -0
- package/dist/emit/emitters/entrypoint-synthesizer.d.ts +2 -0
- package/dist/emit/emitters/entrypoint-synthesizer.js +175 -0
- package/dist/emit/emitters/function-emitter-impl.d.ts +19 -0
- package/dist/emit/emitters/function-emitter-impl.js +428 -0
- package/dist/emit/emitters/line-appender.d.ts +15 -0
- package/dist/emit/emitters/line-appender.js +344 -0
- package/dist/emit/emitters/namespace-emitter.d.ts +2 -0
- package/dist/emit/emitters/namespace-emitter.js +324 -0
- package/dist/emit/emitters/output-finalizer.d.ts +4 -0
- package/dist/emit/emitters/output-finalizer.js +267 -0
- package/dist/emit/emitters/setup.d.ts +3 -0
- package/dist/emit/emitters/setup.js +851 -0
- package/dist/emit/emitters/top-level-prep.d.ts +2 -0
- package/dist/emit/emitters/top-level-prep.js +706 -0
- package/dist/emit/emitters/type-decl-emitter.d.ts +2 -0
- package/dist/emit/emitters/type-decl-emitter.js +251 -0
- package/dist/emit/emitters/ui-emitter.d.ts +21 -0
- package/dist/emit/emitters/ui-emitter.js +564 -0
- package/dist/emit/enum-emitter.d.ts +1 -0
- package/dist/emit/enum-emitter.js +1 -0
- package/dist/emit/expression-renderer.d.ts +283 -0
- package/dist/emit/expression-renderer.js +1477 -0
- package/dist/emit/native-helpers-emitter.d.ts +11 -0
- package/dist/emit/native-helpers-emitter.js +34 -0
- package/dist/emit/route-hal-op.d.ts +5 -0
- package/dist/emit/route-hal-op.js +13 -0
- package/dist/emit/snprintf-helpers.d.ts +17 -0
- package/dist/emit/snprintf-helpers.js +462 -0
- package/dist/emit/statement-renderer.d.ts +189 -0
- package/dist/emit/statement-renderer.js +948 -0
- package/dist/emit/utils/async-state-machine.d.ts +42 -0
- package/dist/emit/utils/async-state-machine.js +343 -0
- package/dist/emit/utils/comment-helpers.d.ts +20 -0
- package/dist/emit/utils/comment-helpers.js +31 -0
- package/dist/emit/utils/cpp-helpers.d.ts +47 -0
- package/dist/emit/utils/cpp-helpers.js +85 -0
- package/dist/emit/utils/include-resolver.d.ts +58 -0
- package/dist/emit/utils/include-resolver.js +155 -0
- package/dist/emit/utils/index.d.ts +8 -0
- package/dist/emit/utils/index.js +12 -0
- package/dist/emit/utils/type-inference.d.ts +159 -0
- package/dist/emit/utils/type-inference.js +559 -0
- package/dist/eslint-check.d.ts +10 -0
- package/dist/eslint-check.js +102 -0
- package/dist/framework-package.d.ts +10 -0
- package/dist/framework-package.js +58 -0
- package/dist/framework-registry.d.ts +39 -0
- package/dist/framework-registry.js +30 -0
- package/dist/incremental-cache.d.ts +116 -0
- package/dist/incremental-cache.js +335 -0
- package/dist/ir/adc-range-validation.d.ts +11 -0
- package/dist/ir/adc-range-validation.js +250 -0
- package/dist/ir/ast-node-utils.d.ts +8 -0
- package/dist/ir/ast-node-utils.js +32 -0
- package/dist/ir/board-pin-utils.d.ts +13 -0
- package/dist/ir/board-pin-utils.js +65 -0
- package/dist/ir/board-resolver.d.ts +36 -0
- package/dist/ir/board-resolver.js +437 -0
- package/dist/ir/build-ir-state.d.ts +167 -0
- package/dist/ir/build-ir-state.js +306 -0
- package/dist/ir/build-ir.d.ts +2 -0
- package/dist/ir/build-ir.js +808 -0
- package/dist/ir/call-graph.d.ts +35 -0
- package/dist/ir/call-graph.js +194 -0
- package/dist/ir/compile-time-only.d.ts +2 -0
- package/dist/ir/compile-time-only.js +16 -0
- package/dist/ir/declaration-builders.d.ts +9 -0
- package/dist/ir/declaration-builders.js +608 -0
- package/dist/ir/entry-points.d.ts +30 -0
- package/dist/ir/entry-points.js +191 -0
- package/dist/ir/expression-to-ir.d.ts +5 -0
- package/dist/ir/expression-to-ir.js +2249 -0
- package/dist/ir/extract-property-chain.d.ts +10 -0
- package/dist/ir/extract-property-chain.js +30 -0
- package/dist/ir/feature-prescan.d.ts +3 -0
- package/dist/ir/feature-prescan.js +43 -0
- package/dist/ir/feature-registry.d.ts +30 -0
- package/dist/ir/feature-registry.js +816 -0
- package/dist/ir/filter.d.ts +7 -0
- package/dist/ir/filter.js +85 -0
- package/dist/ir/function-builder.d.ts +19 -0
- package/dist/ir/function-builder.js +670 -0
- package/dist/ir/hal/hal-emitter.d.ts +62 -0
- package/dist/ir/hal/hal-emitter.js +735 -0
- package/dist/ir/hal/hal-parser.d.ts +61 -0
- package/dist/ir/hal/hal-parser.js +473 -0
- package/dist/ir/hal/hal-plugins.d.ts +34 -0
- package/dist/ir/hal/hal-plugins.js +706 -0
- package/dist/ir/hal-resolver.d.ts +5 -0
- package/dist/ir/hal-resolver.js +5 -0
- package/dist/ir/heap-analysis.d.ts +8 -0
- package/dist/ir/heap-analysis.js +350 -0
- package/dist/ir/heap-array-validation.d.ts +24 -0
- package/dist/ir/heap-array-validation.js +29 -0
- package/dist/ir/identifier-collector.d.ts +18 -0
- package/dist/ir/identifier-collector.js +439 -0
- package/dist/ir/interrupt-analysis.d.ts +39 -0
- package/dist/ir/interrupt-analysis.js +467 -0
- package/dist/ir/memory-budget-validation.d.ts +10 -0
- package/dist/ir/memory-budget-validation.js +86 -0
- package/dist/ir/namespace-builder.d.ts +7 -0
- package/dist/ir/namespace-builder.js +152 -0
- package/dist/ir/ownership-analysis.d.ts +9 -0
- package/dist/ir/ownership-analysis.js +1207 -0
- package/dist/ir/peripheral-ownership.d.ts +3 -0
- package/dist/ir/peripheral-ownership.js +152 -0
- package/dist/ir/peripheral-symbols.d.ts +7 -0
- package/dist/ir/peripheral-symbols.js +57 -0
- package/dist/ir/peripheral-usage.d.ts +54 -0
- package/dist/ir/peripheral-usage.js +691 -0
- package/dist/ir/peripheral-validation.d.ts +8 -0
- package/dist/ir/peripheral-validation.js +77 -0
- package/dist/ir/pin-alias-conflict.d.ts +4 -0
- package/dist/ir/pin-alias-conflict.js +34 -0
- package/dist/ir/pin-capability-validation.d.ts +6 -0
- package/dist/ir/pin-capability-validation.js +413 -0
- package/dist/ir/pin-mode-validation.d.ts +10 -0
- package/dist/ir/pin-mode-validation.js +301 -0
- package/dist/ir/pin-safety.d.ts +11 -0
- package/dist/ir/pin-safety.js +58 -0
- package/dist/ir/program-analysis.d.ts +44 -0
- package/dist/ir/program-analysis.js +589 -0
- package/dist/ir/pulldown-validation.d.ts +7 -0
- package/dist/ir/pulldown-validation.js +49 -0
- package/dist/ir/pwm-timer-sharing.d.ts +4 -0
- package/dist/ir/pwm-timer-sharing.js +93 -0
- package/dist/ir/reachability.d.ts +68 -0
- package/dist/ir/reachability.js +224 -0
- package/dist/ir/register-decorators.d.ts +6 -0
- package/dist/ir/register-decorators.js +49 -0
- package/dist/ir/render-expr.d.ts +4 -0
- package/dist/ir/render-expr.js +127 -0
- package/dist/ir/resource-analysis.d.ts +19 -0
- package/dist/ir/resource-analysis.js +153 -0
- package/dist/ir/statement-to-ir.d.ts +23 -0
- package/dist/ir/statement-to-ir.js +349 -0
- package/dist/ir/symbol-table.d.ts +68 -0
- package/dist/ir/symbol-table.js +179 -0
- package/dist/ir/symbol-types.d.ts +56 -0
- package/dist/ir/symbol-types.js +107 -0
- package/dist/ir/timer0-pwm-timing-conflict.d.ts +4 -0
- package/dist/ir/timer0-pwm-timing-conflict.js +71 -0
- package/dist/ir/timing-validation.d.ts +7 -0
- package/dist/ir/timing-validation.js +95 -0
- package/dist/ir/transformers/array-methods.d.ts +8 -0
- package/dist/ir/transformers/array-methods.js +493 -0
- package/dist/ir/transformers/call-statement.d.ts +5 -0
- package/dist/ir/transformers/call-statement.js +758 -0
- package/dist/ir/transformers/callback-context-registry.d.ts +20 -0
- package/dist/ir/transformers/callback-context-registry.js +55 -0
- package/dist/ir/transformers/canvas-lowering.d.ts +42 -0
- package/dist/ir/transformers/canvas-lowering.js +249 -0
- package/dist/ir/transformers/control-flow.d.ts +8 -0
- package/dist/ir/transformers/control-flow.js +508 -0
- package/dist/ir/transformers/expressions.d.ts +7 -0
- package/dist/ir/transformers/expressions.js +406 -0
- package/dist/ir/transformers/hal-call-resolver.d.ts +29 -0
- package/dist/ir/transformers/hal-call-resolver.js +274 -0
- package/dist/ir/transformers/hal-emit-helpers.d.ts +14 -0
- package/dist/ir/transformers/hal-emit-helpers.js +63 -0
- package/dist/ir/transformers/namespace-methods.d.ts +20 -0
- package/dist/ir/transformers/namespace-methods.js +110 -0
- package/dist/ir/transformers/register-assignment.d.ts +5 -0
- package/dist/ir/transformers/register-assignment.js +56 -0
- package/dist/ir/transformers/ui-call-resolver.d.ts +149 -0
- package/dist/ir/transformers/ui-call-resolver.js +1136 -0
- package/dist/ir/transformers/ui-callback-lowering.d.ts +62 -0
- package/dist/ir/transformers/ui-callback-lowering.js +623 -0
- package/dist/ir/transformers/ui-mount.d.ts +20 -0
- package/dist/ir/transformers/ui-mount.js +35 -0
- package/dist/ir/transformers/ui-reactive.d.ts +68 -0
- package/dist/ir/transformers/ui-reactive.js +129 -0
- package/dist/ir/transformers/variables.d.ts +14 -0
- package/dist/ir/transformers/variables.js +1045 -0
- package/dist/ir/try-catch-validation.d.ts +26 -0
- package/dist/ir/try-catch-validation.js +128 -0
- package/dist/ir/type-resolution.d.ts +31 -0
- package/dist/ir/type-resolution.js +977 -0
- package/dist/ir/ui-element-auto-wire.d.ts +50 -0
- package/dist/ir/ui-element-auto-wire.js +218 -0
- package/dist/ir/unit-suspicion-validation.d.ts +12 -0
- package/dist/ir/unit-suspicion-validation.js +296 -0
- package/dist/ir/utils/walk-ir.d.ts +6 -0
- package/dist/ir/utils/walk-ir.js +162 -0
- package/dist/ir/validation-orchestrator.d.ts +4 -0
- package/dist/ir/validation-orchestrator.js +45 -0
- package/dist/libdef/base-class-resolver.d.ts +23 -0
- package/dist/libdef/base-class-resolver.js +50 -0
- package/dist/libdef/cpp-to-decl.d.ts +34 -0
- package/dist/libdef/cpp-to-decl.js +584 -0
- package/dist/libdef/header-parser.d.ts +53 -0
- package/dist/libdef/header-parser.js +269 -0
- package/dist/libdef/registry.d.ts +10 -0
- package/dist/libdef/registry.js +142 -0
- package/dist/mapping/peripheral-names.d.ts +23 -0
- package/dist/mapping/peripheral-names.js +63 -0
- package/dist/mapping/source-map.d.ts +46 -0
- package/dist/mapping/source-map.js +207 -0
- package/dist/orchestrator/dts-generator.d.ts +17 -0
- package/dist/orchestrator/dts-generator.js +83 -0
- package/dist/orchestrator/graph-builder.d.ts +18 -0
- package/dist/orchestrator/graph-builder.js +218 -0
- package/dist/orchestrator/semantic-facts-verifier.d.ts +38 -0
- package/dist/orchestrator/semantic-facts-verifier.js +121 -0
- package/dist/orchestrator/semantic-facts.d.ts +141 -0
- package/dist/orchestrator/semantic-facts.js +652 -0
- package/dist/orchestrator/type-checker.d.ts +39 -0
- package/dist/orchestrator/type-checker.js +760 -0
- package/dist/platform/async-runtime.d.ts +8 -0
- package/dist/platform/async-runtime.js +26 -0
- package/dist/platform/generic-strategy.d.ts +101 -0
- package/dist/platform/generic-strategy.js +289 -0
- package/dist/platform/index.d.ts +4 -0
- package/dist/platform/index.js +9 -0
- package/dist/platform/registry.d.ts +21 -0
- package/dist/platform/registry.js +42 -0
- package/dist/platform/toolchain.d.ts +4 -0
- package/dist/platform/toolchain.js +43 -0
- package/dist/preview/client.d.ts +1 -0
- package/dist/preview/client.js +137 -0
- package/dist/preview/server.d.ts +5 -0
- package/dist/preview/server.js +199 -0
- package/dist/profiler/index.d.ts +28 -0
- package/dist/profiler/index.js +28 -0
- package/dist/profiler/profiler.d.ts +95 -0
- package/dist/profiler/profiler.js +338 -0
- package/dist/profiler/types.d.ts +87 -0
- package/dist/profiler/types.js +12 -0
- package/dist/stores/display-profile-store.d.ts +16 -0
- package/dist/stores/display-profile-store.js +48 -0
- package/dist/stores/theme-store.d.ts +13 -0
- package/dist/stores/theme-store.js +30 -0
- package/dist/testing.d.ts +45 -0
- package/dist/testing.js +47 -0
- package/dist/transpile/resolution.d.ts +29 -0
- package/dist/transpile/resolution.js +282 -0
- package/dist/transpile.d.ts +3 -0
- package/dist/transpile.js +722 -0
- package/dist/types.d.ts +217 -0
- package/dist/types.js +1 -0
- package/dist/ui/ui-bridge.d.ts +4 -0
- package/dist/ui/ui-bridge.js +34 -0
- package/dist/ui-hook.d.ts +84 -0
- package/dist/ui-hook.js +45 -0
- package/dist/utils/cli.d.ts +3 -0
- package/dist/utils/cli.js +463 -0
- package/dist/utils/fs.d.ts +4 -0
- package/dist/utils/fs.js +23 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +8 -0
- package/dist/utils/strings.d.ts +35 -0
- package/dist/utils/strings.js +142 -0
- package/dist/utils/ui.d.ts +66 -0
- package/dist/utils/ui.js +149 -0
- package/dist/watch.d.ts +35 -0
- package/dist/watch.js +111 -0
- package/package.json +127 -0
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// UI runtime emitter — injects the C++ runtime header + static tables into the
|
|
3
|
+
// entry translation unit when a UI is mounted.
|
|
4
|
+
//
|
|
5
|
+
// Called from emitCpp between the preamble (includes) and type declarations,
|
|
6
|
+
// gated on ctx.isEntryFile && entryHasUI(). Everything emitted here is
|
|
7
|
+
// file-scope (structs, static tables, extern decls) so it must precede any
|
|
8
|
+
// function that references it.
|
|
9
|
+
//
|
|
10
|
+
// Sources:
|
|
11
|
+
// - emitRuntimeHeader() → the guarded struct/helper block (once per TU)
|
|
12
|
+
// - allLoweredUIModules() → __ui_nodes[] + __ui_trans[] per mounted tree
|
|
13
|
+
// - uiSignalDecls() → one device variable per recorded signal
|
|
14
|
+
// - emitBindingTable(uiBindings()) → __ui_bindings[]
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
import { appendSourceLine, appendRenderedStatement } from "./line-appender.js";
|
|
17
|
+
import { createChildEmissionScope, createEmissionScopeState } from "../snprintf-helpers.js";
|
|
18
|
+
import { entryHasUI, requireUIHook } from "../../ui-hook.js";
|
|
19
|
+
import { uiSignalDecls, uiBindings, uiPressBindings, watchPinSpecs, clickHandlers } from "../../ir/transformers/ui-call-resolver.js";
|
|
20
|
+
import { emitBindingTable, emitListBindingTable, getListBindings, emitInputBindingTable, getInputBindings } from "../../ir/transformers/ui-reactive.js";
|
|
21
|
+
import { emitCanvasBindings, canvasBindings } from "../../ir/transformers/canvas-lowering.js";
|
|
22
|
+
import { getDisplayProfile } from "../../stores/display-profile-store.js";
|
|
23
|
+
import { effectiveDisplaySize, generateTouchAdapter, normalizeDisplayRotation, resolveScrollConfig, } from "../../api/shared/display-profile.js";
|
|
24
|
+
import { deriveCapabilities } from "../../api/shared/display-capabilities.js";
|
|
25
|
+
import { generateDisplayAdapter } from "../../api/shared/display-adapter.js";
|
|
26
|
+
import { getRadioGroups } from "../../ir/ui-element-auto-wire.js";
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Touch poll body generator — extracted from emitUIRuntime for testability.
|
|
29
|
+
//
|
|
30
|
+
// Emits the ui_poll_touch() body that reads raw touch coords, applies the
|
|
31
|
+
// calibration map+rotation, and dispatches to ui_handle_touch/ui_handle_no_touch.
|
|
32
|
+
//
|
|
33
|
+
// Capacitive controllers (FT6336U, sdl) have no real z/pressure, so the
|
|
34
|
+
// minPressure gate is skipped for them. Resistive controllers (XPT2046,
|
|
35
|
+
// STMPE610, Adafruit_TouchScreen) keep the gate.
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
const CAPACITIVE_TOUCH_LIBS = new Set(["FT6336U", "sdl"]);
|
|
38
|
+
/** Generate the ui_poll_touch() body, or null for the no-touch path. */
|
|
39
|
+
export function generateTouchPollBody(input) {
|
|
40
|
+
const { library, minPressure, calibration, profile } = input;
|
|
41
|
+
if (!library)
|
|
42
|
+
return null;
|
|
43
|
+
const { xMin, xMax, yMin, yMax } = calibration;
|
|
44
|
+
const rotation = normalizeDisplayRotation(profile.rotation);
|
|
45
|
+
const screen = effectiveDisplaySize(profile);
|
|
46
|
+
const hasNativeSize = profile.nativeWidth !== undefined && profile.nativeHeight !== undefined;
|
|
47
|
+
const nativeWidth = profile.nativeWidth ?? profile.width;
|
|
48
|
+
const nativeHeight = profile.nativeHeight ?? profile.height;
|
|
49
|
+
const rawMapX = `map(__rawX, ${xMin}, ${xMax}, 0, ${nativeWidth})`;
|
|
50
|
+
const rawMapY = `map(__rawY, ${yMin}, ${yMax}, 0, ${nativeHeight})`;
|
|
51
|
+
let mapX, mapY;
|
|
52
|
+
let sdlClamp = false;
|
|
53
|
+
if (library === "sdl") {
|
|
54
|
+
// SDL mouse coords are already window/screen pixel coords — no axis swap,
|
|
55
|
+
// no calibration. Pass them through 1:1 and clamp to the LIVE display
|
|
56
|
+
// dimensions (display_width()/display_height(), which return the SDL
|
|
57
|
+
// target's constructed w_/h_). This way clicks track the window size
|
|
58
|
+
// regardless of the touch.calibration config — the calibration is a
|
|
59
|
+
// hardware-controller concept (resistive raw ranges) that doesn't apply to
|
|
60
|
+
// a mouse, and baking it forced users to keep xMax/yMax in sync with
|
|
61
|
+
// width/height or clicks landed in the wrong place after resizing.
|
|
62
|
+
mapX = `__rawX`;
|
|
63
|
+
mapY = `__rawY`;
|
|
64
|
+
sdlClamp = true;
|
|
65
|
+
}
|
|
66
|
+
else if (!hasNativeSize) {
|
|
67
|
+
const isLandscape = rotation === 1 || rotation === 3;
|
|
68
|
+
const invertX = rotation === 1 || rotation === 2;
|
|
69
|
+
const invertY = rotation === 1 || rotation === 2;
|
|
70
|
+
if (isLandscape) {
|
|
71
|
+
mapX = invertX
|
|
72
|
+
? `map(__rawX, ${xMin}, ${xMax}, ${profile.width}, 0)`
|
|
73
|
+
: `map(__rawX, ${xMin}, ${xMax}, 0, ${profile.width})`;
|
|
74
|
+
mapY = invertY
|
|
75
|
+
? `map(__rawY, ${yMin}, ${yMax}, ${profile.height}, 0)`
|
|
76
|
+
: `map(__rawY, ${yMin}, ${yMax}, 0, ${profile.height})`;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
mapX = `map(__rawY, ${yMin}, ${yMax}, ${profile.width}, 0)`;
|
|
80
|
+
mapY = `map(__rawX, ${xMin}, ${xMax}, 0, ${profile.height})`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
switch (rotation) {
|
|
85
|
+
case 1:
|
|
86
|
+
mapX = rawMapY;
|
|
87
|
+
mapY = `${nativeWidth} - (${rawMapX})`;
|
|
88
|
+
break;
|
|
89
|
+
case 2:
|
|
90
|
+
mapX = `${nativeWidth} - (${rawMapX})`;
|
|
91
|
+
mapY = `${nativeHeight} - (${rawMapY})`;
|
|
92
|
+
break;
|
|
93
|
+
case 3:
|
|
94
|
+
mapX = `${nativeHeight} - (${rawMapY})`;
|
|
95
|
+
mapY = rawMapX;
|
|
96
|
+
break;
|
|
97
|
+
default:
|
|
98
|
+
mapX = rawMapX;
|
|
99
|
+
mapY = rawMapY;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const clampLines = (indent) => sdlClamp
|
|
104
|
+
? [
|
|
105
|
+
// SDL: clamp to live display dimensions (display_width()/height() return
|
|
106
|
+
// the SDL target's constructed w_/h_), so clicks track the window size.
|
|
107
|
+
`${indent}int16_t __dw = display_width(), __dh = display_height();`,
|
|
108
|
+
`${indent}if (__tx < 0) __tx = 0; else if (__tx >= __dw) __tx = __dw - 1;`,
|
|
109
|
+
`${indent}if (__ty < 0) __ty = 0; else if (__ty >= __dh) __ty = __dh - 1;`,
|
|
110
|
+
]
|
|
111
|
+
: [
|
|
112
|
+
`${indent}if (__tx < 0) __tx = 0; else if (__tx >= ${screen.width}) __tx = ${screen.width - 1};`,
|
|
113
|
+
`${indent}if (__ty < 0) __ty = 0; else if (__ty >= ${screen.height}) __ty = ${screen.height - 1};`,
|
|
114
|
+
];
|
|
115
|
+
const isCapacitive = CAPACITIVE_TOUCH_LIBS.has(library);
|
|
116
|
+
const lines = [
|
|
117
|
+
`void ui_poll_touch() {`,
|
|
118
|
+
` if (touch_isTouched()) {`,
|
|
119
|
+
` int16_t __rawX = 0, __rawY = 0, __rawZ = 0;`,
|
|
120
|
+
` touch_readRaw(&__rawX, &__rawY, &__rawZ);`,
|
|
121
|
+
];
|
|
122
|
+
if (isCapacitive) {
|
|
123
|
+
lines.push(` int16_t __tx = ${mapX};`, ` int16_t __ty = ${mapY};`, ...clampLines(" "), ` ui_handle_touch(__tx, __ty);`);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
lines.push(` if (__rawZ >= ${minPressure}) {`, ` int16_t __tx = ${mapX};`, ` int16_t __ty = ${mapY};`, ...clampLines(" "), ` ui_handle_touch(__tx, __ty);`, ` } else {`, ` ui_handle_no_touch();`, ` }`);
|
|
127
|
+
}
|
|
128
|
+
lines.push(` } else {`, ` ui_handle_no_touch();`, ` }`, `}`);
|
|
129
|
+
return lines.join("\n");
|
|
130
|
+
}
|
|
131
|
+
export function emitUIRuntime(ctx) {
|
|
132
|
+
// Only the entry file carries the UI runtime + tables.
|
|
133
|
+
if (!ctx.isEntryFile || !entryHasUI())
|
|
134
|
+
return;
|
|
135
|
+
// The hook is guaranteed set when entryHasUI() returns true.
|
|
136
|
+
const ui = requireUIHook();
|
|
137
|
+
const emitRuntimeHeader = ui.emitRuntimeHeader;
|
|
138
|
+
const allLoweredUIModules = ui.allLoweredUIModules;
|
|
139
|
+
// 0.5. Display adapter: includes + object declaration + inline functions.
|
|
140
|
+
// Generated per display driver type (ILI9341, ST7789, etc.) via the
|
|
141
|
+
// display-adapter registry. Must precede the runtime header so the
|
|
142
|
+
// adapter functions (__tc_display, display_init, etc.) are available.
|
|
143
|
+
const profile = getDisplayProfile();
|
|
144
|
+
// 0. SPI-bus display drivers (ILI9341, ST7796, …) need the SPI library.
|
|
145
|
+
// Host render targets (sdl, native-preview) have no SPI bus, so skip it.
|
|
146
|
+
const spiDriver = profile?.driver !== "sdl" && profile?.driver !== "native-preview";
|
|
147
|
+
if (spiDriver && !ctx.includes.includes("<SPI.h>")) {
|
|
148
|
+
ctx.includes.push("<SPI.h>");
|
|
149
|
+
}
|
|
150
|
+
// NOTE: <stdio.h> (needed by text-binding snprintf bodies) is pushed in
|
|
151
|
+
// buildEmitterContext (setup.ts), NOT here — emitPreamble runs before this
|
|
152
|
+
// function, so a push here would be too late to land in the output.
|
|
153
|
+
if (profile) {
|
|
154
|
+
const adapter = generateDisplayAdapter(profile);
|
|
155
|
+
// Prepend includes to the very front — Arduino's auto-prototyper scans
|
|
156
|
+
// the whole .ino and generates prototypes that reference GFXcanvas16
|
|
157
|
+
// etc. before any #include, so the GFX header must come first.
|
|
158
|
+
ctx.sourceLines.unshift(adapter.includes);
|
|
159
|
+
// Color-depth preamble: UI_COLOR_T is referenced by adapter draw
|
|
160
|
+
// signatures, so the depth + type defines must precede the adapter
|
|
161
|
+
// functions (which come before the runtime header). Under 565/mono
|
|
162
|
+
// these resolve to uint16_t/0x7BEF — byte-identical with history.
|
|
163
|
+
const is888 = profile.colorFormat === "rgb666" || profile.colorFormat === "rgb888";
|
|
164
|
+
ctx.sourceLines.push(is888
|
|
165
|
+
? "#define UI_COLOR_DEPTH 888\n#define UI_COLOR_T uint32_t\n#define UI_DIM_MASK 0x7F7F7Fu"
|
|
166
|
+
: "#define UI_COLOR_DEPTH 565\n#define UI_COLOR_T uint16_t\n#define UI_DIM_MASK 0x7BEFu");
|
|
167
|
+
ctx.sourceLines.push(adapter.declaration);
|
|
168
|
+
ctx.sourceLines.push(adapter.functions);
|
|
169
|
+
}
|
|
170
|
+
// 0.6. Touch adapter: includes + declaration (zero-cost, transpile-time).
|
|
171
|
+
// Same pattern as the display adapter — generated inline C++ per library type.
|
|
172
|
+
let touchAdapter = null;
|
|
173
|
+
if (profile.touch) {
|
|
174
|
+
const t = profile.touch;
|
|
175
|
+
if (t.library) {
|
|
176
|
+
touchAdapter = generateTouchAdapter(t);
|
|
177
|
+
// Prepend includes to the front (Arduino auto-prototyper safety).
|
|
178
|
+
ctx.sourceLines.unshift(touchAdapter.includes[0]);
|
|
179
|
+
for (let i = 1; i < touchAdapter.includes.length; i++) {
|
|
180
|
+
ctx.sourceLines.push(touchAdapter.includes[i]);
|
|
181
|
+
}
|
|
182
|
+
ctx.sourceLines.push(touchAdapter.declaration);
|
|
183
|
+
ctx.sourceLines.push(touchAdapter.functions);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// SDL_MAIN_HANDLED must be the absolute first line — it must precede every
|
|
187
|
+
// #include <SDL2/SDL.h> (the display adapter's and the touch shim's). The
|
|
188
|
+
// touch unshift above runs after the display-adapter block, so this final
|
|
189
|
+
// unshift lands at the very front.
|
|
190
|
+
if (profile.driver === "sdl") {
|
|
191
|
+
ctx.sourceLines.unshift("#define SDL_MAIN_HANDLED");
|
|
192
|
+
}
|
|
193
|
+
// Forward declaration for touch poll (used inside the runtime header's ui_tick).
|
|
194
|
+
// Always emitted — ui_tick calls it unconditionally; the body is a no-op when
|
|
195
|
+
// no touch hardware is configured.
|
|
196
|
+
ctx.sourceLines.push("void ui_poll_touch();");
|
|
197
|
+
// 1. Runtime header (structs + helpers, guarded so repeat emission is safe).
|
|
198
|
+
// Emit the antialiasing compile-time flag before the header so the AA
|
|
199
|
+
// code paths are compiled in.
|
|
200
|
+
const loweredModules = allLoweredUIModules();
|
|
201
|
+
// AA compiles in only when the profile opts in. A node-level fontAntialias=1
|
|
202
|
+
// flag alone is not enough — that would silently re-enable a heavy code path
|
|
203
|
+
// the author explicitly disabled with `antialias: false`. (The runtime still
|
|
204
|
+
// honors fontAntialias=0 at the per-node level when AA is compiled in.)
|
|
205
|
+
const needsAntialias = profile.antialias === true;
|
|
206
|
+
if (needsAntialias) {
|
|
207
|
+
ctx.sourceLines.push("#define UI_AA 1");
|
|
208
|
+
}
|
|
209
|
+
// The SDL desktop target has a real keyboard (the host event loop routes
|
|
210
|
+
// SDL_TEXTINPUT into the on-screen-keyboard editing session), so the 6×4 OSK
|
|
211
|
+
// grid is redundant clutter that covers the app. UI_HIDE_OSK suppresses the
|
|
212
|
+
// OSK *draw* only — the editing session (buffer, target, commit-on-close)
|
|
213
|
+
// still runs, so typing works. Hardware targets don't define it: their only
|
|
214
|
+
// text-entry path is the visible grid.
|
|
215
|
+
if (profile.driver === "sdl") {
|
|
216
|
+
ctx.sourceLines.push("#define UI_HIDE_OSK 1");
|
|
217
|
+
}
|
|
218
|
+
// 1a. Color depth — drives blend/lerp selection in the runtime header. 565
|
|
219
|
+
// for TFT byte-identity (node fields hold 565 values); 888 for rgb666+
|
|
220
|
+
// targets (node fields hold 888 values, blend via ui_blend888). The value
|
|
221
|
+
// depth and blend math switch TOGETHER — see Phase 1 counterexample.
|
|
222
|
+
// Guarded: the display-adapter preamble (0.5) may already define it so
|
|
223
|
+
// UI_COLOR_T precedes the adapter's draw signatures.
|
|
224
|
+
ctx.sourceLines.push(profile.colorFormat === "rgb666" || profile.colorFormat === "rgb888"
|
|
225
|
+
? "#ifndef UI_COLOR_DEPTH\n#define UI_COLOR_DEPTH 888\n#endif"
|
|
226
|
+
: "#ifndef UI_COLOR_DEPTH\n#define UI_COLOR_DEPTH 565\n#endif");
|
|
227
|
+
// 1a-bis. Refresh model + native format from derived capabilities. E-ink
|
|
228
|
+
// (displayClass: "eink") derives refreshModel "deferred-partial" +
|
|
229
|
+
// requiresBackingStore; TFT stays "immediate". When these are absent
|
|
230
|
+
// (TFT), the runtime's backing-store + refresh-scheduler + clear-
|
|
231
|
+
// suppression paths compile out — byte-identical with pre-Phase-4.
|
|
232
|
+
const caps = deriveCapabilities(profile);
|
|
233
|
+
if (caps.refreshModel !== "immediate") {
|
|
234
|
+
ctx.sourceLines.push("#define UI_REFRESH_DEFERRED 1");
|
|
235
|
+
}
|
|
236
|
+
if (caps.nativeFormat === "mono") {
|
|
237
|
+
ctx.sourceLines.push("#define UI_NATIVE_MONO 1");
|
|
238
|
+
}
|
|
239
|
+
if (caps.requiresBackingStore) {
|
|
240
|
+
ctx.sourceLines.push("#define UI_REQUIRES_BACKING_STORE 1");
|
|
241
|
+
}
|
|
242
|
+
// NOTE: per-frame SPI-write batching via UI_BATCH_SPI_WRITES was investigated
|
|
243
|
+
// and reverted — the Adafruit_GFX version in this repo does NOT reference-
|
|
244
|
+
// count startWrite/endWrite (every endWrite raises CS unconditionally), so
|
|
245
|
+
// wrapping the frame in startWrite/endWrite causes inner draw calls (e.g. the
|
|
246
|
+
// scroll-canvas composite) to close the transaction mid-frame → black screen.
|
|
247
|
+
// The three-branch dispatch in runtime-header.ts still ships (it's a clearer
|
|
248
|
+
// structure than the old #ifndef/#else), but TFT now falls through to the
|
|
249
|
+
// no-op default until a ref-counted library version or a bypass approach lands.
|
|
250
|
+
// See docs/superpowers/specs/2026-07-06-tft-spi-write-batching-design.md.
|
|
251
|
+
// 1b. Scroll capability + physics overrides — emitted BEFORE the runtime
|
|
252
|
+
// header so its #ifndef guards adopt them. Source of truth:
|
|
253
|
+
// resolveScrollConfig(profile.scroll). Defaults derive from the declared
|
|
254
|
+
// touch hardware, so the demo (XPT2046) gets resistive+full with no config.
|
|
255
|
+
const scroll = resolveScrollConfig(profile, { buildTarget: profile._buildTarget });
|
|
256
|
+
ctx.sourceLines.push(`#define UI_SCROLL_MAX_OVERSCROLL ${scroll.maxOverscroll}`, `#define UI_SCROLL_STIFFNESS_X10 ${Math.round(scroll.stiffness * 10)}`, `#define UI_SCROLL_EDGE_SNAP_PX ${scroll.edgeSnapPx}`, `#define UI_SCROLL_DRAG_SCALE_X10 ${Math.round(scroll.dragScale * 10)}`, `#define UI_SCROLL_INPUT_TIER_CAPACITIVE ${scroll.inputTier === "capacitive" ? 1 : 0}`, `#define UI_SCROLL_INPUT_TIER_RESISTIVE ${scroll.inputTier === "resistive" ? 1 : 0}`, `#define UI_SCROLL_INPUT_TIER_NONE ${scroll.inputTier === "none" ? 1 : 0}`, `#define UI_SCROLL_RENDER_TIER_FULL ${scroll.renderTier === "full" ? 1 : 0}`, `#define UI_SCROLL_RENDER_TIER_CONSTRAINED ${scroll.renderTier === "constrained" ? 1 : 0}`, `#define UI_SCROLL_CANVAS_BUDGET_BYTES ${scroll.scrollCanvasBudgetBytes}`, ...(scroll.debug ? [`#define UI_SCROLL_DEBUG 1`] : []));
|
|
257
|
+
// Forward-declare __ui_kb_set_onchange before the runtime header: the
|
|
258
|
+
// header's keyboard-open function calls it, but the definition is emitted
|
|
259
|
+
// later (section 8b). On native (single TU) this must precede the header.
|
|
260
|
+
ctx.sourceLines.push(`void __ui_kb_set_onchange();`);
|
|
261
|
+
ctx.sourceLines.push(emitRuntimeHeader());
|
|
262
|
+
// 1.5. Touch poll function (uses the adapter pattern). Body is generated by
|
|
263
|
+
// generateTouchPollBody — extracted for unit-testability of the capacitive
|
|
264
|
+
// vs resistive branch (FT6336U/SDL skip the minPressure gate).
|
|
265
|
+
if (profile.touch && touchAdapter) {
|
|
266
|
+
const body = generateTouchPollBody({
|
|
267
|
+
library: profile.touch.library,
|
|
268
|
+
minPressure: profile.touch.minPressure ?? 10,
|
|
269
|
+
calibration: profile.touch.calibration,
|
|
270
|
+
profile: {
|
|
271
|
+
width: profile.width,
|
|
272
|
+
height: profile.height,
|
|
273
|
+
nativeWidth: profile.nativeWidth,
|
|
274
|
+
nativeHeight: profile.nativeHeight,
|
|
275
|
+
rotation: profile.rotation,
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
ctx.sourceLines.push(body ?? "void ui_poll_touch() {}");
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
// No touch hardware — emit a no-op so ui_tick's unconditional call resolves.
|
|
282
|
+
ctx.sourceLines.push("void ui_poll_touch() {}");
|
|
283
|
+
}
|
|
284
|
+
// 2. Static node + transition tables for every mounted UI tree.
|
|
285
|
+
for (const { lowered } of allLoweredUIModules()) {
|
|
286
|
+
ctx.sourceLines.push(lowered.fontTables);
|
|
287
|
+
if (lowered.imageTables)
|
|
288
|
+
ctx.sourceLines.push(lowered.imageTables);
|
|
289
|
+
if (lowered.keyframeTables)
|
|
290
|
+
ctx.sourceLines.push(lowered.keyframeTables);
|
|
291
|
+
ctx.sourceLines.push(lowered.nodeTable);
|
|
292
|
+
ctx.sourceLines.push(lowered.transitionTable);
|
|
293
|
+
}
|
|
294
|
+
// 2b. Keyboard loader functions + dispatch table (for <input> support).
|
|
295
|
+
for (const { lowered } of allLoweredUIModules()) {
|
|
296
|
+
if (lowered.keyboardLoaders)
|
|
297
|
+
ctx.sourceLines.push(lowered.keyboardLoaders);
|
|
298
|
+
if (lowered.keyboardDispatch)
|
|
299
|
+
ctx.sourceLines.push(lowered.keyboardDispatch);
|
|
300
|
+
}
|
|
301
|
+
// 3. Signal variables (one per ui.signal / const X = ui.signal).
|
|
302
|
+
for (const decl of uiSignalDecls()) {
|
|
303
|
+
ctx.sourceLines.push(decl);
|
|
304
|
+
}
|
|
305
|
+
// 4. Binding table (accumulated from ui.bind calls).
|
|
306
|
+
// Forward-declare the binding compute functions first: the table references
|
|
307
|
+
// them via fn/textFn, but the function bodies are emitted below (4b). On
|
|
308
|
+
// native (single TU, no Arduino auto-prototyper) the use-before-def would
|
|
309
|
+
// fail to compile.
|
|
310
|
+
for (const spec of uiBindings()) {
|
|
311
|
+
if (spec.property === "text") {
|
|
312
|
+
ctx.sourceLines.push(`void ${spec.fnName}(char* buf, uint8_t size);`);
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
ctx.sourceLines.push(`uint32_t ${spec.fnName}();`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
ctx.sourceLines.push(emitBindingTable(uiBindings()));
|
|
319
|
+
// 4a. List binding functions + table (from ui.bindList calls).
|
|
320
|
+
for (const spec of getListBindings()) {
|
|
321
|
+
emitCallbackWrapper(ctx, spec.countFnName, spec.countFnBody, spec.countStatements, spec.countSourceSpan, "()", "uint16_t");
|
|
322
|
+
emitCallbackWrapper(ctx, spec.itemFnName, spec.itemFnBody, spec.itemStatements, spec.itemSourceSpan, "(uint16_t idx, char* buf, uint8_t size)", "void");
|
|
323
|
+
if (spec.tapFnName && (spec.tapStatements || spec.tapFnBody)) {
|
|
324
|
+
emitCallbackWrapper(ctx, spec.tapFnName, spec.tapFnBody ?? "", spec.tapStatements, spec.tapSourceSpan, "(uint16_t idx)", "void");
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
ctx.sourceLines.push(emitListBindingTable(getListBindings()));
|
|
328
|
+
// 4a'. Input binding functions + table (from ui.bindInput calls).
|
|
329
|
+
for (const spec of getInputBindings()) {
|
|
330
|
+
emitCallbackWrapper(ctx, spec.cbFnName, spec.cbFnBody, spec.bodyStatements, spec.sourceSpan, "(const char* text)", "void");
|
|
331
|
+
}
|
|
332
|
+
ctx.sourceLines.push(emitInputBindingTable(getInputBindings()));
|
|
333
|
+
// 4b. Binding compute functions. Each ui.bind(node, prop, fn) records a
|
|
334
|
+
// BindingSpec whose fnName is referenced by the table. v1 emits a stub that
|
|
335
|
+
// returns the node's current property value — structurally valid so the
|
|
336
|
+
// table compiles. Full arrow-function → C++ lowering is a follow-up.
|
|
337
|
+
for (const spec of uiBindings()) {
|
|
338
|
+
// If the binding has a lowered C++ expression (from the arrow body), use it;
|
|
339
|
+
// otherwise fall back to returning the node's current value.
|
|
340
|
+
const isTextBinding = spec.property === "text";
|
|
341
|
+
if (isTextBinding) {
|
|
342
|
+
// Text bindings are void fill-style: they write into (buf, size).
|
|
343
|
+
// cppBody is the imperative snprintf statement from lowerTextBindingBody.
|
|
344
|
+
const body = spec.cppBody ?? "buf[0] = 0;";
|
|
345
|
+
ctx.sourceLines.push(`void ${spec.fnName}(char* buf, uint8_t size) { ${body} }`);
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
const access = spec.property === "background" ? "bg" : spec.property === "color" ? "fg" : "bg";
|
|
349
|
+
// Prefer the stashed ExpressionIR: render it through the strategy-aware
|
|
350
|
+
// ExpressionRenderer so division promotion, modulo→fmod, and concat
|
|
351
|
+
// wrapping match top-level code. Fall back to the prerendered cppExpr
|
|
352
|
+
// (color-resolved via resolveColorLiterals) when no IR is available.
|
|
353
|
+
if (spec.bodyIR) {
|
|
354
|
+
const rendered = ctx.exprRenderer.render(spec.bodyIR);
|
|
355
|
+
const prelude = ctx.exprRenderer.drainPrelude();
|
|
356
|
+
// Prelude lines (e.g. __cuttlefish_str_N buffer decls for snprintf
|
|
357
|
+
// expansions) must land inside the function body before the return;
|
|
358
|
+
// promote char→static char so a returned buffer pointer doesn't dangle
|
|
359
|
+
// (mirrors statement-renderer's return+prelude handling).
|
|
360
|
+
const body = prelude.length > 0
|
|
361
|
+
? `${prelude.map((l) => l.replace(/\bchar\s+/, "static char ")).join(" ")} return ${rendered};`
|
|
362
|
+
: `return ${rendered};`;
|
|
363
|
+
ctx.sourceLines.push(`uint32_t ${spec.fnName}(void) { ${body} }`);
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
const body = spec.cppExpr || `__ui_nodes[${spec.nodeIndex}].${access}`;
|
|
367
|
+
ctx.sourceLines.push(`uint32_t ${spec.fnName}(void) { return ${body}; }`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// 5. Node count externs the runtime header references.
|
|
372
|
+
const totalNodes = allLoweredUIModules().reduce((sum, { lowered }) => sum + countNodes(lowered.nodeTable), 0);
|
|
373
|
+
ctx.sourceLines.push(`const uint16_t __ui_node_count = ${totalNodes};`);
|
|
374
|
+
ctx.sourceLines.push(`const uint16_t __ui_trans_count = ${countTransitions()};`);
|
|
375
|
+
ctx.sourceLines.push(`const uint16_t __ui_binding_count = ${uiBindings().length};`);
|
|
376
|
+
// Count screens across all modules (for multi-screen navigation).
|
|
377
|
+
let maxScreens = 1;
|
|
378
|
+
for (const { lowered } of allLoweredUIModules()) {
|
|
379
|
+
if (lowered.screenCount > maxScreens)
|
|
380
|
+
maxScreens = lowered.screenCount;
|
|
381
|
+
}
|
|
382
|
+
ctx.sourceLines.push(`const uint16_t __ui_screen_count = ${maxScreens};`);
|
|
383
|
+
// 6. Press/release handler functions (from screen.btn.onPress/onRelease).
|
|
384
|
+
// Each calls ui_on_press/ui_on_release(nodeIndex), defined in the runtime
|
|
385
|
+
// header. The attachInterrupt calls that wire these to pins are injected
|
|
386
|
+
// into setup() by the entrypoint synthesizer.
|
|
387
|
+
for (const pb of uiPressBindings()) {
|
|
388
|
+
const fn = pb.edge === "press" ? "ui_on_press" : "ui_on_release";
|
|
389
|
+
ctx.sourceLines.push(`void ${pb.handlerName}() { ${fn}(${pb.nodeIndex}); }`);
|
|
390
|
+
}
|
|
391
|
+
// 7. Pin-watcher callbacks + table (from ui.watchPin(pin, callback)).
|
|
392
|
+
// Each callback is a plain function; the runtime's ui_poll_inputs() calls it
|
|
393
|
+
// on falling edge. No async runtime needed — runs in the main loop frame.
|
|
394
|
+
for (const wp of watchPinSpecs()) {
|
|
395
|
+
emitCallbackWrapper(ctx, wp.fnName, wp.callbackBody, wp.bodyStatements, wp.sourceSpan);
|
|
396
|
+
}
|
|
397
|
+
if (watchPinSpecs().length > 0) {
|
|
398
|
+
ctx.sourceLines.push(`UIPinWatch __ui_pin_watches[] = {`);
|
|
399
|
+
for (const wp of watchPinSpecs()) {
|
|
400
|
+
ctx.sourceLines.push(` { .pin=${wp.pin}, .lastState=1, .cb=${wp.fnName} },`);
|
|
401
|
+
}
|
|
402
|
+
ctx.sourceLines.push(`};`);
|
|
403
|
+
ctx.sourceLines.push(`const uint16_t __ui_pin_watch_count = ${watchPinSpecs().length};`);
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
ctx.sourceLines.push(`UIPinWatch __ui_pin_watches[] = {};`);
|
|
407
|
+
ctx.sourceLines.push(`const uint16_t __ui_pin_watch_count = 0;`);
|
|
408
|
+
}
|
|
409
|
+
// 8. Touch: handler functions + tables (click, hold, release).
|
|
410
|
+
// "change" (input commit) and "rangechange" (slider drag) are emitted
|
|
411
|
+
// separately in 8b/8c — they have their own dispatch points.
|
|
412
|
+
const touchHandlers = clickHandlers().filter(h => h.kind !== "change" && h.kind !== "rangechange");
|
|
413
|
+
const maxIdx = touchHandlers.reduce((max, h) => Math.max(max, h.nodeIndex), -1);
|
|
414
|
+
const tableSize = Math.max(maxIdx + 1, 1);
|
|
415
|
+
// Forward-declare named-ref handlers (on:* attributes) BEFORE the handler
|
|
416
|
+
// tables — the table references these author-declared functions by name, but
|
|
417
|
+
// their definitions are emitted later (in the author-function section). The
|
|
418
|
+
// general transpiler's own forward declarations land after the UI tables, so
|
|
419
|
+
// without these the table's reference is out of scope.
|
|
420
|
+
for (const ch of touchHandlers) {
|
|
421
|
+
if (ch.isNamedRef) {
|
|
422
|
+
ctx.sourceLines.push(`void ${ch.fnName}();`);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// Handler functions (skip named-ref handlers — their function is the author's
|
|
426
|
+
// own exported function, already emitted by the general transpiler pipeline).
|
|
427
|
+
for (const ch of touchHandlers) {
|
|
428
|
+
if (ch.isNamedRef)
|
|
429
|
+
continue;
|
|
430
|
+
emitCallbackWrapper(ctx, ch.fnName, ch.callbackBody, ch.bodyStatements, ch.sourceSpan);
|
|
431
|
+
}
|
|
432
|
+
// Build lookup tables (null for nodes without handlers)
|
|
433
|
+
const buildTable = (kind) => {
|
|
434
|
+
const entries = [];
|
|
435
|
+
for (let i = 0; i < tableSize; i++) {
|
|
436
|
+
const handler = clickHandlers().find(h => h.nodeIndex === i && h.kind === kind);
|
|
437
|
+
entries.push(handler ? handler.fnName : "nullptr");
|
|
438
|
+
}
|
|
439
|
+
return entries.join(", ");
|
|
440
|
+
};
|
|
441
|
+
if (profile.touch) {
|
|
442
|
+
ctx.sourceLines.push(`void (*__ui_click_handlers[])() = { ${buildTable("click")} };`);
|
|
443
|
+
ctx.sourceLines.push(`void (*__ui_hold_handlers[])() = { ${buildTable("hold")} };`);
|
|
444
|
+
ctx.sourceLines.push(`void (*__ui_release_handlers[])() = { ${buildTable("release")} };`);
|
|
445
|
+
ctx.sourceLines.push(`const uint16_t __ui_click_handler_count = ${tableSize};`);
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
ctx.sourceLines.push(`void (*__ui_click_handlers[])() = {};`);
|
|
449
|
+
ctx.sourceLines.push(`void (*__ui_hold_handlers[])() = {};`);
|
|
450
|
+
ctx.sourceLines.push(`void (*__ui_release_handlers[])() = {};`);
|
|
451
|
+
ctx.sourceLines.push(`const uint16_t __ui_click_handler_count = 0;`);
|
|
452
|
+
}
|
|
453
|
+
// 8b. Input onChange dispatch — assigns __ui_kb_onchange based on __ui_kb_target.
|
|
454
|
+
// Forward-declare __ui_kb_set_onchange unconditionally: the runtime header's
|
|
455
|
+
// keyboard-open function (emitted earlier) calls it, and on native (single
|
|
456
|
+
// TU) the definition below would be too late.
|
|
457
|
+
ctx.sourceLines.push(`void __ui_kb_set_onchange();`);
|
|
458
|
+
const inputChangeHandlers = clickHandlers().filter(h => h.kind === "change");
|
|
459
|
+
if (inputChangeHandlers.length > 0) {
|
|
460
|
+
// Forward-declare named-ref onChange handlers before the dispatch wiring.
|
|
461
|
+
for (const h of inputChangeHandlers) {
|
|
462
|
+
if (h.isNamedRef)
|
|
463
|
+
ctx.sourceLines.push(`void ${h.fnName}();`);
|
|
464
|
+
}
|
|
465
|
+
for (const h of inputChangeHandlers) {
|
|
466
|
+
if (h.isNamedRef)
|
|
467
|
+
continue; // author's own function; no wrapper.
|
|
468
|
+
emitCallbackWrapper(ctx, h.fnName, h.callbackBody, h.bodyStatements, h.sourceSpan);
|
|
469
|
+
}
|
|
470
|
+
ctx.sourceLines.push(`void __ui_kb_set_onchange() {`);
|
|
471
|
+
ctx.sourceLines.push(` __ui_kb_onchange = nullptr;`);
|
|
472
|
+
for (const h of inputChangeHandlers) {
|
|
473
|
+
ctx.sourceLines.push(` if (__ui_kb_target == ${h.nodeIndex}) __ui_kb_onchange = ${h.fnName};`);
|
|
474
|
+
}
|
|
475
|
+
ctx.sourceLines.push(`}`);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
ctx.sourceLines.push(`void __ui_kb_set_onchange() { __ui_kb_onchange = nullptr; }`);
|
|
479
|
+
}
|
|
480
|
+
// 8c. Range onChange dispatch — per-node handler table fired from the slider
|
|
481
|
+
// drag loop on every value change. Mirrors the click/hold/release tables.
|
|
482
|
+
const rangeChangeHandlers = clickHandlers().filter(h => h.kind === "rangechange");
|
|
483
|
+
const rcMaxIdx = rangeChangeHandlers.reduce((max, h) => Math.max(max, h.nodeIndex), -1);
|
|
484
|
+
const rcTableSize = Math.max(rcMaxIdx + 1, 1);
|
|
485
|
+
for (const h of rangeChangeHandlers) {
|
|
486
|
+
emitCallbackWrapper(ctx, h.fnName, h.callbackBody, h.bodyStatements, h.sourceSpan);
|
|
487
|
+
}
|
|
488
|
+
if (profile.touch && rangeChangeHandlers.length > 0) {
|
|
489
|
+
const rcEntries = [];
|
|
490
|
+
for (let i = 0; i < rcTableSize; i++) {
|
|
491
|
+
const handler = rangeChangeHandlers.find(h => h.nodeIndex === i);
|
|
492
|
+
rcEntries.push(handler ? handler.fnName : "nullptr");
|
|
493
|
+
}
|
|
494
|
+
ctx.sourceLines.push(`void (*__ui_rangechange_handlers[])() = { ${rcEntries.join(", ")} };`);
|
|
495
|
+
ctx.sourceLines.push(`const uint16_t __ui_rangechange_handler_count = ${rcTableSize};`);
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
ctx.sourceLines.push(`void (*__ui_rangechange_handlers[])() = {};`);
|
|
499
|
+
ctx.sourceLines.push(`const uint16_t __ui_rangechange_handler_count = 0;`);
|
|
500
|
+
}
|
|
501
|
+
// 9. Radio group table (from auto-wire).
|
|
502
|
+
const radioGroups = getRadioGroups();
|
|
503
|
+
if (radioGroups.size > 0) {
|
|
504
|
+
const groupEntries = Array.from(radioGroups.entries());
|
|
505
|
+
ctx.sourceLines.push(`UIRadioGroup __ui_radio_groups[] = {`);
|
|
506
|
+
for (const [, members] of groupEntries) {
|
|
507
|
+
const indices = members.map((m) => m.nodeIndex).join(", ");
|
|
508
|
+
ctx.sourceLines.push(` { .nodeIndices={${indices}}, .count=${members.length} },`);
|
|
509
|
+
}
|
|
510
|
+
ctx.sourceLines.push(`};`);
|
|
511
|
+
ctx.sourceLines.push(`const uint16_t __ui_radio_group_count = ${groupEntries.length};`);
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
ctx.sourceLines.push(`UIRadioGroup __ui_radio_groups[] = {};`);
|
|
515
|
+
ctx.sourceLines.push(`const uint16_t __ui_radio_group_count = 0;`);
|
|
516
|
+
}
|
|
517
|
+
// 10. Canvas draw bindings (ui.drawCanvas). Each spec emits a draw wrapper
|
|
518
|
+
// that runs the lowered callback body against the node's offscreen canvas.
|
|
519
|
+
emitCanvasBindings(canvasBindings(), (line, span) => {
|
|
520
|
+
appendSourceLine(ctx, line, span ? { tsSpan: span, nodeKind: "ui-canvas-callback" } : undefined);
|
|
521
|
+
}, (statements, indent) => {
|
|
522
|
+
const scope = createChildEmissionScope(createEmissionScopeState());
|
|
523
|
+
for (const stmt of statements) {
|
|
524
|
+
appendRenderedStatement(ctx, stmt, indent, scope);
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
/** Emit a synthesized callback wrapper. When bodyStatements is present, each
|
|
529
|
+
* statement is rendered through StatementRenderer (same path setInterval /
|
|
530
|
+
* free functions use). Falls back to pasting callbackBody for auto-wire /
|
|
531
|
+
* legacy string-baked specs. Skipped by callers for isNamedRef handlers. */
|
|
532
|
+
function emitCallbackWrapper(ctx, fnName, callbackBody, bodyStatements, sourceSpan, signature = "()", returnType = "void") {
|
|
533
|
+
const mapEntry = sourceSpan
|
|
534
|
+
? { tsSpan: sourceSpan, nodeKind: "ui-callback", symbolName: fnName }
|
|
535
|
+
: undefined;
|
|
536
|
+
if (bodyStatements && bodyStatements.length > 0) {
|
|
537
|
+
appendSourceLine(ctx, `${returnType} ${fnName}${signature} {`, mapEntry);
|
|
538
|
+
const scope = createChildEmissionScope(createEmissionScopeState());
|
|
539
|
+
for (const stmt of bodyStatements) {
|
|
540
|
+
appendRenderedStatement(ctx, stmt, " ", scope);
|
|
541
|
+
}
|
|
542
|
+
appendSourceLine(ctx, `}`);
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
appendSourceLine(ctx, `${returnType} ${fnName}${signature} { ${callbackBody || ""} }`, mapEntry);
|
|
546
|
+
}
|
|
547
|
+
/** Count NODE_FILL/NODE_TEXT entries in the emitted node table (one per node). */
|
|
548
|
+
function countNodes(nodeTable) {
|
|
549
|
+
// Count node-table entries directly. Every node initializer starts with
|
|
550
|
+
// "{ .box="; counting those cannot under/over-count regardless of kind (the
|
|
551
|
+
// previous kind-regex approach missed NODE_CANVAS and any future kind,
|
|
552
|
+
// undercounting __ui_node_count so the last node(s) were never drawn).
|
|
553
|
+
const matches = nodeTable.match(/\{\s*\.box=/g);
|
|
554
|
+
return matches ? matches.length : 0;
|
|
555
|
+
}
|
|
556
|
+
/** Count armed transitions across all modules. */
|
|
557
|
+
function countTransitions() {
|
|
558
|
+
let count = 0;
|
|
559
|
+
for (const { lowered } of requireUIHook().allLoweredUIModules()) {
|
|
560
|
+
const matches = lowered.transitionTable.match(/\.durationMs=/g);
|
|
561
|
+
count += matches ? matches.length : 0;
|
|
562
|
+
}
|
|
563
|
+
return count;
|
|
564
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|