THE BUG IS FIXED. tests/kicad/quasimodal-strand.spec.ts flips from a test.fail() pin to a plain green regression test: 3/3 runs closed=true dialogs=0 refused-resumes=0 (was closed=false dialogs=1 refused-resumes=1 on every run). Mechanism: a quasi-modal's nested event loop parked on the TOOL COROUTINE's stack, which suspends the fiber's body where the fiber layer cannot see it — so the stale-fiber guard quarantined the fiber and then refused its own resume, the dispatch guard was never released, and every click after that was deferred forever. Bouncing the nested loop onto the main stack leaves the coroutine suspended the legitimate way (a recorded fiber swap), so nothing is quarantined and nothing is refused. Layering, so this is not a pile of WASM ifdefs in KiCad: - wx (3d37db3bf1) owns the POLICY and the hook; it must not know what a coroutine is. - wasm/bindings/main_stack_runner.h is the only place that may know both sides: it finds the frame's TOOL_MANAGER and bounces via RunMainStack. Header-only and self-installing, so no build-script change; included by every editor's binding TU. - KiCad gets ONE ifdef-free method (2c777efede), needed only because TOOL_STATE is opaque outside TOOL_MANAGER. libcontext and dialog_shim are untouched — an earlier draft edited both and was reverted. This also reframes the remaining plan: the doc-19 class is closed WITHOUT migrating tool coroutines onto scheduler contexts. Note it does not make the wait a context yield — waits still park in place, just never on a coroutine stack. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TEHGiiXMShNXbBr7gSJ7iz
92 lines
2.9 KiB
C++
92 lines
2.9 KiB
C++
/*
|
|
* GerbView embind bindings.
|
|
*
|
|
* GerbView boots without a document (it is one of `FILELESS_TOOLS`), but a
|
|
* project route CAN name a gerber: clicking `Production/gerbers/board-F_Cu.gbr`
|
|
* on the project page deep-links here. Until this TU existed the bundle had no
|
|
* embind surface at all, so the shell staged the file into MEMFS and then had
|
|
* no way to say "open it" — GerbView came up empty and the user had to walk
|
|
* File→Open themselves.
|
|
*
|
|
* A gerber is rarely useful alone (a fabrication set is a stack of layers plus
|
|
* drill files), and `GERBVIEW_FRAME::OpenProjectFiles` already takes a LIST and
|
|
* auto-routes each entry by filename to the gerber / Excellon / job / archive
|
|
* loader, then zoom-fits. So the primary entry point here is the multi-file
|
|
* one: the shell hands over every gerber+drill sibling in the clicked file's
|
|
* folder, and the whole board renders.
|
|
*/
|
|
#include <emscripten.h>
|
|
#include <emscripten/bind.h>
|
|
#include <gerbview_frame.h>
|
|
#include <kiway_player.h>
|
|
#include <nlohmann/json.hpp>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <wx/app.h>
|
|
#include <wx/string.h>
|
|
#include "main_stack_runner.h"
|
|
#include "open_gate.h"
|
|
|
|
using namespace emscripten;
|
|
using json = nlohmann::json;
|
|
|
|
static GERBVIEW_FRAME* gerbFrame()
|
|
{
|
|
return wxTheApp ? dynamic_cast<GERBVIEW_FRAME*>( wxTheApp->GetTopWindow() ) : nullptr;
|
|
}
|
|
|
|
static bool openFileSet( const std::vector<wxString>& aFiles )
|
|
{
|
|
// Held across every Asyncify park of the load (open_gate.h): the layer load
|
|
// parks, and a wx timer dispatched into a half-built layer set traps.
|
|
pcbjam_open::BusyGuard busy;
|
|
|
|
GERBVIEW_FRAME* frame = gerbFrame();
|
|
|
|
if( !frame || aFiles.empty() )
|
|
return false;
|
|
|
|
return frame->OpenProjectFiles( aFiles, 0 );
|
|
}
|
|
|
|
/** Open ONE gerber/drill file (the generic single-file entry every app has). */
|
|
static bool kicadOpenFile( std::string path )
|
|
{
|
|
return openFileSet( { wxString::FromUTF8( path.c_str() ) } );
|
|
}
|
|
|
|
/**
|
|
* Open a whole fabrication set: a JSON array of MEMFS paths. Order is the
|
|
* caller's (the shell sorts, so layer order is stable across reloads).
|
|
* GERBVIEW_FRAME caps the set at GERBER_DRAWLAYERS_COUNT internally.
|
|
*/
|
|
static bool kicadOpenFiles( std::string pathsJson )
|
|
{
|
|
json paths = json::parse( pathsJson, nullptr, /*allow_exceptions*/ false );
|
|
|
|
if( !paths.is_array() )
|
|
return false;
|
|
|
|
std::vector<wxString> files;
|
|
|
|
for( const auto& entry : paths )
|
|
{
|
|
if( entry.is_string() )
|
|
files.push_back( wxString::FromUTF8( entry.get<std::string>().c_str() ) );
|
|
}
|
|
|
|
return openFileSet( files );
|
|
}
|
|
|
|
/** JS-pollable open-in-flight probe — same contract as the editors. */
|
|
static bool kicadOpenFileBusy()
|
|
{
|
|
return pcbjam_open::busy();
|
|
}
|
|
|
|
EMSCRIPTEN_BINDINGS( gerbview )
|
|
{
|
|
function( "kicadOpenFile", &kicadOpenFile );
|
|
function( "kicadOpenFiles", &kicadOpenFiles );
|
|
function( "kicadOpenFileBusy", &kicadOpenFileBusy );
|
|
}
|