Skip to content

build: Add support building with msvc#59

Open
lygstate wants to merge 34 commits into
lvgl:masterfrom
lygstate:msvc
Open

build: Add support building with msvc#59
lygstate wants to merge 34 commits into
lvgl:masterfrom
lygstate:msvc

Conversation

@lygstate

@lygstate lygstate commented May 10, 2026

Copy link
Copy Markdown
        "-DCMAKE_PREFIX_PATH=C:/work/vcpkg/packages/zlib_x64-windows;C:/work/vcpkg/packages/curl_x64-windows;C:/work/vcpkg/packages/libffi_x64-windows;C:/work/vcpkg/packages/sdl2_x64-windows",
        "-DBUILD_LVGL_SIMULATOR=ON",
        "-DUSE_EXTERNAL_FFI=ON",
        "-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$<$<CONFIG:Debug>:Debug>"

vcpkg install:

.\vcpkg.exe install curl zlib libffi sdl2 libffi:x64-windows-static

Summary by cubic

Add MSVC build support, a simulator PNG screenshot API, and cross‑platform CI (Linux/macOS/Windows) with GUI/CLI tests. Split LVGL config for sim/device, consolidate native includes, add a typed React bridge, and upgrade txiki to v26.5.0 and LVGL to v8.3.0 for stable, portable builds.

  • New Features & Fixes

    • MSVC: C++23 on MSVC (C++14 elsewhere), Arc name clash resolved; -DBUILD_LVGL_SIMULATOR=ON to build the simulator.
    • PNG capture: NativeRender.RenderUtil.captureDisplay(path) via LVGL snapshot; consistent screenshots across OSes; freezes animations/GIFs for capture.
    • Headers/config: add deps/lvgl.h and lv_bindings_js.h to unify LVGL/QuickJS/private includes; split lv_conf into lv_conf_sim.h and lv_conf_device.h.
    • React/TypeScript: typed bridge with GetBridge(); safer prop typing; no‑op child hooks on leaf widgets.
    • Assets/runtime: shared asset loaders with import.meta.dirname; pass ArrayBuffer for images/GIFs; demo assets moved to ./assets/.
    • Native robustness: correct argc guards; explicit enum casts; split lv_obj_add_flag calls; style setter type fixes and correct animation duration order; initialize is_new; return JS_UNDEFINED from stopPropagation; GIF pause/resume for stable capture.
    • CI/tooling: GitHub Actions for Linux/macOS/Windows (MSVC + vcpkg), xvfb simulator runs, screenshot artifacts, GUI/CLI test harness, gen:all(:check) codegen/typecheck, ASCII normalization scripts; faster parallel esbuild.
    • Dependencies: upgrade txiki and @txikijs/types to ^26.5.0; bump LVGL to v8.3.0.
  • Migration

    • Install with vcpkg: .\vcpkg.exe install curl zlib libffi sdl2 libffi:x64-windows-static
    • Example CMake: -DBUILD_LVGL_SIMULATOR=ON -DUSE_EXTERNAL_FFI=ON -DCMAKE_PREFIX_PATH=C:/work/vcpkg/packages/zlib_x64-windows;C:/work/vcpkg/packages/curl_x64-windows;C:/work/vcpkg/packages/libffi_x64-windows;C:/work/vcpkg/packages/sdl2_x64-windows -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded$<$<CONFIG:Debug>:Debug>

Written for commit 8d73ace. Summary will update on new commits.

Review in cubic

Closes: #57

@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.

5 issues found across 45 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/engine/hal/simulator/simulator.cpp">

<violation number="1" location="src/engine/hal/simulator/simulator.cpp:3">
P1: Wrapping `LV_IMG_DECLARE(mouse_cursor_icon)` in `extern "C"` creates a C-linkage reference, but `mouse_cursor_icon.c` is compiled with the C++ compiler in this build, so the definition will not match and linking can fail.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread src/render/native/components/roller/roller_wrap.cpp Outdated
Comment thread src/render/native/components/dropdownlist/dropdownlist_wrap.cpp Outdated
Comment on lines +3 to +7
extern "C" {

LV_IMG_DECLARE(mouse_cursor_icon); /*Declare the image file.*/

}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Wrapping LV_IMG_DECLARE(mouse_cursor_icon) in extern "C" creates a C-linkage reference, but mouse_cursor_icon.c is compiled with the C++ compiler in this build, so the definition will not match and linking can fail.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/engine/hal/simulator/simulator.cpp, line 3:

<comment>Wrapping `LV_IMG_DECLARE(mouse_cursor_icon)` in `extern "C"` creates a C-linkage reference, but `mouse_cursor_icon.c` is compiled with the C++ compiler in this build, so the definition will not match and linking can fail.</comment>

<file context>
@@ -1,5 +1,11 @@
 #include "./simulator.hpp"
 
+extern "C" {
+
+LV_IMG_DECLARE(mouse_cursor_icon); /*Declare the image file.*/
</file context>
Suggested change
extern "C" {
LV_IMG_DECLARE(mouse_cursor_icon); /*Declare the image file.*/
}
LV_IMG_DECLARE(mouse_cursor_icon); /*Declare the image file.*/

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

You were wrong about mouse_cursor_icon.c, the compiler for mouse_cursor_icon.c is default to c, that's why I use
extern "C"

Comment thread src/render/native/components/line/line_wrap.cpp Outdated
Comment thread src/render/native/components/calendar/calendar_wrap.cpp Outdated
@lygstate

Copy link
Copy Markdown
Author

I create a fork https://github.com/lvgljs/lvgljs to get this merged

lygstate added 4 commits June 3, 2026 02:46
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.
- 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.
@lygstate lygstate force-pushed the msvc branch 2 times, most recently from b899d1f to bb33146 Compare June 10, 2026 04:14
lygstate added 18 commits June 11, 2026 01:02
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
…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
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.
lv_anim_set_time was called before JS_ToInt32, so animations used an uninitialized duration instead of the value from the style object.
Introduce deps/lvgl.h and switch project includes from lvgl/lvgl.h to angle-bracket lvgl.h.
…riable]

[build] /mnt/c/work/lvgl/lv_binding_js/deps/txiki/deps/libwebsockets/lib/system/async-dns/async-dns.c:801:27: error: variable ‘c’ set but not used [-Werror=unused-but-set-variable]
[build]   801 |         lws_adns_cache_t *c;
[build]       |                           ^
[build] cc1: all warnings being treated as errors
lygstate added 11 commits June 11, 2026 01:03
The WRAPPED_STOPPROPAGATION macro defines NativeEventStopPropagation,
a QuickJS cfunc bound as event.stopPropagation(). Add an explicit
return JS_UNDEFINED so the handler satisfies the JSValue return type.
Use lv_coord_t, lv_opa_t, and uint8_t where the LVGL APIs expect them, including fixing flexGrow which incorrectly cast to lv_flex_flow_t.
Add explicit enum casts in component constructors and style setters so LVGL API types compile under MSVC. Call lv_obj_add_flag separately for EVENT_BUBBLE and CLICK_FOCUSABLE instead of OR-ing flags, fixing broken grid layout in the widgets demo.
MSVC does not support zero-length arrays. Drop unused empty ComponentClassFuncs tables and JS_SetPropertyFunctionList calls from native component bindings.

Signed-off-by: Yonggang Luo <luoyonggang@gmail.com>
Skip infinite rotate/scale animations when TEST_CAPTURE is set, matching other animated GUI tests.
- Add native pause/resume on the GIF LVGL timer
- Expose pause/resume in the JS bridge and GIFComp
- Add paused prop and apply it after async GIF load
- Pause GIF in the test when TEST_CAPTURE is set
Problem:
- test/gif/1 failed when loading a remote GIF via fetch() + arrayBuffer()
- getGIFBinary relies on octet-stream responses and arrayBuffer(), which is
  not implemented correctly in the current JavaScript runtime

GIF test (test/gif/1):
- Add sample.gif bundled next to index.jsx
- Point src at ./sample.gif instead of a remote GIF URL
- Drop the 8000ms render override; default window is enough for a local file

Preserve fetch + arrayBuffer coverage (test/runtime/fetch/1):
- Add fetchBinaryWithArrayBuffer() using httpbun.com/bytes/N
- Switch other fetch tests from httpbin.org to httpbun.com
- Exit with TEST_EXIT_OK on success so the harness can detect pass/fail
- Mark test/runtime/fetch/1/xfail until arrayBuffer is implemented

Test harness (scripts/run-tests.js, cli-test-runner.js):
- Route test/runtime/** through scripts/cli-test-runner.js (async CLI tests)
- Keep test/** GUI tests on scripts/gui-test-runner.js
- Support expected failures: empty xfail file beside index.js (XFAIL/XPASS)
- Print failure stdout/stderr for FAIL and XFAIL; list failed tests before summary
- Include runtime tests when --capture is used (no screenshot for CLI tests)
- Fix Window proto: nativeSetStyle and addEventListener arity 4 -> 0
- Fix theme.setTheme declared arity 1 -> 0; guard argc and object arg
- Free primaryColor, secondPrimary, and fontSize JS values in SetTheme
- Match QuickJS declared length with runtime checks used by WRAPPED_* macros
Correct the scroll style pipe filename and update manifest pipe references.
- Point deps/txiki submodule at release v26.5.0 (9d7b4db).
- Bump @txikijs/types from ^24.6.0 to ^26.5.0 in package.json; refresh yarn.lock.
- scripts/run-tests.js: align bundled-test harness with txiki v26 runtime API
  - tjs.exepath -> tjs.exePath when spawning GUI and CLI child runners
  - tjs.readdir -> tjs.readDir for recursive test discovery under test/
  - tjs.mkdir -> tjs.makeDir when creating the --capture screenshot directory
  - Read child stdout/stderr via proc.stdout.text() / proc.stderr.text() instead of slurpStdio
- Native C++ bindings: QuickJS JS_IsArray is single-argument in v26.5.0
  - Drop ctx from JS_IsArray in component.hpp and style.cpp
  - Update arc, calendar, chart, dropdownlist, line, roller, slider, and tabview wraps
- .github/workflows/build.yml: remove -DCMAKE_POLICY_VERSION_MINIMUM=3.5 from CI cmake
  configures; no longer required after the txiki upgrade
- test/runtime/fetch/1: remove xfail; Response.arrayBuffer() is available in txiki v26.5.0

The upgrade is needed because the trans.ts transitionProperty are only matched the v8.3.0 version
- Add CMake and source fixes for MSVC (C++23, Arc rename, mouse cursor)
- Add a windows-2025 workflow job that installs static deps via vcpkg,
  builds with MSVC, runs the screenshot test suite, and uploads artifacts
- Include Windows in screenshot collection
- Remove CMAKE_POLICY_VERSION_MINIMUM from Linux/macOS Ninja configures
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.

Add msvc support by upgrading txiki and other deps

1 participant