From c148443b7bb6974d6d07021d4af4ffcf70d8f151 Mon Sep 17 00:00:00 2001 From: Istvan Matejcsok <119620946+matejcsok-ee@users.noreply.github.com> Date: Fri, 5 Jun 2026 13:18:24 +0200 Subject: [PATCH] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20remove=20orphaned=20?= =?UTF-8?q?files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/git-workflow/assert-on-branch.sh | 36 --- scripts/kicad/build-kicad-target.sh | 4 +- wasm/CMakeLists.txt | 26 -- wasm/README.md | 95 ++++--- wasm/cmake/FindKiplatformWASM.cmake | 49 ---- wasm/cmake/FindLibcontextWASM.cmake | 41 --- wasm/cmake/KiCadWASMConfig.cmake | 50 ---- wasm/kiplatform/CMakeLists.txt | 25 -- wasm/libcontext/CMakeLists.txt | 23 -- wasm/libcontext/libcontext_wasm.cpp | 180 ------------ wasm/libcontext/libcontext_wasm.h | 52 ---- wasm/shims/gl_immediate_shim.js | 178 ------------ wasm/stubs/glu_wasm_impl.cpp | 333 ----------------------- 13 files changed, 54 insertions(+), 1038 deletions(-) delete mode 100755 scripts/git-workflow/assert-on-branch.sh delete mode 100644 wasm/CMakeLists.txt delete mode 100644 wasm/cmake/FindKiplatformWASM.cmake delete mode 100644 wasm/cmake/FindLibcontextWASM.cmake delete mode 100644 wasm/cmake/KiCadWASMConfig.cmake delete mode 100644 wasm/kiplatform/CMakeLists.txt delete mode 100644 wasm/libcontext/CMakeLists.txt delete mode 100644 wasm/libcontext/libcontext_wasm.cpp delete mode 100644 wasm/libcontext/libcontext_wasm.h delete mode 100644 wasm/shims/gl_immediate_shim.js delete mode 100644 wasm/stubs/glu_wasm_impl.cpp diff --git a/scripts/git-workflow/assert-on-branch.sh b/scripts/git-workflow/assert-on-branch.sh deleted file mode 100755 index a2951aa..0000000 --- a/scripts/git-workflow/assert-on-branch.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -# Assert all 3 repos are on the given branch. -# Exit 0 if all match. Exit 1 with details if any mismatch (including detached HEAD). -# Usage: ./assert-on-branch.sh - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# shellcheck source=./repos.sh -source "$SCRIPT_DIR/repos.sh" - -if [ $# -ne 1 ]; then - echo "Usage: $0 " >&2 - exit 2 -fi - -WANT="$1" -mismatched=() -for repo in "${REPOS[@]}"; do - p=$(repo_path "$repo") - cur=$(git -C "$p" branch --show-current) - if [ "$cur" != "$WANT" ]; then - mismatched+=("$repo: on '${cur:-DETACHED-HEAD}' (want '$WANT')") - fi -done - -if [ ${#mismatched[@]} -eq 0 ]; then - echo "All 3 repos on branch: $WANT" - exit 0 -fi - -echo "Branch mismatch:" >&2 -for line in "${mismatched[@]}"; do - echo " $line" >&2 -done -exit 1 diff --git a/scripts/kicad/build-kicad-target.sh b/scripts/kicad/build-kicad-target.sh index ade6605..87f4393 100755 --- a/scripts/kicad/build-kicad-target.sh +++ b/scripts/kicad/build-kicad-target.sh @@ -237,8 +237,8 @@ emar rcs "${STUBS_BUILD}/libgit2_stub.a" "${STUBS_BUILD}/libgit2_stub.o" emcc -c "${STUBS_DIR}/curl_stub.c" -o "${STUBS_BUILD}/curl_stub.o" emar rcs "${STUBS_BUILD}/libcurl_stub.a" "${STUBS_BUILD}/curl_stub.o" -# Note: GLU tesselator is now implemented in wasm/stubs/glu_wasm_impl.cpp -# It's compiled as part of the GAL library (requires KiCad headers) +# Note: the GLU tesselator is provided by kicad/libs/kimath/glu_tess/glu_tess_impl.cpp, +# compiled as part of the GAL library (requires KiCad headers). # Compile NNG stub (IPC API requires NNG but sockets don't work in WASM) emcc -c -I"${STUBS_DIR}" "${STUBS_DIR}/nng_stub.c" -o "${STUBS_BUILD}/nng_stub.o" diff --git a/wasm/CMakeLists.txt b/wasm/CMakeLists.txt deleted file mode 100644 index 13ee1bf..0000000 --- a/wasm/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -# WASM Compatibility Layer for KiCad -# This directory contains WASM-specific implementations that replace -# platform-specific code in KiCad without modifying KiCad's source. - -cmake_minimum_required(VERSION 3.22) -project(kicad_wasm_compat) - -# Only build for Emscripten -if(NOT EMSCRIPTEN) - message(FATAL_ERROR "This project is only for Emscripten builds") -endif() - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# Include directories -include_directories( - ${CMAKE_CURRENT_SOURCE_DIR}/config - ${CMAKE_CURRENT_SOURCE_DIR}/shims -) - -# kiplatform WASM implementation -add_subdirectory(kiplatform) - -# libcontext Asyncify fiber implementation -add_subdirectory(libcontext) diff --git a/wasm/README.md b/wasm/README.md index e9c3e26..b543e47 100644 --- a/wasm/README.md +++ b/wasm/README.md @@ -1,74 +1,83 @@ # WASM Compatibility Layer -This directory contains WASM-specific implementations that allow KiCad to run in a web browser **without modifying KiCad's source code**. +This directory contains WASM-specific implementations that allow KiCad to run in a +web browser while keeping our KiCad fork **as close to upstream as possible**. ## Principle Instead of patching KiCad source files, we: -1. Override include paths to use our headers first -2. Provide alternative implementations for platform-specific code -3. Link our libraries instead of system libraries +1. Provide alternative implementations for platform-specific code (kiplatform) +2. Stub out libraries/features that can't work in the browser (libgit2, curl, nng, scripting, 3D viewer, ...) +3. Expose KiCad to JavaScript via Embind bindings +4. Override host package detection during cross-compilation (cmake find-modules) + +The KiCad-side hooks for this are small `if(EMSCRIPTEN)` branches in KiCad's own +CMakeLists that pull sources from this directory — see "How it's wired" below. ## Directory Structure ``` wasm/ -├── CMakeLists.txt # Master CMake for compatibility layer ├── README.md # This file -├── kiplatform/ # Platform abstraction implementations -│ ├── CMakeLists.txt +├── kiplatform/ # Platform abstraction implementations (compiled into KiCad) │ ├── app.cpp # App lifecycle (paths, startup) │ ├── drivers.cpp # GPU detection (returns "WebGL") -│ ├── environment.cpp # Environment variables (localStorage) +│ ├── environment.cpp # Environment variables │ ├── io.cpp # File I/O (WASM virtual filesystem) │ ├── policy.cpp # Security policy (always permissive) -│ ├── secrets.cpp # Credential storage (localStorage) +│ ├── secrets.cpp # Credential storage │ ├── sysinfo.cpp # System information -│ └── printing.cpp # Print support (browser print()) -├── libcontext/ # Coroutine/fiber implementation -│ ├── CMakeLists.txt -│ └── fcontext_wasm.cpp # Emscripten Asyncify fibers -├── shims/ # Header overrides -│ └── *.h # Headers that redirect to our impls -└── config/ # Build configuration - ├── kicad_wasm_config.h # Version and feature config - └── setup.h # Platform setup +│ ├── printing.cpp # Print support (browser print()) +│ └── ui.cpp # UI helpers +├── bindings/ # Embind bindings exposing each app to JavaScript +│ ├── pcbnew_embind.cpp +│ ├── eeschema_embind.cpp +│ ├── pl_editor_embind.cpp +│ └── calculator_embind.cpp +├── stubs/ # Stub implementations + header shims for unavailable deps +│ ├── *.c / *.cpp # libgit2, curl, nng, scripting, 3D viewer, frame stubs, ... +│ ├── char_traits_uint16_workaround.h +│ └── GL/ nng/ ngspice/ # Header stubs found via include paths +└── cmake/ # CMake find-module overrides for cross-compilation + └── Find*.cmake / Use*.cmake ``` -## How It Works +## How it's wired -### Include Path Override +### kiplatform — compiled into KiCad -When building KiCad for WASM, we add our directories first in the include path: +The `kiplatform/*.cpp` files are added directly to KiCad's kiplatform library by an +`if(EMSCRIPTEN)` branch in `kicad/libs/kiplatform/CMakeLists.txt`, which references +them as `${PROJECT_SOURCE_DIR}/../wasm/kiplatform/*.cpp`. There is no separate +`libkiplatform_wasm.a`. -```bash --I$PROJECT_ROOT/wasm/shims --I$PROJECT_ROOT/wasm/kiplatform --I$PROJECT_ROOT/stubs/include -``` +### stubs — compiled by the build script and KiCad CMakeLists -This means when KiCad includes ``, it finds our version first. +`scripts/kicad/build-kicad-target.sh` compiles the C stubs (`libgit2_stub.c`, +`curl_stub.c`, `nng_stub.c`) and force-includes `char_traits_uint16_workaround.h`. +App-specific `*_frame_stub.cpp` / `*_scripting_stub.cpp` are picked up per app, and +the remaining `*_stub.cpp` files are pulled in by `if(EMSCRIPTEN)` branches in the +KiCad fork's own CMakeLists. Header stubs under `GL/`, `nng/`, `ngspice/` are resolved +via include paths. -### Library Override +### bindings — per app -We build `libkiplatform_wasm.a` and link it instead of the native kiplatform: +`build-kicad-target.sh` compiles `wasm/bindings/_embind.cpp` for the app being +built (apps without an embind file get an empty placeholder object). -```bash --L$BUILD_ROOT/wasm -lkiplatform_wasm -``` +### cmake — module path -### CMake Integration +`build-kicad-target.sh` passes `-DCMAKE_MODULE_PATH="${PROJECT_ROOT}/wasm/cmake"` so +the WASM find-module stubs override host package detection. -The main KiCad build is configured to find our implementations: - -```cmake --DCMAKE_MODULE_PATH="$PROJECT_ROOT/cmake" --DKIPLATFORM_LIBRARY="$BUILD_ROOT/wasm/libkiplatform_wasm.a" -``` +> Coroutine/fiber support is **not** in this directory — it comes from the KiCad fork's +> `kicad/thirdparty/libcontext/libcontext.cpp` (`LIBCONTEXT_PLATFORM_wasm32`). The GLU +> tesselator comes from `kicad/libs/kimath/glu_tess/glu_tess_impl.cpp`. ## Adding New Implementations -1. Create the implementation file in the appropriate directory -2. Add it to the CMakeLists.txt -3. Ensure the header interface matches KiCad's expected interface -4. Test with a minimal build before full integration +1. Create the implementation file in the appropriate directory (`kiplatform/`, `stubs/`, `bindings/`). +2. Wire it in: a stub C file goes in `build-kicad-target.sh`; a kiplatform/app source + goes in the relevant `if(EMSCRIPTEN)` branch of the KiCad-side CMakeLists. +3. Ensure the header interface matches KiCad's expected interface. +4. Test with a minimal build before full integration. diff --git a/wasm/cmake/FindKiplatformWASM.cmake b/wasm/cmake/FindKiplatformWASM.cmake deleted file mode 100644 index 416c249..0000000 --- a/wasm/cmake/FindKiplatformWASM.cmake +++ /dev/null @@ -1,49 +0,0 @@ -# FindKiplatformWASM.cmake -# Find module for WASM implementation of kiplatform -# -# This module is used instead of the platform-specific kiplatform -# when building KiCad for WebAssembly. -# -# This module defines: -# KIPLATFORM_FOUND - System has kiplatform for WASM -# KIPLATFORM_INCLUDE_DIRS - Include directories -# KIPLATFORM_LIBRARIES - Libraries to link -# KIPLATFORM_SOURCES - Source files to compile - -include(FindPackageHandleStandardArgs) - -if(NOT EMSCRIPTEN) - message(FATAL_ERROR "FindKiplatformWASM is only for Emscripten builds") -endif() - -# Get the directory containing this file -get_filename_component(_FIND_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) -get_filename_component(KIPLATFORM_WASM_DIR "${_FIND_DIR}/../kiplatform" ABSOLUTE) - -# Define source files -set(KIPLATFORM_SOURCES - ${KIPLATFORM_WASM_DIR}/app.cpp - ${KIPLATFORM_WASM_DIR}/drivers.cpp - ${KIPLATFORM_WASM_DIR}/environment.cpp - ${KIPLATFORM_WASM_DIR}/io.cpp - ${KIPLATFORM_WASM_DIR}/policy.cpp - ${KIPLATFORM_WASM_DIR}/secrets.cpp - ${KIPLATFORM_WASM_DIR}/sysinfo.cpp - ${KIPLATFORM_WASM_DIR}/ui.cpp -) - -# Include directory for platform headers -# We use KiCad's own headers, just provide our implementation -set(KIPLATFORM_INCLUDE_DIRS ${KIPLATFORM_WASM_DIR}) - -# No prebuilt library - sources are compiled directly into KiCad -set(KIPLATFORM_LIBRARIES "") - -# Mark as found -set(KIPLATFORM_FOUND TRUE) - -find_package_handle_standard_args(KiplatformWASM - REQUIRED_VARS KIPLATFORM_SOURCES KIPLATFORM_INCLUDE_DIRS -) - -mark_as_advanced(KIPLATFORM_SOURCES KIPLATFORM_INCLUDE_DIRS KIPLATFORM_LIBRARIES) diff --git a/wasm/cmake/FindLibcontextWASM.cmake b/wasm/cmake/FindLibcontextWASM.cmake deleted file mode 100644 index 945463c..0000000 --- a/wasm/cmake/FindLibcontextWASM.cmake +++ /dev/null @@ -1,41 +0,0 @@ -# FindLibcontextWASM.cmake -# Find module for WASM implementation of libcontext using Asyncify -# -# This module replaces the platform-specific libcontext assembly -# when building KiCad for WebAssembly. -# -# This module defines: -# LIBCONTEXT_FOUND - System has libcontext for WASM -# LIBCONTEXT_INCLUDE_DIRS - Include directories -# LIBCONTEXT_LIBRARIES - Libraries to link (none for WASM) -# LIBCONTEXT_SOURCES - Source files to compile - -include(FindPackageHandleStandardArgs) - -if(NOT EMSCRIPTEN) - message(FATAL_ERROR "FindLibcontextWASM is only for Emscripten builds") -endif() - -# Get the directory containing this file -get_filename_component(_FIND_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) -get_filename_component(LIBCONTEXT_WASM_DIR "${_FIND_DIR}/../libcontext" ABSOLUTE) - -# Define source files -set(LIBCONTEXT_SOURCES - ${LIBCONTEXT_WASM_DIR}/libcontext_wasm.cpp -) - -# Include directories -set(LIBCONTEXT_INCLUDE_DIRS ${LIBCONTEXT_WASM_DIR}) - -# No prebuilt library -set(LIBCONTEXT_LIBRARIES "") - -# Mark as found -set(LIBCONTEXT_FOUND TRUE) - -find_package_handle_standard_args(LibcontextWASM - REQUIRED_VARS LIBCONTEXT_SOURCES LIBCONTEXT_INCLUDE_DIRS -) - -mark_as_advanced(LIBCONTEXT_SOURCES LIBCONTEXT_INCLUDE_DIRS LIBCONTEXT_LIBRARIES) diff --git a/wasm/cmake/KiCadWASMConfig.cmake b/wasm/cmake/KiCadWASMConfig.cmake deleted file mode 100644 index bcb85b5..0000000 --- a/wasm/cmake/KiCadWASMConfig.cmake +++ /dev/null @@ -1,50 +0,0 @@ -# KiCad WASM Configuration -# This file configures KiCad to use WASM-specific implementations - -if(NOT EMSCRIPTEN) - message(FATAL_ERROR "KiCadWASMConfig.cmake is only for Emscripten builds") -endif() - -# Set the path to WASM compatibility layer -get_filename_component(KICAD_WASM_DIR "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE) - -message(STATUS "KiCad WASM compatibility layer: ${KICAD_WASM_DIR}") - -# Add include directories for WASM implementations -# These will be searched BEFORE KiCad's own includes, allowing us to -# provide our own implementations without modifying KiCad source -set(KICAD_WASM_INCLUDE_DIRS - ${KICAD_WASM_DIR}/kiplatform - ${KICAD_WASM_DIR}/libcontext - ${KICAD_WASM_DIR}/config - ${KICAD_WASM_DIR}/shims -) - -# Define macro to indicate we're using WASM platform -add_definitions(-DKICAD_PLATFORM_WASM=1) -add_definitions(-D__WXUNIVERSAL__=1) - -# Emscripten-specific compile options -add_compile_options( - -pthread - -sUSE_PTHREADS=1 -) - -# Emscripten-specific link options -add_link_options( - -pthread - -sUSE_PTHREADS=1 - -sPTHREAD_POOL_SIZE=4 - -sASYNCIFY=1 - -sASYNCIFY_STACK_SIZE=65536 - -sALLOW_MEMORY_GROWTH=1 - -sINITIAL_MEMORY=256MB - -sMAXIMUM_MEMORY=4GB - -sMODULARIZE=1 - -sEXPORT_ES6=1 - -sENVIRONMENT=web,worker -) - -# Export variables for use by parent CMakeLists -set(KICAD_WASM_INCLUDE_DIRS ${KICAD_WASM_INCLUDE_DIRS} PARENT_SCOPE) -set(KICAD_WASM_DIR ${KICAD_WASM_DIR} PARENT_SCOPE) diff --git a/wasm/kiplatform/CMakeLists.txt b/wasm/kiplatform/CMakeLists.txt deleted file mode 100644 index 31b8e98..0000000 --- a/wasm/kiplatform/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -# WASM implementation of kiplatform -# Provides WASM-specific implementations of KiCad's platform abstraction layer - -set(KIPLATFORM_WASM_SRCS - app.cpp - drivers.cpp - environment.cpp - io.cpp - policy.cpp - secrets.cpp - sysinfo.cpp - ui.cpp -) - -add_library(kiplatform_wasm STATIC ${KIPLATFORM_WASM_SRCS}) - -target_include_directories(kiplatform_wasm PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/../config - ${PROJECT_ROOT}/kicad/libs/kiplatform/include -) - -# Link with wxWidgets -target_link_libraries(kiplatform_wasm - # wxWidgets will be added during main build -) diff --git a/wasm/libcontext/CMakeLists.txt b/wasm/libcontext/CMakeLists.txt deleted file mode 100644 index a948121..0000000 --- a/wasm/libcontext/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# WASM implementation of libcontext using Emscripten Asyncify -# Provides fiber/coroutine support for KiCad's router - -set(LIBCONTEXT_WASM_SRCS - libcontext_wasm.cpp -) - -add_library(libcontext_wasm STATIC ${LIBCONTEXT_WASM_SRCS}) - -target_include_directories(libcontext_wasm PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} -) - -# Asyncify is required for fiber support -# These flags must also be set on the final executable -target_compile_options(libcontext_wasm PRIVATE - -pthread -) - -target_link_options(libcontext_wasm INTERFACE - -sASYNCIFY=1 - -sASYNCIFY_STACK_SIZE=65536 -) diff --git a/wasm/libcontext/libcontext_wasm.cpp b/wasm/libcontext/libcontext_wasm.cpp deleted file mode 100644 index acd45ef..0000000 --- a/wasm/libcontext/libcontext_wasm.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * WASM implementation of libcontext using Emscripten Asyncify - * - * Emscripten's Asyncify allows us to implement fiber/coroutine semantics - * by saving and restoring the WebAssembly call stack. - * - * Implementation strategy: - * - Each fiber context stores an Asyncify data buffer - * - jump_fcontext suspends current execution and resumes target - * - make_fcontext creates a new context with a function entry point - * - * Note: This requires the WASM module to be compiled with: - * -sASYNCIFY=1 - * -sASYNCIFY_STACK_SIZE=65536 (or larger if needed) - * - * For more information on Asyncify: - * https://emscripten.org/docs/porting/asyncify.html - */ - -#include -#include -#include -#include - -#ifdef __EMSCRIPTEN__ -#include -#include -#endif - -// Match the API from libcontext.h -#define LIBCONTEXT_CALL_CONVENTION - -#ifdef __cplusplus -extern "C" { -#endif - -namespace libcontext -{ - -// Context structure that stores fiber state -struct fiber_context { - emscripten_fiber_t fiber; - void (*entry_func)(intptr_t); - intptr_t entry_arg; - bool initialized; - bool running; - // Stack for asyncify data - char asyncify_stack[65536]; - // C stack - char* c_stack; - size_t c_stack_size; -}; - -// Current running context -static fiber_context* g_current_context = nullptr; -static fiber_context g_main_context; -static bool g_main_initialized = false; - -// Fiber entry wrapper -static void fiber_entry_wrapper(void* arg) -{ - fiber_context* ctx = (fiber_context*)arg; - if (ctx && ctx->entry_func) { - ctx->entry_func(ctx->entry_arg); - } - // If entry function returns, we need to handle it - // In original libcontext, this would call _exit - // For WASM, we'll just return to main context -} - -typedef void* fcontext_t; - -void LIBCONTEXT_CALL_CONVENTION release_fcontext( fcontext_t ctx ) -{ -#ifdef __EMSCRIPTEN__ - if (ctx) { - fiber_context* fctx = (fiber_context*)ctx; - if (fctx->c_stack) { - free(fctx->c_stack); - } - free(fctx); - } -#endif -} - -intptr_t LIBCONTEXT_CALL_CONVENTION jump_fcontext( fcontext_t* ofc, fcontext_t nfc, - intptr_t vp, bool preserve_fpu ) -{ -#ifdef __EMSCRIPTEN__ - // Initialize main context if needed - if (!g_main_initialized) { - memset(&g_main_context, 0, sizeof(g_main_context)); - emscripten_fiber_init_from_current_context( - &g_main_context.fiber, - g_main_context.asyncify_stack, - sizeof(g_main_context.asyncify_stack) - ); - g_main_context.initialized = true; - g_main_context.running = true; - g_current_context = &g_main_context; - g_main_initialized = true; - } - - fiber_context* old_ctx = g_current_context; - fiber_context* new_ctx = (fiber_context*)nfc; - - if (!new_ctx || !new_ctx->initialized) { - fprintf(stderr, "jump_fcontext: invalid target context\n"); - return 0; - } - - // Store the argument in the new context - new_ctx->entry_arg = vp; - - // Save the old context pointer - if (ofc) { - *ofc = (fcontext_t)old_ctx; - } - - // Switch contexts - g_current_context = new_ctx; - old_ctx->running = false; - new_ctx->running = true; - - // Perform the fiber switch - emscripten_fiber_swap(&old_ctx->fiber, &new_ctx->fiber); - - // When we return here, we've been switched back to - // Return the value passed to us - return g_current_context->entry_arg; -#else - return 0; -#endif -} - -fcontext_t LIBCONTEXT_CALL_CONVENTION make_fcontext( void* sp, size_t size, - void (* fn)( intptr_t ) ) -{ -#ifdef __EMSCRIPTEN__ - // Allocate context structure - fiber_context* ctx = (fiber_context*)malloc(sizeof(fiber_context)); - if (!ctx) { - return nullptr; - } - memset(ctx, 0, sizeof(fiber_context)); - - ctx->entry_func = fn; - ctx->entry_arg = 0; - ctx->c_stack = (char*)sp - size; // sp points to top of stack - ctx->c_stack_size = size; - - // Initialize the fiber - // Note: sp is the TOP of the stack (highest address) - // The stack grows downward, so we need to pass the bottom - void* stack_bottom = (char*)sp - size; - - emscripten_fiber_init( - &ctx->fiber, - fiber_entry_wrapper, - ctx, // User data for entry function - stack_bottom, // C stack (bottom) - size, // C stack size - ctx->asyncify_stack, // Asyncify stack - sizeof(ctx->asyncify_stack) // Asyncify stack size - ); - - ctx->initialized = true; - ctx->running = false; - - return (fcontext_t)ctx; -#else - return nullptr; -#endif -} - -}; // namespace libcontext - -#ifdef __cplusplus -}; -#endif diff --git a/wasm/libcontext/libcontext_wasm.h b/wasm/libcontext/libcontext_wasm.h deleted file mode 100644 index 21b3b99..0000000 --- a/wasm/libcontext/libcontext_wasm.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * WASM implementation of libcontext using Emscripten Asyncify - * - * This header provides WASM-specific definitions for the libcontext API. - * The implementation uses Emscripten's Asyncify feature to implement - * fiber-like context switching in WebAssembly. - * - * Asyncify works by: - * 1. Instrumenting the WASM code to save/restore the call stack - * 2. Allowing execution to suspend at any point - * 3. Resuming execution from where it was suspended - * - * This is used by KiCad's router for coroutine-based routing. - */ - -#ifndef LIBCONTEXT_WASM_H -#define LIBCONTEXT_WASM_H - -#include -#include - -// Define WASM platform -#define LIBCONTEXT_PLATFORM_wasm -#define LIBCONTEXT_CALL_CONVENTION - -#ifdef __cplusplus -namespace libcontext { -#endif - -typedef void* fcontext_t; - -#ifdef __cplusplus -extern "C" { -#endif - -void LIBCONTEXT_CALL_CONVENTION release_fcontext( fcontext_t ctx ); - -intptr_t LIBCONTEXT_CALL_CONVENTION jump_fcontext( fcontext_t* ofc, fcontext_t nfc, - intptr_t vp, bool preserve_fpu = true ); - -fcontext_t LIBCONTEXT_CALL_CONVENTION make_fcontext( void* sp, size_t size, - void (* fn)( intptr_t ) ); - -#ifdef __cplusplus -} // extern "C" -#endif - -#ifdef __cplusplus -} // namespace libcontext -#endif - -#endif // LIBCONTEXT_WASM_H diff --git a/wasm/shims/gl_immediate_shim.js b/wasm/shims/gl_immediate_shim.js deleted file mode 100644 index 0d71567..0000000 --- a/wasm/shims/gl_immediate_shim.js +++ /dev/null @@ -1,178 +0,0 @@ -/** - * gl_immediate_shim.js - * - * Custom OpenGL immediate mode shims for KiCad WASM port. - * Fixes Emscripten LEGACY_GL_EMULATION issues: - * 1. Color-per-vertex requirement - injects color before each vertex automatically - * 2. Missing double-precision functions (glVertex2d, glVertex3d, glColor3d, glColor4d) - * - * Usage: emcc ... --js-library=lib/gl_immediate_shim.js - */ - -addToLibrary({ - // ================================================================== - // GLImmediateShim - State tracking and function wrapping - // ================================================================== - - $GLImmediateShim__deps: ['$GLImmediate', 'glBegin', 'glEnd', 'glVertex2f', 'glVertex3f', 'glColor3f', 'glColor4f'], - $GLImmediateShim__postset: 'GLImmediateShim.init();', - $GLImmediateShim: { - // Current color state (persistent across vertices) - currentColor: null, - - // Track if we're inside glBegin/glEnd block - inBeginEnd: false, - - // Track if color was called since the last vertex - // This prevents double-injection when code already calls color per vertex - colorCalledSinceLastVertex: false, - - // Original functions we're wrapping - origFns: {}, - - // Initialization flag - initialized: false, - - init: function() { - if (GLImmediateShim.initialized) return; - if (typeof GLImmediate === 'undefined') { - // GLImmediate not ready yet, will be called again - console.log('[GLImmediateShim] Waiting for GLImmediate...'); - return; - } - - console.log('[GLImmediateShim] Initializing OpenGL immediate mode shims'); - - // Initialize current color to white (OpenGL default) - GLImmediateShim.currentColor = new Float32Array([1.0, 1.0, 1.0, 1.0]); - - // Store original functions - GLImmediateShim.origFns = { - glBegin: _glBegin, - glEnd: _glEnd, - glVertex2f: _glVertex2f, - glVertex3f: _glVertex3f, - glColor3f: _glColor3f, - glColor4f: _glColor4f, - }; - - // Install shims - _glBegin = GLImmediateShim.shimBegin; - _glEnd = GLImmediateShim.shimEnd; - _glVertex2f = GLImmediateShim.shimVertex2f; - _glVertex3f = GLImmediateShim.shimVertex3f; - _glColor3f = GLImmediateShim.shimColor3f; - _glColor4f = GLImmediateShim.shimColor4f; - - GLImmediateShim.initialized = true; - console.log('[GLImmediateShim] Initialized successfully'); - }, - - // ================================================================== - // Shim implementations - // ================================================================== - - shimBegin: function(mode) { - GLImmediateShim.inBeginEnd = true; - GLImmediateShim.colorCalledSinceLastVertex = false; - GLImmediateShim.origFns.glBegin(mode); - }, - - shimEnd: function() { - GLImmediateShim.inBeginEnd = false; - GLImmediateShim.origFns.glEnd(); - }, - - shimColor3f: function(r, g, b) { - var c = GLImmediateShim.currentColor; - c[0] = r; - c[1] = g; - c[2] = b; - c[3] = 1.0; - - if (GLImmediateShim.inBeginEnd) { - // Mark that color was explicitly called for this vertex - GLImmediateShim.colorCalledSinceLastVertex = true; - } - - GLImmediateShim.origFns.glColor3f(r, g, b); - }, - - shimColor4f: function(r, g, b, a) { - var c = GLImmediateShim.currentColor; - c[0] = r; - c[1] = g; - c[2] = b; - c[3] = a; - - if (GLImmediateShim.inBeginEnd) { - // Mark that color was explicitly called for this vertex - GLImmediateShim.colorCalledSinceLastVertex = true; - } - - GLImmediateShim.origFns.glColor4f(r, g, b, a); - }, - - // Inject current color before each vertex (only if not already called) - injectColor: function() { - if (!GLImmediateShim.inBeginEnd) return; - - // Only inject color if it wasn't already called for this vertex - if (!GLImmediateShim.colorCalledSinceLastVertex) { - var c = GLImmediateShim.currentColor; - GLImmediateShim.origFns.glColor4f(c[0], c[1], c[2], c[3]); - } - - // Reset the flag for the next vertex - GLImmediateShim.colorCalledSinceLastVertex = false; - }, - - shimVertex2f: function(x, y) { - GLImmediateShim.injectColor(); - GLImmediateShim.origFns.glVertex2f(x, y); - }, - - shimVertex3f: function(x, y, z) { - GLImmediateShim.injectColor(); - GLImmediateShim.origFns.glVertex3f(x, y, z); - }, - }, - - // ================================================================== - // Double-precision vertex functions (missing in Emscripten) - // ================================================================== - - glVertex2d__deps: ['glVertex2f'], - glVertex2d: function(x, y) { - _glVertex2f(x, y); - }, - - glVertex3d__deps: ['glVertex3f'], - glVertex3d: function(x, y, z) { - _glVertex3f(x, y, z); - }, - - glVertex4d__deps: ['glVertex4f'], - glVertex4d: function(x, y, z, w) { - _glVertex4f(x, y, z, w); - }, - - // ================================================================== - // Double-precision color functions - // ================================================================== - - glColor3d__deps: ['glColor3f'], - glColor3d: function(r, g, b) { - _glColor3f(r, g, b); - }, - - glColor4d__deps: ['glColor4f'], - glColor4d: function(r, g, b, a) { - _glColor4f(r, g, b, a); - }, -}); - -// Ensure GLImmediateShim is included in the build -if (typeof extraLibraryFuncs !== 'undefined') { - extraLibraryFuncs.push('$GLImmediateShim'); -} diff --git a/wasm/stubs/glu_wasm_impl.cpp b/wasm/stubs/glu_wasm_impl.cpp deleted file mode 100644 index df0737d..0000000 --- a/wasm/stubs/glu_wasm_impl.cpp +++ /dev/null @@ -1,333 +0,0 @@ -/** - * GLU Tesselator implementation for WebAssembly builds - * - * Uses KiCad's earcut-based POLYGON_TRIANGULATION instead of GLU library. - * This enables OpenGL GAL to work in WASM with proper polygon triangulation. - */ - -#include -#include -#include - -#include -#include -#include - -// GL types -typedef double GLdouble; -typedef float GLfloat; -typedef unsigned int GLenum; -typedef unsigned char GLboolean; -typedef void GLvoid; -typedef void (*_GLUfuncptr)(void); - -// GL constants -#ifndef GL_TRUE -#define GL_TRUE 1 -#endif -#ifndef GL_FALSE -#define GL_FALSE 0 -#endif - -// GLU constants -#define GLU_TESS_BEGIN 100100 -#define GLU_TESS_VERTEX 100101 -#define GLU_TESS_END 100102 -#define GLU_TESS_ERROR 100103 -#define GLU_TESS_EDGE_FLAG 100104 -#define GLU_TESS_COMBINE 100105 -#define GLU_TESS_BEGIN_DATA 100106 -#define GLU_TESS_VERTEX_DATA 100107 -#define GLU_TESS_END_DATA 100108 -#define GLU_TESS_ERROR_DATA 100109 -#define GLU_TESS_EDGE_FLAG_DATA 100110 -#define GLU_TESS_COMBINE_DATA 100111 - -#define GLU_TESS_WINDING_RULE 100140 -#define GLU_TESS_WINDING_ODD 100130 -#define GLU_TESS_WINDING_NONZERO 100131 -#define GLU_TESS_WINDING_POSITIVE 100132 -#define GLU_TESS_WINDING_NEGATIVE 100133 -#define GLU_TESS_WINDING_ABS_GEQ_TWO 100134 - -// Vertex data stored during tesselation -struct TessVertex -{ - std::array coords; - void* userData; // The data pointer passed to gluTessVertex -}; - -struct GLUtesselator -{ - // Callbacks - void (*vertexCallback)(void* vertex) = nullptr; - void (*vertexDataCallback)(void* vertex, void* userData) = nullptr; - void (*combineCallback)(GLdouble coords[3], void* vertex_data[4], - GLfloat weight[4], void** dataOut) = nullptr; - void (*combineDataCallback)(GLdouble coords[3], void* vertex_data[4], - GLfloat weight[4], void** dataOut, void* userData) = nullptr; - void (*edgeFlagCallback)(GLboolean flag) = nullptr; - void (*edgeFlagDataCallback)(GLboolean flag, void* userData) = nullptr; - void (*errorCallback)(GLenum error) = nullptr; - void (*errorDataCallback)(GLenum error, void* userData) = nullptr; - void (*beginCallback)(GLenum type) = nullptr; - void (*beginDataCallback)(GLenum type, void* userData) = nullptr; - void (*endCallback)() = nullptr; - void (*endDataCallback)(void* userData) = nullptr; - - // Contour data - each contour is a list of vertices - std::vector> contours; - std::vector* currentContour = nullptr; - - // User data passed to gluTessBeginPolygon - void* polygonUserData = nullptr; - - // Properties - GLenum windingRule = GLU_TESS_WINDING_POSITIVE; -}; - -extern "C" { - -GLUtesselator* gluNewTess() -{ - return new GLUtesselator(); -} - -void gluDeleteTess(GLUtesselator* tess) -{ - delete tess; -} - -void gluTessCallback(GLUtesselator* tess, GLenum which, _GLUfuncptr fn) -{ - if (!tess) return; - - switch (which) { - case GLU_TESS_VERTEX: - tess->vertexCallback = (void(*)(void*))fn; - break; - case GLU_TESS_VERTEX_DATA: - tess->vertexDataCallback = (void(*)(void*, void*))fn; - break; - case GLU_TESS_COMBINE: - tess->combineCallback = (void(*)(GLdouble[3], void*[4], GLfloat[4], void**))fn; - break; - case GLU_TESS_COMBINE_DATA: - tess->combineDataCallback = (void(*)(GLdouble[3], void*[4], GLfloat[4], void**, void*))fn; - break; - case GLU_TESS_EDGE_FLAG: - tess->edgeFlagCallback = (void(*)(GLboolean))fn; - break; - case GLU_TESS_EDGE_FLAG_DATA: - tess->edgeFlagDataCallback = (void(*)(GLboolean, void*))fn; - break; - case GLU_TESS_ERROR: - tess->errorCallback = (void(*)(GLenum))fn; - break; - case GLU_TESS_ERROR_DATA: - tess->errorDataCallback = (void(*)(GLenum, void*))fn; - break; - case GLU_TESS_BEGIN: - tess->beginCallback = (void(*)(GLenum))fn; - break; - case GLU_TESS_BEGIN_DATA: - tess->beginDataCallback = (void(*)(GLenum, void*))fn; - break; - case GLU_TESS_END: - tess->endCallback = (void(*)())fn; - break; - case GLU_TESS_END_DATA: - tess->endDataCallback = (void(*)(void*))fn; - break; - } -} - -void gluTessProperty(GLUtesselator* tess, GLenum which, GLdouble value) -{ - if (!tess) return; - - if (which == GLU_TESS_WINDING_RULE) - tess->windingRule = static_cast(value); -} - -void gluGetTessProperty(GLUtesselator* tess, GLenum which, GLdouble* value) -{ - if (!tess || !value) return; - - if (which == GLU_TESS_WINDING_RULE) - *value = static_cast(tess->windingRule); -} - -void gluTessNormal(GLUtesselator* tess, GLdouble x, GLdouble y, GLdouble z) -{ - // Ignored - earcut works in 2D (XY plane) - (void)tess; (void)x; (void)y; (void)z; -} - -void gluTessBeginPolygon(GLUtesselator* tess, void* userData) -{ - if (!tess) return; - - tess->contours.clear(); - tess->currentContour = nullptr; - tess->polygonUserData = userData; -} - -void gluTessBeginContour(GLUtesselator* tess) -{ - if (!tess) return; - - tess->contours.emplace_back(); - tess->currentContour = &tess->contours.back(); -} - -void gluTessVertex(GLUtesselator* tess, GLdouble coords[3], void* data) -{ - if (!tess || !tess->currentContour) return; - - TessVertex v; - v.coords = {coords[0], coords[1], coords[2]}; - v.userData = data; - tess->currentContour->push_back(v); -} - -void gluTessEndContour(GLUtesselator* tess) -{ - if (!tess) return; - tess->currentContour = nullptr; -} - -void gluTessEndPolygon(GLUtesselator* tess) -{ - if (!tess) return; - - // Need at least one contour with 3+ vertices - if (tess->contours.empty()) - return; - - const auto& mainContour = tess->contours[0]; - if (mainContour.size() < 3) - return; - - // Convert to SHAPE_LINE_CHAIN for earcut - // Note: earcut works with integer coordinates, so we scale appropriately - // KiCad uses nanometers internally, so coordinates are already integers - SHAPE_LINE_CHAIN chain; - for (const auto& v : mainContour) { - chain.Append(VECTOR2I(static_cast(v.coords[0]), - static_cast(v.coords[1]))); - } - chain.SetClosed(true); - - // Triangulate using KiCad's earcut implementation - SHAPE_POLY_SET::TRIANGULATED_POLYGON result(-1); - POLYGON_TRIANGULATION triangulator(result); - - if (!triangulator.TesselatePolygon(chain, nullptr)) { - // Tesselation failed - call error callback if registered - if (tess->errorDataCallback) - tess->errorDataCallback(100151, tess->polygonUserData); // GLU_TESS_ERROR1 - else if (tess->errorCallback) - tess->errorCallback(100151); - return; - } - - // Call edge flag callback to indicate we're producing triangles - // (edge flag callback forces GLU to output only triangles, which earcut always does) - if (tess->edgeFlagDataCallback) - tess->edgeFlagDataCallback(GL_TRUE, tess->polygonUserData); - else if (tess->edgeFlagCallback) - tess->edgeFlagCallback(GL_TRUE); - - // Output triangles via vertex callback - // The result contains triangle indices that map back to input vertices - size_t triCount = result.GetTriangleCount(); - - for (size_t i = 0; i < triCount; i++) { - VECTOR2I a, b, c; - result.GetTriangle(i, a, b, c); - - // Find the original vertex indices by matching coordinates - // (POLYGON_TRIANGULATION preserves vertex order for the first N vertices) - auto findVertex = [&](const VECTOR2I& pt) -> size_t { - for (size_t j = 0; j < mainContour.size(); j++) { - if (static_cast(mainContour[j].coords[0]) == pt.x && - static_cast(mainContour[j].coords[1]) == pt.y) { - return j; - } - } - return 0; // fallback - }; - - size_t idxA = findVertex(a); - size_t idxB = findVertex(b); - size_t idxC = findVertex(c); - - // Call vertex callback for each triangle vertex - // The callback receives the user data pointer that was passed to gluTessVertex - if (tess->vertexDataCallback) { - tess->vertexDataCallback(mainContour[idxA].userData, tess->polygonUserData); - tess->vertexDataCallback(mainContour[idxB].userData, tess->polygonUserData); - tess->vertexDataCallback(mainContour[idxC].userData, tess->polygonUserData); - } else if (tess->vertexCallback) { - tess->vertexCallback(mainContour[idxA].userData); - tess->vertexCallback(mainContour[idxB].userData); - tess->vertexCallback(mainContour[idxC].userData); - } - } -} - -const unsigned char* gluErrorString(GLenum error) -{ - static const unsigned char errStr[] = "GLU tesselator error"; - (void)error; - return errStr; -} - -//============================================================================= -// Quadric stubs - not implemented (used for 3D primitives, not critical) -//============================================================================= - -typedef struct GLUquadric GLUquadric; - -GLUquadric* gluNewQuadric() -{ - return nullptr; -} - -void gluDeleteQuadric(GLUquadric* q) -{ - (void)q; -} - -void gluQuadricDrawStyle(GLUquadric* q, int s) -{ - (void)q; (void)s; -} - -void gluQuadricNormals(GLUquadric* q, int n) -{ - (void)q; (void)n; -} - -void gluCylinder(GLUquadric* q, double b, double t, double h, int sl, int st) -{ - (void)q; (void)b; (void)t; (void)h; (void)sl; (void)st; -} - -void gluDisk(GLUquadric* q, double i, double o, int sl, int lp) -{ - (void)q; (void)i; (void)o; (void)sl; (void)lp; -} - -void gluSphere(GLUquadric* q, double r, int sl, int st) -{ - (void)q; (void)r; (void)sl; (void)st; -} - -void gluPerspective(double fovy, double aspect, double zNear, double zFar) -{ - (void)fovy; (void)aspect; (void)zNear; (void)zFar; -} - -} // extern "C"