Skip to content

Develop/v9.5#61

Open
lygstate wants to merge 101 commits into
lvgl:masterfrom
lvgljs:develop/v9.5
Open

Develop/v9.5#61
lygstate wants to merge 101 commits into
lvgl:masterfrom
lvgljs:develop/v9.5

Conversation

@lygstate

@lygstate lygstate commented Jun 14, 2026

Copy link
Copy Markdown

Summary by cubic

Upgrade to LVGL 9.5 with a new simulator/device HAL, cross‑platform CI, and deterministic screenshot tests with packaged baselines and HTML diff reports. Adds Windows simulator support (MSVC), a BUILD_LVGL_SIMULATOR CMake option, ports Chart to v9, introduces a codegen pipeline and a batched style system, improves JS builds/tests, and exposes a PNG screenshot API on the simulator.

  • New Features

    • NativeRender.RenderUtil.captureDisplay(path) to save PNGs; docs in doc/jsapi/captureDisplay.md.
    • Screenshot tooling across Linux/macOS/Windows: capture, pixel‑diff, HTML report, and baseline fetch/publish from GitHub Releases with Actions fallback.
    • Windows simulator build and CI job (MSVC + vcpkg), BUILD_LVGL_SIMULATOR CMake option, and Windows setup docs.
    • Typed React bridge with a GetBridge() accessor; shared asset loaders for the txiki runtime; parallel esbuild bundling; yarn scripts for build, typecheck, specs, and tests. Adds GUI/CLI test harness with expected‑failure support and leak probes.
    • Per‑axis transform scale for View/Image/GIF; GIF adds pause/resume and a paused prop for tests.
  • Migration

    • Upgrade deps/lvgl to v9.5; drop deps/lv_drivers; route config via top‑level lv_conf.h to deps/lv_conf_sim.h / deps/lv_conf_device.h. Port HAL to LVGL v9 built‑in SDL/display APIs; device builds target Linux; use the simulator on Windows. Engine timer now reschedules from lv_timer_handler() for smoother frames.
    • Port Chart to v9 (axis scale widgets and text sources, series data APIs, 256‑based zoom via a scrollable viewport) with unchanged JS surface.
    • Refresh codegen for LVGL v9 headers; consolidate native/JS includes under lv_bindings_js.h; apply styles via a Uint32Array batch with generated enums and easing indices.
    • Upgrade deps/txiki to v26.5.0; align QuickJS bindings (single‑arg JS_IsArray), update CI/build scripts, and harden MSVC builds.
    • Migrate Image/GIF to v9 image APIs and native alpha layout; fix PNG channel order; add a snapshot‑based screenshot path; fix component teardown leaks with idempotent close() and reconciler detach updates.

Written for commit 1bb1d12. Summary will update on new commits.

Review in cubic

lygstate added 2 commits June 1, 2026 20:01
Add lv_bindings_js.h as a single entry point for lvgl, quickjs, and private.h instead of repeating extern C blocks across native render headers.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9 issues found and verified against the latest diff

Note: This PR contains a large number of files. cubic only reviews up to 100 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.
On a pro plan you can use ultrareview for larger PRs.

Re-trigger cubic

Comment thread src/engine/hal/framebuffer32_to_rgba.c
Comment thread src/engine/hal/device/device.cpp
Comment thread scripts/lib/normalize_common.py
Comment thread src/engine/hal/screenshot.cpp Outdated
Comment thread scripts/normalize_commit_msg.py Outdated
Comment thread scripts/normalize_commit_msg.py Outdated
Comment thread scripts/capture-screenshots.js
Comment thread scripts/lib/fetch-screenshot-baseline.js Outdated
Comment thread src/engine/hal/framebuffer32_to_rgba.c Outdated
@lygstate lygstate force-pushed the develop/v9.5 branch 4 times, most recently from 5825b1c to cc703ee Compare June 14, 2026 03:39
lygstate added 9 commits June 14, 2026 15:13
- Expose NativeRender.RenderUtil.captureDisplay(path) when LV_USE_SNAPSHOT is enabled
- Capture the active screen with lv_snapshot_take (TRUE_COLOR_ALPHA) and write a 32-bit PNG via lodepng
- Convert snapshot pixels with framebuffer32_to_rgba before encoding
- Register the QuickJS binding from render bootstrap (NativeScreenshotInit)
- Document API, simulator vs device availability, and usage in doc/jsapi/captureDisplay.md
- Add screenshot HAL sources and link them from CMakeLists.txt
- Add utils/assets.ts with loadLocalAsset (import.meta.dirname + tjs.readFile) and fetchAssetBinary for remote assets.

