Skip to content

Commit fdf8d02

Browse files
committed
Debug Tools: Added io.ConfigDebugIsDebuggerPresent and Debug Break buttons. (#2673)
1 parent 788bb58 commit fdf8d02

File tree

6 files changed

+152
-15
lines changed

6 files changed

+152
-15
lines changed

docs/CHANGELOG.txt

+7-1
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,17 @@ Other changes:
8383
movements toward another parent BeginMenu() can keep the wrong child menu open. (#6671, #6926)
8484
- Settings: Fixed an issue marking settings as dirty when merely clicking on a border or resize
8585
grip without moving it.
86+
- Debug Tools: Added io.ConfigDebugIsDebuggerPresent option. When enabled, this adds buttons
87+
in various locations of Metrics/Debugger to manually break in debugger in selected places:
88+
- Request a debug break in a Begin() call.
89+
- Request a debug break in a ItemAdd() call via debug log and hovering 0xXXXXXX identifiers.
90+
- Request a debug break in a BeginTable() call.
91+
- Request a debug break in a SetShortcutRouting()/Shortcut() call. [Internal]
92+
- Debug Tools: Metrics: Reorganize Tools menu.
8693
- Debug Tools: Added DebugFlashStyleColor() to identify a style color. Added to Style Editor.
8794
- Debug Tools: Debug Log: Hide its own clipper log to reduce noise in the output. (#5855)
8895
- Debug Tools: Debug Log: Clicking any filter with SHIFT held enables it for 2 frames only,
8996
making it easier when dealing with spammy logs. (#5855)
90-
- Debug Tools: Metrics: Reorganize Tools menu.
9197
- Misc: Added IMGUI_USER_H_FILENAME to change the path included when using
9298
IMGUI_INCLUDE_IMGUI_USER_H. (#7039) [@bryceberger]
9399
- Misc: Rework debug display of texture id in Metrics window to avoid compile-error when

imgui.cpp

+101-5
Original file line numberDiff line numberDiff line change
@@ -4787,7 +4787,10 @@ void ImGui::NewFrame()
47874787
UpdateDebugToolStackQueries();
47884788
UpdateDebugToolFlashStyleColor();
47894789
if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0)
4790+
{
47904791
g.DebugLocateId = 0;
4792+
g.DebugBreakInLocateId = false;
4793+
}
47914794
if (g.DebugLogAutoDisableFrames > 0 && --g.DebugLogAutoDisableFrames == 0)
47924795
{
47934796
DebugLog("(Debug Log: Auto-disabled some ImGuiDebugLogFlags after 2 frames)\n");
@@ -6372,6 +6375,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
63726375
if (window_just_created)
63736376
window = CreateNewWindow(name, flags);
63746377

6378+
// [DEBUG] Debug break requested by user
6379+
if (g.DebugBreakInWindow == window->ID)
6380+
IM_DEBUG_BREAK();
6381+
63756382
// Automatically disable manual moving/resizing when NoInputs is set
63766383
if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)
63776384
flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
@@ -8365,6 +8372,10 @@ bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiI
83658372
else
83668373
IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteMask_)); // Check that only 1 routing flag is used
83678374

8375+
// [DEBUG] Debug break requested by user
8376+
if (g.DebugBreakInShortcutRouting == key_chord)
8377+
IM_DEBUG_BREAK();
8378+
83688379
if (flags & ImGuiInputFlags_RouteUnlessBgFocused)
83698380
if (g.NavWindow == NULL)
83708381
return false;
@@ -14013,6 +14024,9 @@ void ImGui::ShowMetricsWindow(bool* p_open)
1401314024
return;
1401414025
}
1401514026

14027+
// [DEBUG] Clear debug breaks hooks after exactly one cycle.
14028+
DebugBreakClearData();
14029+
1401614030
// Basic info
1401714031
Text("Dear ImGui %s", GetVersion());
1401814032
Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
@@ -14072,13 +14086,14 @@ void ImGui::ShowMetricsWindow(bool* p_open)
1407214086
// Tools
1407314087
if (TreeNode("Tools"))
1407414088
{
14075-
SeparatorText("Debug breaks");
14076-
14089+
// Debug Break features
1407714090
// The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.
14078-
if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)
14079-
DebugStartItemPicker();
14091+
SeparatorTextEx(0, "Debug breaks", NULL, CalcTextSize("(?)").x + g.Style.SeparatorTextPadding.x);
1408014092
SameLine();
1408114093
MetricsHelpMarker("Will call the IM_DEBUG_BREAK() macro to break in debugger.\nWarning: If you don't have a debugger attached, this will probably crash.");
14094+
if (Checkbox("Show Item Picker", &g.DebugItemPickerActive) && g.DebugItemPickerActive)
14095+
DebugStartItemPicker();
14096+
Checkbox("Show \"Debug Break\" buttons in other sections", &g.IO.ConfigDebugIsDebuggerPresent);
1408214097

1408314098
SeparatorText("Visualize");
1408414099

@@ -14166,7 +14181,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
1416614181
{
1416714182
static char buf[64] = "";
1416814183
SetNextItemWidth(-FLT_MIN);
14169-
InputText("##Text", buf, IM_ARRAYSIZE(buf));
14184+
InputText("##DebugTextEncodingBuf", buf, IM_ARRAYSIZE(buf));
1417014185
if (buf[0] != 0)
1417114186
DebugTextEncoding(buf);
1417214187
}
@@ -14434,6 +14449,12 @@ void ImGui::ShowMetricsWindow(bool* p_open)
1443414449
ImGuiKeyChord key_chord = key | routing_data->Mods;
1443514450
Text("%s: 0x%08X", GetKeyChordName(key_chord, key_chord_name, IM_ARRAYSIZE(key_chord_name)), routing_data->RoutingCurr);
1443614451
DebugLocateItemOnHover(routing_data->RoutingCurr);
14452+
if (g.IO.ConfigDebugIsDebuggerPresent)
14453+
{
14454+
SameLine();
14455+
if (DebugBreakButton("**DebugBreak**", "in SetShortcutRouting() for this KeyChord"))
14456+
g.DebugBreakInShortcutRouting = key_chord;
14457+
}
1443714458
idx = routing_data->NextEntryIndex;
1443814459
}
1443914460
}
@@ -14545,6 +14566,64 @@ void ImGui::ShowMetricsWindow(bool* p_open)
1454514566
End();
1454614567
}
1454714568

14569+
void ImGui::DebugBreakClearData()
14570+
{
14571+
// Those fields are scattered in their respective subsystem to stay in hot-data locations
14572+
ImGuiContext& g = *GImGui;
14573+
g.DebugBreakInWindow = 0;
14574+
g.DebugBreakInTable = 0;
14575+
g.DebugBreakInShortcutRouting = ImGuiKey_None;
14576+
}
14577+
14578+
void ImGui::DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location)
14579+
{
14580+
if (!BeginItemTooltip())
14581+
return;
14582+
Text("To call IM_DEBUG_BREAK() %s:", description_of_location);
14583+
Separator();
14584+
TextUnformatted(keyboard_only ? "- Press 'Pause/Break' on keyboard." : "- Press 'Pause/Break' on keyboard.\n- or Click (may alter focus/active id).\n- or navigate using keyboard and press space.");
14585+
Separator();
14586+
TextUnformatted("Choose one way that doesn't interfere with what you are trying to debug!\nYou need a debugger attached or this will crash!");
14587+
EndTooltip();
14588+
}
14589+
14590+
// Special button that doesn't take focus, doesn't take input owner, and can be activated without a click etc.
14591+
// In order to reduce interferences with the contents we are trying to debug into.
14592+
bool ImGui::DebugBreakButton(const char* label, const char* description_of_location)
14593+
{
14594+
ImGuiWindow* window = GetCurrentWindow();
14595+
if (window->SkipItems)
14596+
return false;
14597+
14598+
ImGuiContext& g = *GImGui;
14599+
const ImGuiID id = window->GetID(label);
14600+
const ImVec2 label_size = CalcTextSize(label, NULL, true);
14601+
ImVec2 pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrLineTextBaseOffset);
14602+
ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x * 2.0f, label_size.y);
14603+
14604+
const ImRect bb(pos, pos + size);
14605+
ItemSize(size, 0.0f);
14606+
if (!ItemAdd(bb, id))
14607+
return false;
14608+
14609+
// WE DO NOT USE ButtonEx() or ButtonBehavior() in order to reduce our side-effects.
14610+
bool hovered = ItemHoverable(bb, id, g.CurrentItemFlags);
14611+
bool pressed = hovered && (IsKeyChordPressed(g.DebugBreakKeyChord) || IsMouseClicked(0) || g.NavActivateId == id);
14612+
DebugBreakButtonTooltip(false, description_of_location);
14613+
14614+
ImVec4 col4f = GetStyleColorVec4(hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
14615+
ImVec4 hsv;
14616+
ColorConvertRGBtoHSV(col4f.x, col4f.y, col4f.z, hsv.x, hsv.y, hsv.z);
14617+
ColorConvertHSVtoRGB(hsv.x + 0.20f, hsv.y, hsv.z, col4f.x, col4f.y, col4f.z);
14618+
14619+
RenderNavHighlight(bb, id);
14620+
RenderFrame(bb.Min, bb.Max, GetColorU32(col4f), true, g.Style.FrameRounding);
14621+
RenderTextClipped(bb.Min, bb.Max, label, NULL, &label_size, g.Style.ButtonTextAlign, &bb);
14622+
14623+
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);
14624+
return pressed;
14625+
}
14626+
1454814627
// [DEBUG] Display contents of Columns
1454914628
void ImGui::DebugNodeColumns(ImGuiOldColumns* columns)
1455014629
{
@@ -14883,6 +14962,9 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)
1488314962
if (window->MemoryCompacted)
1488414963
TextDisabled("Note: some memory buffers have been compacted/freed.");
1488514964

