From bfe4ae8be9785918c0b6303a32653c568dd29209 Mon Sep 17 00:00:00 2001 From: Viktor Vaczi Date: Mon, 15 Jun 2026 11:32:12 +0200 Subject: [PATCH] Kicad fork cleanup docs. --- .../fork-cleanup/01-revert-dead-code.md | 88 ++++++++++++ .../features/fork-cleanup/02-cmake-dechurn.md | 112 +++++++++++++++ .../fork-cleanup/03-config-not-code.md | 81 +++++++++++ .../fork-cleanup/04-stub-tu-relocation.md | 77 ++++++++++ .../fork-cleanup/05-wx-layer-fixes.md | 81 +++++++++++ .../fork-cleanup/06-upstreamable-patches.md | 90 ++++++++++++ .../07-native-build-bugs-and-tooling.md | 83 +++++++++++ docs/features/fork-cleanup/08-importers.md | 76 ++++++++++ .../fork-cleanup/09-symbol-libraries.md | 67 +++++++++ docs/features/fork-cleanup/10-3d-viewer.md | 135 ++++++++++++++++++ .../fork-cleanup/11-ngspice-simulator.md | 63 ++++++++ .../features/fork-cleanup/12-network-stack.md | 80 +++++++++++ docs/features/fork-cleanup/README.md | 126 ++++++++++++++++ 13 files changed, 1159 insertions(+) create mode 100644 docs/features/fork-cleanup/01-revert-dead-code.md create mode 100644 docs/features/fork-cleanup/02-cmake-dechurn.md create mode 100644 docs/features/fork-cleanup/03-config-not-code.md create mode 100644 docs/features/fork-cleanup/04-stub-tu-relocation.md create mode 100644 docs/features/fork-cleanup/05-wx-layer-fixes.md create mode 100644 docs/features/fork-cleanup/06-upstreamable-patches.md create mode 100644 docs/features/fork-cleanup/07-native-build-bugs-and-tooling.md create mode 100644 docs/features/fork-cleanup/08-importers.md create mode 100644 docs/features/fork-cleanup/09-symbol-libraries.md create mode 100644 docs/features/fork-cleanup/10-3d-viewer.md create mode 100644 docs/features/fork-cleanup/11-ngspice-simulator.md create mode 100644 docs/features/fork-cleanup/12-network-stack.md create mode 100644 docs/features/fork-cleanup/README.md diff --git a/docs/features/fork-cleanup/01-revert-dead-code.md b/docs/features/fork-cleanup/01-revert-dead-code.md new file mode 100644 index 0000000..4ee3745 --- /dev/null +++ b/docs/features/fork-cleanup/01-revert-dead-code.md @@ -0,0 +1,88 @@ +# 01 — Revert dead code + +> ~480 lines of fork diff across ~22 upstream files compile **only in configurations the +> shipping build never uses**. Reverting them is pure subtraction: zero behavior change to +> the WASM product, and it removes the single largest tranche of divergence. + +## A. `#ifdef KICAD_IPC_API` gating — 18 files, ~115 lines + +**The gates are dead.** `scripts/kicad/build-kicad-target.sh:354` passes +`-DKICAD_IPC_API=ON` and links a wasm-built `libprotobuf.a` +(`-DProtobuf_LIBRARY=${SYSROOT}/lib/libprotobuf.a`). So every `#ifdef KICAD_IPC_API` +guard is always taken — the protobuf path always compiles. The OFF configuration these +gates nominally enable **cannot even link**: the class headers (`pad.h`, `zone.h`, +`footprint.h`, `pcb_track.h:292,401,751`, …) still declare +`void Serialize(google::protobuf::Any&) const override;` *unconditionally*, so compiling +out the definitions would leave undefined vtable symbols. Upstream confirms this is +deliberate — protobuf is "required even when the IPC API is not enabled" (dev-docs build +page), so the gating is not upstreamable either. + +Files to revert to pristine upstream (all from commit `e6a59ab94a`): + +``` +common/eda_shape.cpp common/eda_text.cpp common/netclass.cpp +include/api/api_utils.h pcbnew/api/api_pcb_utils.h pcbnew/board_connected_item.cpp +pcbnew/board_stackup_manager/board_stackup.cpp pcbnew/footprint.cpp +pcbnew/pad.cpp pcbnew/padstack.cpp pcbnew/pcb_dimension.cpp +pcbnew/pcb_field.cpp pcbnew/pcb_group.cpp pcbnew/pcb_shape.cpp +pcbnew/pcb_text.cpp pcbnew/pcb_textbox.cpp pcbnew/pcb_track.cpp +pcbnew/zone.cpp +``` + +Note an internal inconsistency that confirms the gates were never exercised: `netclass.cpp` +adds `#else` stub bodies for the vtable, but `eda_shape.cpp`/`eda_text.cpp` do not — an +actual `KICAD_IPC_API=OFF` build would fail to link regardless. **Just revert all 18.** + +## B. `include/gal/opengl/kiglew.h` — +189/−1, dead + +The +189-line `#if defined(__EMSCRIPTEN__)` block (GLEW stubs, `glewInit` no-op, +`glVertex2d`→`glVertex2f` wrappers, display-list / lighting no-ops) was added for the old +LEGACY_GL_EMULATION 3D-viewer path. But: + +- The OpenGL GAL is compiled only `if(NOT EMSCRIPTEN)` (since the WebGL GAL split). +- The live WebGL GAL uses its **own** new header, `include/gal/webgl/kiglew.h`. +- The 3D viewer is not built (`KICAD_BUILD_3D_VIEWER_WASM=OFF`). + +No wasm-compiled translation unit includes this header. The block only perturbs native +builds. **Revert.** If the 3D viewer comes back via WebGL2 (see [10](10-3d-viewer.md)) it +brings its own headers; if it ever needs a `GL/` shim, use an include-path-shadowed header +in a new dir — the pattern is already proven by `include/gal/webgl/kiglew.h` and the +existing `wasm/stubs/GL/`. + +## C. `common/gal/opengl/opengl_gal.cpp` — +32/−31, dead + +A `Connect(...)` → `Bind(...)` event-hookup refactor plus some `GAL::` qualifications, to +dodge a multiple-inheritance ambiguity that appeared during an early wasm compile. The file +is excluded from the Emscripten build (same OpenGL-GAL exclusion as B), so this now only +changes *native* behavior. **Revert** (or, if you like the modernization, upstream it — but +reverting is cleaner). + +## D. Diagnostic call sites — ~30 lines, debugging a solved problem + +The `KI_DIAG_GAL` / `KI_DIAG_COROUTINE` / `KI_DIAG_CTOR` macros (from +`include/kicad_wasm_diag.h`, a fork-only header gated behind `--diag` build flags) were +added to trace the Chrome Asyncify-rewind stall family — **which is solved** (see the +`chrome-asyncify-rewind-crash` memory; fixed systemically by `wasm-opt -O2` after +`--asyncify`). The call sites are marked "TEMPORARY … Remove once the crash is fixed": + +- `common/tool/tool_manager.cpp` +11 (`dispatchInternal` coroutine call) +- `common/draw_panel_gal.cpp` ~7 lines +- `pcbnew/pcb_edit_frame.cpp` 15 `KI_DIAG_CTOR` lines tracing ctor phases +- `common/confirm.cpp` +4 (unconditional `wxFprintf(stderr, ...)` before `ShowModal` — + also affects native; move to the wx wasm modal shim if logging is still wanted) + +**Delete the call sites now.** Keep the `include/kicad_wasm_diag.h` header itself (a new +file, costs nothing) for ad-hoc reinsertion — its only *in-upstream-file* users disappear +once the call sites go. + +## Net + +| Item | Files | ~Lines | Risk | +|---|---|---|---| +| IPC-API gates | 18 | 115 | none (dead) | +| `kiglew.h` | 1 | 190 | none (dead in wasm; reverts a native-only change) | +| `opengl_gal.cpp` | 1 | 63 | none (dead in wasm) | +| Diagnostics | ~3 | 30 | none | +| **Total** | **~23** | **~400** | reverts also undo 4 accidental native-build changes | + +This is the recommended first action — see the [sequencing](README.md#suggested-sequencing). diff --git a/docs/features/fork-cleanup/02-cmake-dechurn.md b/docs/features/fork-cleanup/02-cmake-dechurn.md new file mode 100644 index 0000000..07cea8e --- /dev/null +++ b/docs/features/fork-cleanup/02-cmake-dechurn.md @@ -0,0 +1,112 @@ +# 02 — CMake de-churn + +> The build-system diff is **~1,600 changed lines across 17 `CMakeLists.txt` files**, and +> **35–40% of it (~600 lines) is reindentation and duplication** — not logic. The logic +> that *is* there (single-binary kiface linking, stub injection, the WebGL GAL) is mostly +> legitimate; it's expressed in the most invasive way possible. This refactor keeps the +> behavior and cuts the touched-line count to roughly **120–180** inside upstream files. + +## The anti-patterns and their fixes + +### 1. Wrap-and-reindent → early-return guard + +`pcbnew/CMakeLists.txt`: the ~110-line Python-module install section was wrapped in +`if(KICAD_SCRIPTING) … endif()` **and reindented**, turning ~110 lines into ~215 diff +lines. The section runs to EOF, so a 3-line guard does the same job with ~5 diff lines: + +```cmake +if( NOT KICAD_SCRIPTING ) + return() +endif() +# ... upstream python-install body, unindented, byte-identical ... +``` + +`scripting/CMakeLists.txt` already does the minimal version of this (top-level `if(...)` / +bottom `endif()`, **no reindent**) — copy that discipline. Same treatment for the root +`CMakeLists.txt` Python/SWIG discovery section (~100 lines of pure reindent today). + +### 2. if/else source duplication → `list(REMOVE_ITEM)` + +The additive pattern is already proven in-tree for the ngspice BSIM data files +(`eeschema/CMakeLists.txt`): keep the upstream list byte-identical, then remove/replace +under `if(EMSCRIPTEN)`: + +```cmake +# upstream SRCS list stays exactly as-is, then: +if( EMSCRIPTEN ) + list( REMOVE_ITEM FOO_SRCS path/to/native_only.cpp ) + list( APPEND FOO_SRCS ${KICAD_WASM_LAYER}/stubs/foo_stub.cpp ) +endif() +``` + +Apply to: the OpenGL-GAL sources in `common/gal/CMakeLists.txt` (currently moved into an +`if(NOT EMSCRIPTEN)` block + reindented), the eeschema importer block, the +`common/CMakeLists.txt` exclusions (`python_scripting.cpp`, `api_utils.cpp`, altium, +database, webview), and the `PCBNEW_IO_LIBRARIES` altium dual-list in `pcbnew/CMakeLists.txt`. + +### 3. pcb_calculator: whole file forked → block + `return()` + +`pcb_calculator/CMakeLists.txt` (+130/−89) forks the *entire* file `if(EMSCRIPTEN) … else()` +with the upstream body reindented in the `else`. Instead, insert the wasm single-binary +block (duplicating only `make_lexer`) followed by `return()` *before* the upstream +`add_executable`, leaving the upstream body untouched. ~160 diff lines → ~55 additive. + +### 4. Shader calls: 85-line duplicate → redefine-the-function hook + +`common/gal/CMakeLists.txt` duplicates all 10 upstream `add_shader(...)` calls in an +`else()` branch for the ES3 variant. Instead, after upstream's `add_shader` definition, +`include()` a wasm `.cmake` that *redefines* `add_shader` to the ES3 path (later definition +wins) — upstream's 10 calls are then reused verbatim. + +### 5. `BUILD_KIWAY_DLL` source-property forks (5 apps) + +eeschema/pcbnew/gerbview/pl_editor/pcb_calculator each fork the `set_source_files_properties` +defs `if/else`. Replace with a single additive `if(EMSCRIPTEN)` block *after* the upstream +lines that re-sets the properties (last set wins). ~120 diff lines → ~35 additive. + +### 6. navlib ×3: probably deletable entirely + +`pcbnew/navlib`, `gerbview/navlib`, `pagelayout_editor/navlib` each fork their CMakeLists to +build a stub from `wasm/stubs/nl_*_plugin_stub.cpp` with the upstream body reindented in the +`else`. **But `eeschema/navlib` is unchanged** and builds its *real* sources under wasm +against the bundled `thirdparty/3dxware_sdk` stub. Strong evidence the other three stub +forks are unnecessary — try reverting all three CMakeLists and deleting the 3 stub `.cpp` +files. (This also fixes a native bug — see [07](07-native-build-bugs-and-tooling.md) on the +duplicate `add_subdirectory(navlib)`.) Fallback if a link fails: a 4-line early-return stub +`include()` at the top of each, with zero reindent. + +### 7. `kiapi` SHARED-vs-STATIC: variable, not duplicate list + +`api/CMakeLists.txt` (+14/−5) duplicates the source list across an +`add_library(kiapi SHARED)` / `STATIC` if/else. Collapse to `set(_KIAPI_TYPE ...)` + one +`add_library(kiapi ${_KIAPI_TYPE} ...)`. + +## Relocate the surviving `if(EMSCRIPTEN)` logic + +The legitimate blocks (static-kiface linking, stub `target_sources`, the `symbol_editor` +target, WebGL GAL sources) don't shrink in *content*, but they can move out of upstream +files into a new merge-conflict-free directory: + +- Create `kicad/cmake/wasm/*.cmake` and reduce each upstream `CMakeLists.txt` touch to a + 1–3 line `if(EMSCRIPTEN) include(wasm/foo.cmake) endif()` hook. +- Replace the hardcoded `${CMAKE_SOURCE_DIR}/../wasm` paths (which reach outside the + submodule and currently prevent it from configuring standalone) with a `KICAD_WASM_LAYER` + cache variable, defaulted to `../wasm` and passed by `build-kicad-target.sh`. + +## Mechanisms that were evaluated and rejected + +- **`CMAKE_PROJECT_INCLUDE` / `CMAKE_PROJECT_KICAD_INCLUDE`**: runs at `project()` time — + *before* upstream's `set(CMAKE_MODULE_PATH ...)` overwrite — so it can't even replace the + 1-line module-path fix, and can't reach subdirectory logic. Worth ~2% of the diff; skip. +- **Super-project `add_subdirectory(kicad)` wrapper**: infeasible. KiCad uses + `${CMAKE_SOURCE_DIR}` pervasively (incl. the `../wasm` reach-outs and install logic); it + would re-root to the super-project and break. + +## Net + +Moves 1–7 alone cut the build-system diff from ~1,600 to roughly **500–600** lines; adding +the `cmake/wasm/` relocation drops the lines touched *inside upstream files* to roughly +**120–180**. Irreducible in-place edits that remain: the `CMAKE_MODULE_PATH` preserve +(1 line), the wx-port regex `(msw|qt|gtk|osx)` → `(…|wasm)` (1 line), the `KICAD_SCRIPTING` +option, root `add_subdirectory` gates, `kiapi` genex gates in `common`, and the +`thirdparty/lemon` cross-compile block. diff --git a/docs/features/fork-cleanup/03-config-not-code.md b/docs/features/fork-cleanup/03-config-not-code.md new file mode 100644 index 0000000..1fb0b2c --- /dev/null +++ b/docs/features/fork-cleanup/03-config-not-code.md @@ -0,0 +1,81 @@ +# 03 — Config, not code + +> Several `#ifdef __EMSCRIPTEN__` patches re-implement, in C++, behavior that KiCad already +> exposes as a runtime setting or a CMake cache variable. Replacing the patch with a shipped +> config file (or a `-D`) gives identical behavior, removes the source edit, and keeps the +> knob user-changeable. + +## 1. Zoom-to-cursor → `input.center_on_zoom` + +**Patch today:** `common/view/wx_view_controls.cpp` (+9) and part of +`common/draw_panel_gal.cpp` force `m_warpCursor = false` under `__EMSCRIPTEN__` (browsers +can't warp the OS pointer, so center-on-zoom is wrong; zoom-to-cursor is correct). + +**But this is already a user preference.** `common/settings/common_settings.cpp:228` defines +`PARAM("input.center_on_zoom", …, true)` (the "Center and warp cursor on zoom" +checkbox in Mouse & Touchpad). The two lines the fork patched are *exactly* the consumption +sites: `wx_view_controls.cpp:172` and `draw_panel_gal.cpp:712`. The wheel handler already +implements point-under-cursor-stays-fixed when the pref is false. + +**Do instead:** seed the browser FS with a first-run `kicad_common.json` containing +`"input": { "center_on_zoom": false }`. Covers both sites, stays toggleable. Belt-and- +suspenders: the wasm kiplatform `WarpPointer()` should return `false` — callers +(`wx_view_controls.cpp:342,972`) already handle that gracefully, so even if a user re-enables +the pref it degrades, not breaks. Removes 2 source patches. + +## 2. Backspace-deletes → default `user.hotkeys` + +**Patch today:** `common/tool/actions.cpp` (+7) adds `DefaultHotkeyAlt(WXK_BACK)` to the +delete action under `__EMSCRIPTEN__` so Mac-browser users' Backspace deletes. + +**Do instead:** ship a default `user.hotkeys` file in the FS image mapping both +Delete and Backspace to the delete action — pure runtime config. + +> ⚠️ The companion change in `common/tool/action_manager.cpp` (+5) is **not** config — it's a +> genuine upstream bug fix (`m_defaultHotKeyAlt` is never applied, so `DefaultHotkeyAlt()` is +> dead upstream in default configs). That one belongs in [06](06-upstreamable-patches.md), +> and the default-`user.hotkeys` route only needs it for the *default*-alt path; an explicit +> user-hotkeys mapping works without it. + +## 3. Profile timer → `HAVE_CLOCK_GETTIME` + +**Patch today:** `libs/core/profile.cpp` (+9) adds an `#elif defined(__EMSCRIPTEN__)` +branch to `GetRunningMicroSecs()` using `emscripten_get_now()`. The existing upstream +branches gate on the configure-time macros `HAVE_CLOCK_GETTIME` / `HAVE_GETTIMEOFDAY_FUNC`. + +**Do instead:** Emscripten supports `clock_gettime`, so set `HAVE_CLOCK_GETTIME` in the wasm +CMake cache / toolchain and the existing branch compiles. Zero source change. + +## 4. Static KIFACE startup → call `OnKifaceStart` from the shell + +**Patch today:** `common/kiway.cpp` (+33) special-cases `KIWAY::KiFACE()` under +`__EMSCRIPTEN__`: it treats `m_kiface_version == 0` as "OnKifaceStart not yet called" and +either calls the statically-linked `KIFACE_GETTER` or backfills the version for a +pre-`set_kiface()`'d face, then calls `OnKifaceStart`. + +**Do instead:** the fork controls startup, so the wasm shell can call +`kiface->OnKifaceStart(&Pgm(), ctl, &kiway)` itself at its `set_kiface()` call site. Then +upstream's existing `if(m_kiface[aFaceId]) return m_kiface[aFaceId];` early-return path works +unmodified, and the single static `KIFACE_GETTER` fallback also lives in the shell. Removes +the 33-line in-source block. + +## 5. Keep the scripting member unconditional + +`include/pgm_base.h` (+2) wraps `std::unique_ptr m_python_scripting` in +`#ifdef KICAD_SCRIPTING`. A `unique_ptr` to a forward-declared type compiles without the +full header and is simply never populated when scripting is off — so the member can stay +unconditional and the header reverts. (The `.cpp` creation site in `common/pgm_base.cpp` +still needs its guard; that's part of the coherent scripting-optional patch in +[05](05-wx-layer-fixes.md)/[06](06-upstreamable-patches.md), not this doc.) + +## Net + +| Patch | Replacement | Removes | +|---|---|---| +| `wx_view_controls.cpp`, `draw_panel_gal.cpp` warpCursor | `kicad_common.json` default | 2 sites | +| `actions.cpp` Backspace | default `user.hotkeys` | 1 site (+ see 06) | +| `profile.cpp` timer | `HAVE_CLOCK_GETTIME` cache var | 1 file | +| `kiway.cpp` static KIFACE | `OnKifaceStart` from shell | 33 lines | +| `pgm_base.h` member guard | unconditional `unique_ptr` | 1 header | + +The config files live in the `wasm/` / `web/` layer — no upstream footprint. diff --git a/docs/features/fork-cleanup/04-stub-tu-relocation.md b/docs/features/fork-cleanup/04-stub-tu-relocation.md new file mode 100644 index 0000000..a128eea --- /dev/null +++ b/docs/features/fork-cleanup/04-stub-tu-relocation.md @@ -0,0 +1,77 @@ +# 04 — Relocate in-file stubs to new translation units + +> Some patches put a wasm `#else` implementation *inside* an upstream `.cpp`, doubling the +> file's divergence (the stub **plus** churn in the native half). The fix is the same in +> each case: select a separate translation unit by CMake (`list(REMOVE_ITEM)` + append, see +> [02](02-cmake-dechurn.md)), and the upstream file reverts to pristine. `wasm/stubs/` is +> already a mature home for exactly this. + +## 1. fontconfig — +154/−39, the biggest live source patch + +`common/font/fontconfig.cpp` is split `#if KICAD_USE_FONTCONFIG` (native) `#else` (wasm). +The wasm half is a self-contained stub: `Version()` returns `"WASM (no fontconfig)"`, +`FindFont`/`ListFonts` match against `aEmbeddedFiles` filenames + the built-in stroke font. +Fontconfig genuinely isn't built for wasm, so the stub is live — but it doesn't belong +inline. The patch also **deletes ~39 lines of comments from the native half** (gratuitous +churn). + +**Do:** move the `#else` body to a new TU (`common/font/fontconfig_wasm.cpp`, or +`wasm/stubs/`), select it by CMake, and restore the deleted comments. `include/font/fontconfig.h` +(+13/−22) only guards the `` include for the `.cpp`'s benefit — +moving that include into the `.cpp` is an upstreamable cleanup; private helper decls don't +need guarding (unused private non-virtuals need no definition). Header reverts to near-zero. + +> This is also the seam where **system-font support** could later be added: the non-fontconfig +> `FindFont`/`ListFonts` path is where the browser Local Font Access API would map in (wx +> fontenum already works per `tests/WHATWORKS.md`). Moderate effort, optional. + +## 2. libcontext — +333/−1 `.cpp`, +8/−1 header + +`thirdparty/libcontext/libcontext.cpp` gains a complete Emscripten-fiber backend +(`make_fcontext`/`jump_fcontext`/`release_fcontext` over `emscripten_fiber_*`) for a new +`LIBCONTEXT_PLATFORM_wasm32`. It's one self-contained `#if defined(LIBCONTEXT_PLATFORM_wasm32)` +region; the only edit to *pre-existing* code is wrapping the upstream no-op `release_fcontext` +in `#if !defined(...)` to avoid a duplicate definition. + +**Do:** move the wasm region to `thirdparty/libcontext/libcontext_emscripten.cpp` and have +`thirdparty/libcontext/CMakeLists.txt` *replace* (not append) the source under `if(EMSCRIPTEN)`. +Replacing makes `libcontext.cpp` byte-identical to upstream (everything else in it is +platform-guarded asm that compiles to nothing on wasm) and removes the `#if !defined` guard +need. The header's +8 platform branch becomes +`target_compile_definitions(libcontext PUBLIC LIBCONTEXT_PLATFORM_wasm32 LIBCONTEXT_COMPILER_gcc LIBCONTEXT_CALL_CONVENTION=)` — verified to propagate to the only includer +(`include/tool/coroutine.h`) via `common`'s PUBLIC link. (Keeping the 9-line additive header +hunk is also fine if you prefer; it's tiny.) + +## 3. SpaceMouse / navlib `#ifdef`s → throwing-ctor stubs + +The 3D-viewer SpaceMouse integration is gated with `#ifdef __EMSCRIPTEN__` in four files +(`3d-viewer/3d_viewer/eda_3d_viewer_frame.cpp`/`.h`, +`3d-viewer/dialogs/panel_preview_3d_model.cpp`/`.h`). Upstream already wraps the plugin +construction in `try/catch` and null-checks every use, so a stub whose constructor *throws* +(mirroring the existing `wasm/stubs/nl_pcbnew_plugin_stub.cpp`) reverts all four files. +Emscripten doesn't define `__linux__`, so upstream's `#else` branch already selects the +`NL_*` class — only the stub body is needed. + +> These four files only matter once the 3D viewer is built (see [10](10-3d-viewer.md)). Do +> this relocation as part of turning 3D on, not before. The CMake-level navlib stub forks +> (`nl_pcbnew/gerbview/pl_editor_plugin_stub.cpp`) are addressed in [02](02-cmake-dechurn.md) +> §6 — they may be deletable entirely. + +## What stays inline (deliberately) + +- **`thirdparty/thread-pool/bs_thread_pool.hpp` (+16)** — the run-inline-under-Asyncify + patch in `detach_task()`. The alternative (wrapping KiCad's `thread_pool` typedef) touches + 3 KiCad-owned files, and subclass-shadowing doesn't work because `detach_task` is + non-virtual and `submit_task` binds to it internally. The 16-line vendored patch is the + smaller, self-documenting option. Re-apply on each re-vendor. +- **`common/widgets/wx_progress_reporters.cpp` (+9)** — `updateUI()` returns `true` under + `__EMSCRIPTEN__` to skip `wxProgressDialog`'s nested event loop (which unwinds Asyncify + mid-load). The clean fix is making the wx wasm `wxProgressDialog::Update` non-pumping (see + the [async dossier](../async/README.md)); until then the 9-line guard is defensible. + +## Note on `exporter_vrml.cpp` + +The +64-line in-file VRML-export stub is **not** relocated here — under the new policy VRML +export comes *back* (it only needs the 3D model cache, no GL). Deleting the `#else` stub and +linking the real exporter is covered in [10](10-3d-viewer.md). That's a divergence +*reduction*, not a relocation. diff --git a/docs/features/fork-cleanup/05-wx-layer-fixes.md b/docs/features/fork-cleanup/05-wx-layer-fixes.md new file mode 100644 index 0000000..c098b3c --- /dev/null +++ b/docs/features/fork-cleanup/05-wx-layer-fixes.md @@ -0,0 +1,81 @@ +# 05 — Fix in the wxWidgets wasm port, not in KiCad + +> Several KiCad-side `#ifdef __EMSCRIPTEN__` patches work around quirks of the wxUniversal / +> wasm port. The project rule is to fix wasm-only bugs **in the wasm layer**. Moving these +> into the `wxwidgets` fork's wasm port removes the KiCad divergence — and in the tooltip +> case, *re-enables a whole class of UI*. The wx wasm port is the sanctioned place for this +> work, so these don't count against the "minimize upstream edits" goal the way KiCad edits do. + +## 1. HiDPI scale factor — currently changes native behavior + +`common/gal/hidpi_gl_canvas.cpp` (+2/−2) changes `GetScaleFactor()` from +`GetContentScaleFactor()` to `GetDPIScaleFactor()` **unconditionally** — and those two +differ on MSW, so this silently alters native Windows builds (see +[07](07-native-build-bugs-and-tooling.md)). Likewise `pcbnew/widgets/appearance_controls.cpp` +(+4/−1) swaps `GetScaleFactor()`→`GetContentScaleFactor()` in `GetBestSize()`. + +**Do:** implement `GetContentScaleFactor()` correctly (devicePixelRatio) in the wx wasm port +and revert both KiCad edits. + +## 2. `GetCurrentSelection()` on wxUniversal choices + +`common/eda_draw_frame.cpp` (+8), `pcbnew/dialogs/panel_setup_layers.cpp` (+2/−2), +`eeschema/dialogs/dialog_sim_command.cpp` (+1/−1) work around wxUniversal's +wxChoice/wxComboBox where `GetCurrentSelection()` / `IsEmpty()` misbehave, by substituting +`GetSelection()` / `GetCount()`. + +**Do:** make `GetCurrentSelection()` delegate to `GetSelection()` in the wxUniv/wasm layer — +removes all of these at once. (Some of these are also upstream-friendly as plain wx fixes; +see [06](06-upstreamable-patches.md).) + +## 3. Header self-sufficiency + +`include/gal/hidpi_gl_canvas.h` (+1) adds `#include ` before `wx/glcanvas.h` +because the wx wasm port's `glcanvas.h` isn't self-sufficient. + +**Do:** fix the include self-sufficiency in the wx wasm port's `glcanvas.h`; revert the +KiCad include. (Trivially upstreamable as header hygiene either way.) + +## 4. Tooltips — implement `wxToolTip` in the wasm port (this re-enables a feature) + +`wxUSE_TOOLTIPS` is **0** in the generated wasm setup.h because upstream wxWidgets configure +hard-disables tooltips for wxUniversal ("wxTooltip not supported yet in wxUniversal", +`wxwidgets/configure.in:7316`) — there is no `src/univ/tooltip.cpp`. KiCad carries ~12 files +of `wxUSE_TOOLTIPS` guards / `#ifndef __EMSCRIPTEN__ wxToolTip::Enable` to cope. + +**Do (porting task, 2–4 days, in the wx fork):** add `src/wasm/tooltip.cpp` + +`include/wx/wasm/tooltip.h` — a generic tooltip (hover timer + a `wxTipWindow`/`wxPopupWindow` +near the pointer; popups are already proven working per `tests/WHATWORKS.md`), patch the +configure gate for the wasm port, regen configure, wire into the makefiles. + +**Then, KiCad-side, this becomes pure deletion:** + +- Remove `#ifndef __EMSCRIPTEN__` around `wxToolTip::Enable(...)` in + `common/pgm_base.cpp`, `common/dialogs/dialog_design_block_properties.cpp`, + `eeschema/dialogs/dialog_sheet_properties.cpp`, `eeschema/dialogs/dialog_symbol_properties.cpp`. +- The `#if wxUSE_TOOLTIPS` guards (`include/widgets/wx_dataviewctrl.h`, + `common/dialogs/dialog_paste_special.cpp`, `common/widgets/wx_infobar.cpp`, + `pcbnew/dialogs/dialog_board_reannotate.cpp`, `include/pcb_base_frame.h`) auto-activate — + and can be dropped as fork diff, ~8 files reclaimed. + +Big UX win: every `SetToolTip(...)` in KiCad lights up. (The interim upstream-friendly move +is to re-spell the bare `#ifndef __EMSCRIPTEN__` guards as `#if wxUSE_TOOLTIPS` — see +[06](06-upstreamable-patches.md) — so they're correct regardless of when the wx work lands.) + +## 5. Other wx-port-shaped items + +- `common/pgm_base.cpp` `wxToolTip::Enable/SetAutoPop` under `#ifndef __EMSCRIPTEN__` — folds + into §4, or provide a no-op `wxToolTip` in the port. +- `common/tool/actions.cpp` Backspace hotkey — handled by config in [03](03-config-not-code.md). +- `wxUSE_FSWATCHER` (off): leave off — MEMFS has no change notification and nothing edits + files externally in a single-user browser. KiCad's `#if wxUSE_FSWATCHER` guards are + upstream-friendly as-is (see [06](06-upstreamable-patches.md)). + +## Net + +| KiCad edit | wx-port fix | Removes | +|---|---|---| +| `hidpi_gl_canvas.cpp`, `appearance_controls.cpp` | `GetContentScaleFactor()` | 2 files + a native bug | +| `eda_draw_frame.cpp`, `panel_setup_layers.cpp`, `dialog_sim_command.cpp` | `GetCurrentSelection()` delegate | 3 files | +| `hidpi_gl_canvas.h` | self-sufficient `glcanvas.h` | 1 header | +| ~12 tooltip-guard files | `src/wasm/tooltip.cpp` | ~12 files **+ enables tooltips** | diff --git a/docs/features/fork-cleanup/06-upstreamable-patches.md b/docs/features/fork-cleanup/06-upstreamable-patches.md new file mode 100644 index 0000000..a90c404 --- /dev/null +++ b/docs/features/fork-cleanup/06-upstreamable-patches.md @@ -0,0 +1,90 @@ +# 06 — Upstreamable patches + +> Some of the diff is genuine bug fixes or standard portability guards that upstream KiCad / +> wxWidgets would plausibly accept. Once merged upstream, the divergence **disappears on the +> next submodule bump** — net-zero, and it's the right thing to do regardless. This doc is +> the submit list. Trim fork-specific comments (e.g. references to `features/…` paths) before +> sending. + +## Submit-ready bug fixes + +### RTree `ELEMTYPEREAL` overflow — `libs/kimath/src/geometry/shape_poly_set.cpp` (+9/−1) + +`splitCollinearOutlines()` instantiated `RTree`. On wasm32 +`intptr_t` is 32-bit, but `ELEMTYPEREAL` holds `sumOfSquares` values ~10¹⁴ → overflow → the +`rtree.h:1771` Classify assert fires on **every PCB load**. Every other KiCad RTree +instantiation already uses `double`. The fix is `intptr_t` → `double` for the +`ELEMTYPEREAL` template arg. This is an upstream typo, not a wasm issue — submit as-is. +(Recorded in the `rtree-intptr-overflow-fix` memory.) + +### Default alternate hotkey never applied — `common/tool/action_manager.cpp` (+5) + +`processHotKey` never copies `m_defaultHotKeyAlt` → `m_hotKeyAlt`, so `DefaultHotkeyAlt()` is +dead in default configs upstream. One-line fix (`aAction->m_hotKeyAlt = aAction->m_defaultHotKeyAlt;`). +Submit as a standalone bug fix. (Note: this is the real fix behind the Backspace-delete +behavior; see [03](03-config-not-code.md) §2.) + +## Standard portability guards (no-ops for native builds) + +These re-spell wasm-driven conditionals using the wx feature-test macros upstream already +honors, so they're harmless on native and make the wasm build correct: + +- **`wxUSE_TOOLTIPS` guards** — `include/widgets/wx_dataviewctrl.h` (the + `DoSetToolTipText() override` only exists when the base virtual does), + `common/dialogs/dialog_paste_special.cpp`, `common/widgets/wx_infobar.cpp`, + `pcbnew/dialogs/dialog_board_reannotate.cpp`. Re-spell the bare `#ifndef __EMSCRIPTEN__` + variants (in the dialogs from [05](05-wx-layer-fixes.md) §4) to `#if wxUSE_TOOLTIPS` and + submit the lot. +- **`wxUSE_FSWATCHER` guards** — `include/pcb_base_frame.h`, `pcbnew/pcb_base_frame.cpp`, + `eeschema/sch_base_frame.cpp`/`.h` (watcher members + `setFPWatcher`/`OnFPChange` bodies). + Standard feature test; upstream builds compile unchanged. +- **ngspice header guard** — `common/build_version.cpp` (+3) shouldn't require + `` when SPICE is off; the version-print dispatch already has an + `"unknown"` fallback. (`#ifdef KICAD_SPICE` would be even cleaner.) + +## Output-correctness fixes + +- **UTF-8 s-expr output** — `common/io/kicad/kicad_io_utils.cpp` (+2/−2): + `Print("(%ls …)", aKey.wc_str())` → `Print("(%s …)", aKey.ToUTF8().data())`. `%ls` + misbehaves in the wasm libc printf path; UTF-8 is strictly more correct for the format. +- **IWYU includes** — `gerbview/events_called_functions.cpp`, `gerbview/toolbars_gerber.cpp`, + `gerbview/tools/gerbview_control.cpp` add `#include ` (transitively present on + native ports, missing on wasm). Pure include hygiene. +- **wxUniv choice fixes** — `eeschema/dialogs/dialog_sim_command.cpp` + (`IsEmpty()`→`GetCount()>0`), `pcbnew/dialogs/panel_setup_layers.cpp` + (`GetCurrentSelection()`→`GetSelection()`), `dialog_board_reannotate.cpp` + (`wxS(" ")`→`wxT(" ")` ternary-type fix). Correct on all ports. + +## Feature proposals (larger, medium prospects) + +- **Drawing-sheet item KIID** — the collab/yjs feature adds `KIID m_Uuid` to `DS_DATA_ITEM` + and an optional `(uuid …)` token in `.kicad_wks` (`common/drawing_sheet/drawing_sheet.keywords`, + `drawing_sheet_parser.cpp` +24, `ds_data_model_io.cpp` +4, `include/drawing_sheet/ds_data_item.h` + +4). This is a real file-format change — no runtime hook can substitute (there's no + drawing-sheet listener API upstream, and persistence is the point). But "give drawing-sheet + items stable identity like every other `EDA_ITEM`" is a defensible upstream proposal, and + the parser ignores-unknown so it's forward-compatible. If accepted, the format fork + vanishes. (Related: [10](10-3d-viewer.md) has no bearing here; the collab hook itself is in + `pl_editor_frame.cpp` and stays — see below.) +- **`python_scripting.h` header hygiene** — moving `#include ` out of the header + into the `.cpp` would dissolve the ~7 scripting include-gates across KiCad files + (see [`../../../features/python/research.md`](../../../features/python/research.md)). +- **Plugin self-registration** — moving the `REGISTER_PLUGIN` statics into each plugin's own + `.cpp` (the pattern `PCB_IO_MGR` half-uses) lets the importer exclusions in + [08](08-importers.md) be pure CMake instead of `#ifndef` blocks in `sch_io_mgr.cpp` / + `pcb_io_mgr.cpp`. + +## Not worth proposing + +- **Optional protobuf** — upstream explicitly keeps protobuf mandatory ("required even when + the IPC API is not enabled … likely to be used in other applications in the future"). It + doesn't matter anyway: our IPC-API gates are dead and get reverted ([01](01-revert-dead-code.md)). + +## The one collab hook that has no upstream home + +`pagelayout_editor/pl_editor_frame.cpp` (+15) calls `kicadCollabOnModify()` from +`OnModify()` under `__EMSCRIPTEN__`. There is **no** drawing-sheet listener/observer API +upstream (pcbnew uses `BOARD_LISTENER` with zero source hooks; eeschema has +`SCHEMATIC_LISTENER`; the drawing-sheet model has neither). So this hook genuinely stays — +though 11 of its 15 lines are comment and can be trimmed. The clean exit is proposing a +drawing-sheet `OnModify` notification upstream (low-medium prospect). diff --git a/docs/features/fork-cleanup/07-native-build-bugs-and-tooling.md b/docs/features/fork-cleanup/07-native-build-bugs-and-tooling.md new file mode 100644 index 0000000..ae76d4c --- /dev/null +++ b/docs/features/fork-cleanup/07-native-build-bugs-and-tooling.md @@ -0,0 +1,83 @@ +# 07 — Native-build regressions & tooling + +> While shrinking the diff, the research surfaced **six places where the fork silently +> changed *native* (non-wasm) build behavior** — these are bugs regardless of the cleanup, +> worth fixing on their own. Plus: three dead build-script flags and a diff-stat tool that +> lies about how far the fork has diverged. + +## Native-build regressions + +### 1. `COMMON_WIDGETS_SRCS` typo — drops a source from *all* builds + +`common/CMakeLists.txt` appends the webview gating to a variable named `COMMON_WIDGETS_SRCS`, +but the real list is `COMMON_WIDGET_SRCS` (singular). Result: `widgets/webview_panel.cpp` is +silently dropped from **every** build, including native. Fix the name (and keep the wasm +exclusion conditional). + +### 2. Duplicate `add_subdirectory(navlib)` — breaks native macOS/Windows configure + +`pcbnew/CMakeLists.txt` adds an **unconditional** `add_subdirectory(navlib)` near the top and +puts `pcbnew_navlib` in `PCBNEW_KIFACE_LIBRARIES`. But upstream `common/CMakeLists.txt` +(unchanged, ~line 1074) already does `add_subdirectory(../pcbnew/navlib ./navlib)` under +`if(APPLE OR NOT UNIX)` → duplicate `pcbnew_navlib` target → configure failure on native +macOS/Windows. Make the fork's addition `if(EMSCRIPTEN)`-only (and revisit the stub forks per +[02](02-cmake-dechurn.md) §6). + +### 3. HiDPI scale factor changed for native — `hidpi_gl_canvas.cpp` + +`GetScaleFactor()` switched from `GetContentScaleFactor()` to `GetDPIScaleFactor()` +*unconditionally*; these differ on MSW, so native Windows rendering is altered. Fix in the wx +wasm port and revert ([05](05-wx-layer-fixes.md) §1). + +### 4. Toolbar customization accidentally disabled on wasm — `pcbnew/pcbnew.cpp` + +The `#ifndef __EMSCRIPTEN__` block that gates the 3D settings panels also swallows +`PANEL_TOOLBAR_CUSTOMIZATION`, which is *not* a 3D panel — so toolbar customization settings +are silently off on wasm. Re-scope the gate to the 3D panels only. (Goes away anyway when 3D +is re-enabled — see [10](10-3d-viewer.md).) + +### 5. `KICAD_IPC_API` default flipped — affects native + +Root `CMakeLists.txt` flips the `KICAD_IPC_API` option default ON→OFF. The wasm build passes +`=ON` explicitly, so this only changes *native* behavior. Revert the default. + +### 6. `pl_editor` OBJECT-library restructure — all platforms + +`pagelayout_editor/CMakeLists.txt` introduces a `pl_editor_kiface_objects` OBJECT library and +leaves `add_library(pl_editor_kiface MODULE)` empty, for **all** platforms (it works via +object-lib propagation, but it's an unconditional restructure of upstream build shape). Make +it `EMSCRIPTEN`-only or upstream-shaped. + +## Dead build-script flags + +`scripts/kicad/build-kicad-target.sh` passes flags that **are not options in this KiCad +version** — they configure nothing and mislead: + +- `KICAD_SPICE=OFF` — no such option (only `KICAD_SPICE_QA` exists). The simulator is always + built; the real disable is the `Findngspice.cmake` stub ([11](11-ngspice-simulator.md)). +- `KICAD_PCM=OFF` — no such option. PCM is an unconditional library linked only into the + `kicad` / `kicad-cli` targets, which aren't built for wasm. +- `BUILD_GITHUB_PLUGIN=OFF` — no such option. + +Remove all three so the script reflects reality. + +## `kicad-diff-stats.sh` mis-detects the fork point + +`scripts/kicad-diff-stats.sh` reports **0 fork commits** because it finds the fork point by +walking `git log` and taking the first commit whose author email isn't in an allowlist +(`OUR_AUTHORS` = only `viktor.vaczi@…` + `noreply@anthropic.com`). The submodule HEAD is +authored by `balint.ipkovich@…`, so the loop terminates on iteration 1 and calls HEAD itself +"upstream." The real fork history `4bfed3f174..HEAD` has **24 commits by 4 authors** (viktor +×14, torcsvari.gergo ×5, balint ×4, matejcsok-ee ×1) — three of four are missing from the +allowlist. + +**Fix (any one):** + +- Pin the fork-base SHA (`4bfed3f174`) in a script variable / file; or +- Use the `upstream` remote that already exists: `git merge-base HEAD upstream/master`; or +- Match the *upstream* side (KiCad maintainer commit domains) instead of enumerating "our" + people. + +The author-allowlist heuristic is inherently rot-prone; the merge-base approach is robust. +(Secondary: the `AUTHOR_PATTERN` grep-building block is dead code — the loop does exact +string matching.) diff --git a/docs/features/fork-cleanup/08-importers.md b/docs/features/fork-cleanup/08-importers.md new file mode 100644 index 0000000..3e4436b --- /dev/null +++ b/docs/features/fork-cleanup/08-importers.md @@ -0,0 +1,76 @@ +# 08 — Re-enable file-format importers + +> **Verdict: re-enable now.** The exclusions are stale or incidental, not technical. The +> Altium blocker was fixed months ago (the fix is already force-included into every TU), and +> the other five eeschema importers were cut for "MVP scoping" while their parsers compile +> for wasm anyway. Re-enabling is **net-negative diff** — it deletes fork gates. Configure- +> confident but runtime-untested: smoke-test each with a sample file before declaring done. + +## Altium (eeschema + pcbnew) — the blocker is already fixed + +**The recorded blocker was real:** `thirdparty/compoundfilereader/compoundfilereader.h:264` +does `typedef std::basic_string utf16string;`, and the libc++ shipped in Emscripten +4.0.2 removed the generic `std::char_traits` base template (deprecated LLVM 18, removed LLVM +19 via llvm/llvm-project#72694 — the same break hit nlohmann/json #4490, qpdf #1024). Plain +`std::basic_string` fails to compile with +`implicit instantiation of undefined template 'std::char_traits'`. + +**The fix already exists and is already active.** `wasm/stubs/char_traits_uint16_workaround.h` +defines `std::char_traits` and is **force-included into every KiCad TU** via +`-include` in `CMAKE_CXX_FLAGS` (`scripts/kicad/build-kicad-target.sh:343`). A standalone +compile test with the project's own emsdk confirms `compoundfilereader.h` compiles clean with +the specialization in scope. (Upstream KiCad hit the identical bug on Apple clang ≥17 and +ships the same specialization gated `#ifdef __APPLE__` in the vendored header.) No other +`basic_string` use exists anywhere in the tree outside this header. + +So the Altium exclusions are simply stale. **Remove these fork-added gates** (each removal +*reduces* the diff): + +``` +common/CMakeLists.txt # io/altium 4 files gated NOT EMSCRIPTEN +eeschema/CMakeLists.txt # importer block; altium lines +eeschema/sch_io/sch_io_mgr.cpp # #ifndef __EMSCRIPTEN__ includes + factory cases +pcbnew/CMakeLists.txt # add_subdirectory(pcb_io/altium) gate + PCBNEW_IO_LIBRARIES dual list +pcbnew/pcb_io/pcb_io_mgr.cpp # #ifndef blocks (CircuitMaker/Studio/Designer + Solidworks) +``` + +`pcb_io/altium`'s deps (pcbcommon, compoundfilereader, magic_enum) are all already built for +wasm. One root cause fixes both the eeschema and pcbnew sides. + +## Eagle / CADSTAR / LTspice / EasyEDA / EasyEDA-Pro (eeschema) — incidental MVP cut + +`eeschema/CMakeLists.txt` excludes these in an `if(NOT EMSCRIPTEN)` block with the comment +"cross-tool importers we don't need for the MVP and they bring transitive incompatible +dependencies in." The dependency half is **false today**, and there's strong evidence the +cut was incidental: + +- All their shared parsers already compile **unconditionally** in `common/CMakeLists.txt` + (`io/cadstar/cadstar_archive_parser.cpp`, `io/eagle/eagle_parser.cpp`, `io/easyeda/*`, + `io/easyedapro/*`). +- **pcbnew's wasm build already ships the equivalents** — CADSTAR, EasyEDA, EasyEDA-Pro, + Fabmaster, IPC-2581, ODB++, P-CAD (`pcbnew/CMakeLists.txt`), plus eagle + geda compiled + straight into pcbcommon. Only Altium was excluded on the pcbnew side. If pcbnew builds them + but eeschema doesn't, the eeschema exclusion is incidental. +- Deps are all present: expat (eagle XML), nlohmann (easyeda), wxZip/zlib (easyedapro), plain + text (ltspice, cadstar). + +**Do:** move the five importer groups out of the `if(NOT EMSCRIPTEN)` block in +`eeschema/CMakeLists.txt` and drop the matching `#ifndef __EMSCRIPTEN__` cases in +`sch_io_mgr.cpp`. (Altium-sch additionally needs the Altium common code from the section +above.) Net-negative fork diff; no platform deps. + +## Stays impossible / policy + +- **Database (`SCH_IO_DATABASE`)** — needs nanodbc → ODBC driver manager → native drivers + opening TCP to a DB server. No browser path; keep stubbed. (`database_lib_settings.cpp` + still compiles for settings-file compatibility — fine.) +- **HTTP library (`SCH_IO_HTTP_LIB`)** — already compiled and *registered*, but dead at + runtime because the curl stub returns NULL. Reviving it is the curl→fetch shim in + [12](12-network-stack.md), not part of this refactor. + +## Verification + +Build, then import: an Eagle `.sch`, an Altium `.SchDoc`/`.PcbDoc`, and one of +CADSTAR/EasyEDA. Confirm the importer appears in the File → Import menu and round-trips +geometry. The import dialogs use `wxFileDialog` (PARTIAL per `tests/WHATWORKS.md`), so verify +the file-open flow too. diff --git a/docs/features/fork-cleanup/09-symbol-libraries.md b/docs/features/fork-cleanup/09-symbol-libraries.md new file mode 100644 index 0000000..271813f --- /dev/null +++ b/docs/features/fork-cleanup/09-symbol-libraries.md @@ -0,0 +1,67 @@ +# 09 — Re-enable the symbol chooser & viewer + +> **The chooser and viewer aren't broken — the browser FS just ships no libraries.** The +> `#ifdef __EMSCRIPTEN__ return nullptr` gates in `eeschema.cpp` are *asset-availability* +> gates, not code gates. Un-gating is ~10 lines deleted; the real work is a small library- +> provisioning pipeline in the `web/` layer (no upstream footprint). + +## What's actually gated + +`eeschema/eeschema.cpp` returns `nullptr` for `FRAME_SCH_VIEWER` ("symbol viewer is not +supported") and `FRAME_SYMBOL_CHOOSER` (comment: "no bundled libs"). Both frames' sources +**are compiled** (`eeschema/CMakeLists.txt`). Callers currently hitting the nullptr: +`dialog_change_symbols.cpp`, `grid_text_button_helpers.cpp` (symbol-browse buttons silently +no-op), `sch_edit_frame.cpp`. + +## Evidence the frames work + +- `features/yjs-bridge/0007-eeschema-essential-ops-findings.md` records the in-process symbol + **chooser dialog already opening and working** in wasm ("symbol chooser now center with OK + reachable") — it just shows "0 items loaded" because `/usr/share/kicad/symbols` is absent. +- Every UI dependency is proven working in wxUniv-wasm (`tests/WHATWORKS.md`): the + `wxDataViewCtrl` LIB_TREE, the `wxHtmlWindow` details pane, the GAL preview canvas. The + **symbol editor** — same LIB_TREE + preview machinery — is already a shipped, ported target. +- pcbnew's footprint browse/choose paths are **already un-gated**; they start working the + moment `fp-lib-table` has content. + +## Why the FS is empty today + +The boot path (`web/standalone/src/wasm/boot.ts`, mirrored by the test harness) ships only +`images.tar.gz` (UI bitmaps) and seeds **empty** `sym-lib-table` / `fp-lib-table` / +`design-block-lib-table`, plus `kicad_common.json` flags that suppress the first-run +StartWizard (its modal loop crashes Asyncify — see the [async dossier](../async/README.md)). +No symbol libraries, footprint libraries, 3D models, or templates are packaged. So un-gating +*alone* yields an empty tree — correct behavior, just not useful. + +> Background: designs embed the parts they use, so *opening an existing file* never needed the +> libraries (see [`../libraries/0001-library-management.md`](../libraries/0001-library-management.md)). +> The chooser/viewer are about *placing new* parts — that's what needs shipped libraries. + +## Recipe + +1. **Un-gate** (≈ −10 lines fork diff): delete the two `#ifdef __EMSCRIPTEN__` blocks in + `eeschema/eeschema.cpp`. +2. **Provision a starter library set** (web/wasm layer only, no upstream files): + - Package a curated subset of `kicad-symbols` (e.g. Device, power, Connector, Switch, …) + as an assets tarball, fetched and written into `/usr/share/kicad/symbols` in `preRun` + (same mechanism as `images.tar.gz`). Optionally a `.pretty` footprint subset into + `/usr/share/kicad/footprints`. + - Write **real** `sym-lib-table` / `fp-lib-table` rows in `boot.ts` instead of the empty + seeds. + - For the full official set (~200 MB-class), prefer **lazy per-library fetch** into MEMFS + on first reference rather than a giant preload — no engine obstacle, just engineering. + +The repo doesn't currently vendor `kicad-symbols` (only `kicad/demos/*` and +`qa/data/libraries/power.kicad_sym`), so step 2 introduces a new asset source — decide +whether to vendor a curated subset or fetch from a CDN. + +## Net + +| Action | Layer | Diff impact | +|---|---|---| +| Delete viewer/chooser gates | `eeschema.cpp` | −10 lines | +| Library asset tarball + lib-table seeding | `web/standalone/` | new web assets only | +| Lazy per-lib MEMFS fetch (optional, for full set) | `web/`/`wasm/` | new code, no upstream | + +Effort: gates are minutes; a usable curated-library bundle is ~1–2 days; full lazy-fetch is +incremental on top. diff --git a/docs/features/fork-cleanup/10-3d-viewer.md b/docs/features/fork-cleanup/10-3d-viewer.md new file mode 100644 index 0000000..ab177c6 --- /dev/null +++ b/docs/features/fork-cleanup/10-3d-viewer.md @@ -0,0 +1,135 @@ +# 10 — Re-enable the 3D viewer + +> **Verdict: port — and there's a cheap first step.** KiCad ships *two* 3D renderers: an +> OpenGL one (pure fixed-function GL 1.x — far from WebGL2) and a **pure-CPU raytracer that +> needs zero OpenGL**. The fork currently nulls the raytracer out. Turning 3D on via the +> raytracer (**Route C**) is low-risk and brings the render-to-PNG job and VRML export back +> nearly free; a WebGL2 port of the interactive renderer (**Route B**) is the follow-up. + +## Why it's off (timeline, with primary sources) + +| Date | Commit (kicad) | What | +|---|---|---| +| 2025-12-21 | `46bb16e86b` | "Enable 3D viewer build for WASM with OpenGL stubs" — compiled *only* so headers resolve. Message: "OpenGL rendering won't work at runtime … avoids complex stubbing." | +| 2026-01-03 | `a61d2ea406` | OpenGL GAL on `-sLEGACY_GL_EMULATION`; added the +190-line `kiglew.h` `__EMSCRIPTEN__` branch (display lists + lighting as **no-ops**). 3D "built" but couldn't draw. | +| 2026-01-10 | `d8a9ac4304` | "Disable 3D viewer functionality for WASM builds" — same day `WEBGL_GAL` landed and the build swapped emulation → `-sFULL_ES3`. Message: 3D "require[s] native OpenGL rendering (not WebGL)". Built with `KICAD_BUILD_3D_VIEWER_WASM=OFF`. | +| 2026-03-20 | `4ddb9b47f5` | "Migrate to pure WebGL 2.0 without FULL_ES3 emulation" — 2D GAL goes pure WebGL2; the only GL link flag today is `-sMAX_WEBGL_VERSION=2`. | + +Recorded rationale: `features/gl-article/README.md:274-276,421-422` — the 3D viewer is "a +separate legacy-GL renderer, not GAL" needing "GLU + fixed-function", declared out of scope +when the strategy became "progressively remove the Emscripten crutch." So it was a deliberate +scoping decision when the emulation layer it depended on was removed — not a crash. + +## What the OpenGL renderer actually uses + +`3d-viewer/3d_rendering/opengl/` = 6 files, **4,555 lines**. It's pure GL 1.x fixed-function: + +- **Immediate mode** — 153 tokens (`glBegin`/`glEnd` ×28, `glVertex*`/`glNormal3*`/`glTexCoord2*`). +- **Display lists** — 36 refs in 2 files, and they're the **primary board-geometry path**: + `layer_triangles.cpp:582-714` bakes every layer's triangles into `glGenLists`/`glNewList` + wrapping a `glDrawArrays`; `render_3d_opengl.cpp:1303-1315` for the grid. +- **Fixed-function matrix/lighting** — ~117 tokens (`glMaterialfv`, `glLightfv`, + `glMatrixMode`/`glPush/PopMatrix`, `GL_LIGHTING`, …). +- **Client-state arrays** — 49 tokens. **GLU** — 36 (`gluNewQuadric`/`gluCylinder`/`gluSphere` + for vias/pads). +- **Modern** — zero shaders, zero VAOs; only `3d_model.cpp` uses VBOs (GL 1.5 style). + +Structurally friendlier than the raw counts suggest: geometry already lives in CPU triangle +containers, display lists are thin `glDrawArrays` wrappers, `3d_model.cpp` is already VBO, +camera/transforms are already glm. + +## Route A — LEGACY_GL_EMULATION for the 3D viewer: REJECTED + +- The flag is **module-global at link**: `GLEmulation.init()` replaces the module-wide JS + bindings (`glDrawArrays`, `glEnable`, `glBindBuffer`, `glGetString`, …) for *all* contexts, + including the flagship 2D `WEBGL_GAL`'s. Concretely it corrupts the GAL's ES3 shaders — + emscripten prepends `#extension GL_OES_standard_derivatives` to any fragment shader using + `dFdx` (`kicad_frag.glsl:138` does), and prepending anything before `#version 300 es` is a + guaranteed ES3 compile error. Separate canvases/contexts do **not** isolate this (hooks are + per-module). +- Even ignoring that: emscripten never implemented display lists (issue #688 — the main board + path draws nothing), GLU quadrics are absent, lighting emulation has `throw 'TODO'` holes. + You'd rewrite the display-list and quadric paths anyway (the hard half of Route B) while + regressing 2D and re-adding ~200 KB of the fragility the project already escaped. **Reject.** + +## Route C — CPU raytracer + blit (do this first, low risk) + +`RENDER_3D_RAYTRACE_RAM` (`render_3d_raytrace_ram.cpp`, 159 lines) is **100% GL-free**: it +renders into a plain `uint8_t` RGBA buffer (`initPbo()` is just `new uint8_t[w*h*4]`) and +exposes `GetBuffer()`. It's **progressive by design** — each `Redraw()` traces blocks up to a +400/750 ms budget then yields, so a serial browser loop still animates. Both existing +consumers already convert buffer → `wxImage` with no GL (`eda_3d_viewer_frame.cpp:846-866` +screenshot; `pcbnew_jobs_handler.cpp:817-885` render job). + +Recipe: + +1. `KICAD_BUILD_3D_VIEWER_WASM=ON`, and **genex-out the GL renderer TUs** + (`render_3d_opengl.cpp`, `layer_triangles.cpp`, …) the same way `render_3d_raytrace_gl.cpp` + is already excluded (`3d-viewer/CMakeLists.txt:49`) — this sidesteps the legacy-GL link + errors entirely. The raytracing subtree (12,232 lines) has no GL. +2. Have `EDA_3D_CANVAS` instantiate `RENDER_3D_RAYTRACE_RAM` under `__EMSCRIPTEN__` (it's + renderer-agnostic via `RENDER_3D_BASE`), and **blit** the buffer — either a plain wxWindow + painting the `wxImage` through the wx-wasm 2D canvas DC (zero GL), or a ~100-line WebGL2 + textured quad (infra exists in `common/gal/webgl/fullscreen_quad.cpp`). +3. **Delete the fork gates** — they're keyed on `__EMSCRIPTEN__`, *not* the CMake option, so + flipping the flag alone isn't enough: `eda_3d_canvas.cpp` (raytracer nulled, raytracing + request early-returned), `pcbnew/pcbnew.cpp` (3D settings panels — see the + `PANEL_TOOLBAR_CUSTOMIZATION` bug in [07](07-native-build-bugs-and-tooling.md)), + `pcbnew_jobs_handler.cpp`/`.h` (render job), and `exporter_vrml.cpp` (in-file stub). Delete + the three big stub TUs too (`wasm/stubs/3d_viewer_stub.cpp`, `3d_canvas_stub.cpp`, + `3d_scenegraph_stub.cpp`, ~1,200 lines). + +Caveats: main tracing runs **serial** under the `bs_thread_pool` inline patch (still usable, +because progressive); the preview / SSAO / DLAA passes use raw `std::thread` + a `sleep_for` +spin-wait — pthreads *are* enabled in the build (`-pthread -sUSE_PTHREADS=1 +-sPTHREAD_POOL_SIZE=…`), so they run, but the spin-wait blocks the browser main thread per +pass and is worth reviewing for jank. + +## Route B — WebGL2 port of the interactive renderer (follow-up) + +Scope is ~4,555 lines / 6 files — roughly **1/5 of the 2D GAL port** and far less novel (no +compositor, no multi-FBO juggling). The work: display-list → VBO (mechanical, the lists wrap +`glDrawArrays` of CPU triangle containers), client-state arrays → generic vertex attribs, +fixed-function transforms → a small matrix-stack helper (already glm), ~2 shader pairs +(Phong-lit per-material + flat), GLU quadrics → a triangle helper (small; vias/pads/gizmo). +Reuse `common/gal/webgl/`'s shader infra + `convert_glsl_es3.py`. + +Divergence: mirror the 2D precedent — new files (`3d_rendering/webgl/`), swapped via a CMake +genex like the existing `render_3d_raytrace_gl.cpp` exclusion. Near-zero upstream-file diff +beyond the swap + un-gating. Gives the **interactive** GPU-speed orbit/pan view. Risk: medium +(GPU correctness; the e2e screenshot harness pattern exists). Prereq: model loading (below). + +## Model loading (needed for both B and C to show components) + +Upstream loads `plugins/3d/{vrml,oce,idf}` as **runtime DSOs** — +`S3D_PLUGIN_MANAGER::loadPlugins()` scans a directory and `wxDynamicLibrary`-loads each. There +is **no static-registration path upstream**, and `wasm/stubs/3d_scenegraph_stub.cpp` (440 +lines) currently stubs the whole `S3D_CACHE` + scenegraph + `S3D::WriteVRML`. + +Static linking entails: compiling the plugin sources into the binary with **per-plugin symbol +prefixes** (they export identical C ABI names — `Load`, `GetFileFilter`, … — so they collide; +wrap each TU with `-Dname=vrml_name` or a wrapper), plus a small `__EMSCRIPTEN__` path (or a +subclassed `KICAD_PLUGIN_LDR_3D`) with a 3-entry static registry — cleanest as one new +wasm-layer file. Sizes: **vrml** (VRML1/2 + X3D, fully in-tree, **no external deps**) = 17,835 +lines; **oce** (STEP/IGES — needs OCC, which **is already built for wasm**: OCC 7.8 via +`--with-occ`, verified `libTK*.a` in the sysroot) = 1,407; **idf** = 931. `kicad_3dsg` must +become STATIC for wasm (it's `add_library(SHARED)` today). Plus: ship 3D model assets to the +FS (models are *referenced*, not embedded — see +[`../libraries/0001-library-management.md`](../libraries/0001-library-management.md)). + +Start with the VRML plugin (no deps) for `.wrl` models; add OCE for STEP once VRML works. + +## Render job & VRML export — free with either route + +Both only need the 3D model cache / scenegraph / raytracer, **not** the GL renderer: + +- `JobExportRender` (`pcbnew_jobs_handler.cpp:641-898`) is `RENDER_3D_RAYTRACE_RAM` + + `BOARD_ADAPTER` + 3d_cache → board-render-to-PNG/JPEG works as soon as 3d-viewer links. +- `exporter_vrml.cpp`'s real implementation needs `S3D_CACHE` + the SGNODE/IFSG API — un-stub + by **deleting** the fork's `#else` block (a divergence *reduction*). Board-only export works + immediately; embedding component models additionally needs the static VRML plugin above. + +## Sequencing + +**C first** (working 3D view + render job + VRML export, low risk, mostly deletes gates) → +**B second** (interactive upgrade) → **A never**. diff --git a/docs/features/fork-cleanup/11-ngspice-simulator.md b/docs/features/fork-cleanup/11-ngspice-simulator.md new file mode 100644 index 0000000..e94284b --- /dev/null +++ b/docs/features/fork-cleanup/11-ngspice-simulator.md @@ -0,0 +1,63 @@ +# 11 — Re-enable the SPICE simulator + +> **Verdict: port, ~1–2 weeks.** The groundwork is half-done in-repo: a wasm ngspice build +> script already exists, the simulator sources already compile, and the only unavoidable +> upstream-file edit is a ~25-line block swapping `wxDynamicLibrary` for direct static +> symbols. Multiple working emscripten ngspice ports exist as precedent. + +## Current state (more nuanced than "KICAD_SPICE=OFF") + +- `-DKICAD_SPICE=OFF` in `build-kicad-target.sh` is **vestigial** — there's no such option in + this KiCad (only `KICAD_SPICE_QA`). The simulator is *always* built; all `eeschema/sim/` + sources compile for wasm. +- The dep is satisfied by a header-only stub: `wasm/cmake/Findngspice.cmake` points includes + at `wasm/stubs/ngspice/sharedspice.h` with an empty link line. +- **Runtime kill-point:** `SIMULATOR_FRAME` ctor → `NGSPICE` init → `ngspice.cpp:474` + `m_dll.Load(wxDynamicLibrary::CanonicalizeName("ngspice"))` fails (no `dlopen` without + `-sMAIN_MODULE`) → throws → caught at `eeschema/eeschema.cpp:208-221` → simulator silently + returns `nullptr`. + +## The groundwork already here + +- **A wasm ngspice build script exists:** `scripts/deps/build-ngspice.sh` (ngspice 45.2, + static, cider + xspice, pthread; opt-in via `build-all-deps.sh --with-ngspice`). +- **A known size landmine is already handled:** the 4 giant model-data initializers + (`sim_model_ngspice_data_{bsim4,b3soi,b4soi,hsim}.cpp`) exceeded the JS engines' "too many + locals" validation limit and are stubbed (`eeschema/CMakeLists.txt:292-305` + + `wasm/stubs/eeschema_ngspice_data_stubs.cpp`). + +## Why static linking works + +KiCad resolves 9–11 ngspice function pointers via `GetSymbol` (`ngspice.cpp:503-517`) and +registers C callbacks (`cbSendChar`, `cbBGThreadRunning`, … `ngspice.cpp:519`). With a +**statically linked** `libngspice.a`, those become plain function pointers and direct symbol +references — no `dlopen` needed. Threading: KiCad drives sims via `bg_run`/`bg_halt` +(`ngspice.cpp:335,342`) using ngspice's internal pthread; the wasm build is already +full-pthread, and native code already assumes callbacks fire on the bg thread — same model. + +Precedent (all static, OpenMP off, shared-lib mode unsupported under emscripten): +wokwi/ngspice-wasm, EEcircuit (eelab-dev), danchitnis/ngspice, plus upstream ngspice WASM +patches (#96, #99). + +## Recipe + +1. Verify `scripts/deps/build-ngspice.sh` completes for 45.2. +2. Make `wasm/cmake/Findngspice.cmake` return the **real** sysroot lib + headers when present + (fall back to the stub otherwise). +3. **The one unavoidable upstream-file edit:** a `#ifdef __EMSCRIPTEN__` branch in + `eeschema/sim/ngspice.cpp` `init_dll()` that binds the `m_ngSpice_*` pointers directly to + the statically-linked symbols instead of via `wxDynamicLibrary` (~25 lines, one block). +4. Ship `spinit` / `.cm` codemodels into MEMFS if xspice device models are wanted. +5. **Retest the model-data stubs** — the "too many locals" failure predates the now-default + post-asyncify `wasm-opt -O2` pass that solved the same engine-limit family elsewhere (see + the `chrome-asyncify-rewind-crash` memory). If it's resolved, restore the 4 real model-data + files; if not, the simulator works minus BSIM4/SOI/HSIM device models. + +## Residual risks (verify at runtime) + +- `SIMULATOR_FRAME`'s plot widget behavior under wxUniversal (untested). +- Asyncify vs. the ngspice bg-thread interplay during a running simulation (see the + [async dossier](../async/README.md) for the suspension model). + +Diff impact: ~25 lines in one upstream file (`ngspice.cpp`) + a `Findngspice.cmake` change +(wasm layer). Everything else is the dep build and FS assets. diff --git a/docs/features/fork-cleanup/12-network-stack.md b/docs/features/fork-cleanup/12-network-stack.md new file mode 100644 index 0000000..897065a --- /dev/null +++ b/docs/features/fork-cleanup/12-network-stack.md @@ -0,0 +1,80 @@ +# 12 — Network stack & the impossible catalog + +> Three link-time stubs (`libcurl_stub.a`, `libgit2_stub.a`, `libnng_stub.a`) gate a cluster +> of features. Two are revivable in the browser (HTTP libraries, local history); one is +> genuinely impossible (the IPC socket transport). This doc covers those plus the full +> catalog of what stays off and **why** — so the "everything web-feasible should work" policy +> has a clear boundary. + +## curl → emscripten Fetch (revives HTTP libraries) + +`wasm/stubs/curl_stub.c` makes `curl_easy_init()` return `NULL`, so +`KICAD_CURL_EASY`'s ctor throws `"Unable to initialize CURL session"` +(`common/kicad_curl/kicad_curl_easy.cpp:127-130`). That kills: the HTTP schematic library +(`SCH_IO_HTTP_LIB`, which is otherwise compiled and registered — see [08](08-importers.md)), +the update check, and PCM downloads. + +**The right move is *not* porting libcurl** (browsers can't do raw TCP; the real libcurl-wasm +ports tunnel TCP over WebSocket proxies — wrong tool). Instead, replace the stub with a +functional implementation of the ~12 `curl_easy_*` entry points `KICAD_CURL_EASY` actually +uses (setopt URL/headers/POST/writefunction, perform, getinfo response-code) over the +**emscripten Fetch API** — Asyncify-friendly (the build already runs full Asyncify), same +pattern as the clipboard. New file in `wasm/stubs/`, **zero KiCad-file changes**. Constraint: +target servers must send CORS headers. ~2–4 days. Same shim revives every other curl consumer +(version checks in `pgm_base.cpp` / `build_version.cpp`, currently failing silently). + +## libgit2 → real static build (fixes silently-broken local history) + +`wasm/stubs/libgit2_stub.c` makes all git ops fail with -1. This kills two things: + +- **Project git integration** — UI lives in the `kicad` project-manager (not a wasm target), + so mostly moot. +- **Local history** — **not moot.** `common/local_history.cpp` (git-backed save snapshots) is + wired into the *shipped* editors (`pcb_edit_frame.cpp`, `files.cpp`, `sch_edit_frame.cpp`, + `eda_base_frame.cpp`) and currently **fails silently on every save.** This is a real + product gap worth a deliberate decision. + +**Port (medium, ~1 week):** build a real static `libgit2.a` for wasm (wasm-git / +petersalomonsen proves libgit2 compiles to emscripten cleanly) and drop the stub for the +editors. Local-only commits need no network. Remote push/pull would additionally need +wasm-git's HTTP smart-transport — skip that. **Alternatively**, if local history isn't a +priority, disable the feature via config so it stops failing silently — but pick one; the +current state (compiled-in, failing every save) is the worst option. + +## nng / IPC API transport — keep stubbed (impossible) + +`KICAD_IPC_API=ON` builds the API handlers + protobuf (that's what the ON flag is for), but +`api_server.cpp` / `api_plugin_manager.cpp` are excluded and `kinng` isn't linked +(`common/CMakeLists.txt`). nng is raw sockets and the IPC clients are **external OS +processes** — neither exists in a browser sandbox. Keep `libnng_stub.a`. + +> The mainline-aligned future for plugins (post-SWIG KiCad 11) is this same IPC API, reachable +> from the browser only via a JS bridge (postMessage/WebSocket) implementing the request/reply +> surface — a *different* transport, not nng. That's a separate project; see +> [`../../../features/python/research.md`](../../../features/python/research.md). + +## The impossible catalog (and why) + +| Feature | Why it can't work in a browser | +|---|---| +| 3Dconnexion SpaceMouse / navlib | needs the native 3DxWare driver/daemon; WebHID can't replace the SDK protocol | +| Dynamic KIFACE / DSO loading | no `dlopen` of native DSOs in wasm; the static single-kiface architecture is correct (`kiway.cpp` / `kiway.h`) | +| ODBC database (`nanodbc`) | driver-manager + native driver model has no browser equivalent ([08](08-importers.md)) | +| nng / IPC transport | raw sockets to external OS processes (above) | +| `wxUSE_FSWATCHER` | no inotify/kqueue; MEMFS has no change notification and nothing edits files externally | +| `wxUSE_WEBVIEW` | no browser backend for the wasm port; only the unshipped project-manager uses it | +| OS keychain (`wxUSE_SECRETSTORE`) | no OS credential service; only insecure approximations (localStorage) exist | +| Pointer warping (`WarpPointer`) | browsers can't move the OS pointer — handled by zoom-to-cursor ([03](03-config-not-code.md)) | +| System trash / host env / process info | sandbox (kiplatform `environment.cpp`, `sysinfo.cpp`) | +| `PYTHON_MANAGER` subprocess execution | no `fork`/subprocess in emscripten; an embedded interpreter is the only path (policy-off) | + +## Policy-off (not impossible — chosen) + +- **Python scripting** — `KICAD_SCRIPTING` OFF + stubs. Stays off per the standing decision; + the mainline path is the IPC API, not SWIG (see + [`../../../features/python/research.md`](../../../features/python/research.md)). +- **PCM, update check** — networking features the team skipped; the curl shim above would + revive the update check if ever wanted. +- **`kicad` project manager, `cvpcb`, `bitmap2component`** — never given wasm targets; the web + app is the "manager" and the product ships per-tool editors (pcbnew, eeschema, symbol_editor, + gerbview, pl_editor, calculator). diff --git a/docs/features/fork-cleanup/README.md b/docs/features/fork-cleanup/README.md new file mode 100644 index 0000000..b0e8ece --- /dev/null +++ b/docs/features/fork-cleanup/README.md @@ -0,0 +1,126 @@ +# Fork cleanup: minimize divergence & re-enable web-feasible features + +> **Status:** research / planning only. No code has been changed. Authored June 2026. +> All `file:line` references are against the `kicad` submodule at its current HEAD +> (`ac7d733787`), upstream merge-base `4bfed3f174`, and the root repo at the same time. +> Verify line numbers before editing — they drift. + +## Why this exists + +Getting KiCad to build for the browser meant disabling, stubbing, and `#ifdef`-ing a +lot of things "because we had to." Two goals now pull in the same direction: + +1. **Keep the `kicad` and `wxwidgets` forks as close to mainline as possible.** Where we + add things, they should live in *new files/folders* (or build flags, runtime config, + the `wasm/` layer) — not as edits to upstream source. +2. **Everything that *can* work in a browser should actually work.** The 3D viewer, + importers, the symbol chooser — turn them back on. Only Python scripting and things + that are genuinely impossible on the web stay off. + +The key reframe that ties them together: **re-enabling features mostly *deletes* fork +code.** Almost everything disabled is disabled by a fork-added gate, stub, or exclusion. +Turning it back on removes that divergence. The two goals are one project. + +## TL;DR — the baseline and the target + +Fork state vs. merge-base `4bfed3f174` (24 commits): + +| | Count | +|---|---| +| New files (policy-compliant) | 44 (~28k lines, mostly the WebGL GAL copy) | +| Modified upstream files | **104** | +| Changed lines in modified files | **~2,400** (~1,600 of it CMake) | + +Estimated reachable end-state after the refactors below: **~15–20 modified files / +~150–250 lines**, with the WASM product *gaining* the 3D viewer, render-to-PNG jobs, +VRML export, five importers, the symbol browser, and optionally simulation, tooltips, +HTTP libraries, and working local history. + +Three facts do most of the work: + +- **Big chunks of the diff are dead code.** All `#ifdef KICAD_IPC_API` gating, the + +189-line `kiglew.h` shim, and the `opengl_gal.cpp` refactor compile in configurations + the shipping build never uses. See [01](01-revert-dead-code.md). +- **Several "disabled" features are disabled by stale mechanisms.** The Altium importer's + blocker was fixed months ago (the fix is force-included into every TU); three + build-script `-D` flags don't even exist as options in this KiCad version. See + [08](08-importers.md) and [07](07-native-build-bugs-and-tooling.md). +- **The 3D viewer has a cheap path.** KiCad ships a pure-CPU raytracer that needs zero + OpenGL, and the fork currently *nulls it out*. See [10](10-3d-viewer.md). + +## Master inventory + +Every disabled/divergent item, with verdict. Detail + recipes in the linked docs. + +### Reduce divergence (shrink the submodule diff — no behavior change) + +| Item | Mechanism | Action | Doc | +|---|---|---|---| +| `#ifdef KICAD_IPC_API` gates (18 files) | dead code (build sets `=ON`) | revert | [01](01-revert-dead-code.md) | +| `kiglew.h` +189, `opengl_gal.cpp` +32/−31 | dead (opengl GAL not built for wasm) | revert | [01](01-revert-dead-code.md) | +| `KI_DIAG_*` call sites (~30 lines) | debugging a solved crash | delete | [01](01-revert-dead-code.md) | +| ~600 lines of CMake reindent/duplication | wrap-and-reindent instead of additive | de-churn | [02](02-cmake-dechurn.md) | +| zoom warp, Backspace, timer, kiface init | `#ifdef` behavior changes | runtime config | [03](03-config-not-code.md) | +| fontconfig/libcontext/SpaceMouse stubs | in-file `#ifdef` | move to new TUs | [04](04-stub-tu-relocation.md) | +| scale factor, selection, header hygiene | wx-port quirks patched in KiCad | fix in wx fork | [05](05-wx-layer-fixes.md) | +| RTree fix, hotkey bug, `wxUSE_*` guards | upstreamable | send upstream | [06](06-upstreamable-patches.md) | +| 6 native-build regressions + diff tooling | fork bugs | fix | [07](07-native-build-bugs-and-tooling.md) | + +### Re-enable (web-feasible — mostly deletes gates) + +| Item | Blocker today | Verdict | Doc | +|---|---|---|---| +| Altium importer (sch+pcb) | stale exclusion (fix already active) | **re-enable now** | [08](08-importers.md) | +| Eagle/CADSTAR/LTspice/EasyEDA(Pro) sch | MVP-scoping exclusion | **re-enable now** | [08](08-importers.md) | +| Symbol chooser + viewer | empty lib tables (asset gap) | un-gate + asset pipeline | [09](09-symbol-libraries.md) | +| 3D viewer + render job + VRML export | `KICAD_BUILD_3D_VIEWER_WASM=OFF` + gates | port (raytracer first) | [10](10-3d-viewer.md) | +| SPICE simulator | ngspice dep stubbed | port (~1–2 wk) | [11](11-ngspice-simulator.md) | +| HTTP libraries / local history | curl & libgit2 stubbed | port | [12](12-network-stack.md) | +| Tooltips | wxUniversal never implemented them | port in wx fork | [05](05-wx-layer-fixes.md) | + +### Keep off + +- **Impossible:** SpaceMouse/navlib, ODBC database, nng/IPC transport, fswatcher, webview, + dynamic KIFACE `dlopen`, OS keychain, pointer warping. (See [12](12-network-stack.md) for + the full catalog and *why* each is impossible.) +- **Policy-off:** Python scripting (see [`../../../features/python/research.md`](../../../features/python/research.md)), + PCM, update check, the `kicad` project-manager / `cvpcb` apps (never wasm targets). + +## Suggested sequencing + +1. **Quick wins + dead-code reverts** ([01](01-revert-dead-code.md), [07](07-native-build-bugs-and-tooling.md), + [08](08-importers.md)) — one build, one e2e run; large diff reduction *and* new + importers. Importers are configure-confident but runtime-untested → smoke-test with a + sample Eagle + Altium file. +2. **3D viewer Route C** ([10](10-3d-viewer.md)) — raytracer + blit; brings render job and + VRML export back nearly free. +3. **Symbol-library asset pipeline** ([09](09-symbol-libraries.md)). +4. **CMake de-churn + config-not-code + stub relocation + wx fixes** ([02](02-cmake-dechurn.md)–[05](05-wx-layer-fixes.md)) + — the bulk of the remaining line-count reduction. +5. **Upstream PRs** ([06](06-upstreamable-patches.md)) — divergence that vanishes on merge. +6. **3D Route B (WebGL2) and Tier-3 ports** ([10](10-3d-viewer.md)–[12](12-network-stack.md)) per product priority. + +## Document index + +| File | Contents | +|---|---| +| [01-revert-dead-code.md](01-revert-dead-code.md) | Diff that compiles only in unused configs: IPC-API gates, `kiglew.h`, `opengl_gal.cpp`, diagnostics. ~480 lines, zero behavior change. | +| [02-cmake-dechurn.md](02-cmake-dechurn.md) | ~600 lines of CMake reindent/duplication → early-return guards, `list(REMOVE_ITEM)`, shader-hook, relocate `if(EMSCRIPTEN)` to `cmake/wasm/`. | +| [03-config-not-code.md](03-config-not-code.md) | Patches that re-implement existing settings: zoom pref, hotkeys file, `HAVE_CLOCK_GETTIME`, kiface init from the shell. | +| [04-stub-tu-relocation.md](04-stub-tu-relocation.md) | Move in-file `#else` stubs to new translation units: fontconfig, libcontext, SpaceMouse. | +| [05-wx-layer-fixes.md](05-wx-layer-fixes.md) | KiCad patches that belong in the wxWidgets wasm port: scale factor, selection, header self-sufficiency, **tooltips**. | +| [06-upstreamable-patches.md](06-upstreamable-patches.md) | Bug fixes & portability guards to send to KiCad/wxWidgets upstream so the diff disappears on merge. | +| [07-native-build-bugs-and-tooling.md](07-native-build-bugs-and-tooling.md) | 6 regressions the fork introduced into *native* builds + dead build-script flags + the broken `kicad-diff-stats.sh`. | +| [08-importers.md](08-importers.md) | Re-enable Altium + Eagle/CADSTAR/LTspice/EasyEDA(Pro). Net-negative diff. | +| [09-symbol-libraries.md](09-symbol-libraries.md) | Un-gate the symbol chooser/viewer + ship a starter library set to the browser FS. | +| [10-3d-viewer.md](10-3d-viewer.md) | Why it's off; Route C (CPU raytracer), Route B (WebGL2 port), Route A (rejected); model loading; render job; VRML export. | +| [11-ngspice-simulator.md](11-ngspice-simulator.md) | Static-link a wasm ngspice + the one `init_dll` branch; retest the model-data stubs. | +| [12-network-stack.md](12-network-stack.md) | curl→fetch shim, libgit2/local-history, and the catalog of genuinely-impossible features. | + +## Related docs + +- [`../async/README.md`](../async/README.md) — the Asyncify model these gates dance around. +- [`../wasm-exceptions/README.md`](../wasm-exceptions/README.md) — the EH-model work that shrinks the binary. +- [`../libraries/0001-library-management.md`](../libraries/0001-library-management.md) — why designs open without libraries (background for [09](09-symbol-libraries.md)). +- [`../../../features/python/research.md`](../../../features/python/research.md) — Python re-enable research (stays off; IPC API is the mainline path). +- [`../../../features/gl-article/README.md`](../../../features/gl-article/README.md) — the OpenGL→WebGL GAL migration history (context for [10](10-3d-viewer.md)).