From a28c9c3a827bbae9a2fcfe0434bdfabaecdb91e4 Mon Sep 17 00:00:00 2001 From: Downtime Date: Mon, 12 Oct 2026 18:25:17 -0500 Subject: [PATCH] fix(gui): guard gizmo actions before deferred initialization (#16268) GLGizmosManager can be called while its gizmo list is empty, either at startup before the canvas has loaded the gizmos or after their init() failed. reset_all_states(), open_gizmo(), open_gizmo_by_shortcut() and activate_gizmo() now return early in that case. This fixes a Debug-build assert when a new project resets the gizmos at startup, and an out-of-bounds read in release builds when a gizmo shortcut key is pressed after gizmo init failed. activate_gizmo(Undefined) still returns true, since no gizmo is open. The reset_all_states() guard was first proposed in #16074 by kenrap. Co-authored-by: kenrap <101818165+kenrap@users.noreply.github.com> --- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index d840754ac2..41d4811664 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -420,7 +420,10 @@ void GLGizmosManager::refresh_on_off_state() void GLGizmosManager::reset_all_states() { - if (! m_enabled || m_serializing) + // The restore/new-project event can run before deferred OpenGL resource + // loading has populated the gizmo list. There is nothing to reset until + // initialization completes. + if (! m_enabled || m_serializing || m_gizmos.empty()) return; const EType current = get_current_type(); @@ -435,6 +438,9 @@ void GLGizmosManager::reset_all_states() bool GLGizmosManager::open_gizmo(EType type) { + if (m_gizmos.empty()) + return false; + int idx = static_cast(type); // re-open same type cause closing @@ -522,7 +528,7 @@ bool GLGizmosManager::is_running() const bool GLGizmosManager::open_gizmo_by_shortcut(Shortcut shortcut) { - if (!m_enabled) + if (!m_enabled || m_gizmos.empty()) return false; // The text tool opens without a selection because it creates its own object. @@ -1427,10 +1433,12 @@ void GLGizmosManager::update_hover_state(const EType &type) bool GLGizmosManager::activate_gizmo(EType type) { - assert(!m_gizmos.empty()); - // already activated - if (m_current == type) return true; + if (m_current == type) + return true; + + if (m_gizmos.empty()) + return false; if (m_current != Undefined) { // clean up previous gizmo