14965+
if (g.IO.ConfigDebugIsDebuggerPresent && DebugBreakButton("**DebugBreak**", "in Begin()"))
14966+
g.DebugBreakInWindow = window->ID;
14967+
1488614968
ImGuiWindowFlags flags = window->Flags;
1488714969
DebugNodeDrawList(window, window->Viewport, window->DrawList, "DrawList");
1488814970
BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y);
@@ -15117,6 +15199,7 @@ void ImGui::DebugLocateItem(ImGuiID target_id)
1511715199
ImGuiContext& g = *GImGui;
1511815200
g.DebugLocateId = target_id;
1511915201
g.DebugLocateFrames = 2;
15202+
g.DebugBreakInLocateId = false;
1512015203
}
1512115204

1512215205
void ImGui::DebugLocateItemOnHover(ImGuiID target_id)
@@ -15126,11 +15209,24 @@ void ImGui::DebugLocateItemOnHover(ImGuiID target_id)
1512615209
ImGuiContext& g = *GImGui;
1512715210
DebugLocateItem(target_id);
1512815211
GetForegroundDrawList(g.CurrentWindow)->AddRect(g.LastItemData.Rect.Min - ImVec2(3.0f, 3.0f), g.LastItemData.Rect.Max + ImVec2(3.0f, 3.0f), DEBUG_LOCATE_ITEM_COLOR);
15212+
15213+
// Can't easily use a context menu here because it will mess with focus, active id etc.
15214+
if (g.IO.ConfigDebugIsDebuggerPresent && g.MouseStationaryTimer > 1.0f)
15215+
{
15216+
DebugBreakButtonTooltip(false, "in ItemAdd()");
15217+
if (IsKeyChordPressed(g.DebugBreakKeyChord))
15218+
g.DebugBreakInLocateId = true;
15219+
}
1512915220
}
1513015221