- Image, GIF, and style/post.ts use shared helpers instead of path/fs/fetch and __dirname, which break in the txiki module runtime.

- Pass fetch arrayBuffer results directly; txiki has no Node.js Buffer.

- Demo widgets: use module-relative ./assets/ paths.
Enable static typing for the runtime bridge exposed via globalThis[Symbol.for("lvgljs")],
so React render code can reference native widget constructors and methods without casts.

Bridge module (src/render/react/core/bridge.ts):
- LvgljsNativeComponent base with style, dataset, nativeSetStyle, addEventListener, getBoundingClientRect
- Layered subclasses: ChildContainer, Alignable, Scrollable, Widget, BackgroundWidget, List
- Per-widget declare classes (Arc, Button, View, Text, Image, Switch, Checkbox, Textarea, Keyboard, Dropdownlist, ProgressBar, Roller, Slider, Line, Calendar, GIF, TabView, Chart, Mask, Window)
- Registry static ctor map and LvgljsBridge / GetBridge() accessor (230 lines)

Global ambient types (src/render/react/lvgljs.d.ts):
- tjs.readFile() declaration for legacy txiki.js API used by asset loaders
- StylePropValueCSS, StylePropValue, StylePropBatch, StylePropTransition for batch style channel

Public exports (src/render/react/index.ts):
- Re-export all bridge symbols via export * from "./core/bridge"

TypeScript configuration (tsconfig.json):
- target ES2020, module ESNext, moduleResolution Bundler, noEmit true
- include narrowed to src/render/react/**/*.ts for focused typecheck scope

Do not add nativeSetStyle, as it's will be changed when style refactoring, and not used yet.
Complete the bridge migration by replacing inline globalThis symbol lookups
across the React render layer with the centralized GetBridge() helper, so
bridge.ts remains the sole untyped global accessor and callers get typed access.

