fix(eeschema): wasm dialog positioning (web .window CSS) + collab SCH_SHAPE add via coroutine fiber stack
Thrust A — dialogs render top-left with OK clipped in the WEB app (not the test harness): root cause was the React shell missing the .window / .window-canvas CSS that wx.js relies on (it positions each dialog div via inline left/top, which need position:absolute). Added the rules to web/apps/frontend/src/index.css. Native draw-text now works end-to-end; symbol/power choosers render (placing still blocked by absent libraries). Thrust B — collab apply of a newly-added SCH_SHAPE trapped in KiCad core (SCH_COMMIT::Push CHT_ADD -> GAL view->Add, an asyncify invoke_* mis-dispatch) because doApply ran off a fiber stack. doApply now runs inside a COROUTINE so it executes on a libcontext fiber, the same context native draws use; the add dispatches correctly. Re-enabled the SCH_SHAPE converter (rect/circle). Added thirdparty/libcontext to the embind include path (tool/coroutine.h needs it). Verified two-tab: rectangle + circle drawn in tab A sync + render in tab B. Extended eeschema-collab.spec.ts apply test with a SCH_SHAPE add. All changes root-repo only; kicad and wxwidgets forks untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6bd17aeb26
commit
b577c05fae
6 changed files with 373 additions and 16 deletions
|
|
@ -37,6 +37,7 @@
|
|||
#include <sch_screen.h>
|
||||
#include <sch_sheet_path.h>
|
||||
#include <schematic_settings.h>
|
||||
#include <tool/coroutine.h>
|
||||
|
||||
using namespace emscripten;
|
||||
using json = nlohmann::json;
|
||||
|
|
@ -202,17 +203,46 @@ SCH_ITEM* makeItem( const json& j )
|
|||
|
||||
item = lbl;
|
||||
}
|
||||
// SCH_SHAPE `added` reconstruction is DEFERRED (returns nullptr → logged "no converter").
|
||||
// A constructed shape builds fine, but committing a *new* one traps in SCH_COMMIT::Push's
|
||||
// CHT_ADD path (the GAL view->Add of a new SCH_SHAPE) with a wasm "memory access out of
|
||||
// bounds". The identical view->Add succeeds when the shape is loaded from file and when an
|
||||
// existing shape is MOVED (CHT_MODIFY) — so this is the asyncify indirect-call (invoke_viii)
|
||||
// mis-dispatch class: a SCH_SHAPE add-path virtual that mis-dispatches only from the
|
||||
// programmatic CallAfter/apply context (same family as the original SCH_ITEM::Move trap,
|
||||
// 0003). The dyncall shim only catches the "signature mismatch" variant, and this trap
|
||||
// fires inside the wrong function so it can't be safely retried. itemToJson still emits the
|
||||
// shape geometry (forward-compatible + drives `changed`/move); only `added` is skipped.
|
||||
// Same blocker as SCH_SYMBOL add — see features/yjs-bridge/0006.
|
||||
else if( type == "SCH_SHAPE" )
|
||||
{
|
||||
// Reconstruct from the geometry itemToJson emits. Committing a *new* SCH_SHAPE used to
|
||||
// trap in SCH_COMMIT::Push's CHT_ADD path (GAL view->Add → an asyncify invoke_viii
|
||||
// mis-dispatch, "memory access out of bounds") because doApply ran off a fiber stack;
|
||||
// doApply now runs inside a COROUTINE (kicadCollabApply) so the add dispatches correctly,
|
||||
// exactly as a native draw does. (0006/0007.) NB FILL_T::NO_FILL == 1, not 0.
|
||||
SHAPE_T st = (SHAPE_T) j.value( "stype", (int) SHAPE_T::RECTANGLE );
|
||||
int layer = j.value( "layer", (int) LAYER_NOTES );
|
||||
int width = j.value( "width", 0 );
|
||||
FILL_T fill = (FILL_T) j.value( "fill", (int) FILL_T::NO_FILL );
|
||||
|
||||
auto* shp = new SCH_SHAPE( st, (SCH_LAYER_ID) layer, width, fill );
|
||||
|
||||
// Rectangle: two corners. Circle: start = center, end = a point on the radius. Both are
|
||||
// fully defined by start+end (what itemToJson emits via GetStart()/GetEnd()).
|
||||
shp->SetStart( VECTOR2I( j.value( "sx", 0 ), j.value( "sy", 0 ) ) );
|
||||
shp->SetEnd( VECTOR2I( j.value( "ex", 0 ), j.value( "ey", 0 ) ) );
|
||||
|
||||
if( st == SHAPE_T::ARC && j.contains( "cx" ) )
|
||||
{
|
||||
shp->SetCenterX( j["cx"].get<int>() );
|
||||
shp->SetCenterY( j["cy"].get<int>() );
|
||||
}
|
||||
else if( st == SHAPE_T::BEZIER )
|
||||
{
|
||||
if( j.contains( "c1x" ) )
|
||||
shp->SetBezierC1( VECTOR2I( j["c1x"].get<int>(), j["c1y"].get<int>() ) );
|
||||
if( j.contains( "c2x" ) )
|
||||
shp->SetBezierC2( VECTOR2I( j["c2x"].get<int>(), j["c2y"].get<int>() ) );
|
||||
}
|
||||
|
||||
if( SCH_EDIT_FRAME* fr = schFrame() )
|
||||
shp->SetParent( &fr->Schematic() );
|
||||
|
||||
item = shp;
|
||||
}
|
||||
// SCH_SYMBOL `added` is still deferred (needs the s-expr clipboard-blob emit + LoadContent
|
||||
// reconstruction; symbol PLACEMENT is also natively blocked until symbol libraries are
|
||||
// bundled — see features/yjs-bridge tasks). Symbol move/position already syncs via `changed`.
|
||||
|
||||
if( item )
|
||||
const_cast<KIID&>( item->m_Uuid ) = KIID( wxString::FromUTF8( j.value( "id", "" ).c_str() ) );
|
||||
|
|
@ -493,8 +523,22 @@ void kicadCollabApply( std::string aJson )
|
|||
if( !fr )
|
||||
return;
|
||||
|
||||
// Defer to the editor's main-loop context so SCH_COMMIT runs like a normal edit.
|
||||
fr->CallAfter( [fr, delta]() { doApply( fr, delta ); } );
|
||||
// Defer to the editor's main-loop context so SCH_COMMIT runs like a normal edit, AND run the
|
||||
// mutation inside a COROUTINE so it executes on a libcontext fiber stack — the exact context
|
||||
// KiCad tool actions (native draws/edits) run in. SCH_COMMIT::Push's CHT_ADD of a *new*
|
||||
// SCH_SHAPE/SCH_SYMBOL dispatches GAL virtuals (view->Add → ViewGetLayers) through asyncify-
|
||||
// instrumented invoke_*; off the fiber stack those mis-dispatch and trap inside KiCad core
|
||||
// ("memory access out of bounds" / "table index out of bounds"), which the bridge can't
|
||||
// devirtualize. On the fiber stack they dispatch correctly. CallAfter runs on the app main
|
||||
// stack (ProcessPendingEvents), which is where COROUTINE::Call must be invoked from. (0007.)
|
||||
fr->CallAfter( [fr, delta]() {
|
||||
COROUTINE<int, int> cor( [fr, delta]( int ) -> int
|
||||
{
|
||||
doApply( fr, delta );
|
||||
return 0;
|
||||
} );
|
||||
cor.Call( 0 );
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue