using System.Diagnostics; using ImGuiNET; using static Dalamud.NativeFunctions; namespace Dalamud.Interface.Internal.ManagedAsserts { /// /// Report ImGui problems with a MessageBox dialog. /// internal static class ImGuiManagedAsserts { /// /// Gets or sets a value indicating whether asserts are enabled for ImGui. /// public static bool AssertsEnabled { get; set; } /// /// Create a snapshot of the current ImGui context. /// Should be called before rendering an ImGui frame. /// /// A snapshot of the current context. public static unsafe ImGuiContextSnapshot GetSnapshot() { var contextPtr = ImGui.GetCurrentContext(); var styleVarStack = *((int*)contextPtr + ImGuiContextOffsets.StyleVarStackOffset); // ImVector.Size var colorStack = *((int*)contextPtr + ImGuiContextOffsets.ColorStackOffset); // ImVector.Size var fontStack = *((int*)contextPtr + ImGuiContextOffsets.FontStackOffset); // ImVector.Size var popupStack = *((int*)contextPtr + ImGuiContextOffsets.BeginPopupStackOffset); // ImVector.Size var windowStack = *((int*)contextPtr + ImGuiContextOffsets.CurrentWindowStackOffset); // ImVector.Size return new ImGuiContextSnapshot { StyleVarStackSize = styleVarStack, ColorStackSize = colorStack, FontStackSize = fontStack, BeginPopupStackSize = popupStack, WindowStackSize = windowStack, }; } /// /// Compare a snapshot to the current post-draw state and report any errors in a MessageBox dialog. /// /// The source of any problems, something to blame. /// ImGui context snapshot. public static void ReportProblems(string source, ImGuiContextSnapshot before) { if (!AssertsEnabled) { return; } var cSnap = GetSnapshot(); if (before.StyleVarStackSize != cSnap.StyleVarStackSize) { ShowAssert(source, $"You forgot to pop a style var!\n\nBefore: {before.StyleVarStackSize}, after: {cSnap.StyleVarStackSize}"); return; } if (before.ColorStackSize != cSnap.ColorStackSize) { ShowAssert(source, $"You forgot to pop a color!\n\nBefore: {before.ColorStackSize}, after: {cSnap.ColorStackSize}"); return; } if (before.FontStackSize != cSnap.FontStackSize) { ShowAssert(source, $"You forgot to pop a font!\n\nBefore: {before.FontStackSize}, after: {cSnap.FontStackSize}"); return; } if (before.BeginPopupStackSize != cSnap.BeginPopupStackSize) { ShowAssert(source, $"You forgot to end a popup!\n\nBefore: {before.BeginPopupStackSize}, after: {cSnap.BeginPopupStackSize}"); return; } if (cSnap.WindowStackSize != 1) { if (cSnap.WindowStackSize > 1) { ShowAssert(source, $"Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?\n\ncSnap.WindowStackSize = {cSnap.WindowStackSize}"); } else { ShowAssert(source, $"Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?\n\ncSnap.WindowStackSize = {cSnap.WindowStackSize}"); } } } private static void ShowAssert(string source, string message) { var caption = $"You fucked up"; message = $"{message}\n\nSource: {source}\n\nAsserts are now disabled. You may re-enable them."; var flags = MessageBoxType.Ok | MessageBoxType.IconError; _ = MessageBoxW(Process.GetCurrentProcess().MainWindowHandle, message, caption, flags); AssertsEnabled = false; } /// /// A snapshot of various ImGui context properties. /// public class ImGuiContextSnapshot { /// /// Gets the ImGui style var stack size. /// public int StyleVarStackSize { get; init; } /// /// Gets the ImGui color stack size. /// public int ColorStackSize { get; init; } /// /// Gets the ImGui font stack size. /// public int FontStackSize { get; init; } /// /// Gets the ImGui begin popup stack size. /// public int BeginPopupStackSize { get; init; } /// /// Gets the ImGui window stack size. /// public int WindowStackSize { get; init; } } } }