1513115222
void ImGui::DebugLocateItemResolveWithLastItem()
1513215223
{
1513315224
ImGuiContext& g = *GImGui;
15225+
15226+
// [DEBUG] Debug break requested by user
15227+
if (g.DebugBreakInLocateId)
15228+
IM_DEBUG_BREAK();
15229+
1513415230
ImGuiLastItemData item_data = g.LastItemData;
1513515231
g.DebugLocateId = 0;
1513615232
ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow);

imgui.h

+13-7
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
// Library Version
2525
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
2626
#define IMGUI_VERSION "1.90.1 WIP"
27-
#define IMGUI_VERSION_NUM 19003
27+
#define IMGUI_VERSION_NUM 19004
2828
#define IMGUI_HAS_TABLE
2929

3030
/*
@@ -2089,16 +2089,22 @@ struct ImGuiIO
20892089
// Debug options
20902090
//------------------------------------------------------------------
20912091

2092+
// Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro.
2093+
// - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability.
2094+
// - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.
2095+
// e.g. io.ConfigDebugIsDebuggerPresent = ::IsDebuggerPresent() on Win32, or refer to ImOsIsDebuggerPresent() imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version).
2096+
bool ConfigDebugIsDebuggerPresent; // = false // Enable various tools calling IM_DEBUG_BREAK().
2097+
20922098
// Tools to test correct Begin/End and BeginChild/EndChild behaviors.
2093-
// Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX()
2094-
// This is inconsistent with other BeginXXX functions and create confusion for many users.
2095-
// We expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior.
2099+
// - Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX()
2100+
// - This is inconsistent with other BeginXXX functions and create confusion for many users.
2101+
// - We expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior.
20962102
bool ConfigDebugBeginReturnValueOnce;// = false // First-time calls to Begin()/BeginChild() will return false. NEEDS TO BE SET AT APPLICATION BOOT TIME if you don't want to miss windows.
20972103
bool ConfigDebugBeginReturnValueLoop;// = false // Some calls to Begin()/BeginChild() will return false. Will cycle through window depths then repeat. Suggested use: add "io.ConfigDebugBeginReturnValue = io.KeyShift" in your main loop then occasionally press SHIFT. Windows should be flickering while running.
20982104

2099-
// Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data.
2100-
// Backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them.
2101-
// Consider using e.g. Win32's IsDebuggerPresent() as an additional filter (or see ImOsIsDebuggerPresent() in imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version).
2105+
// Option to deactivate io.AddFocusEvent(false) handling.
2106+
// - May facilitate interactions with a debugger when focus loss leads to clearing inputs data.
2107+
// - Backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them.
21022108
bool ConfigDebugIgnoreFocusLoss; // = false // Ignore io.AddFocusEvent(false), consequently not calling io.ClearInputKeys() in input processing.
21032109

21042110
// Options to audit .ini data

imgui_demo.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -478,10 +478,12 @@ void ImGui::ShowDemoWindow(bool* p_open)
478478
ImGui::Text("Also see Style->Rendering for rendering options.");
479479

480480
ImGui::SeparatorText("Debug");
481+
ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent);
482+
ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application.");
481483
ImGui::BeginDisabled();
482484
ImGui::Checkbox("io.ConfigDebugBeginReturnValueOnce", &io.ConfigDebugBeginReturnValueOnce); // .
483485
ImGui::EndDisabled();
484-
ImGui::SameLine(); HelpMarker("First calls to Begin()/BeginChild() will return false.\n\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover");
486+
ImGui::SameLine(); HelpMarker("First calls to Begin()/BeginChild() will return false.\n\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover.");
485487
ImGui::Checkbox("io.ConfigDebugBeginReturnValueLoop", &io.ConfigDebugBeginReturnValueLoop);
486488
ImGui::SameLine(); HelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running.");
487489
ImGui::Checkbox("io.ConfigDebugIgnoreFocusLoss", &io.ConfigDebugIgnoreFocusLoss);

imgui_internal.h

+15
Original file line numberDiff line numberDiff line change
@@ -1907,6 +1907,7 @@ struct ImGuiContext
19071907
ImGuiStorage WindowsById; // Map window's ImGuiID to ImGuiWindow*
19081908
int WindowsActiveCount; // Number of unique windows submitted by frame
19091909
ImVec2 WindowsHoverPadding; // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING)
1910+
ImGuiID DebugBreakInWindow; // Set to break in Begin() call.
19101911
ImGuiWindow* CurrentWindow; // Window being drawn into
19111912
ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs.
19121913
ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set.
@@ -1958,6 +1959,7 @@ struct ImGuiContext
19581959
ImGuiKeyRoutingTable KeysRoutingTable;
19591960
ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it)
19601961
bool ActiveIdUsingAllKeyboardKeys; // Active widget will want to read all keyboard keys inputs. (FIXME: This is a shortcut for not taking ownership of 100+ keys but perhaps best to not have the inconsistency)
1962+
ImGuiKeyChord DebugBreakInShortcutRouting; // Set to break in SetShortcutRouting()/Shortcut() calls.
19611963
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
19621964
ImU32 ActiveIdUsingNavInputMask; // If you used this. Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);'
19631965
#endif
@@ -2074,6 +2076,7 @@ struct ImGuiContext
20742076

20752077
// Tables
20762078
ImGuiTable* CurrentTable;
2079+
ImGuiID DebugBreakInTable; // Set to break in BeginTable() call.
20772080
int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size)
20782081
ImVector<ImGuiTableTempData> TablesTempData; // Temporary table data (buffers reused/shared across instances, support nesting)
20792082
ImPool<ImGuiTable> Tables; // Persistent table data
@@ -2166,6 +2169,8 @@ struct ImGuiContext
21662169
ImGuiDebugLogFlags DebugLogAutoDisableFlags;
21672170
ImU8 DebugLogAutoDisableFrames;
21682171
ImU8 DebugLocateFrames; // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above.
2172+
bool DebugBreakInLocateId; // Debug break in ItemAdd() call for g.DebugLocateId.
2173+
ImGuiKeyChord DebugBreakKeyChord; // = ImGuiKey_Pause
21692174
ImS8 DebugBeginReturnValueCullDepth; // Cycle between 0..9 then wrap around.
21702175
bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker())
21712176
ImU8 DebugItemPickerMouseButton;
@@ -2360,6 +2365,13 @@ struct ImGuiContext
23602365
DebugFlashStyleColorTime = 0.0f;
23612366
DebugFlashStyleColorIdx = ImGuiCol_COUNT;
23622367

2368+
// Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations
2369+
DebugBreakInWindow = 0;
2370+
DebugBreakInTable = 0;
2371+
DebugBreakInLocateId = false;
2372+
DebugBreakKeyChord = ImGuiKey_Pause;
2373+
DebugBreakInShortcutRouting = ImGuiKey_None;
2374+
23632375
memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));
23642376
FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;
23652377
FramerateSecPerFrameAccum = 0.0f;
@@ -3406,6 +3418,9 @@ namespace ImGui
34063418
IMGUI_API void DebugLocateItem(ImGuiID target_id); // Call sparingly: only 1 at the same time!
34073419
IMGUI_API void DebugLocateItemOnHover(ImGuiID target_id); // Only call on reaction to a mouse Hover: because only 1 at the same time!
34083420
IMGUI_API void DebugLocateItemResolveWithLastItem();
3421+
IMGUI_API void DebugBreakClearData();
3422+
IMGUI_API bool DebugBreakButton(const char* label, const char* description_of_location);
3423+
IMGUI_API void DebugBreakButtonTooltip(bool keyboard_only, const char* description_of_location);
34093424
inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; }
34103425
IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas);
34113426
IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end);

0 commit comments

Comments
 (0)