Component modules (src/render/react/components/*/comp.ts, 22 files):
- Arc, Button, Calendar, Canvas, Chart, Checkbox, Dropdownlist, GIF, Image,
  Input, Keyboard, Line, Mask, ProgressBar, Roller, Slider, Switch, Tabs,
  Text, Textarea, View, Window
- Each imports GetBridge from ../../core/bridge instead of
  globalThis[Symbol.for('lvgljs')]

Core modules (src/render/react/core/):
- animate/index.js - NativeRender.Animate via GetBridge()
- dimensions/index.js - NativeRender.dimensions via GetBridge()
- theme/index.js - NativeRender.theme via GetBridge()
- Each imports GetBridge from ../bridge
Problem:
- createInstance in component configs calls instance.setProps(newProps, {})
- TypeScript reported TS2345: {} is not assignable to Props when Props
  includes required fields (title, src, items, placeholder, etc.)

Solution:
- Use Partial<Props> for oldProps in prop setter helpers and setProps()
- Reflects mount semantics: no prior props on first setProps call
- commitUpdate still passes full oldProps from the reconciler (assignable to Partial)

Files changed:
- Calendar/config.ts — commitUpdate and setProps oldProps: Partial<CalendarProps>
- Dropdownlist/comp.ts — setListProps, setProps
- GIF/comp.ts — setGIFProps, setProps
- Image/comp.ts — setImageProps, setProps
- Input/comp.ts — setInputProps, setProps
- Keyboard/comp.ts — setKeyboardProps, setProps
- Line/comp.ts — setLineProps (typed), setProps
- ProgressBar/comp.ts — setProgressBarProps, setProps
- Roller/comp.ts — setRollerProps, setProps
- Slider/comp.ts — setSliderProps, setProps
- Tabs/comp.ts — setTabsProps, setProps
- Text/comp.ts — setTextProps, setProps
- Textarea/comp.ts — setTextareaProps, setProps
- Window/comp.ts — setWindowProps, setProps
Leaf widgets do not accept React children, so delegating appendChild,
removeChild, and related reconciler hooks to super or parent helpers can
run unintended native tree logic. Align these components with the
existing no-op pattern used by Text, Image, ProgressBar, and other
leaf comps.

Child-management no-ops (appendChild, removeChild):
- src/render/react/components/Arc/comp.ts
- src/render/react/components/Calendar/comp.ts
- src/render/react/components/Chart/comp.ts
- src/render/react/components/Line/comp.ts
- src/render/react/components/Slider/comp.ts

Child-management no-ops (insertBefore, appendInitialChild, appendChild, removeChild):
- src/render/react/components/Checkbox/comp.ts
- src/render/react/components/Switch/comp.ts

Layer and scroll no-ops (moveToFront, moveToBackground, scrollIntoView):
- src/render/react/components/Mask/comp.ts
Proxy get handlers pass string | symbol to includes; widen the array type so style proxies compile without TS2345.
- ProgressBar/comp.ts: compare range updates against oldProps.range instead of nonexistent oldProps.arr

- Roller/comp.ts: import OnChangeEvent from common/index for onChange prop typing

- Switch/comp.ts: compare checked state against oldProps.checked instead of oldProps.value

- core/style/index.ts: return true from Proxy set trap to satisfy ProxyHandler<boolean>

- core/style/pipe/grid.ts: coerce fr-unit regex capture to Number before isNaN checks

- core/style/pipe/text.ts: parse font-size strings to number before arithmetic and clamping

- core/style/pipe/trans.ts: use separate matches variable for transform regex; split translate parts without reassigning val
test\image\2 used: 'background-color': 'gray'

Signed-off-by: Yonggang Luo <luoyonggang@gmail.com>
@lygstate lygstate force-pushed the develop/v9.5 branch 4 times, most recently from c44a584 to af027f2 Compare June 14, 2026 10:00
lygstate added 9 commits June 14, 2026 18:21
…ooling

- Add .github/workflows/build.yml: gen:all:check in linux-device job (no SDL),
  Linux simulator under xvfb, macOS, and merged screenshot artifacts
- Add scripts/run-tests.js, gui-test-runner.js, and cli-test-runner.js for
  GUI/CLI test discovery, xfail handling, and optional PNG capture in CI
- Add shared normalize_* Python scripts for ASCII-safe commit messages and
  repo text on Windows (normalize_common, normalize_commit_msg, normalize_markdown)
- Replace package-lock.json with yarn.lock; add codegen.py, gen:all/check, and
  typecheck scripts; parallelize build.js esbuild jobs
- Extend .gitignore for screenshot output and build artifacts
- Extend test/runtime/fetch/1 with fetchBinaryWithArrayBuffer (xfail); use
  httpbun.com for more stable fetch tests
- Generate a side-by-side HTML report at _screenshots/html/index.html
  after each diff run, with baseline, actual, and diff thumbnails
- Add scripts/lib/screenshot-diff-html.js and _screenshots/html path
- Support --html, --no-html, and SCREENSHOT_DIFF_HTML to control output
- Write diff PNGs for equal images too (black overlay when pixels match)
Several JS-callable component methods guarded with a smaller argc than the highest argv[] index they read. Since these functions are registered with declared length 0, QuickJS does not pad argv, so passing too few arguments caused out-of-bounds reads of the argument array. Align each argc check with the highest argv index accessed.
Signed-off-by: Yonggang Luo <luoyonggang@gmail.com>
Set TEST_CAPTURE during --capture and freeze animation, random data,
and interval-driven updates so PNG comparison is consistent on Linux,
macOS, and device builds.
lygstate and others added 24 commits June 14, 2026 18:22
Add CLI runtime tests that gate comp_map size after Mask/Calendar toggles
and native View create/close/GC paths, per Component Memory Leak Fix Plan.

Tests
- test/runtime/calendar-leak/ (widgets-style Mask + Calendar x1000)
- test/runtime/binding-leak/ (native View close, GC, parent/child, events)
- test/runtime/lib/probe.ts (getCompMapSize, runGc, assert helpers)

Build and probe API
- src/render/native/core/refresh/refresh.cpp: RenderUtil.getCompMapSize()
- src/render/react/index.ts: updateTestTree() for reconciler leak probes

The test is for lvgl#19
handleEvent unregisters native listeners when event props are cleared,
but only addEventListener was bound on component prototypes. Wire
removeEventListener so prop updates tear down LVGL callbacks correctly.

Native bridge:
- component.hpp: add WRAPPED_JS_RemoveEventListener macro
- *_wrap.cpp: register removeEventListener on all 21 component protos
- comp.cpp: detach LVGL callback only when registeEvents becomes empty
Null comp after delete so a second close() or finalizer path does not
double-free the same BasicComponent.

Native
- component.hpp: WRAPPED_JS_CLOSE_COMPONENT guards s->comp
React unmount leaked comp_map entries and JS shells; instanceMap used
bracket syntax on a Map. Reconciler now tracks host children, closes
children before parents, and calls close() on detach.

Reconciler (src/render/react/core/reconciler/index.js)
- instanceMap: .set() / .get() / .delete(); detachInstance with _detached guard
- _hostChildren tracking; recursive child detach before parent close
- removeChild, removeChildFromContainer, detachDeletedInstance call detachInstance
- Teardown order owned by JS (children close before parent); comments document contract

Native (src/render/native/core/basic/comp.cpp)
- removeChild: bookkeeping only (parent_instance); no reparent to window root
- destructor: style reset, lv_obj_remove_event_cb, lv_obj_del on close() path
Child unmount is owned by reconciler detachInstance (untrackHostChildren,
unRegistEvent, close). Native and per-component removeChild were dead or
bookkeeping-only; strip them so teardown has a single path.

Native
- src/render/native/core/basic/comp.cpp, comp.hpp: drop BasicComponent::removeChild
- src/render/native/components/component.hpp: remove WRAPPED_REMOVE_CHILD macro
- src/render/native/components/*_wrap.cpp: drop removeChild proto (button, list,
  mask, switch, tabview, view, window)

React
- src/render/react/core/reconciler/index.js: removeChild calls detachInstance only
- src/render/react/core/bridge.ts: document reconciler-owned child removal
- src/render/react/components/config.ts: drop removeChild from LvgljsComponentConfig
- src/render/react/components/*/comp.ts, */config.ts: remove no-op removeChild stubs
….5.0

Since LVGL v9, the drivers are integrated in the main LVGL library and
the drivers submodule is no longer needed.
Driver integration:
Replace the lv_drivers-based SDL integration with LVGL v9 built-in drivers. Find SDL2 before adding the lvgl subdirectory and link it directly to the lvgl target.

LVGL config:
Move deps/lv_conf.h to a top-level lv_conf.h that routes to deps/lv_conf_sim.h or deps/lv_conf_device.h via IS_SIM / IS_DEVICE.

Cleanup:
Remove the legacy lv_drv_conf.h and deps/lvgl.h wrapper headers.
Copy deps/lvgl/lv_conf_template.h unchanged as the starting point before re-applying v8 project overrides.
Restore project-specific settings from the v8 backups onto the v9.5.0 template baseline, including IS_SIM/IS_DEVICE guards and per-profile defines.
enable LV_USE_SDL in simulator lv_conf for LVGL 9

Restore the simulator SDL driver flag required by the LVGL 9 SDL HAL (lv_sdl_window_create and related indev helpers).

Enable LV_USE_ARABIC_PERSIAN_CHARS that sync with Sim
Replace stored lv_theme_t with theme_saved_t and read defaults via
lv_theme_get_color_primary(), lv_theme_get_color_secondary(), and
lv_theme_get_font_normal() from the active screen.
…(won't be wrong)

Replace direct struct field access with lv_display_*, lv_event_get_*, lv_obj_get_user_data, and lv_screen_active helpers across native bindings.
- Wrap runtime images in lv_image_dsc_t with depth-aware NATIVE_WITH_ALPHA stride and header magic
- update the PNG decoder to lv_image_decoder_*, and remove obsolete lv_img_set_size_mode from the GIF widget.
- Use dsc.data from the wrapped lv_image_dsc_t and lv_style_set_arc_image_src after the image API rework.

fix PNG channel order and Image binary buffer for correct rendering

Swap RGB channels in 32-bit PNG decode to match LVGL BGRA layout
Update lv_conf_gen and its manifest so yarn gen:all can parse LVGL 9.5
headers again after the v9 directory and style API reshuffle.

Manifest and generator:
- Point lv_part_t/lv_state_t at core/lv_obj_style.h instead of lv_obj.h
- Move flex/grid/widget/event/gradient/text headers to layouts/* and
  widgets/* paths; read lv_style props from enum _lv_style_id_t
- Drop separate flex/grid extern style-prop sections (built into v9 enum)
- Read style setter kinds from misc/lv_style_gen.c only

Mapping inputs:
- Rename v8 style symbols in cpp_lv_symbol_mapping.json and
  transition_lvgl_compare.json (IMAGE_*, SHADOW_OFFSET_*, ANIM_DURATION,
  TRANSFORM_ROTATION/SCALE_X, etc.)
- Remove background-dither-mode and animate-speed entries that no longer
  exist in LVGL 9

Regenerated outputs and tests:
- Refresh lv_conf.cpp/ts, lv_types.ts, style_prop.ts, and anim_path.cpp
- Resync lv_conf.stub.ts to v9 prop ids and flex-flow bit values
- Treat radial gradient as valid in v9; keep unknown-direction coverage
  via a nonexistent keyword in background.spec.ts
Replace uniform img-scale with img-scaleX/Y backed by lv_image_set_scale_x/y,
add scaleX/scaleY style keys for object transforms, and route Image/GIF
transforms through img-* props. Regenerate lv_conf/style_prop outputs and
update TransStyle tests for the new mapping.
Align coord, percentage, and grid track helpers with the C headers so auto sizing and lv_pct encoding match native LVGL behavior.
Replace removed lv_drivers SDL integration with built-in lv_sdl_* APIs, update image descriptors for v9, and capture screenshots via lv_snapshot with stride-aware framebuffer conversion.

Update to the new driver api

Re-work the HAL code to the new LVGL driver api.

hal.hpp sharing things

lv_linux_fbdev_create and lv_evdev_create can return null when hardware
is missing; avoid null dereference on startup.

Native (src/engine/hal/device/device.cpp)
- null checks with stderr log and early return
LVGL 9 requires a flag argument when registering custom style properties;
lvjs_style_register_prop now forwards LV_STYLE_PROP_FLAG_NONE so the build
succeeds on v9.5.
The fixed 30ms uv_timer beat against LV_DEF_REFR_PERIOD (33ms) and caused
uneven frame spacing. Reschedule the one-shot timer from lv_timer_handler().

Engine
- src/engine/engine.cpp: schedule_lv_timer() uses handler return delay
- src/engine/engine.cpp: fallback to LV_DEF_REFR_PERIOD when no timer ready
- src/engine/engine.cpp: replace fixed uv_timer_start(30, 30) with adaptive pump
LVGL 9 removed several chart APIs that the native layer still called,
breaking the build and distorting zoomed charts at runtime. Port the
Chart component to the v9 widget model while keeping the existing JS
surface unchanged.

Axis ticks and labels:
- Replace lv_chart_set_axis_tick() with standalone lv_scale widgets per
  axis (left/right/top/bottom).
- Replace LV_EVENT_DRAW_PART_BEGIN tick-label callback with
  lv_scale_set_text_src(), keeping label strings alive in Chart.
- Map v8 major/minor tick counts onto lv_scale total_tick_count and
  major_tick_every.
- Sync axis ranges to both lv_chart_set_axis_range() and
  lv_scale_set_range().

Series data:
- Stop accessing opaque lv_chart_series_t internals; write through
  lv_chart_get_series_y_array() / _x_array() and call lv_chart_refresh().
- Fix setRightAxisData to bind series to SECONDARY_Y (was PRIMARY_Y).
- Implement setTopAxisData / setBottomAxisData and re-enable their JS
  bindings in chart_wrap.cpp.

Zoom (scaleX / scaleY styles):
- lv_chart_set_zoom_x/y() no longer exists; emulate v8 content zoom with
  a scrollable viewport wrapper sized to the styled dimensions while the
  inner chart is enlarged by the 256-based factor.
- Scales along the zoomed axis scroll with the chart content; scales on
  the other axis stay fixed at the viewport edge.
- Add BasicComponent::alignInstance() so align/align_to target the
  viewport wrapper instead of the oversized inner chart.

Verified: full test harness passes (56/56), chart screenshot tests pass.
Restore setTopAxisData and setBottomAxisData for LVGL 9.5 charts so
PRIMARY_X and SECONDARY_X series follow the same axis_data pattern as
left and right axes.

Native bridge:
- chart.cpp: implement setTopAxisData and setBottomAxisData with series
  rebuild, per-series colors, fillSeriesY, and lv_chart_refresh
- chart_wrap.cpp: add ParseAxisDataArg helper and expose
  setTopAxisData/setBottomAxisData on the JS prototype
@donghwan-yu

Copy link
Copy Markdown

Confirmed that this PR works well. I want to contribute some patches.

@lygstate

lygstate commented Jul 8, 2026

Copy link
Copy Markdown
Author

Confirmed that this PR works well. I want to contribute some patches.

Yeap, I fixed most issue, Maybe there is little chart diverge, others should be most fixed( including memory leak)

@donghwan-yu

Copy link
Copy Markdown

@lygstate IMO this PR looks good. Is there any plan to be merged into the master branch? Master branch needs(?) the latest LVGL 9.5 version.

@lygstate

Copy link
Copy Markdown
Author

@lygstate IMO this PR looks good. Is there any plan to be merged into the master branch? Master branch needs(?) the latest LVGL 9.5 version.

i am not the miantainer, also charts needs some fix, wanting maintainer to response. Another way is maintaining a fork instead

@donghwan-yu

Copy link
Copy Markdown

@derekstavis Could your review this PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants