From f6c551540e4ef82d7e1cbf024e4ad858a8f00de6 Mon Sep 17 00:00:00 2001 From: Tommaso Bianchi Date: Sat, 5 Sep 2026 16:06:34 +0200 Subject: [PATCH] The key tracer had been printing one letter of the answer all along MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit focus= in [KEYTRACE] was never a class name. GetClassName() returns const wxChar* — wchar_t* in this build — and the line cast that to const char* and printed it with %s, so it emitted the first byte and stopped at the padding NUL. "wxGLCanvas" came out as "w". So did "wxWindow". Every focus reading taken from this instrument for a whole day of diagnosis was a single character, and the one question it existed to answer — WHICH widget has the keyboard — was the one it could not answer. Printed through wxString now, and it says focus=wxGLCanvas: the canvas does hold wx focus while the field is open, which removes the focus hypothesis for good. Also here, and HONESTLY LABELLED AS INCONCLUSIVE: a wxEVT_CHAR probe on the canvas. It logged nothing (cc=0), and the tempting reading is "the characters never reach the canvas". That reading is not available, because the probe is bound in the DesignCanvas constructor BEFORE GLCanvas3D::bind_event_handlers(), and wx runs the most recently bound handler first — GLCanvas3D::on_char returns without Skip() exactly when ImGui consumes a character, which is precisely the case under test. A silent probe is therefore consistent with ImGui consuming the keys correctly AND with them never arriving. It measures nothing. Rebind it after bind_event_handlers(), or instrument update_key_data itself, before believing anything about it. Writing this down rather than acting on it: I came within one commit of "fixing" a mechanism I had inferred from an instrument that could not see it, which is the same mistake as the focus= field above and the same mistake that cost a whole session in September. Deployed binary restored to 00d6c191dc (md5 0eeb9a58cef5). Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_011FbJKJAJxxkhDTs9XdZzKA --- src/slic3r/GUI/CAD/DesignCanvas.cpp | 11 +++++++++++ src/slic3r/GUI/CAD/DesignPanel.cpp | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/CAD/DesignCanvas.cpp b/src/slic3r/GUI/CAD/DesignCanvas.cpp index 40ac171264..7dd2b90875 100644 --- a/src/slic3r/GUI/CAD/DesignCanvas.cpp +++ b/src/slic3r/GUI/CAD/DesignCanvas.cpp @@ -93,6 +93,17 @@ DesignCanvas::DesignCanvas(wxWindow* parent) // re-entrancy guard giving up looks like. Refresh() posts a paint event instead: the current // frame finishes, the event loop runs (which is where ImGui's queued characters are consumed), // and the next frame starts clean. + // MEASUREMENT: does a typed character reach the GL canvas at all? Everything downstream of + // this point is known good (ImGui reports want_text=1 and our InputText active), so if these + // lines do not appear the character never got past the panel's CHAR_HOOK / the focus chain, + // and no amount of work inside the field will help. Skips always: a pure observer. + if (m_canvas_widget != nullptr && std::getenv("SNAPORCA_UXTRACE")) { + m_canvas_widget->Bind(wxEVT_CHAR, [](wxKeyEvent& e) { + fprintf(stderr, "[UX] canvas_char key=%d\n", e.GetKeyCode()); + fflush(stderr); + e.Skip(); + }); + } m_inline_editor->request_frame = [this] { // BOTH halves, and the dirty flag first: GLCanvas3D's paint handler returns without // rendering when the canvas is not marked dirty, so a bare Refresh() posts an event that diff --git a/src/slic3r/GUI/CAD/DesignPanel.cpp b/src/slic3r/GUI/CAD/DesignPanel.cpp index 4d767ddd5e..a84d370ea0 100644 --- a/src/slic3r/GUI/CAD/DesignPanel.cpp +++ b/src/slic3r/GUI/CAD/DesignPanel.cpp @@ -4184,7 +4184,14 @@ DesignPanel::DesignPanel(wxWindow* parent) fprintf(stderr, "[KEYTRACE] key=%d ui_mode=%d is_sketching=%d in_text=%d inline_busy=%d focus=%s\n", key, int(m_ui_mode), (m_viewport && m_viewport->is_sketching()) ? 1 : 0, in_text ? 1 : 0, (m_viewport && m_viewport->inline_busy()) ? 1 : 0, - fw ? (const char*) fw->GetClassInfo()->GetClassName() : "(none)"); + // wxString, not a cast: GetClassName() returns const wxChar* — wchar_t* in + // this build — and casting THAT to const char* and printing it with %s emits + // the first byte and stops at the padding NUL. Every focus= field this tracer + // has ever printed was a single letter: "wxGLCanvas" came out as "w", and so + // did "wxWindow". An instrument that silently truncates its most important + // field is worse than no instrument, and this one was trusted for a whole + // day's diagnosis. + fw ? wxString(fw->GetClassInfo()->GetClassName()).utf8_str().data() : "(none)"); fflush(stderr); }