docs: ✏️ cleanup and organize docs
This commit is contained in:
parent
8db6cfadc3
commit
8e413f89ec
29 changed files with 85 additions and 3340 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -63,3 +63,7 @@ output/
|
||||||
.playwright-mcp
|
.playwright-mcp
|
||||||
.claude/worktrees/
|
.claude/worktrees/
|
||||||
|
|
||||||
|
# Feature patch scratch (generated by scripts/create-feature-patches.sh).
|
||||||
|
# Curated design docs live under docs/features/ and ARE committed.
|
||||||
|
/features/
|
||||||
|
|
||||||
|
|
|
||||||
18
README.md
18
README.md
|
|
@ -64,13 +64,12 @@ kicad-wasm/
|
||||||
|
|
||||||
## Feature Branches
|
## Feature Branches
|
||||||
|
|
||||||
Each feature branch has a folder in `features/<branch-name>/` containing:
|
Curated design docs and research notes for each feature live in
|
||||||
- Documentation and research notes
|
[`docs/features/<branch-name>/`](docs/features/) (committed).
|
||||||
- `root.patch` - changes to main repo
|
|
||||||
- `kicad.patch` - changes to KiCad fork
|
|
||||||
- `wxwidgets.patch` - changes to wxWidgets fork
|
|
||||||
|
|
||||||
Generate patches: `./scripts/create-feature-patches.sh [branch-name]`
|
`./scripts/create-feature-patches.sh [branch-name]` generates per-branch patches
|
||||||
|
(`root.patch`, `kicad.patch`, `wxwidgets.patch`) into a local `features/<branch-name>/`
|
||||||
|
scratch dir. That dir is gitignored — the patches are local history, not committed.
|
||||||
|
|
||||||
## Two Build Workflows
|
## Two Build Workflows
|
||||||
|
|
||||||
|
|
@ -91,7 +90,7 @@ cd tests && npm install && npm run test:kicad
|
||||||
|
|
||||||
Output: `output/pcbnew.js`, `output/pcbnew.wasm`
|
Output: `output/pcbnew.js`, `output/pcbnew.wasm`
|
||||||
|
|
||||||
See [build.md](build.md) for detailed build documentation.
|
See [docs/build.md](docs/build.md) for detailed build documentation.
|
||||||
|
|
||||||
### 2. wxWidgets Test Apps (Local)
|
### 2. wxWidgets Test Apps (Local)
|
||||||
|
|
||||||
|
|
@ -152,8 +151,11 @@ See [tests/README.md](tests/README.md) for test documentation.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- [Build System](build.md) - Docker build details
|
See **[docs/README.md](docs/README.md)** for the full documentation map. Highlights:
|
||||||
|
|
||||||
|
- [Build System](docs/build.md) - Docker build details
|
||||||
- [Docker README](docker/README.md) - Container setup
|
- [Docker README](docker/README.md) - Container setup
|
||||||
|
- [Debugging Guide](docs/debugging/DEBUG.md) - Asyncify/WASM debugging
|
||||||
- [Tests README](tests/README.md) - Test infrastructure
|
- [Tests README](tests/README.md) - Test infrastructure
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
|
||||||
58
docs/README.md
Normal file
58
docs/README.md
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
# Documentation Map
|
||||||
|
|
||||||
|
A central index of the documentation in this repo. The goal of the project is to
|
||||||
|
build KiCad with WASM and run it in a browser.
|
||||||
|
|
||||||
|
> Note: per-area `README.md` files stay next to the code they describe (they're linked
|
||||||
|
> below). Cross-cutting guides live under `docs/`. Per-feature design notes live under
|
||||||
|
> [`features/`](features/).
|
||||||
|
|
||||||
|
## Start here
|
||||||
|
|
||||||
|
- [Project README](../README.md) — overview, prerequisites, quick start, project structure
|
||||||
|
- [CLAUDE.md](../CLAUDE.md) — project/agent context and contribution conventions
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
- [docs/build.md](build.md) — Docker-based KiCad WASM build system (two-phase build, outputs, memory)
|
||||||
|
- [docker/README.md](../docker/README.md) — Docker build environment, branch-specific containers, troubleshooting
|
||||||
|
- [wasm/README.md](../wasm/README.md) — WASM compatibility layer (overrides/shims without patching KiCad)
|
||||||
|
|
||||||
|
## Debugging & Asyncify
|
||||||
|
|
||||||
|
- [docs/debugging/DEBUG.md](debugging/DEBUG.md) — debugging guide: Asyncify stalls vs crashes, shim/codegen coupling, stub-bisection
|
||||||
|
- [docs/debugging/learning.md](debugging/learning.md) — Asyncify + consecutive modal dialogs: the lock pattern
|
||||||
|
- [docs/research/threading_1.md](research/threading_1.md) — deep dive: the Asyncify single-slot `currData` collision bug and the fix
|
||||||
|
- [docs/research/threading_2.md](research/threading_2.md) — external research: JSPI/WasmFX/state-machine alternatives, QEMU analysis
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
- [wasm/README.md](../wasm/README.md) — WASM compatibility layer structure
|
||||||
|
- [web/README.md](../web/README.md) — web app (create/open KiCad projects), tech stack, URL routing, WASM artifact serving
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- [tests/README.md](../tests/README.md) — Playwright test infrastructure, element registry, logs, screenshots
|
||||||
|
- [tests/WHATWORKS.md](../tests/WHATWORKS.md) — wxWidgets-in-WASM feature coverage matrix and KiCad readiness
|
||||||
|
- [tests/GL_README.md](../tests/GL_README.md) — Emscripten legacy GL immediate-mode quirks (color-per-vertex)
|
||||||
|
- [tests/gal-regression/README.md](../tests/gal-regression/README.md) — GAL visual regression suite (native OpenGL vs WebGL WASM)
|
||||||
|
|
||||||
|
## Feature design docs
|
||||||
|
|
||||||
|
Per-feature design notes and porting records live under [`features/`](features/):
|
||||||
|
|
||||||
|
- [web-init](features/web-init/) — web app spec
|
||||||
|
- [schematic](features/schematic/) — eeschema WASM bring-up
|
||||||
|
- [symbol-editor](features/symbol-editor/) — symbol editor (eeschema kiface launcher)
|
||||||
|
- [gerbview](features/gerbview/) — Gerber viewer port
|
||||||
|
- [pl-editor](features/pl-editor/) — page-layout editor port (incl. file-dialog usability fixes)
|
||||||
|
- [browser-tools](features/browser-tools/) — tool-activation / coroutine deep dives
|
||||||
|
- [fix-asyncify-O2-and-modal-promise-rejection](features/fix-asyncify-O2-and-modal-promise-rejection/) — RTree wasm overflow bug investigation
|
||||||
|
|
||||||
|
### Archived / historical
|
||||||
|
|
||||||
|
[`features/archive/`](features/archive/) holds docs whose work is done or superseded
|
||||||
|
(each carries a status banner):
|
||||||
|
|
||||||
|
- [webgl](features/archive/webgl/) — WebGL-GAL strategy/plan (since implemented in `kicad/common/gal/webgl/`)
|
||||||
|
- [ipc-api](features/archive/ipc-api/) — IPC-API guard cleanup TODO (revert not yet actioned)
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
# KiCad IPC API - WASM Fork Changes
|
# KiCad IPC API - WASM Fork Changes
|
||||||
|
|
||||||
|
> **ARCHIVED — open cleanup TODO, not yet actioned.** Describes `#ifdef KICAD_IPC_API` guards added to ~18 KiCad source files that could be reverted to reduce fork diff. Archiving this doc does **not** perform the revert — that remains a separate task.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
We added `#ifdef KICAD_IPC_API` guards to 18 KiCad source files. These guards wrap:
|
We added `#ifdef KICAD_IPC_API` guards to 18 KiCad source files. These guards wrap:
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
# OpenGL, GAL, and WebGL Strategy for KiCad WASM
|
# OpenGL, GAL, and WebGL Strategy for KiCad WASM
|
||||||
|
|
||||||
|
> **ARCHIVED / HISTORICAL** — the WebGL-GAL backend described here was implemented (see `kicad/common/gal/webgl/`, ~22.5k lines). Live test docs: [`tests/gal-regression/README.md`](../../../../tests/gal-regression/README.md). Kept for design rationale.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
This document analyzes KiCad's graphics architecture and evaluates strategies for WebGL rendering in the WASM build.
|
This document analyzes KiCad's graphics architecture and evaluates strategies for WebGL rendering in the WASM build.
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
# GAL Native Test Harness Architecture
|
# GAL Native Test Harness Architecture
|
||||||
|
|
||||||
|
> **ARCHIVED / HISTORICAL** — the WebGL-GAL backend described here was implemented (see `kicad/common/gal/webgl/`). Live test docs: [`tests/gal-regression/README.md`](../../../../tests/gal-regression/README.md). Kept for design rationale.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The GAL native test harness is a standalone macOS application that compiles KiCad's actual `OPENGL_GAL` rendering engine against system wxWidgets. It generates baseline PNG screenshots for visual regression testing of WebGL rendering in the WASM build.
|
The GAL native test harness is a standalone macOS application that compiles KiCad's actual `OPENGL_GAL` rendering engine against system wxWidgets. It generates baseline PNG screenshots for visual regression testing of WebGL rendering in the WASM build.
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
# WebGL GAL Port - Implementation Complete
|
# WebGL GAL Port - Implementation Complete
|
||||||
|
|
||||||
|
> **ARCHIVED / HISTORICAL** — the WebGL-GAL backend described here was implemented (see `kicad/common/gal/webgl/`). Live test docs: [`tests/gal-regression/README.md`](../../../../tests/gal-regression/README.md). Kept for design rationale.
|
||||||
|
|
||||||
## Status: ALL PHASES COMPLETE ✅
|
## Status: ALL PHASES COMPLETE ✅
|
||||||
|
|
||||||
**Branch:** `webgl` (21+ commits)
|
**Branch:** `webgl` (21+ commits)
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
The nested Asyncify collision bug (see `0002-wasm-coroutine-deep-dive.md` and `research/threading_1.md`) is fixed. KiCad WASM now loads through the startup wizard without crashing, and the full PCBnew UI renders — menus, left drawing-tool sidebar with Line/Circle/Rectangle icons, layer panel, PCB canvas — all visible.
|
The nested Asyncify collision bug (see `0002-wasm-coroutine-deep-dive.md` and `../../research/threading_1.md`) is fixed. KiCad WASM now loads through the startup wizard without crashing, and the full PCBnew UI renders — menus, left drawing-tool sidebar with Line/Circle/Rectangle icons, layer panel, PCB canvas — all visible.
|
||||||
|
|
||||||
But tools still don't work end-to-end:
|
But tools still don't work end-to-end:
|
||||||
|
|
||||||
|
|
@ -34,7 +34,7 @@ verbatim.
|
||||||
mirroring the eeschema/pcbnew static-link pattern.
|
mirroring the eeschema/pcbnew static-link pattern.
|
||||||
- **`kicad/eeschema/eeschema.cpp`** — `IFACE::CreateKiWindow`'s `FRAME_SCH_SYMBOL_EDITOR`
|
- **`kicad/eeschema/eeschema.cpp`** — `IFACE::CreateKiWindow`'s `FRAME_SCH_SYMBOL_EDITOR`
|
||||||
case was stubbed to `return nullptr` on `__EMSCRIPTEN__` during the eeschema MVP
|
case was stubbed to `return nullptr` on `__EMSCRIPTEN__` during the eeschema MVP
|
||||||
(see `features/schematic/0001-eeschema-iface-stubs.md`). That stub is now removed so
|
(see `../schematic/0001-eeschema-iface-stubs.md`). That stub is now removed so
|
||||||
the frame is constructed in WASM like the native build. This was THE blocker: with the
|
the frame is constructed in WASM like the native build. This was THE blocker: with the
|
||||||
stub, `Kiway.Player(FRAME_SCH_SYMBOL_EDITOR)` returned null, `single_top`'s `OnInit`
|
stub, `Kiway.Player(FRAME_SCH_SYMBOL_EDITOR)` returned null, `single_top`'s `OnInit`
|
||||||
bailed, and the app sat idle with a blank canvas (no abort, no error).
|
bailed, and the app sat idle with a blank canvas (no abort, no error).
|
||||||
|
|
@ -148,7 +148,7 @@ The `emscripten_fiber_swap` mechanism handles this correctly because the fiber J
|
||||||
|
|
||||||
### The `setTimeout(wakeUp, 0)` Pattern
|
### The `setTimeout(wakeUp, 0)` Pattern
|
||||||
|
|
||||||
When Asyncify.wakeUp() is called while compiled code is still on the JS call stack, it corrupts state. The fix is always to defer: `setTimeout(wakeUp, 0)` ensures the previous operation has fully unwound before starting the next rewind. Our modal dialog code uses `setTimeout(0)` twice (double-deferred) for this reason — documented in `learning.md`.
|
When Asyncify.wakeUp() is called while compiled code is still on the JS call stack, it corrupts state. The fix is always to defer: `setTimeout(wakeUp, 0)` ensures the previous operation has fully unwound before starting the next rewind. Our modal dialog code uses `setTimeout(0)` twice (double-deferred) for this reason — documented in `../debugging/learning.md`.
|
||||||
|
|
||||||
**Sources**: [emscripten #16291](https://github.com/emscripten-core/emscripten/issues/16291), [emscripten #18412](https://github.com/emscripten-core/emscripten/issues/18412), [emscripten #10515](https://github.com/emscripten-core/emscripten/issues/10515)
|
**Sources**: [emscripten #16291](https://github.com/emscripten-core/emscripten/issues/16291), [emscripten #18412](https://github.com/emscripten-core/emscripten/issues/18412), [emscripten #10515](https://github.com/emscripten-core/emscripten/issues/10515)
|
||||||
|
|
||||||
|
|
@ -1,194 +0,0 @@
|
||||||
diff --git a/pagelayout_editor/CMakeLists.txt b/pagelayout_editor/CMakeLists.txt
|
|
||||||
index f287e5368a..dfc01a9b9c 100644
|
|
||||||
--- a/pagelayout_editor/CMakeLists.txt
|
|
||||||
+++ b/pagelayout_editor/CMakeLists.txt
|
|
||||||
@@ -77,55 +77,103 @@ add_executable( pl_editor WIN32 MACOSX_BUNDLE
|
|
||||||
${PL_EDITOR_RESOURCES}
|
|
||||||
)
|
|
||||||
|
|
||||||
-set_source_files_properties( ${CMAKE_SOURCE_DIR}/common/single_top.cpp PROPERTIES
|
|
||||||
- COMPILE_DEFINITIONS "TOP_FRAME=FRAME_PL_EDITOR;PGM_DATA_FILE_EXT=\"kicad_wks\";BUILD_KIWAY_DLL"
|
|
||||||
- )
|
|
||||||
-target_link_libraries( pl_editor
|
|
||||||
- kicommon
|
|
||||||
- ${wxWidgets_LIBRARIES}
|
|
||||||
- )
|
|
||||||
+if( EMSCRIPTEN )
|
|
||||||
+ # WASM: Static kiface linking - don't define BUILD_KIWAY_DLL
|
|
||||||
+ set_source_files_properties( ${CMAKE_SOURCE_DIR}/common/single_top.cpp PROPERTIES
|
|
||||||
+ COMPILE_DEFINITIONS "TOP_FRAME=FRAME_PL_EDITOR;PGM_DATA_FILE_EXT=\"kicad_wks\""
|
|
||||||
+ )
|
|
||||||
+else()
|
|
||||||
+ set_source_files_properties( ${CMAKE_SOURCE_DIR}/common/single_top.cpp PROPERTIES
|
|
||||||
+ COMPILE_DEFINITIONS "TOP_FRAME=FRAME_PL_EDITOR;PGM_DATA_FILE_EXT=\"kicad_wks\";BUILD_KIWAY_DLL"
|
|
||||||
+ )
|
|
||||||
+endif()
|
|
||||||
+
|
|
||||||
+if( NOT EMSCRIPTEN )
|
|
||||||
+ # Native: minimal link, kiface loaded dynamically
|
|
||||||
+ target_link_libraries( pl_editor
|
|
||||||
+ kicommon
|
|
||||||
+ ${wxWidgets_LIBRARIES}
|
|
||||||
+ )
|
|
||||||
+endif()
|
|
||||||
+# WASM linking is done after PL_EDITOR_KIFACE_LIBRARIES is defined
|
|
||||||
|
|
||||||
target_link_options( pl_editor PRIVATE
|
|
||||||
$<$<BOOL:${KICAD_MAKE_LINK_MAPS}>:-Wl,--cref,-Map=pl_editor.map>
|
|
||||||
)
|
|
||||||
|
|
||||||
-# the main pl_editor program, in DSO form.
|
|
||||||
-add_library( pl_editor_kiface MODULE
|
|
||||||
+# the main pl_editor program, in OBJECT form so it can be statically linked on WASM.
|
|
||||||
+add_library( pl_editor_kiface_objects OBJECT
|
|
||||||
pl_editor.cpp
|
|
||||||
${PL_EDITOR_SRCS}
|
|
||||||
${DIALOGS_SRCS}
|
|
||||||
${PL_EDITOR_EXTRA_SRCS}
|
|
||||||
)
|
|
||||||
-target_link_libraries( pl_editor_kiface
|
|
||||||
- gal
|
|
||||||
- common
|
|
||||||
- core
|
|
||||||
- ${wxWidgets_LIBRARIES}
|
|
||||||
+
|
|
||||||
+target_link_libraries( pl_editor_kiface_objects
|
|
||||||
+ PRIVATE
|
|
||||||
+ common
|
|
||||||
+ core
|
|
||||||
+ ${wxWidgets_LIBRARIES}
|
|
||||||
)
|
|
||||||
+
|
|
||||||
+add_library( pl_editor_kiface MODULE )
|
|
||||||
+
|
|
||||||
set_target_properties( pl_editor_kiface PROPERTIES
|
|
||||||
OUTPUT_NAME pl_editor
|
|
||||||
PREFIX ${KIFACE_PREFIX}
|
|
||||||
SUFFIX ${KIFACE_SUFFIX}
|
|
||||||
)
|
|
||||||
|
|
||||||
-set_source_files_properties( pl_editor.cpp PROPERTIES
|
|
||||||
- # The KIFACE is in pcbnew.cpp, export it:
|
|
||||||
- COMPILE_DEFINITIONS "BUILD_KIWAY_DLL;COMPILING_DLL"
|
|
||||||
- )
|
|
||||||
+if( EMSCRIPTEN )
|
|
||||||
+ # WASM: Static linking - don't define BUILD_KIWAY_DLL
|
|
||||||
+ set_source_files_properties( pl_editor.cpp PROPERTIES
|
|
||||||
+ COMPILE_DEFINITIONS "COMPILING_DLL"
|
|
||||||
+ )
|
|
||||||
+else()
|
|
||||||
+ set_source_files_properties( pl_editor.cpp PROPERTIES
|
|
||||||
+ # The KIFACE is in pl_editor.cpp, export it:
|
|
||||||
+ COMPILE_DEFINITIONS "BUILD_KIWAY_DLL;COMPILING_DLL"
|
|
||||||
+ )
|
|
||||||
+endif()
|
|
||||||
|
|
||||||
target_link_options( pl_editor_kiface PRIVATE
|
|
||||||
$<$<BOOL:${KICAD_MAKE_LINK_MAPS}>:-Wl,--cref,-Map=_pl_editor.kiface.map>
|
|
||||||
)
|
|
||||||
|
|
||||||
-# if building pl_editor, then also build pl_editor_kiface if out of date.
|
|
||||||
-add_dependencies( pl_editor pl_editor_kiface )
|
|
||||||
-
|
|
||||||
message( STATUS "Including 3Dconnexion SpaceMouse navigation support in pagelayout editor" )
|
|
||||||
add_subdirectory( navlib )
|
|
||||||
|
|
||||||
-target_link_libraries( pl_editor_kiface pl_editor_navlib)
|
|
||||||
+set( PL_EDITOR_KIFACE_LIBRARIES
|
|
||||||
+ pl_editor_kiface_objects
|
|
||||||
+ pl_editor_navlib
|
|
||||||
+ kicommon
|
|
||||||
+ kiplatform
|
|
||||||
+ common
|
|
||||||
+ gal
|
|
||||||
+ core
|
|
||||||
+ ${wxWidgets_LIBRARIES}
|
|
||||||
+ )
|
|
||||||
+
|
|
||||||
+# WASM: Link kiface objects directly into pl_editor executable (static linking)
|
|
||||||
+if( EMSCRIPTEN )
|
|
||||||
+ target_link_libraries( pl_editor
|
|
||||||
+ PRIVATE
|
|
||||||
+ ${PL_EDITOR_KIFACE_LIBRARIES}
|
|
||||||
+ )
|
|
||||||
+ target_link_options( pl_editor PRIVATE
|
|
||||||
+ "LINKER:--allow-multiple-definition"
|
|
||||||
+ )
|
|
||||||
+endif()
|
|
||||||
+
|
|
||||||
+target_link_libraries( pl_editor_kiface
|
|
||||||
+ PRIVATE
|
|
||||||
+ ${PL_EDITOR_KIFACE_LIBRARIES}
|
|
||||||
+ )
|
|
||||||
+
|
|
||||||
+# if building pl_editor, then also build pl_editor_kiface if out of date.
|
|
||||||
+add_dependencies( pl_editor pl_editor_kiface )
|
|
||||||
|
|
||||||
-add_dependencies( pl_editor_kiface pl_editor_navlib)
|
|
||||||
+add_dependencies( pl_editor_kiface pl_editor_navlib )
|
|
||||||
|
|
||||||
# these 2 binaries are a matched set, keep them together:
|
|
||||||
if( APPLE )
|
|
||||||
diff --git a/pagelayout_editor/navlib/CMakeLists.txt b/pagelayout_editor/navlib/CMakeLists.txt
|
|
||||||
index ad3c388736..89e94e6abb 100644
|
|
||||||
--- a/pagelayout_editor/navlib/CMakeLists.txt
|
|
||||||
+++ b/pagelayout_editor/navlib/CMakeLists.txt
|
|
||||||
@@ -1,25 +1,36 @@
|
|
||||||
|
|
||||||
-add_library(pl_editor_navlib STATIC
|
|
||||||
- "nl_pl_editor_plugin.cpp"
|
|
||||||
- "nl_pl_editor_plugin_impl.cpp"
|
|
||||||
-)
|
|
||||||
+# WASM: 3D mouse support not available, use stubs
|
|
||||||
+if( EMSCRIPTEN )
|
|
||||||
+ add_library(pl_editor_navlib STATIC
|
|
||||||
+ "${CMAKE_SOURCE_DIR}/../wasm/stubs/nl_pl_editor_plugin_stub.cpp"
|
|
||||||
+ )
|
|
||||||
+ target_include_directories(pl_editor_navlib PRIVATE
|
|
||||||
+ ${CMAKE_SOURCE_DIR}/pagelayout_editor
|
|
||||||
+ ${CMAKE_SOURCE_DIR}/include
|
|
||||||
+ )
|
|
||||||
+else()
|
|
||||||
+ add_library(pl_editor_navlib STATIC
|
|
||||||
+ "nl_pl_editor_plugin.cpp"
|
|
||||||
+ "nl_pl_editor_plugin_impl.cpp"
|
|
||||||
+ )
|
|
||||||
|
|
||||||
-# pl_editor_navlib depends on make_lexer outputs in common
|
|
||||||
-add_dependencies( pl_editor_navlib common )
|
|
||||||
+ # pl_editor_navlib depends on make_lexer outputs in common
|
|
||||||
+ add_dependencies( pl_editor_navlib common )
|
|
||||||
|
|
||||||
-# Find the 3DxWare SDK component 3DxWare::NlClient
|
|
||||||
-# find_package(TDxWare_SDK 4.0 REQUIRED COMPONENTS 3DxWare::Navlib)
|
|
||||||
-target_compile_definitions(pl_editor_navlib PRIVATE
|
|
||||||
- $<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_COMPILE_DEFINITIONS>
|
|
||||||
-)
|
|
||||||
-target_compile_options(pl_editor_navlib PRIVATE
|
|
||||||
- $<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_COMPILE_OPTIONS>
|
|
||||||
-)
|
|
||||||
-target_include_directories(pl_editor_navlib PRIVATE
|
|
||||||
- $<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_INCLUDE_DIRECTORIES>
|
|
||||||
- $<TARGET_PROPERTY:pl_editor_kiface,INCLUDE_DIRECTORIES>
|
|
||||||
-)
|
|
||||||
-target_link_libraries(pl_editor_navlib
|
|
||||||
- $<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_LINK_LIBRARIES>
|
|
||||||
- 3DxWare::Navlib
|
|
||||||
-)
|
|
||||||
+ # Find the 3DxWare SDK component 3DxWare::NlClient
|
|
||||||
+ # find_package(TDxWare_SDK 4.0 REQUIRED COMPONENTS 3DxWare::Navlib)
|
|
||||||
+ target_compile_definitions(pl_editor_navlib PRIVATE
|
|
||||||
+ $<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_COMPILE_DEFINITIONS>
|
|
||||||
+ )
|
|
||||||
+ target_compile_options(pl_editor_navlib PRIVATE
|
|
||||||
+ $<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_COMPILE_OPTIONS>
|
|
||||||
+ )
|
|
||||||
+ target_include_directories(pl_editor_navlib PRIVATE
|
|
||||||
+ $<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_INCLUDE_DIRECTORIES>
|
|
||||||
+ $<TARGET_PROPERTY:pl_editor_kiface,INCLUDE_DIRECTORIES>
|
|
||||||
+ )
|
|
||||||
+ target_link_libraries(pl_editor_navlib
|
|
||||||
+ $<TARGET_PROPERTY:3DxWare::Navlib,INTERFACE_LINK_LIBRARIES>
|
|
||||||
+ 3DxWare::Navlib
|
|
||||||
+ )
|
|
||||||
+endif()
|
|
||||||
|
|
@ -1,621 +0,0 @@
|
||||||
diff --git a/docker/build.sh b/docker/build.sh
|
|
||||||
index 85a9115..f5da04e 100755
|
|
||||||
--- a/docker/build.sh
|
|
||||||
+++ b/docker/build.sh
|
|
||||||
@@ -29,7 +29,7 @@ set -e
|
|
||||||
|
|
||||||
cd "$(dirname "$0")/.."
|
|
||||||
|
|
||||||
-VALID_APPS="pcbnew | eeschema | calculator | all"
|
|
||||||
+VALID_APPS="pcbnew | eeschema | calculator | pl_editor | all"
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
echo "Usage: ./docker/build.sh <app> [args...]" >&2
|
|
||||||
@@ -52,7 +52,7 @@ APP_NAME="$1"
|
|
||||||
shift
|
|
||||||
|
|
||||||
case "$APP_NAME" in
|
|
||||||
- pcbnew|eeschema|calculator|all) ;;
|
|
||||||
+ pcbnew|eeschema|calculator|pl_editor|all) ;;
|
|
||||||
*)
|
|
||||||
echo "Error: unknown app '$APP_NAME' (expected: ${VALID_APPS})" >&2
|
|
||||||
usage
|
|
||||||
@@ -115,6 +115,7 @@ fi
|
|
||||||
kicad_subdir_for() {
|
|
||||||
case "$1" in
|
|
||||||
calculator) echo "pcb_calculator" ;;
|
|
||||||
+ pl_editor) echo "pagelayout_editor" ;;
|
|
||||||
*) echo "$1" ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
@@ -158,6 +159,7 @@ if [[ "${APP_NAME}" == "all" ]]; then
|
|
||||||
build_app pcbnew
|
|
||||||
build_app eeschema
|
|
||||||
build_app calculator
|
|
||||||
+ build_app pl_editor
|
|
||||||
else
|
|
||||||
build_app "${APP_NAME}"
|
|
||||||
fi
|
|
||||||
diff --git a/scripts/kicad/build-kicad-target.sh b/scripts/kicad/build-kicad-target.sh
|
|
||||||
index c89b2ea..aaf00fd 100755
|
|
||||||
--- a/scripts/kicad/build-kicad-target.sh
|
|
||||||
+++ b/scripts/kicad/build-kicad-target.sh
|
|
||||||
@@ -1,11 +1,11 @@
|
|
||||||
#!/bin/bash
|
|
||||||
-# Build a KiCad app (pcbnew, eeschema, calculator) for WebAssembly.
|
|
||||||
+# Build a KiCad app (pcbnew, eeschema, calculator, pl_editor) for WebAssembly.
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# ./scripts/kicad/build-kicad-target.sh <app> [options]
|
|
||||||
#
|
|
||||||
# Args:
|
|
||||||
-# <app> pcbnew | eeschema | calculator (required)
|
|
||||||
+# <app> pcbnew | eeschema | calculator | pl_editor (required)
|
|
||||||
#
|
|
||||||
# Options:
|
|
||||||
# --full Full clean rebuild (dependencies + KiCad)
|
|
||||||
@@ -26,25 +26,26 @@
|
|
||||||
# the source subdirectory. Calculator is the exception: app=calculator but the
|
|
||||||
# upstream target and source subdir are both pcb_calculator (the OUTPUT_NAME
|
|
||||||
# property in pcb_calculator/CMakeLists.txt emits calculator.{js,wasm}).
|
|
||||||
+# pl_editor is the standard case but its source subdir is pagelayout_editor.
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [ -z "$1" ]; then
|
|
||||||
- echo "Error: missing <app> argument (pcbnew | eeschema | calculator)" >&2
|
|
||||||
+ echo "Error: missing <app> argument (pcbnew | eeschema | calculator | pl_editor)" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
APP_NAME="$1"
|
|
||||||
shift
|
|
||||||
|
|
||||||
case "$APP_NAME" in
|
|
||||||
- pcbnew|eeschema)
|
|
||||||
+ pcbnew|eeschema|pl_editor)
|
|
||||||
KICAD_TARGET="$APP_NAME"
|
|
||||||
;;
|
|
||||||
calculator)
|
|
||||||
KICAD_TARGET="pcb_calculator"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
- echo "Error: unknown app '$APP_NAME' (expected: pcbnew | eeschema | calculator)" >&2
|
|
||||||
+ echo "Error: unknown app '$APP_NAME' (expected: pcbnew | eeschema | calculator | pl_editor)" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
diff --git a/scripts/kicad/build-pl_editor.sh b/scripts/kicad/build-pl_editor.sh
|
|
||||||
new file mode 100755
|
|
||||||
index 0000000..17315f1
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/scripts/kicad/build-pl_editor.sh
|
|
||||||
@@ -0,0 +1,7 @@
|
|
||||||
+#!/bin/bash
|
|
||||||
+# Build KiCad pl_editor (drawing-sheet editor) for WebAssembly.
|
|
||||||
+# Thin wrapper around build-kicad-target.sh — see that script for options.
|
|
||||||
+
|
|
||||||
+set -e
|
|
||||||
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
+exec "${SCRIPT_DIR}/build-kicad-target.sh" pl_editor "$@"
|
|
||||||
diff --git a/tests/apps/kicad/pl_editor.html b/tests/apps/kicad/pl_editor.html
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000..516ecca
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tests/apps/kicad/pl_editor.html
|
|
||||||
@@ -0,0 +1,200 @@
|
|
||||||
+<!DOCTYPE html>
|
|
||||||
+<html lang="en-us">
|
|
||||||
+<head>
|
|
||||||
+ <meta charset="utf-8">
|
|
||||||
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
+ <title>KiCad Page Layout Editor WASM</title>
|
|
||||||
+ <style>
|
|
||||||
+ .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
|
|
||||||
+ div.emscripten { text-align: center; }
|
|
||||||
+ /* the canvas *must not* have any border or padding, or mouse coords will be wrong */
|
|
||||||
+ canvas.emscripten { border: 0px none; }
|
|
||||||
+
|
|
||||||
+ .window {
|
|
||||||
+ position: absolute;
|
|
||||||
+ pointer-events: none;
|
|
||||||
+ z-index: 10;
|
|
||||||
+ background-color: black;
|
|
||||||
+ overflow: hidden;
|
|
||||||
+ width: 0;
|
|
||||||
+ height: 0;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ .window-canvas {
|
|
||||||
+ position: absolute;
|
|
||||||
+ top: 0;
|
|
||||||
+ left: 0;
|
|
||||||
+ pointer-events: none;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ #status {
|
|
||||||
+ position: fixed;
|
|
||||||
+ bottom: 10px;
|
|
||||||
+ left: 10px;
|
|
||||||
+ color: #fff;
|
|
||||||
+ font-family: monospace;
|
|
||||||
+ z-index: 1000;
|
|
||||||
+ background: rgba(0,0,0,0.7);
|
|
||||||
+ padding: 10px;
|
|
||||||
+ border-radius: 5px;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ #progress {
|
|
||||||
+ width: 300px;
|
|
||||||
+ height: 20px;
|
|
||||||
+ background: #333;
|
|
||||||
+ margin-top: 5px;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ #progress-bar {
|
|
||||||
+ height: 100%;
|
|
||||||
+ background: #4CAF50;
|
|
||||||
+ width: 0%;
|
|
||||||
+ transition: width 0.3s;
|
|
||||||
+ }
|
|
||||||
+ </style>
|
|
||||||
+</head>
|
|
||||||
+<body style="margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: #1a1a2e;">
|
|
||||||
+ <div id="main-window" style="width: 100vw; height: 100vh; position: absolute; top: 0; left: 0;"></div>
|
|
||||||
+
|
|
||||||
+ <div id="status">
|
|
||||||
+ <div id="status-text">Initializing...</div>
|
|
||||||
+ <div id="progress"><div id="progress-bar"></div></div>
|
|
||||||
+ </div>
|
|
||||||
+
|
|
||||||
+ <div id="window-container"></div>
|
|
||||||
+
|
|
||||||
+ <script>
|
|
||||||
+ var mainWindow = document.getElementById('main-window');
|
|
||||||
+ var statusText = document.getElementById('status-text');
|
|
||||||
+ var progressBar = document.getElementById('progress-bar');
|
|
||||||
+
|
|
||||||
+ var showError = function(msg) {
|
|
||||||
+ console.error('[KICAD_ERROR] ' + msg);
|
|
||||||
+ statusText.textContent = 'Error: ' + msg;
|
|
||||||
+ statusText.style.color = 'red';
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ var createCanvas = function() {
|
|
||||||
+ var canvas = document.createElement('canvas');
|
|
||||||
+ canvas.id = 'canvas';
|
|
||||||
+ canvas.style.display = 'none';
|
|
||||||
+ var width = window.innerWidth;
|
|
||||||
+ var height = window.innerHeight;
|
|
||||||
+ canvas.style.width = width + 'px';
|
|
||||||
+ canvas.style.height = height + 'px';
|
|
||||||
+ canvas.oncontextmenu = function() { event.preventDefault(); };
|
|
||||||
+ canvas.addEventListener("webglcontextlost", function(e) {
|
|
||||||
+ showError('WebGL context lost. You will need to reload the page.');
|
|
||||||
+ e.preventDefault();
|
|
||||||
+ }, false);
|
|
||||||
+
|
|
||||||
+ mainWindow.appendChild(canvas);
|
|
||||||
+ Module.canvas = canvas;
|
|
||||||
+
|
|
||||||
+ console.log('[KICAD] preRun complete, canvas created: ' + width + 'x' + height);
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ var onRuntimeInitialized = function() {
|
|
||||||
+ console.log('[KICAD] Runtime initialized');
|
|
||||||
+ var canvas = Module.canvas;
|
|
||||||
+ canvas.style.display = 'block';
|
|
||||||
+ document.getElementById('status').style.display = 'none';
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ // Pre-fetched resource data (fetched before pl_editor.js loads)
|
|
||||||
+ var resourceData = null;
|
|
||||||
+
|
|
||||||
+ fetch('images.tar.gz')
|
|
||||||
+ .then(function(response) {
|
|
||||||
+ if (!response.ok) throw new Error('HTTP ' + response.status);
|
|
||||||
+ return response.arrayBuffer();
|
|
||||||
+ })
|
|
||||||
+ .then(function(buffer) {
|
|
||||||
+ resourceData = new Uint8Array(buffer);
|
|
||||||
+ console.log('[KICAD] Prefetched images.tar.gz (' + resourceData.length + ' bytes)');
|
|
||||||
+ })
|
|
||||||
+ .catch(function(err) {
|
|
||||||
+ console.warn('[KICAD] Could not prefetch images.tar.gz:', err.message);
|
|
||||||
+ });
|
|
||||||
+
|
|
||||||
+ var writeResources = function() {
|
|
||||||
+ var resourcePath = '/workspace/build-wasm/sysroot/share/kicad/resources';
|
|
||||||
+ FS.mkdirTree(resourcePath);
|
|
||||||
+
|
|
||||||
+ if (resourceData) {
|
|
||||||
+ FS.writeFile(resourcePath + '/images.tar.gz', resourceData);
|
|
||||||
+ console.log('[KICAD] Wrote images.tar.gz to ' + resourcePath);
|
|
||||||
+ } else {
|
|
||||||
+ console.warn('[KICAD] images.tar.gz not ready yet (WASM loaded faster than fetch)');
|
|
||||||
+ }
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ // Land the user in a sane, writable directory instead of MEMFS root (where
|
|
||||||
+ // the only visible entries are /dev, /proc, /tmp, /workspace, /home).
|
|
||||||
+ // The Open/Save dialogs default to cwd when no explicit defaultDir is set.
|
|
||||||
+ var setupHomeDir = function() {
|
|
||||||
+ var home = '/home/kicad';
|
|
||||||
+ FS.mkdirTree(home);
|
|
||||||
+ FS.chdir(home);
|
|
||||||
+ console.log('[KICAD] cwd set to ' + home);
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ var Module = {
|
|
||||||
+ thisProgram: '/usr/bin/pl_editor', // Fake absolute path for argv[0]
|
|
||||||
+
|
|
||||||
+ preRun: [createCanvas, writeResources, setupHomeDir],
|
|
||||||
+ postRun: [],
|
|
||||||
+
|
|
||||||
+ print: function(text) {
|
|
||||||
+ if (arguments.length > 1)
|
|
||||||
+ text = Array.prototype.slice.call(arguments).join(' ');
|
|
||||||
+ console.log('[KICAD_OUT] ' + text);
|
|
||||||
+ },
|
|
||||||
+
|
|
||||||
+ printErr: function(text) {
|
|
||||||
+ if (arguments.length > 1)
|
|
||||||
+ text = Array.prototype.slice.call(arguments).join(' ');
|
|
||||||
+ console.error('[KICAD_ERR] ' + text);
|
|
||||||
+ },
|
|
||||||
+
|
|
||||||
+ setStatus: function(text) {
|
|
||||||
+ console.log('[KICAD_STATUS] ' + text);
|
|
||||||
+ statusText.textContent = text;
|
|
||||||
+
|
|
||||||
+ var match = text.match(/(\d+)\/(\d+)/);
|
|
||||||
+ if (match) {
|
|
||||||
+ var pct = (parseInt(match[1]) / parseInt(match[2])) * 100;
|
|
||||||
+ progressBar.style.width = pct + '%';
|
|
||||||
+ }
|
|
||||||
+ },
|
|
||||||
+
|
|
||||||
+ totalDependencies: 0,
|
|
||||||
+ monitorRunDependencies: function(left) {
|
|
||||||
+ this.totalDependencies = Math.max(this.totalDependencies, left);
|
|
||||||
+ Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
|
|
||||||
+ },
|
|
||||||
+
|
|
||||||
+ onRuntimeInitialized: onRuntimeInitialized,
|
|
||||||
+
|
|
||||||
+ locateFile: function(path) {
|
|
||||||
+ return path;
|
|
||||||
+ }
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ Module.setStatus('Downloading...');
|
|
||||||
+
|
|
||||||
+ window.onerror = function(msg, url, line) {
|
|
||||||
+ showError(msg + ' at ' + url + ':' + line);
|
|
||||||
+ Module.setStatus = function(text) {
|
|
||||||
+ if (text) Module.printErr('[post-exception status] ' + text);
|
|
||||||
+ };
|
|
||||||
+ return false;
|
|
||||||
+ };
|
|
||||||
+ </script>
|
|
||||||
+
|
|
||||||
+ <!-- wxWidgets WASM glue code (defines getConfigEntryLength, etc.) -->
|
|
||||||
+ <script src="wx.js"></script>
|
|
||||||
+ <script async src="pl_editor.js"></script>
|
|
||||||
+</body>
|
|
||||||
+</html>
|
|
||||||
diff --git a/tests/e2e/filedialog-folder-nav.spec.ts b/tests/e2e/filedialog-folder-nav.spec.ts
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000..89d0720
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tests/e2e/filedialog-folder-nav.spec.ts
|
|
||||||
@@ -0,0 +1,37 @@
|
|
||||||
+// Regression coverage for the wxFileDialog folder-navigation fix
|
|
||||||
+// (wxGenericFileDialog::OnOk now navigates into directories instead of
|
|
||||||
+// closing the dialog with the folder path as a "file").
|
|
||||||
+//
|
|
||||||
+// Reproduces the original bug: select a folder, press Enter, expect the
|
|
||||||
+// dialog to navigate into the folder rather than close.
|
|
||||||
+
|
|
||||||
+import { test, expect, tryLoadApp, waitForRegistry, clickByLabel } from './utils/fixtures';
|
|
||||||
+
|
|
||||||
+test('folder navigation: Enter on a folder navigates instead of closing the dialog', async ({ page, testLogger }) => {
|
|
||||||
+ await page.goto('/standalone/filedialog/filedialog_test.html');
|
|
||||||
+ const loaded = await tryLoadApp(page);
|
|
||||||
+ expect(loaded, 'filedialog_test should load').toBe(true);
|
|
||||||
+
|
|
||||||
+ await waitForRegistry(page);
|
|
||||||
+
|
|
||||||
+ await clickByLabel(page, 'Open File...');
|
|
||||||
+ await page.waitForTimeout(800);
|
|
||||||
+
|
|
||||||
+ // Type a path that's a folder in Emscripten's MEMFS and press Enter.
|
|
||||||
+ // Before the fix, OnOk treated /dev as a file → either showed "Please
|
|
||||||
+ // choose an existing file" (wxFD_FILE_MUST_EXIST) or closed the dialog
|
|
||||||
+ // and surfaced /dev to the calling app as if it were a file.
|
|
||||||
+ await page.keyboard.type('/dev');
|
|
||||||
+ await page.waitForTimeout(200);
|
|
||||||
+ await page.keyboard.press('Enter');
|
|
||||||
+ await page.waitForTimeout(800);
|
|
||||||
+
|
|
||||||
+ await page.screenshot({ path: 'test-results/filedlg-folder-nav.png', fullPage: true });
|
|
||||||
+
|
|
||||||
+ // No "Selected file:" log should appear — the dialog must NOT have closed
|
|
||||||
+ // with /dev as the picked file.
|
|
||||||
+ const closedWithDev = testLogger.consoleLogs.some(l =>
|
|
||||||
+ l.includes('[FILEDIALOG_EVENT] Selected file:') && l.includes('/dev')
|
|
||||||
+ );
|
|
||||||
+ expect(closedWithDev, 'dialog must not close and report /dev as the selected file').toBe(false);
|
|
||||||
+});
|
|
||||||
diff --git a/tests/kicad/pl_editor.spec.ts b/tests/kicad/pl_editor.spec.ts
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000..39084c5
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tests/kicad/pl_editor.spec.ts
|
|
||||||
@@ -0,0 +1,202 @@
|
|
||||||
+import type { Page } from '@playwright/test';
|
|
||||||
+import { test, expect } from './fixtures';
|
|
||||||
+import {
|
|
||||||
+ clickByLabel,
|
|
||||||
+ clickMenuBarItem,
|
|
||||||
+ clickMenuItem,
|
|
||||||
+} from '../e2e/utils/element-tracker';
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
+ * pl_editor (drawing-sheet editor) WASM E2E Tests
|
|
||||||
+ *
|
|
||||||
+ * Mirrors eeschema.spec.ts. Smoke + the wxFileDialog folder-navigation
|
|
||||||
+ * regression we fixed at the wxWidgets level (filedlgg.cpp). The widget-level
|
|
||||||
+ * coverage lives in tests/e2e/filedialog-folder-nav.spec.ts; this file proves
|
|
||||||
+ * the fix also works through pl_editor's own File menu.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+async function completeWizard(page: Page): Promise<void> {
|
|
||||||
+ await expect(page.locator('#canvas')).toBeVisible({ timeout: 90000 });
|
|
||||||
+ await page.waitForFunction(() => !!window.wxElementRegistry, null, { timeout: 90000 });
|
|
||||||
+ await page.waitForTimeout(2000);
|
|
||||||
+
|
|
||||||
+ await page.screenshot({ path: 'test-results/pl_editor-wizard-00-initial.png', scale: 'device' });
|
|
||||||
+
|
|
||||||
+ for (let i = 1; i <= 10; i++) {
|
|
||||||
+ let clicked = await clickByLabel(page, 'Next >');
|
|
||||||
+
|
|
||||||
+ if (!clicked) {
|
|
||||||
+ clicked = await clickByLabel(page, 'Finish');
|
|
||||||
+
|
|
||||||
+ if (clicked) {
|
|
||||||
+ await page.waitForTimeout(500);
|
|
||||||
+ await page.screenshot({
|
|
||||||
+ path: `test-results/pl_editor-wizard-${String(i).padStart(2, '0')}-finish.png`,
|
|
||||||
+ scale: 'device'
|
|
||||||
+ });
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ await page.waitForTimeout(500);
|
|
||||||
+ await page.screenshot({
|
|
||||||
+ path: `test-results/pl_editor-wizard-${String(i).padStart(2, '0')}.png`,
|
|
||||||
+ scale: 'device'
|
|
||||||
+ });
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ await page.waitForTimeout(2000);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+function hasAbort(testLogger: { consoleLogs: string[]; errors: string[] }): boolean {
|
|
||||||
+ return [...testLogger.consoleLogs, ...testLogger.errors].some(line => line.includes('Aborted('));
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+test.describe('pl_editor WASM', () => {
|
|
||||||
+ test.beforeEach(async ({ page }) => {
|
|
||||||
+ await page.goto('/kicad/pl_editor.html');
|
|
||||||
+ });
|
|
||||||
+
|
|
||||||
+ test('app loads, canvas visible, no WASM abort', async ({ page, testLogger }) => {
|
|
||||||
+ await expect(page.locator('#canvas')).toBeVisible({ timeout: 90000 });
|
|
||||||
+ await page.waitForFunction(() => !!window.wxElementRegistry, null, { timeout: 90000 });
|
|
||||||
+ await page.waitForTimeout(1500);
|
|
||||||
+ await page.screenshot({ path: 'test-results/pl_editor-01-loaded.png', scale: 'device' });
|
|
||||||
+
|
|
||||||
+ expect(hasAbort(testLogger), 'no WASM abort during load').toBe(false);
|
|
||||||
+
|
|
||||||
+ const canvasCount = await page.locator('canvas').count();
|
|
||||||
+ expect(canvasCount).toBeGreaterThan(0);
|
|
||||||
+ });
|
|
||||||
+
|
|
||||||
+ test('wizard completes and leaves the editor in a clean state', async ({ page, testLogger }) => {
|
|
||||||
+ await completeWizard(page);
|
|
||||||
+
|
|
||||||
+ // After the wizard, no wxDialog/wxWizard should still be visible.
|
|
||||||
+ const blockingDialogs = await page.evaluate(() => {
|
|
||||||
+ const registry = window.wxElementRegistry;
|
|
||||||
+ if (!registry) return -1;
|
|
||||||
+ return registry.findAll({ visible: true })
|
|
||||||
+ .filter((el: { typeName: string }) =>
|
|
||||||
+ /^wxDialog|Wizard/.test(el.typeName))
|
|
||||||
+ .length;
|
|
||||||
+ });
|
|
||||||
+ expect(blockingDialogs, 'no blocking dialog/wizard visible after completeWizard()').toBe(0);
|
|
||||||
+ expect(hasAbort(testLogger), 'no WASM abort during wizard').toBe(false);
|
|
||||||
+
|
|
||||||
+ await page.screenshot({ path: 'test-results/pl_editor-02-post-wizard.png', scale: 'device' });
|
|
||||||
+ });
|
|
||||||
+
|
|
||||||
+ test('File menu exposes Open... and Save As...', async ({ page, testLogger }) => {
|
|
||||||
+ await completeWizard(page);
|
|
||||||
+
|
|
||||||
+ const fileMenuClicked = await clickMenuBarItem(page, 'File');
|
|
||||||
+ expect(fileMenuClicked, 'File menubar item should be clickable').toBe(true);
|
|
||||||
+ await page.waitForTimeout(400);
|
|
||||||
+
|
|
||||||
+ await page.screenshot({ path: 'test-results/pl_editor-03-file-menu.png', scale: 'device' });
|
|
||||||
+
|
|
||||||
+ // Menu items are tracked in the "rendered" half of the registry (popup
|
|
||||||
+ // widgets), not the regular findAll({visible:true}) set. Use findAllRendered
|
|
||||||
+ // and filter to menuitem elementType — same pattern as load-pcb-probe.spec.ts.
|
|
||||||
+ const menuLabels = await page.evaluate(() => {
|
|
||||||
+ const registry = window.wxElementRegistry;
|
|
||||||
+ if (!registry || !registry.findAllRendered) return [];
|
|
||||||
+ return registry.findAllRendered({})
|
|
||||||
+ .filter((r: { elementType: string }) => r.elementType === 'menuitem')
|
|
||||||
+ .map((r: { label?: string }) => r.label || '')
|
|
||||||
+ .filter((l: string) => l.length > 0);
|
|
||||||
+ });
|
|
||||||
+
|
|
||||||
+ // wxWidgets labels typically end with "..." (three ASCII dots) but some
|
|
||||||
+ // builds use the Unicode horizontal ellipsis "…". Accept either.
|
|
||||||
+ const hasOpen = menuLabels.some(l => /^Open[\.…]/.test(l) || l === 'Open');
|
|
||||||
+ const hasSaveAs = menuLabels.some(l => /^Save As[\.…]/.test(l) || l === 'Save As');
|
|
||||||
+ expect(hasOpen, `menu should contain "Open..." (saw labels: ${menuLabels.slice(0, 30).join(', ')})`).toBe(true);
|
|
||||||
+ expect(hasSaveAs, `menu should contain "Save As..." (saw labels: ${menuLabels.slice(0, 30).join(', ')})`).toBe(true);
|
|
||||||
+
|
|
||||||
+ // Dismiss the menu so we don't leak state into the next test.
|
|
||||||
+ await page.keyboard.press('Escape');
|
|
||||||
+ await page.waitForTimeout(200);
|
|
||||||
+
|
|
||||||
+ expect(hasAbort(testLogger)).toBe(false);
|
|
||||||
+ });
|
|
||||||
+
|
|
||||||
+ test('Save As file dialog: typing a folder + Enter navigates into it (regression)', async ({ page, testLogger }) => {
|
|
||||||
+ await completeWizard(page);
|
|
||||||
+
|
|
||||||
+ // Open File > Save As
|
|
||||||
+ await clickMenuBarItem(page, 'File');
|
|
||||||
+ await page.waitForTimeout(300);
|
|
||||||
+ const savedAsClicked = await clickMenuItem(page, 'Save As...');
|
|
||||||
+ expect(savedAsClicked, 'Save As... menu item should be clickable').toBe(true);
|
|
||||||
+
|
|
||||||
+ // Wait for the wxFileDialog to appear in the registry.
|
|
||||||
+ await page.waitForFunction(() => {
|
|
||||||
+ const registry = window.wxElementRegistry;
|
|
||||||
+ if (!registry) return false;
|
|
||||||
+ return registry.findAll({ visible: true })
|
|
||||||
+ .some((el: { typeName: string }) => el.typeName === 'wxFileDialog');
|
|
||||||
+ }, null, { timeout: 15000 });
|
|
||||||
+
|
|
||||||
+ await page.screenshot({ path: 'test-results/pl_editor-04-save-as-dialog.png', scale: 'device' });
|
|
||||||
+
|
|
||||||
+ // The bug: pressing Enter on a folder name treated it as a file and surfaced
|
|
||||||
+ // "Unable to load /dev file". After the OnOk fix, the dialog should navigate
|
|
||||||
+ // into the folder instead.
|
|
||||||
+ await page.keyboard.type('/dev');
|
|
||||||
+ await page.waitForTimeout(200);
|
|
||||||
+ await page.keyboard.press('Enter');
|
|
||||||
+ await page.waitForTimeout(900);
|
|
||||||
+
|
|
||||||
+ await page.screenshot({ path: 'test-results/pl_editor-04b-after-enter.png', scale: 'device' });
|
|
||||||
+
|
|
||||||
+ // The wxFileDialog should still be visible — we navigated into /dev, didn't close it.
|
|
||||||
+ const dialogStillOpen = await page.evaluate(() => {
|
|
||||||
+ const registry = window.wxElementRegistry;
|
|
||||||
+ if (!registry) return false;
|
|
||||||
+ return registry.findAll({ visible: true })
|
|
||||||
+ .some((el: { typeName: string }) => el.typeName === 'wxFileDialog');
|
|
||||||
+ });
|
|
||||||
+ expect(dialogStillOpen, 'wxFileDialog should remain open after Enter on a folder').toBe(true);
|
|
||||||
+
|
|
||||||
+ // The pre-fix error path surfaced "Unable to load <path> file" through KiCad's
|
|
||||||
+ // logger when the folder was returned as a "file". Ensure it didn't fire.
|
|
||||||
+ const unableToLoad = testLogger.consoleLogs.some(l => /Unable to load.*\/dev/.test(l));
|
|
||||||
+ expect(unableToLoad, 'KiCad must not surface "Unable to load /dev file"').toBe(false);
|
|
||||||
+
|
|
||||||
+ // Close the dialog cleanly so it doesn't leak to a subsequent step.
|
|
||||||
+ await page.keyboard.press('Escape');
|
|
||||||
+ await page.waitForTimeout(300);
|
|
||||||
+
|
|
||||||
+ expect(hasAbort(testLogger)).toBe(false);
|
|
||||||
+ });
|
|
||||||
+
|
|
||||||
+ test('canvas + toolbar metrics look sane', async ({ page, testLogger }) => {
|
|
||||||
+ await completeWizard(page);
|
|
||||||
+
|
|
||||||
+ const metrics = await page.evaluate(() => {
|
|
||||||
+ const registry = window.wxElementRegistry;
|
|
||||||
+ const all = registry ? registry.findAll({ visible: true }) : [];
|
|
||||||
+ const toolbars = all.filter((el: { typeName: string }) => /ToolBar/.test(el.typeName));
|
|
||||||
+ const glCanvas = document.querySelector('canvas[id*="gl"]') as HTMLCanvasElement | null;
|
|
||||||
+
|
|
||||||
+ return {
|
|
||||||
+ registryTotal: all.length,
|
|
||||||
+ toolbarCount: toolbars.length,
|
|
||||||
+ mainCanvasOk: (() => {
|
|
||||||
+ const c = document.getElementById('canvas') as HTMLCanvasElement | null;
|
|
||||||
+ return !!c && c.width > 0 && c.height > 0;
|
|
||||||
+ })(),
|
|
||||||
+ glCanvasOk: !!glCanvas && glCanvas.width > 0 && glCanvas.height > 0,
|
|
||||||
+ };
|
|
||||||
+ });
|
|
||||||
+
|
|
||||||
+ expect(metrics.registryTotal, 'registry should be populated').toBeGreaterThan(10);
|
|
||||||
+ expect(metrics.toolbarCount, 'at least one toolbar should be visible').toBeGreaterThanOrEqual(1);
|
|
||||||
+ expect(metrics.mainCanvasOk, 'main canvas has nonzero dimensions').toBe(true);
|
|
||||||
+ expect(metrics.glCanvasOk, 'GL canvas has nonzero dimensions').toBe(true);
|
|
||||||
+ expect(hasAbort(testLogger)).toBe(false);
|
|
||||||
+ });
|
|
||||||
+});
|
|
||||||
diff --git a/tests/scripts/setup-kicad-wasm.sh b/tests/scripts/setup-kicad-wasm.sh
|
|
||||||
index aa79e89..ae5cdff 100755
|
|
||||||
--- a/tests/scripts/setup-kicad-wasm.sh
|
|
||||||
+++ b/tests/scripts/setup-kicad-wasm.sh
|
|
||||||
@@ -17,10 +17,12 @@ mkdir -p "$KICAD_TEST"
|
|
||||||
|
|
||||||
# Map an app name to its inner CMake build subdirectory. Most apps share their
|
|
||||||
# subdir name with the app name; pcb_calculator emits OUTPUT_NAME=calculator
|
|
||||||
-# but lives under the pcb_calculator/ subtree of the build dir.
|
|
||||||
+# but lives under the pcb_calculator/ subtree of the build dir, and pl_editor's
|
|
||||||
+# source lives under pagelayout_editor/.
|
|
||||||
kicad_subdir_for() {
|
|
||||||
case "$1" in
|
|
||||||
calculator) echo "pcb_calculator" ;;
|
|
||||||
+ pl_editor) echo "pagelayout_editor" ;;
|
|
||||||
*) echo "$1" ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
@@ -64,9 +66,10 @@ found_any=0
|
|
||||||
copy_app pcbnew && found_any=1
|
|
||||||
copy_app eeschema && found_any=1
|
|
||||||
copy_app calculator && found_any=1
|
|
||||||
+copy_app pl_editor && found_any=1
|
|
||||||
|
|
||||||
if [ "$found_any" -eq 0 ]; then
|
|
||||||
- echo "Error: no pcbnew/eeschema/calculator artifacts found in output/ or docker volume" >&2
|
|
||||||
+ echo "Error: no pcbnew/eeschema/calculator/pl_editor artifacts found in output/ or docker volume" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
diff --git a/wasm/stubs/nl_pl_editor_plugin_stub.cpp b/wasm/stubs/nl_pl_editor_plugin_stub.cpp
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000..d3fe4df
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/wasm/stubs/nl_pl_editor_plugin_stub.cpp
|
|
||||||
@@ -0,0 +1,29 @@
|
|
||||||
+/*
|
|
||||||
+ * 3Dconnexion SpaceMouse plugin stubs for KiCad pagelayout_editor WASM build.
|
|
||||||
+ * The 3DxWare driver is unavailable in the browser; these stubs satisfy the
|
|
||||||
+ * symbols referenced from pl_editor_frame.cpp without doing anything.
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+// Minimal definition for NL_PL_EDITOR_PLUGIN_IMPL — required because the
|
|
||||||
+// unique_ptr<NL_PL_EDITOR_PLUGIN_IMPL> destructor needs a complete type.
|
|
||||||
+class NL_PL_EDITOR_PLUGIN_IMPL {};
|
|
||||||
+
|
|
||||||
+#include <navlib/nl_pl_editor_plugin.h>
|
|
||||||
+
|
|
||||||
+NL_PL_EDITOR_PLUGIN::NL_PL_EDITOR_PLUGIN()
|
|
||||||
+{
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+NL_PL_EDITOR_PLUGIN::~NL_PL_EDITOR_PLUGIN()
|
|
||||||
+{
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void NL_PL_EDITOR_PLUGIN::SetCanvas( EDA_DRAW_PANEL_GAL* aViewport )
|
|
||||||
+{
|
|
||||||
+ (void) aViewport;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void NL_PL_EDITOR_PLUGIN::SetFocus( bool aFocus )
|
|
||||||
+{
|
|
||||||
+ (void) aFocus;
|
|
||||||
+}
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
diff --git a/src/generic/filedlgg.cpp b/src/generic/filedlgg.cpp
|
|
||||||
index 4b89dcdc6f4..e895557f86d 100644
|
|
||||||
--- a/src/generic/filedlgg.cpp
|
|
||||||
+++ b/src/generic/filedlgg.cpp
|
|
||||||
@@ -337,6 +337,18 @@ void wxGenericFileDialog::OnOk( wxCommandEvent &WXUNUSED(event) )
|
|
||||||
|
|
||||||
const wxString& path = selectedFiles[0];
|
|
||||||
|
|
||||||
+ // If the user OKs a directory (via single-click + Enter/OK, or via a
|
|
||||||
+ // double-click that routed through here instead of wxGenericFileCtrl's
|
|
||||||
+ // OnActivated), navigate into the directory rather than closing the dialog
|
|
||||||
+ // and surfacing the folder path to the caller as if it were a file.
|
|
||||||
+ // Without this, KiCad's Open Drawing Sheet then tries to LoadDrawingSheetFile
|
|
||||||
+ // on the folder and surfaces "Unable to load /dev file" to the user.
|
|
||||||
+ if (selectedFiles.Count() == 1 && wxDirExists(path))
|
|
||||||
+ {
|
|
||||||
+ m_filectrl->SetDirectory(path);
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
if (selectedFiles.Count() == 1)
|
|
||||||
{
|
|
||||||
SetPath(path);
|
|
||||||
diff --git a/src/wasm/mouse.cpp b/src/wasm/mouse.cpp
|
|
||||||
index e3a2d1cfdef..62fb7c92cba 100644
|
|
||||||
--- a/src/wasm/mouse.cpp
|
|
||||||
+++ b/src/wasm/mouse.cpp
|
|
||||||
@@ -12,7 +12,9 @@
|
|
||||||
#include "wx/log.h"
|
|
||||||
#include <emscripten/html5.h>
|
|
||||||
|
|
||||||
-//#define HAS_MOUSE_DETAIL
|
|
||||||
+// Double-click detection threshold (ms). Matches the default
|
|
||||||
+// wxSYS_DCLICK_MSEC on most platforms.
|
|
||||||
+#define WASM_DCLICK_MSEC 500.0
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
@@ -63,11 +65,32 @@ wxEventType GetMouseEventType(int emscriptenEventType,
|
|
||||||
wxEventType eventType;
|
|
||||||
std::string eventName;
|
|
||||||
|
|
||||||
-#ifdef HAS_MOUSE_DETAIL
|
|
||||||
- int clickCount = event.detail;
|
|
||||||
-#else
|
|
||||||
+ // EmscriptenMouseEvent no longer exposes a click-count field, so we
|
|
||||||
+ // detect double-clicks ourselves: two MOUSEDOWNs of the same button
|
|
||||||
+ // within WASM_DCLICK_MSEC count as a double-click. The browser also
|
|
||||||
+ // dispatches a real 'dblclick' event we could hook, but tracking it
|
|
||||||
+ // on MOUSEDOWN lets wxEVT_LEFT_DCLICK arrive at the same point in the
|
|
||||||
+ // sequence as on desktop (between LEFT_DOWN and LEFT_UP), which is
|
|
||||||
+ // what wxGenericListCtrl's activation logic expects.
|
|
||||||
+ static double lastMouseDownTime = 0.0;
|
|
||||||
+ static unsigned short lastMouseDownButton = 0xFFFF;
|
|
||||||
int clickCount = 1;
|
|
||||||
-#endif
|
|
||||||
+ if (emscriptenEventType == EMSCRIPTEN_EVENT_MOUSEDOWN)
|
|
||||||
+ {
|
|
||||||
+ if (event.button == lastMouseDownButton &&
|
|
||||||
+ (event.timestamp - lastMouseDownTime) < WASM_DCLICK_MSEC)
|
|
||||||
+ {
|
|
||||||
+ clickCount = 2;
|
|
||||||
+ // Reset so a quick third click isn't chained as another DCLICK.
|
|
||||||
+ lastMouseDownTime = 0.0;
|
|
||||||
+ lastMouseDownButton = 0xFFFF;
|
|
||||||
+ }
|
|
||||||
+ else
|
|
||||||
+ {
|
|
||||||
+ lastMouseDownTime = event.timestamp;
|
|
||||||
+ lastMouseDownButton = event.button;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
|
|
||||||
switch (emscriptenEventType)
|
|
||||||
{
|
|
||||||
|
|
@ -1,432 +0,0 @@
|
||||||
diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt
|
|
||||||
index 1be2c291d1..e07d05e6e7 100644
|
|
||||||
--- a/eeschema/CMakeLists.txt
|
|
||||||
+++ b/eeschema/CMakeLists.txt
|
|
||||||
@@ -60,35 +60,48 @@ set( EESCHEMA_SCH_IO
|
|
||||||
|
|
||||||
# HTTP IO plugin
|
|
||||||
sch_io/http_lib/sch_io_http_lib.cpp
|
|
||||||
+ )
|
|
||||||
|
|
||||||
- # Database IO plugin
|
|
||||||
- sch_io/database/sch_io_database.cpp
|
|
||||||
-
|
|
||||||
- # Eagle IO plugin
|
|
||||||
- sch_io/eagle/sch_io_eagle.cpp
|
|
||||||
-
|
|
||||||
- # Altium IO plugin
|
|
||||||
- sch_io/altium/altium_parser_sch.cpp
|
|
||||||
- sch_io/altium/sch_io_altium.cpp
|
|
||||||
-
|
|
||||||
- # Cadstar IO plugin
|
|
||||||
- sch_io/cadstar/cadstar_sch_archive_loader.cpp
|
|
||||||
- sch_io/cadstar/cadstar_sch_archive_parser.cpp
|
|
||||||
- sch_io/cadstar/sch_io_cadstar_archive.cpp
|
|
||||||
-
|
|
||||||
- # LTSpice IO plugin
|
|
||||||
- sch_io/ltspice/ltspice_schematic.cpp
|
|
||||||
- sch_io/ltspice/sch_io_ltspice.cpp
|
|
||||||
- sch_io/ltspice/sch_io_ltspice_parser.cpp
|
|
||||||
-
|
|
||||||
- # EasyEDA IO plugin
|
|
||||||
- sch_io/easyeda/sch_easyeda_parser.cpp
|
|
||||||
- sch_io/easyeda/sch_io_easyeda.cpp
|
|
||||||
-
|
|
||||||
- # EasyEDA Pro IO plugin
|
|
||||||
- sch_io/easyedapro/sch_easyedapro_parser.cpp
|
|
||||||
- sch_io/easyedapro/sch_io_easyedapro.cpp
|
|
||||||
+# Third-party importers and database plugin are not built for WASM:
|
|
||||||
+# - Database needs nanodbc/ODBC (common/database/database_connection.cpp
|
|
||||||
+# is also gated on NOT EMSCRIPTEN in common/CMakeLists.txt).
|
|
||||||
+# - Altium pulls in pcbnew/pcb_io/altium common code which pcbnew itself
|
|
||||||
+# excludes for WASM (third-party uses std::basic_string<unsigned short>
|
|
||||||
+# that the current Emscripten libc++ rejects).
|
|
||||||
+# - Eagle/Cadstar/LTspice/EasyEDA are cross-tool importers we don't need
|
|
||||||
+# for the MVP and they bring transitive incompatible dependencies in.
|
|
||||||
+# sch_io_mgr.cpp's factory cases are guarded with __EMSCRIPTEN__ to match.
|
|
||||||
+if( NOT EMSCRIPTEN )
|
|
||||||
+ list( APPEND EESCHEMA_SCH_IO
|
|
||||||
+ # Database IO plugin
|
|
||||||
+ sch_io/database/sch_io_database.cpp
|
|
||||||
+
|
|
||||||
+ # Eagle IO plugin
|
|
||||||
+ sch_io/eagle/sch_io_eagle.cpp
|
|
||||||
+
|
|
||||||
+ # Altium IO plugin
|
|
||||||
+ sch_io/altium/altium_parser_sch.cpp
|
|
||||||
+ sch_io/altium/sch_io_altium.cpp
|
|
||||||
+
|
|
||||||
+ # Cadstar IO plugin
|
|
||||||
+ sch_io/cadstar/cadstar_sch_archive_loader.cpp
|
|
||||||
+ sch_io/cadstar/cadstar_sch_archive_parser.cpp
|
|
||||||
+ sch_io/cadstar/sch_io_cadstar_archive.cpp
|
|
||||||
+
|
|
||||||
+ # LTSpice IO plugin
|
|
||||||
+ sch_io/ltspice/ltspice_schematic.cpp
|
|
||||||
+ sch_io/ltspice/sch_io_ltspice.cpp
|
|
||||||
+ sch_io/ltspice/sch_io_ltspice_parser.cpp
|
|
||||||
+
|
|
||||||
+ # EasyEDA IO plugin
|
|
||||||
+ sch_io/easyeda/sch_easyeda_parser.cpp
|
|
||||||
+ sch_io/easyeda/sch_io_easyeda.cpp
|
|
||||||
+
|
|
||||||
+ # EasyEDA Pro IO plugin
|
|
||||||
+ sch_io/easyedapro/sch_easyedapro_parser.cpp
|
|
||||||
+ sch_io/easyedapro/sch_io_easyedapro.cpp
|
|
||||||
)
|
|
||||||
+endif()
|
|
||||||
|
|
||||||
set( EESCHEMA_DLGS
|
|
||||||
dialogs/dialog_annotate.cpp
|
|
||||||
@@ -276,6 +289,22 @@ set( EESCHEMA_SIM_SRCS
|
|
||||||
widgets/tuner_slider_base.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
+# WASM: the four largest BSIM/SOI/HSIM data initializer functions exceed the
|
|
||||||
+# V8/SpiderMonkey limit on locals per function ("too many locals") once
|
|
||||||
+# compiled. Replace them with empty stubs — the simulator UI is unreachable
|
|
||||||
+# in WASM anyway (see wasm/stubs/ngspice/sharedspice.h).
|
|
||||||
+if( EMSCRIPTEN )
|
|
||||||
+ list( REMOVE_ITEM EESCHEMA_SIM_SRCS
|
|
||||||
+ sim/sim_model_ngspice_data_bsim4.cpp
|
|
||||||
+ sim/sim_model_ngspice_data_b3soi.cpp
|
|
||||||
+ sim/sim_model_ngspice_data_b4soi.cpp
|
|
||||||
+ sim/sim_model_ngspice_data_hsim.cpp
|
|
||||||
+ )
|
|
||||||
+ list( APPEND EESCHEMA_SIM_SRCS
|
|
||||||
+ ${CMAKE_SOURCE_DIR}/../wasm/stubs/eeschema_ngspice_data_stubs.cpp
|
|
||||||
+ )
|
|
||||||
+endif()
|
|
||||||
+
|
|
||||||
set( EESCHEMA_WIDGETS
|
|
||||||
widgets/hierarchy_pane.cpp
|
|
||||||
widgets/panel_sch_selection_filter_base.cpp
|
|
||||||
@@ -561,14 +590,25 @@ add_executable( eeschema WIN32 MACOSX_BUNDLE
|
|
||||||
${EESCHEMA_RESOURCES}
|
|
||||||
)
|
|
||||||
|
|
||||||
-set_source_files_properties( ${CMAKE_SOURCE_DIR}/common/single_top.cpp PROPERTIES
|
|
||||||
- COMPILE_DEFINITIONS "TOP_FRAME=FRAME_SCH;PGM_DATA_FILE_EXT=\"kicad_sch\";BUILD_KIWAY_DLL"
|
|
||||||
- )
|
|
||||||
+if( EMSCRIPTEN )
|
|
||||||
+ # WASM: Static kiface linking - don't define BUILD_KIWAY_DLL
|
|
||||||
+ set_source_files_properties( ${CMAKE_SOURCE_DIR}/common/single_top.cpp PROPERTIES
|
|
||||||
+ COMPILE_DEFINITIONS "TOP_FRAME=FRAME_SCH;PGM_DATA_FILE_EXT=\"kicad_sch\""
|
|
||||||
+ )
|
|
||||||
+else()
|
|
||||||
+ set_source_files_properties( ${CMAKE_SOURCE_DIR}/common/single_top.cpp PROPERTIES
|
|
||||||
+ COMPILE_DEFINITIONS "TOP_FRAME=FRAME_SCH;PGM_DATA_FILE_EXT=\"kicad_sch\";BUILD_KIWAY_DLL"
|
|
||||||
+ )
|
|
||||||
+endif()
|
|
||||||
|
|
||||||
-target_link_libraries( eeschema
|
|
||||||
- kicommon
|
|
||||||
- ${wxWidgets_LIBRARIES}
|
|
||||||
- )
|
|
||||||
+if( NOT EMSCRIPTEN )
|
|
||||||
+ # Native: minimal link, kiface loaded dynamically
|
|
||||||
+ target_link_libraries( eeschema
|
|
||||||
+ kicommon
|
|
||||||
+ ${wxWidgets_LIBRARIES}
|
|
||||||
+ )
|
|
||||||
+endif()
|
|
||||||
+# WASM linking is done after EESCHEMA_KIFACE_LIBRARIES is defined
|
|
||||||
|
|
||||||
# the main Eeschema program, in DSO form.
|
|
||||||
add_library( eeschema_kiface_objects OBJECT
|
|
||||||
@@ -577,6 +617,15 @@ add_library( eeschema_kiface_objects OBJECT
|
|
||||||
${EESCHEMA_COMMON_SRCS}
|
|
||||||
)
|
|
||||||
|
|
||||||
+# WASM: Bring eeschema.cpp (the KIFACE entry point) and stub sources into the
|
|
||||||
+# kiface_objects translation unit so static linking finds KIFACE_GETTER.
|
|
||||||
+if( EMSCRIPTEN )
|
|
||||||
+ target_sources( eeschema_kiface_objects PRIVATE
|
|
||||||
+ eeschema.cpp
|
|
||||||
+ ${CMAKE_SOURCE_DIR}/../wasm/stubs/eeschema_frame_stub.cpp
|
|
||||||
+ )
|
|
||||||
+endif()
|
|
||||||
+
|
|
||||||
if( KICAD_USE_PCH )
|
|
||||||
target_precompile_headers( eeschema_kiface_objects
|
|
||||||
PRIVATE
|
|
||||||
@@ -612,21 +661,49 @@ target_link_libraries( eeschema_kiface_objects PUBLIC eeschema_navlib)
|
|
||||||
|
|
||||||
add_dependencies( eeschema_kiface_objects eeschema_navlib )
|
|
||||||
|
|
||||||
-add_library( eeschema_kiface MODULE
|
|
||||||
- eeschema.cpp
|
|
||||||
+if( EMSCRIPTEN )
|
|
||||||
+ # WASM: empty MODULE - eeschema.cpp was hoisted into kiface_objects above
|
|
||||||
+ # so the executable can link KIFACE_GETTER statically. The empty MODULE
|
|
||||||
+ # target is kept so post-build steps that reference it still resolve.
|
|
||||||
+ add_library( eeschema_kiface MODULE )
|
|
||||||
+else()
|
|
||||||
+ add_library( eeschema_kiface MODULE
|
|
||||||
+ eeschema.cpp
|
|
||||||
+ )
|
|
||||||
+endif()
|
|
||||||
+
|
|
||||||
+set( EESCHEMA_KIFACE_LIBRARIES
|
|
||||||
+ eeschema_kiface_objects
|
|
||||||
+ common
|
|
||||||
+ eeschema_navlib
|
|
||||||
+ kicommon
|
|
||||||
+ kiplatform
|
|
||||||
+ gal
|
|
||||||
+ scripting
|
|
||||||
+ sexpr
|
|
||||||
+ core
|
|
||||||
+ markdown_lib
|
|
||||||
+ ${wxWidgets_LIBRARIES}
|
|
||||||
+ ${NGSPICE_LIBRARY}
|
|
||||||
+ Boost::headers
|
|
||||||
)
|
|
||||||
|
|
||||||
+# WASM: Link kiface objects directly into eeschema executable (static linking)
|
|
||||||
+if( EMSCRIPTEN )
|
|
||||||
+ target_link_libraries( eeschema
|
|
||||||
+ PRIVATE
|
|
||||||
+ ${EESCHEMA_KIFACE_LIBRARIES}
|
|
||||||
+ )
|
|
||||||
+ # WASM: Allow multiple definitions of symbols that exist in both
|
|
||||||
+ # KiCad and wxWidgets (matches the pcbnew WASM pattern).
|
|
||||||
+ target_link_options( eeschema PRIVATE
|
|
||||||
+ "LINKER:--allow-multiple-definition"
|
|
||||||
+ )
|
|
||||||
+endif()
|
|
||||||
+
|
|
||||||
target_link_libraries( eeschema_kiface
|
|
||||||
PRIVATE
|
|
||||||
- common
|
|
||||||
- eeschema_kiface_objects
|
|
||||||
- markdown_lib
|
|
||||||
- scripting
|
|
||||||
- sexpr
|
|
||||||
- core
|
|
||||||
- Boost::headers
|
|
||||||
- ${wxWidgets_LIBRARIES}
|
|
||||||
- ${NGSPICE_LIBRARY}
|
|
||||||
+ ${EESCHEMA_KIFACE_LIBRARIES}
|
|
||||||
)
|
|
||||||
|
|
||||||
if( MSVC )
|
|
||||||
@@ -649,9 +726,17 @@ set_target_properties( eeschema_kiface PROPERTIES
|
|
||||||
)
|
|
||||||
|
|
||||||
# The KIFACE is in eeschema.cpp, export it:
|
|
||||||
-set_source_files_properties( eeschema.cpp PROPERTIES
|
|
||||||
- COMPILE_DEFINITIONS "BUILD_KIWAY_DLL;COMPILING_DLL"
|
|
||||||
- )
|
|
||||||
+if( EMSCRIPTEN )
|
|
||||||
+ # WASM: Static linking - don't define BUILD_KIWAY_DLL so KIFACE_GETTER
|
|
||||||
+ # is exported with its statically-linkable name (KIFACE_1).
|
|
||||||
+ set_source_files_properties( eeschema.cpp PROPERTIES
|
|
||||||
+ COMPILE_DEFINITIONS "COMPILING_DLL"
|
|
||||||
+ )
|
|
||||||
+else()
|
|
||||||
+ set_source_files_properties( eeschema.cpp PROPERTIES
|
|
||||||
+ COMPILE_DEFINITIONS "BUILD_KIWAY_DLL;COMPILING_DLL"
|
|
||||||
+ )
|
|
||||||
+endif()
|
|
||||||
|
|
||||||
# if building eeschema, then also build eeschema_kiface if out of date.
|
|
||||||
add_dependencies( eeschema eeschema_kiface )
|
|
||||||
diff --git a/eeschema/dialogs/dialog_sim_command.cpp b/eeschema/dialogs/dialog_sim_command.cpp
|
|
||||||
index 7ec105f35a..19fa35d460 100644
|
|
||||||
--- a/eeschema/dialogs/dialog_sim_command.cpp
|
|
||||||
+++ b/eeschema/dialogs/dialog_sim_command.cpp
|
|
||||||
@@ -570,7 +570,7 @@ void DIALOG_SIM_COMMAND::updateDCSources( wxChar aType, wxChoice* aSource )
|
|
||||||
{
|
|
||||||
wxString prevSelection;
|
|
||||||
|
|
||||||
- if( !aSource->IsEmpty() && aSource->GetSelection() >= 0 )
|
|
||||||
+ if( aSource->GetCount() > 0 && aSource->GetSelection() >= 0 )
|
|
||||||
prevSelection = aSource->GetString( aSource->GetSelection() );
|
|
||||||
|
|
||||||
std::set<wxString> sourcesList;
|
|
||||||
diff --git a/eeschema/eeschema.cpp b/eeschema/eeschema.cpp
|
|
||||||
index f3b334d1c8..d56ca181dc 100644
|
|
||||||
--- a/eeschema/eeschema.cpp
|
|
||||||
+++ b/eeschema/eeschema.cpp
|
|
||||||
@@ -201,7 +201,13 @@ static struct IFACE : public KIFACE_BASE, public UNITS_PROVIDER
|
|
||||||
}
|
|
||||||
|
|
||||||
case FRAME_SCH_SYMBOL_EDITOR:
|
|
||||||
+#ifdef __EMSCRIPTEN__
|
|
||||||
+ // WASM build: symbol library editor is not supported (see
|
|
||||||
+ // features/schematic/0001-eeschema-iface-stubs.md).
|
|
||||||
+ return nullptr;
|
|
||||||
+#else
|
|
||||||
return new SYMBOL_EDIT_FRAME( aKiway, aParent );
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
case FRAME_SIMULATOR:
|
|
||||||
{
|
|
||||||
@@ -219,10 +225,19 @@ static struct IFACE : public KIFACE_BASE, public UNITS_PROVIDER
|
|
||||||
}
|
|
||||||
|
|
||||||
case FRAME_SCH_VIEWER:
|
|
||||||
+#ifdef __EMSCRIPTEN__
|
|
||||||
+ // WASM build: symbol viewer is not supported.
|
|
||||||
+ return nullptr;
|
|
||||||
+#else
|
|
||||||
return new SYMBOL_VIEWER_FRAME( aKiway, aParent );
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
case FRAME_SYMBOL_CHOOSER:
|
|
||||||
{
|
|
||||||
+#ifdef __EMSCRIPTEN__
|
|
||||||
+ // WASM build: symbol chooser is not supported (no bundled libs).
|
|
||||||
+ return nullptr;
|
|
||||||
+#else
|
|
||||||
bool cancelled = false;
|
|
||||||
SYMBOL_CHOOSER_FRAME* chooser = new SYMBOL_CHOOSER_FRAME( aKiway, aParent, cancelled );
|
|
||||||
|
|
||||||
@@ -233,6 +248,7 @@ static struct IFACE : public KIFACE_BASE, public UNITS_PROVIDER
|
|
||||||
}
|
|
||||||
|
|
||||||
return chooser;
|
|
||||||
+#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
case DIALOG_SCH_LIBRARY_TABLE:
|
|
||||||
diff --git a/eeschema/sch_base_frame.cpp b/eeschema/sch_base_frame.cpp
|
|
||||||
index 161379a699..e722b7a043 100644
|
|
||||||
--- a/eeschema/sch_base_frame.cpp
|
|
||||||
+++ b/eeschema/sch_base_frame.cpp
|
|
||||||
@@ -775,6 +775,7 @@ wxString SCH_BASE_FRAME::SelectLibrary( const wxString& aDialogTitle, const wxSt
|
|
||||||
|
|
||||||
void SCH_BASE_FRAME::setSymWatcher( const LIB_ID* aID )
|
|
||||||
{
|
|
||||||
+#if wxUSE_FSWATCHER
|
|
||||||
Unbind( wxEVT_FSWATCHER, &SCH_BASE_FRAME::OnSymChange, this );
|
|
||||||
|
|
||||||
if( m_watcher )
|
|
||||||
@@ -824,11 +825,15 @@ void SCH_BASE_FRAME::setSymWatcher( const LIB_ID* aID )
|
|
||||||
wxLogNull silence;
|
|
||||||
m_watcher->Add( fn );
|
|
||||||
}
|
|
||||||
+#else
|
|
||||||
+ (void) aID;
|
|
||||||
+#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SCH_BASE_FRAME::OnSymChange( wxFileSystemWatcherEvent& aEvent )
|
|
||||||
{
|
|
||||||
+#if wxUSE_FSWATCHER
|
|
||||||
LEGACY_SYMBOL_LIBS* libs = PROJECT_SCH::LegacySchLibs( &Prj() );
|
|
||||||
|
|
||||||
wxLogTrace( traceLibWatch, "OnSymChange: %s, watcher file: %s",
|
|
||||||
@@ -846,6 +851,9 @@ void SCH_BASE_FRAME::OnSymChange( wxFileSystemWatcherEvent& aEvent )
|
|
||||||
wxLogTrace( traceLibWatch, "Failed to start the debounce timer" );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
+#else
|
|
||||||
+ (void) aEvent;
|
|
||||||
+#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
diff --git a/eeschema/sch_base_frame.h b/eeschema/sch_base_frame.h
|
|
||||||
index 2d701c7d28..a86ae37237 100644
|
|
||||||
--- a/eeschema/sch_base_frame.h
|
|
||||||
+++ b/eeschema/sch_base_frame.h
|
|
||||||
@@ -323,7 +323,9 @@ protected:
|
|
||||||
private:
|
|
||||||
|
|
||||||
/// These are file watchers for the symbol library tables.
|
|
||||||
+#if wxUSE_FSWATCHER
|
|
||||||
std::unique_ptr<wxFileSystemWatcher> m_watcher;
|
|
||||||
+#endif
|
|
||||||
wxFileName m_watcherFileName;
|
|
||||||
wxDateTime m_watcherLastModified;
|
|
||||||
wxTimer m_watcherDebounceTimer;
|
|
||||||
diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp
|
|
||||||
index fd5a91a388..6eab185446 100644
|
|
||||||
--- a/eeschema/sch_edit_frame.cpp
|
|
||||||
+++ b/eeschema/sch_edit_frame.cpp
|
|
||||||
@@ -56,7 +56,9 @@
|
|
||||||
#include <core/profile.h>
|
|
||||||
#include <project/project_file.h>
|
|
||||||
#include <project/net_settings.h>
|
|
||||||
+#ifdef KICAD_SCRIPTING
|
|
||||||
#include <python_scripting.h>
|
|
||||||
+#endif
|
|
||||||
#include <sch_edit_frame.h>
|
|
||||||
#include <symbol_chooser_frame.h>
|
|
||||||
#include <sch_painter.h>
|
|
||||||
diff --git a/eeschema/sch_io/sch_io_mgr.cpp b/eeschema/sch_io/sch_io_mgr.cpp
|
|
||||||
index 63e55bcdae..922fef0d90 100644
|
|
||||||
--- a/eeschema/sch_io/sch_io_mgr.cpp
|
|
||||||
+++ b/eeschema/sch_io/sch_io_mgr.cpp
|
|
||||||
@@ -24,17 +24,22 @@
|
|
||||||
#include <wx/uri.h>
|
|
||||||
|
|
||||||
#include <sch_io/sch_io_mgr.h>
|
|
||||||
-#include <sch_io/eagle/sch_io_eagle.h>
|
|
||||||
#include <sch_io/kicad_legacy/sch_io_kicad_legacy.h>
|
|
||||||
#include <sch_io/kicad_sexpr/sch_io_kicad_sexpr.h>
|
|
||||||
+#include <sch_io/http_lib/sch_io_http_lib.h>
|
|
||||||
|
|
||||||
+// Third-party importers and the database plugin are excluded from the WASM
|
|
||||||
+// build (see kicad/eeschema/CMakeLists.txt). Their FindPlugin cases below
|
|
||||||
+// return nullptr on WASM.
|
|
||||||
+#ifndef __EMSCRIPTEN__
|
|
||||||
+#include <sch_io/eagle/sch_io_eagle.h>
|
|
||||||
#include <sch_io/altium/sch_io_altium.h>
|
|
||||||
#include <sch_io/cadstar/sch_io_cadstar_archive.h>
|
|
||||||
#include <sch_io/easyeda/sch_io_easyeda.h>
|
|
||||||
#include <sch_io/easyedapro/sch_io_easyedapro.h>
|
|
||||||
#include <sch_io/database/sch_io_database.h>
|
|
||||||
#include <sch_io/ltspice/sch_io_ltspice.h>
|
|
||||||
-#include <sch_io/http_lib/sch_io_http_lib.h>
|
|
||||||
+#endif
|
|
||||||
#include <common.h> // for ExpandEnvVarSubstitutions
|
|
||||||
|
|
||||||
#include <wildcards_and_files_ext.h>
|
|
||||||
@@ -68,6 +73,8 @@ SCH_IO* SCH_IO_MGR::FindPlugin( SCH_FILE_T aFileType )
|
|
||||||
{
|
|
||||||
case SCH_KICAD: return new SCH_IO_KICAD_SEXPR();
|
|
||||||
case SCH_LEGACY: return new SCH_IO_KICAD_LEGACY();
|
|
||||||
+ case SCH_HTTP: return new SCH_IO_HTTP_LIB();
|
|
||||||
+#ifndef __EMSCRIPTEN__
|
|
||||||
case SCH_ALTIUM: return new SCH_IO_ALTIUM();
|
|
||||||
case SCH_CADSTAR_ARCHIVE: return new SCH_IO_CADSTAR_ARCHIVE();
|
|
||||||
case SCH_DATABASE: return new SCH_IO_DATABASE();
|
|
||||||
@@ -75,7 +82,7 @@ SCH_IO* SCH_IO_MGR::FindPlugin( SCH_FILE_T aFileType )
|
|
||||||
case SCH_EASYEDA: return new SCH_IO_EASYEDA();
|
|
||||||
case SCH_EASYEDAPRO: return new SCH_IO_EASYEDAPRO();
|
|
||||||
case SCH_LTSPICE: return new SCH_IO_LTSPICE();
|
|
||||||
- case SCH_HTTP: return new SCH_IO_HTTP_LIB();
|
|
||||||
+#endif
|
|
||||||
default: return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
diff --git a/eeschema/toolbars_sch_editor.cpp b/eeschema/toolbars_sch_editor.cpp
|
|
||||||
index 600f4379bc..a4df373475 100644
|
|
||||||
--- a/eeschema/toolbars_sch_editor.cpp
|
|
||||||
+++ b/eeschema/toolbars_sch_editor.cpp
|
|
||||||
@@ -32,7 +32,9 @@
|
|
||||||
#include <bitmaps.h>
|
|
||||||
#include <eeschema_id.h>
|
|
||||||
#include <pgm_base.h>
|
|
||||||
+#ifdef KICAD_SCRIPTING
|
|
||||||
#include <python_scripting.h>
|
|
||||||
+#endif
|
|
||||||
#include <tool/tool_manager.h>
|
|
||||||
#include <tool/action_toolbar.h>
|
|
||||||
#include <tools/sch_actions.h>
|
|
||||||
@@ -251,7 +253,11 @@ void SCH_EDIT_FRAME::configureToolbars()
|
|
||||||
[this]( ACTION_TOOLBAR* aToolbar )
|
|
||||||
{
|
|
||||||
// Add scripting console and API plugins
|
|
||||||
+ #ifdef KICAD_SCRIPTING
|
|
||||||
bool scriptingAvailable = SCRIPTING::IsWxAvailable();
|
|
||||||
+ #else
|
|
||||||
+ bool scriptingAvailable = false;
|
|
||||||
+ #endif
|
|
||||||
|
|
||||||
#ifdef KICAD_IPC_API
|
|
||||||
bool haveApiPlugins = Pgm().GetCommonSettings()->m_Api.enable_server &&
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -65,7 +65,7 @@ echo ""
|
||||||
echo "Running wasm-opt -O2 on the asyncified wasm..."
|
echo "Running wasm-opt -O2 on the asyncified wasm..."
|
||||||
echo " Purpose: shrink asyncify-instrumented functions back under V8's"
|
echo " Purpose: shrink asyncify-instrumented functions back under V8's"
|
||||||
echo " per-function locals limit (otherwise large coroutine-entry and"
|
echo " per-function locals limit (otherwise large coroutine-entry and"
|
||||||
echo " similar functions silently stall in Chrome's V8). See DEBUG.md §7"
|
echo " similar functions silently stall in Chrome's V8). See docs/debugging/DEBUG.md §7"
|
||||||
echo " and memory/bundle-size-asyncify-optimization.md."
|
echo " and memory/bundle-size-asyncify-optimization.md."
|
||||||
echo " This pass also takes several minutes and ~10-15 GB RAM."
|
echo " This pass also takes several minutes and ~10-15 GB RAM."
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ WASM tools (pcbnew / eeschema / calculator) by URL:
|
||||||
/p/<project>/<tool>/<file-path> e.g. /p/project5/pcbnew/nyak.kicad_pcb
|
/p/<project>/<tool>/<file-path> e.g. /p/project5/pcbnew/nyak.kicad_pcb
|
||||||
```
|
```
|
||||||
|
|
||||||
Design + decisions: [`../features/web-init/0001-web-app-spec.md`](../features/web-init/0001-web-app-spec.md).
|
Design + decisions: [`../docs/features/web-init/0001-web-app-spec.md`](../docs/features/web-init/0001-web-app-spec.md).
|
||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue