From d00a87565bf2d05e8a0b2731c38a823b124fd7eb Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 20 Jul 2026 14:11:21 +0200 Subject: [PATCH 01/47] Fixed build when using IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS + IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#9482) Amend 6df50a0667 --- imgui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index fc1d4d912..32594498a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -11520,7 +11520,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu *p_highlight = g.FrameCount; *p_alive = g.FrameCount; if (*p_highlight >= g.FrameCount - 1) - window->DrawList->AddRect(bb.Min - ImVec2(1, 1), bb.Max + ImVec2(1, 1), IM_COL32(255, 0, 0, 255), 0.0f, ImDrawFlags_None, 2.0f); + window->DrawList->AddRect(bb.Min - ImVec2(1, 1), bb.Max + ImVec2(1, 1), IM_COL32(255, 0, 0, 255), 0.0f, 2.0f); } #endif } From e4f7c5c7223876c8796dfe5be5877cb5747fae0a Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 20 Jul 2026 14:26:57 +0200 Subject: [PATCH 02/47] Disabling ImGuiItemFlags_LiveEditOnInputScalar by default! (#9476, #701) --- docs/CHANGELOG.txt | 39 +++++++++++++++++++++++---------------- imgui.cpp | 5 +++++ imgui.h | 14 ++++++-------- imgui_internal.h | 2 +- 4 files changed, 35 insertions(+), 25 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5cecbdb37..2c88d5706 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -47,6 +47,12 @@ Breaking Changes: of directly poking to io.ConfigColorEditFlags. More consistent and easier to discover. - Drag and Drop: commented out legacy name `ImGuiDragDropFlags_SourceAutoExpirePayload` which obsoleted in 1.90.9 (July 2024). Use `ImGuiDragDropFlags_PayloadAutoExpire`. +- DragXXX, SliderXXX, InputScalar: with `ImGuiItemFlags_LiveEditOnInputScalar` now + defaulting to being disabled: inputting a value with the keyboard doesn't write + intermediate values to the backing variable. (#9476) + Before: DragFloat() with user typing "123" --> write back 1, then 12, then 123. + After: DragFloat() with user typing "123" --> write back 123 when validated/unfocused. + You can enable the flag back for a given scope if needed by specific widget/behavior. Other Changes: @@ -63,9 +69,13 @@ Other Changes: - Added `ImGuiItemFlags_LiveEditOnInputText` and `ImGuiItemFlags_LiveEditOnInputScalar` flags to configure the timing of applying edits of backing variables when typing values using a keyboard. - (#701, #9476, #3936, #3946, #5904, #6284, #8149, #8065, #8665, #9117, #9299, #700, #1351, + (#9476, #701, #3936, #3946, #5904, #6284, #8149, #8065, #8665, #9117, #9299, #700, #1351, #1875, #2060, #2215, #2380, #2550, #3083, #3338, #3556, #4373, #4714, #4885, #5184, #5777, #6707, #6766, #8004, #8303, #8915, #9308) + - ImGuiItemFlags_LiveEditOnInputText is enabled by default (same as before): + - The expectation is that for strings/text, enabling LiveEdit is a better default. + - ImGuiItemFlags_LiveEditOnInputScalar is disabled by default (new behavior): + - The expectation is that for numeric/scalars values, disabling LiveEdit is a better default. - Until now: - Edits where always applied immediately to backing variable, which is equivalent to the `ImGuiItemFlags_LiveEditXXX` flags being enabled. @@ -76,28 +86,25 @@ Other Changes: Workarounds often required a backing store for scalar values, and there were also a few niggles related to `IsItemDeactivatedAfterEdit()` when using +/- buttons of an `InputInt()` widgets. - Many of those situations can now be naturally simplified by disabling - `ImGuiItemFlags_LiveEditOnInputScalar`, which is expected to become the default. + - Many of those situations can now be naturally simplified when disabling + `ImGuiItemFlags_LiveEditOnInputScalar` is disabled. - The new flags allows disabling this behavior selectively for strings fields such as `InputText()` vs scalar fields: `SliderInt()`, `InputFloat()`, etc. - When LiveEdit is disabled, edits are applied when pressing enter, tabbing out, clearing a field or deactivating due to a focus loss. - - The flag may be altered programmatically: - PushItemFlag(ImGuiItemFlags_LiveEditOnInputScalar, false); // Disable for scalars + - The flag may be altered programmatically, e.g. for the entire frame scope: + NewFrame(); + PushItemFlag(ImGuiItemFlags_LiveEditOnInputScalar, true); // Enable for scalars for the whole frame + .... + PopItemFlag(); + EndFrame(); + Or for a specific widget: + PushItemFlag(ImGuiItemFlags_LiveEditOnInput, true); // Enable for one widget SliderInt(...); PopItemFlag(); - PushItemFlag(ImGuiItemFlags_LiveEditOnInput, true); // Enable for all - SliderInt(...); - PopItemFlag(); - But with upcoming new defaults it is expected you shouldn't touch them much. - - Both flags currently defaults to true, which matches previous behavior. - - The expectation is that for strings/text, enabling LiveEdit is a better default. - - The expectation is that for scalars, disable LiveEdit is a better default. - - The short-term intent is to change `ImGuiItemFlags_LiveEditOnInputScalar` to - default to being disabled, as soon as we get more feedback from users (SOON). - We intentionally are not adding `io.ConfigLiveEditXXX` fields to dictate the - default value of each `ImGuiItemFlags_LiveEditXXX`, because this is not - expected to be a user preference but a programmer/widget preferences. + default value of each `ImGuiItemFlags_LiveEditXXX`, because this is not expected + to be a user preference but a programmer/widget preferences. Also, we strive to make the toolkit consistent. - InputText: - Added `style.InputTextCursorSize` to configure cursor/caret thickness. (#7031, #9409) diff --git a/imgui.cpp b/imgui.cpp index 32594498a..ba8236e2b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -395,6 +395,11 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2026/07/20 (1.92.9) - DragXXX, SliderXXX, InputScalar: with `ImGuiItemFlags_LiveEditOnInputScalar` now defaulting to being disabled: + inputting a value with the keyboard doesn't write intermediate values to backing variable. (#9476) + - Before: DragFloat() with user typing "123" --> write back 1, then 12, then 123. + - After: DragFloat() with user typing "123" --> write back 123 when validated/unfocused. + You can enable the flag back for a given scope if needed by specific widget/behavior. - 2026/07/08 (1.92.9) - Drag and Drop: commented out legacy name `ImGuiDragDropFlags_SourceAutoExpirePayload` which obsoleted in 1.90.9 (July 2024). Use `ImGuiDragDropFlags_PayloadAutoExpire`. - 2026/07/06 (1.92.9) - ColorEdit: obsoleted SetColorEditOptions() function added in 1.51 (June 2017) in favor of directly poking to io.ConfigColorEditFlags. More consistent and easier to discover. - 2026/06/02 (1.92.9) - TreeNode: commented out legacy name ImGuiTreeNodeFlags_SpanTextWidth which was obsoleted in 1.90.7 (May 2024). Use ImGuiTreeNodeFlags_SpanLabelWidth instead. diff --git a/imgui.h b/imgui.h index dcd19a0ef..642fcb906 100644 --- a/imgui.h +++ b/imgui.h @@ -30,7 +30,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.9 WIP" -#define IMGUI_VERSION_NUM 19286 +#define IMGUI_VERSION_NUM 19287 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 @@ -1248,18 +1248,16 @@ enum ImGuiItemFlags_ ImGuiItemFlags_Disabled = 1 << 6, // false // [Internal] Disable interactions. DOES NOT affect visuals. This is used by BeginDisabled()/EndDisabled() and only provided here so you can read back via GetItemFlags(). //--------------------------------------------------------------------------------- - // LiveEdit refers to applying edits to backing variables _while_ typing. + // LiveEdit refers to applying edits to backing variables _while_ typing a value using the keyboard. // Widget: | Input: | w/ LiveEdit: | Output: // InputText() | "123" | on(default) | "1" then "12" then "123" // InputText() | "123" | off | "123" after validating or tabbing out or losing focus. - // DragFloat(), SliderInt(), etc. | "123" | on(default)* | 1 then 12 then 123 - // DragFloat(), SliderInt(), etc. | "123" | off* | 123 after validation or tabbing out or losing focus. - //--------------------------------------------------------------------------------- - // (*) The feature is currently being evaluated, and the aim is to change ImGuiItemFlags_LiveEditOnInputScalar to OFF by default. - // The legacy behavior until July 2026 was that LiveEdit was always ON for everything. + // DragFloat(), SliderInt(), etc. | "123" | on* | 1 then 12 then 123 + // DragFloat(), SliderInt(), etc. | "123" | off(default)* | 123 after validation or tabbing out or losing focus. + // (*) Since 1.92.9 (July 2026), ImGuiItemFlags_LiveEditOnInputScalar is OFF by default. In prior version it was ON for everything. //--------------------------------------------------------------------------------- ImGuiItemFlags_LiveEditOnInputText = 1 << 7, // true // InputText: apply keyboard edits to backing value while typing. Otherwise, edits are applied when validating, tabbing out or losing focus. - ImGuiItemFlags_LiveEditOnInputScalar = 1 << 8, // true* // DragXXX, SliderXXX, InputScalar: apply keyboard edits to backing value while typing. Otherwise, edits are applied when validating, tabbing out or losing focus. + ImGuiItemFlags_LiveEditOnInputScalar = 1 << 8, // false // DragXXX, SliderXXX, InputScalar: apply keyboard edits to backing value while typing. Otherwise, edits are applied when validating, tabbing out or losing focus. ImGuiItemFlags_LiveEditOnInput = ImGuiItemFlags_LiveEditOnInputText | ImGuiItemFlags_LiveEditOnInputScalar, }; diff --git a/imgui_internal.h b/imgui_internal.h index 0966819e6..7eb5780f2 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1006,7 +1006,7 @@ enum ImGuiItemFlagsPrivate_ ImGuiItemFlags_HasSelectionUserData = 1 << 21, // false // Set by SetNextItemSelectionUserData() ImGuiItemFlags_IsMultiSelect = 1 << 22, // false // Set by SetNextItemSelectionUserData() - ImGuiItemFlags_Default_ = ImGuiItemFlags_AutoClosePopups | ImGuiItemFlags_LiveEditOnInputText | ImGuiItemFlags_LiveEditOnInputScalar, // Please don't change, use PushItemFlag() instead. + ImGuiItemFlags_Default_ = ImGuiItemFlags_AutoClosePopups | ImGuiItemFlags_LiveEditOnInputText, // Please don't change, use PushItemFlag() instead. // Obsolete //ImGuiItemFlags_SelectableDontClosePopup = !ImGuiItemFlags_AutoClosePopups, // Can't have a redirect as we inverted the behavior From c9c458c40a47d434464eea36f124744af4d672c6 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 20 Jul 2026 15:20:27 +0200 Subject: [PATCH 03/47] Update FAQ.md --- docs/FAQ.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index baf4daef3..8f5bd755a 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -85,14 +85,15 @@ Here's a very simplified comparison between the approach taken by Dear ImGui vs |--------------------------|--------------------------| | UI fully issued on every update. | UI issued once then later modified. | | UI layout is fully dynamic and can change at any time.
UI is generally emitted programmatically, which empowers reflecting a dynamic set of data. | UI layout is mostly static.
UI may be emitted programmatically or from data created by offline tools. UI need extra code to evolve, which is often tedious and error-prone if it needs to be reflecting dynamic data and systems. | -| Application can submit UI based on arbitrary logic and then forget about it. | Application needs more bookkeeping of UI elements. | +| UI system is optimized for the worst case and designed be optimal under constant changes. | UI system is optimized for the case where nothing changes. Performances tends to degrade when UI changes. | +| Application can submit UI based on arbitrary logic without bookkeeping all aspects of that logic. | Application needs more bookkeeping of UI elements. | | UI library stores minimal amounts of data. At one point in time, it typically doesn't know or remember which other widgets are displayed and which widgets are coming next. As a result, certain layout features (alignment, resizing) are not as easy to implement or require ad-hoc code. | UI library stores entire widgets tree and state. UI library can use this retained data to easily layout things. | | UI code may be added anywhere.
You can even create UI to edit a local variable! | UI code needs to be added in dedicated spots. | | UI layout/logic/action/data bindings are all nearby in the code. | UI layout/logic/action/data bindings in distinct functions, files or formats. | | Data is naturally always synchronized. | Use callback/signal/slot for synchronizing data (error-prone). | -| API is simple and easy to learn. In particular, doing basic things is very easy. | API is more complex and specialized. | -| API is low-level (raw language types). | API are higher-level (more abstractions, advanced language features). | -| Less fancy look and feel. | Standard look and feel. | +| API is simple and easy to learn. In particular, doing basic things is very easy.
Attractive to more programmers. | API is more complex and specialized.
Tends to require specialized programmers. | +| API is low-level. Few abstractions, uses raw language types and your existing data. | API are higher-level. More abstractions, advanced language features. | +| Less fancy look and feel. But keeps improving :) | Standard look and feel. | | Compile yourself. Easy to debug, hack, modify, study. | Mostly use precompiled libraries. Compiling, modifying or studying is daunting if not impossible. | | Run on every platform. | Run on limited desktop platforms. | From 295385ff9618cc728175c308ca7b1a770faf43e9 Mon Sep 17 00:00:00 2001 From: omar Date: Mon, 20 Jul 2026 15:23:35 +0200 Subject: [PATCH 04/47] Update FAQ.md --- docs/FAQ.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index 8f5bd755a..e805566d8 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -85,7 +85,7 @@ Here's a very simplified comparison between the approach taken by Dear ImGui vs |--------------------------|--------------------------| | UI fully issued on every update. | UI issued once then later modified. | | UI layout is fully dynamic and can change at any time.
UI is generally emitted programmatically, which empowers reflecting a dynamic set of data. | UI layout is mostly static.
UI may be emitted programmatically or from data created by offline tools. UI need extra code to evolve, which is often tedious and error-prone if it needs to be reflecting dynamic data and systems. | -| UI system is optimized for the worst case and designed be optimal under constant changes. | UI system is optimized for the case where nothing changes. Performances tends to degrade when UI changes. | +| UI system is optimized for the worst case and designed to be optimal even under frequent changes. | UI system is optimized for the case where nothing changes. Performances tends to degrade when anything changes. | | Application can submit UI based on arbitrary logic without bookkeeping all aspects of that logic. | Application needs more bookkeeping of UI elements. | | UI library stores minimal amounts of data. At one point in time, it typically doesn't know or remember which other widgets are displayed and which widgets are coming next. As a result, certain layout features (alignment, resizing) are not as easy to implement or require ad-hoc code. | UI library stores entire widgets tree and state. UI library can use this retained data to easily layout things. | | UI code may be added anywhere.
You can even create UI to edit a local variable! | UI code needs to be added in dedicated spots. | @@ -95,7 +95,7 @@ Here's a very simplified comparison between the approach taken by Dear ImGui vs | API is low-level. Few abstractions, uses raw language types and your existing data. | API are higher-level. More abstractions, advanced language features. | | Less fancy look and feel. But keeps improving :) | Standard look and feel. | | Compile yourself. Easy to debug, hack, modify, study. | Mostly use precompiled libraries. Compiling, modifying or studying is daunting if not impossible. | -| Run on every platform. | Run on limited desktop platforms. | +| Run on every platform (incl. web, consoles, mobiles, legacy systems). | Run on major desktop platforms. | Idiomatic Dear ImGui code: ```cpp From 548389359c4b2edfcc3b7d48d8750e324ebdc82a Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 20 Jul 2026 16:42:07 +0200 Subject: [PATCH 05/47] (Breaking) ImDrawData: marked draw_data->CmdListsCount as obsolete. Use draw_data->CmdLists.Size. # Conflicts: # docs/CHANGELOG.txt # imgui.cpp --- docs/CHANGELOG.txt | 9 ++++++--- imgui.cpp | 12 +++++++++--- imgui.h | 5 ++++- imgui_draw.cpp | 10 +++++++--- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2c88d5706..0dec2215e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -53,7 +53,8 @@ Breaking Changes: Before: DragFloat() with user typing "123" --> write back 1, then 12, then 123. After: DragFloat() with user typing "123" --> write back 123 when validated/unfocused. You can enable the flag back for a given scope if needed by specific widget/behavior. - +- ImDrawData: marked `CmdListsCount` as obsolete (technically was obsoleted in 1.89.8). + Before: `draw_data->CmdListsCount`, After: `draw_data->CmdLists.Size`. Other Changes: @@ -3541,8 +3542,10 @@ Breaking changes: input queue. This is automatically cleared by io.ClearInputKeys(). (#4921) - ImDrawData: CmdLists[] array is now owned, changed from 'ImDrawList**' to 'ImVector'. Majority of users shouldn't be affected, but you - cannot compare to NULL nor reassign manually anymore. - Instead use AddDrawList(). Allocation count are identical. (#6406, #4879, #1878) + cannot compare to NULL nor reassign manually anymore. (#6406, #4879, #1878) + - Allocation count are identical. + - Use `draw_data->CmdLists.Size` instead of `draw_list->CmdListsCount`! + - Use `AddDrawList()` to add to a `ImDrawData`. Other changes: diff --git a/imgui.cpp b/imgui.cpp index ba8236e2b..2e97af26b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -400,6 +400,9 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: - Before: DragFloat() with user typing "123" --> write back 1, then 12, then 123. - After: DragFloat() with user typing "123" --> write back 123 when validated/unfocused. You can enable the flag back for a given scope if needed by specific widget/behavior. + - 2026/07/20 (1.92.9) - ImDrawData: marked ImDrawData::CmdListsCount as obsolete (technically was obsoleted in 1.89.8). + - Before: draw_data->CmdListsCount + - After: draw_data->CmdLists.Size - 2026/07/08 (1.92.9) - Drag and Drop: commented out legacy name `ImGuiDragDropFlags_SourceAutoExpirePayload` which obsoleted in 1.90.9 (July 2024). Use `ImGuiDragDropFlags_PayloadAutoExpire`. - 2026/07/06 (1.92.9) - ColorEdit: obsoleted SetColorEditOptions() function added in 1.51 (June 2017) in favor of directly poking to io.ConfigColorEditFlags. More consistent and easier to discover. - 2026/06/02 (1.92.9) - TreeNode: commented out legacy name ImGuiTreeNodeFlags_SpanTextWidth which was obsoleted in 1.90.7 (May 2024). Use ImGuiTreeNodeFlags_SpanLabelWidth instead. @@ -736,7 +739,9 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: - ImDrawCornerFlags_XXX -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources. - commented out runtime support for hardcoded ~0 or 0x01..0x0F rounding flags values for AddRect()/AddRectFilled()/PathRect()/AddImageRounded() -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details - 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry! - - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. Instead use AddDrawList(). (#6406, #4879, #1878) + - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. (#6406, #4879, #1878) + - use `draw_data->CmdLists.Size` instead of `draw_list->CmdListsCount`. + - use `draw_data->AddDrawList()` to add to a ImDrawData. - 2023/06/28 (1.89.7) - overlapping items: obsoleted 'SetItemAllowOverlap()' (called after item) in favor of calling 'SetNextItemAllowOverlap()' (called before item). 'SetItemAllowOverlap()' didn't and couldn't work reliably since 1.89 (2022-11-15). - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete). - 2023/06/28 (1.89.7) - overlapping items: IsItemHovered() now by default return false when querying an item using AllowOverlap mode which is being overlapped. Use ImGuiHoveredFlags_AllowWhenOverlappedByItem to revert to old behavior. @@ -5958,13 +5963,15 @@ static void InitViewportDrawData(ImGuiViewportP* viewport) viewport->DrawDataBuilder.Layers[1]->resize(0); draw_data->Valid = true; - draw_data->CmdListsCount = 0; draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; draw_data->DisplayPos = viewport->Pos; draw_data->DisplaySize = viewport->Size; draw_data->FramebufferScale = io.DisplayFramebufferScale; draw_data->OwnerViewport = viewport; draw_data->Textures = &ImGui::GetPlatformIO().Textures; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + draw_data->CmdListsCount = 0; +#endif } // Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering. @@ -6232,7 +6239,6 @@ void ImGui::Render() // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch). ImDrawData* draw_data = &viewport->DrawDataP; - IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount); for (ImDrawList* draw_list : draw_data->CmdLists) draw_list->_PopUnusedDrawCmd(); diff --git a/imgui.h b/imgui.h index 642fcb906..d219d5a6d 100644 --- a/imgui.h +++ b/imgui.h @@ -3493,7 +3493,6 @@ struct ImDrawList struct ImDrawData { bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. - int CmdListsCount; // == CmdLists.Size. (OBSOLETE: exists for legacy reasons). Number of ImDrawList* to render. int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size ImVector CmdLists; // Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here. @@ -3503,6 +3502,10 @@ struct ImDrawData ImGuiViewport* OwnerViewport; // Viewport carrying the ImDrawData instance, might be of use to the renderer (generally not). ImVector* Textures; // List of textures to update. Most of the times the list is shared by all ImDrawData, has only 1 texture and it doesn't need any update. This almost always points to ImGui::GetPlatformIO().Textures[]. May be overridden or set to NULL if you want to manually update textures. +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + int CmdListsCount; // Use CmdLists.Size instead. Number of ImDrawList* to render. +#endif + // Functions ImDrawData() { Clear(); } IMGUI_API void Clear(); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 4a68ff4a8..d95dc20ad 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2280,11 +2280,14 @@ void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx) void ImDrawData::Clear() { Valid = false; - CmdListsCount = TotalIdxCount = TotalVtxCount = 0; + TotalIdxCount = TotalVtxCount = 0; CmdLists.resize(0); // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them. DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.0f, 0.0f); OwnerViewport = NULL; Textures = NULL; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + CmdListsCount = 0; +#endif } // Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list @@ -2329,16 +2332,17 @@ void ImGui::AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector // Add to output list + records state in ImDrawData out_list->push_back(draw_list); - draw_data->CmdListsCount++; draw_data->TotalVtxCount += draw_list->VtxBuffer.Size; draw_data->TotalIdxCount += draw_list->IdxBuffer.Size; } void ImDrawData::AddDrawList(ImDrawList* draw_list) { - IM_ASSERT(CmdLists.Size == CmdListsCount); draw_list->_PopUnusedDrawCmd(); ImGui::AddDrawListToDrawDataEx(this, &CmdLists, draw_list); +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + CmdListsCount = CmdLists.Size; +#endif } // For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! From 6f41d13c7a93cf65aca4afe9bf24b610740c3d5a Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 20 Jul 2026 16:57:17 +0200 Subject: [PATCH 06/47] DrawData: added FrameCount field to facilitate debugging multi-threading issues. (#8597, #9136) --- imgui.cpp | 7 ++++--- imgui.h | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 2e97af26b..741d6b2a7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5954,7 +5954,7 @@ static void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder) static void InitViewportDrawData(ImGuiViewportP* viewport) { - ImGuiIO& io = ImGui::GetIO(); + ImGuiContext& g = *GImGui; ImDrawData* draw_data = &viewport->DrawDataP; viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists; @@ -5963,12 +5963,13 @@ static void InitViewportDrawData(ImGuiViewportP* viewport) viewport->DrawDataBuilder.Layers[1]->resize(0); draw_data->Valid = true; + draw_data->FrameCount = g.FrameCount; draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; draw_data->DisplayPos = viewport->Pos; draw_data->DisplaySize = viewport->Size; - draw_data->FramebufferScale = io.DisplayFramebufferScale; + draw_data->FramebufferScale = g.IO.DisplayFramebufferScale; draw_data->OwnerViewport = viewport; - draw_data->Textures = &ImGui::GetPlatformIO().Textures; + draw_data->Textures = &g.PlatformIO.Textures; #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS draw_data->CmdListsCount = 0; #endif diff --git a/imgui.h b/imgui.h index d219d5a6d..155f1093b 100644 --- a/imgui.h +++ b/imgui.h @@ -3493,6 +3493,7 @@ struct ImDrawList struct ImDrawData { bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. + int FrameCount; // Frame counter of the emitter context. For debugging purpose. int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size ImVector CmdLists; // Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here. From b335bf4328054498219afb4188ec39912f2a95b1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 20 Jul 2026 21:16:58 +0200 Subject: [PATCH 07/47] Added ImTextureData::QueueUserData for staged-rendering texture queues e.g. imgui_threaded_rendering.h. (#8597, #9136) --- imgui.h | 3 ++- imgui_draw.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui.h b/imgui.h index 155f1093b..6b7389753 100644 --- a/imgui.h +++ b/imgui.h @@ -3558,13 +3558,14 @@ struct ImTextureRect // Why does we store two identifiers: TexID and BackendUserData? // - ImTextureID TexID = lower-level identifier stored in ImDrawCmd. ImDrawCmd can refer to textures not created by the backend, and for which there's no ImTextureData. // - void* BackendUserData = higher-level opaque storage for backend own book-keeping. Some backends may have enough with TexID and not need both. - // In columns below: who reads/writes each fields? 'r'=read, 'w'=write, 'core'=main library, 'backend'=renderer backend +// In columns below: who reads/writes each fields? 'r'=read, 'w'=write, 'core'=main library, 'backend'=renderer backend struct ImTextureData { //------------------------------------------ core / backend --------------------------------------- int UniqueID; // w - // [DEBUG] Sequential index to facilitate identifying a texture when debugging/printing. Unique per atlas. ImTextureStatus Status; // rw rw // ImTextureStatus_OK/_WantCreate/_WantUpdates/_WantDestroy. Always use SetStatus() to modify! void* BackendUserData; // - rw // Convenience storage for backend. Some backends may have enough with TexID. + void* QueueUserData; // r - // Convenience storage for a staged/multi-threaded rendering texture queue (e.g. imgui_threaded_rendering.h. See #8597). When != NULL, core assumes the texture is referenced by the queue. ImTextureID TexID; // r w // Backend-specific texture identifier. Always use SetTexID() to modify! The identifier will stored in ImDrawCmd::GetTexID() and passed to backend's RenderDrawData function. ImTextureFormat Format; // w r // ImTextureFormat_RGBA32 (default) or ImTextureFormat_Alpha8 int Width; // w r // Texture width diff --git a/imgui_draw.cpp b/imgui_draw.cpp index d95dc20ad..8598d7026 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2866,13 +2866,14 @@ bool ImTextureDataUpdateNewFrame(ImTextureData* tex) // If a texture has never reached the backend, they don't need to know about it. // (note: backends between 1.92.0 and 1.92.4 could set an already destroyed texture to ImTextureStatus_WantDestroy // when invalidating graphics objects twice, which would previously remove it from the list and crash.) - if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL) + if (tex->Status == ImTextureStatus_WantDestroy && tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && tex->QueueUserData == NULL) tex->Status = ImTextureStatus_Destroyed; // Process texture being destroyed if (tex->Status == ImTextureStatus_Destroyed) { IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == NULL && "Backend set texture Status to Destroyed but did not clear TexID/BackendUserData!"); + IM_ASSERT(tex->QueueUserData == NULL && "Texture queue set Status to Destroyed but did not clear QueueUserData!"); if (tex->WantDestroyNextFrame) remove_from_list = true; // Destroy was scheduled by us else From f594633b541cf52f6e5746f99b83fc56ab06e733 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 23 Jul 2026 17:49:39 +0200 Subject: [PATCH 08/47] Popups: added bool return value to OpenPopup(), OpenPopupOnItemClick() functions. (#9429) Following on 795cf6fcb5. --- docs/CHANGELOG.txt | 5 ++++- imgui.cpp | 30 ++++++++++++++++++------------ imgui.h | 10 ++++++---- imgui_internal.h | 2 +- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0dec2215e..0074103db 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -107,6 +107,9 @@ Other Changes: default value of each `ImGuiItemFlags_LiveEditXXX`, because this is not expected to be a user preference but a programmer/widget preferences. Also, we strive to make the toolkit consistent. +- Popups: + - Added bool return value to `OpenPopup()`, `OpenPopupOnItemClick()` functions + to notify when the popup was just opened. (#9429) - InputText: - Added `style.InputTextCursorSize` to configure cursor/caret thickness. (#7031, #9409) This is automatically scaled by `style.ScaleAllSizes()`. @@ -131,7 +134,7 @@ Other Changes: - Headers: fixed label being clipped early to reserve space for a sort marker even when no sort marker is displayed. Auto-fitting a column still accounts for the possible marker, so that sorting after an auto-fit doesn't clip the label. -- Multi-Select +- Multi-Select: - Reworked ImGuiMultiSelectFlags_NoAutoSelect as it carried side-effects that were hardcoded/designed to use multi-selection on checkboxes. (#9391) Specifically removed those undocumented behaviors from _NoAutoSelect: diff --git a/imgui.cpp b/imgui.cpp index 741d6b2a7..b93e48a66 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -12430,24 +12430,26 @@ ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) return NULL; } -void ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) +bool ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiID id = g.CurrentWindow->GetID(str_id); IMGUI_DEBUG_LOG_POPUP("[popup] OpenPopup(\"%s\" -> 0x%08X)\n", str_id, id); - OpenPopupEx(id, popup_flags); + return OpenPopupEx(id, popup_flags); } -void ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags) +bool ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags) { - OpenPopupEx(id, popup_flags); + return OpenPopupEx(id, popup_flags); } // Mark popup as open (toggle toward open state). -// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. -// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). -// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) -void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) +// - Return true when the popup is toggled open, which allows you to capture local state if needed. +// You may also call IsWindowAppearing() inside the later BeginPopup() scope if you need to prepare/compute data for the popup. +// - Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. +// - Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). +// - One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL). +bool ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; ImGuiWindow* parent_window = g.CurrentWindow; @@ -12455,7 +12457,7 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup) if (IsPopupOpen((ImGuiID)0, ImGuiPopupFlags_AnyPopupId)) - return; + return false; ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. popup_ref.PopupId = id; @@ -12470,6 +12472,7 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) if (g.OpenPopupStack.Size < current_stack_size + 1) { g.OpenPopupStack.push_back(popup_ref); + return true; } else { @@ -12497,6 +12500,8 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) // This is equivalent to what ClosePopupToLevel() does. //if (g.OpenPopupStack[current_stack_size].PopupId == id) // FocusWindow(parent_window); + + return !keep_existing; } } @@ -12781,16 +12786,17 @@ bool ImGui::IsPopupOpenRequestForWindow(ImGuiPopupFlags popup_flags) // Helper to open a popup if mouse button is released over the item // - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup() -void ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags) +bool ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; if (IsPopupOpenRequestForItem(popup_flags, g.LastItemData.ID)) { + ImGuiWindow* window = g.CurrentWindow; ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - OpenPopupEx(id, popup_flags); + return OpenPopupEx(id, popup_flags); } + return false; } // This is a helper to handle the simplest case of associating one named popup to one given widget. diff --git a/imgui.h b/imgui.h index 6b7389753..5ef2e61a5 100644 --- a/imgui.h +++ b/imgui.h @@ -850,15 +850,17 @@ namespace ImGui IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! // Popups: open/close functions - // - OpenPopup(): set popup state to open. ImGuiPopupFlags are available for opening options. + // - OpenPopup(): set popup state to open (unless one of the specified ImGuiPopupFlags prevent opening). + // - OpenPopupXXX() functions return true when the popup is toggled open, which allows you to capture local state if needed. + // You may also call IsWindowAppearing() inside the later BeginPopup() scope if you need to prepare/compute data for the popup. // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. // - CloseCurrentPopup(): use inside the BeginPopup()/EndPopup() scope to close manually. // - CloseCurrentPopup() is called by default by Selectable()/MenuItem() when activated (FIXME: need some options). // - Use ImGuiPopupFlags_NoOpenOverExistingPopup to avoid opening a popup if there's already one at the same level. This is equivalent to e.g. testing for !IsAnyPopupOpen() prior to OpenPopup(). // - Use IsWindowAppearing() after BeginPopup() to tell if a window just opened. - IMGUI_API void OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0); // call to mark popup as open (don't call every frame!). - IMGUI_API void OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks - IMGUI_API void OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) + IMGUI_API bool OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0); // call to mark popup as open (don't call every frame!). + IMGUI_API bool OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0); // id overload to facilitate calling from nested stacks + IMGUI_API bool OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 0); // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors) IMGUI_API void CloseCurrentPopup(); // manually close the popup we have begin-ed into. // Popups: Open+Begin popup combined functions helpers to create context menus. diff --git a/imgui_internal.h b/imgui_internal.h index 7eb5780f2..584914fc4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3504,7 +3504,7 @@ namespace ImGui // Popups, Modals IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags); IMGUI_API bool BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags extra_window_flags); - IMGUI_API void OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None); + IMGUI_API bool OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None); IMGUI_API void ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup); IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup); IMGUI_API void ClosePopupsExceptModals(); From f31f98f4cda8552531d9a65ef170b527c32bd51f Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 23 Jul 2026 17:55:43 +0200 Subject: [PATCH 09/47] ImDrawData: fixed late initialized field. (Amend 6f41d13) Would effectively be initialized on use but best to clear on constructor. --- imgui.h | 2 +- imgui_draw.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui.h b/imgui.h index 5ef2e61a5..918d253d2 100644 --- a/imgui.h +++ b/imgui.h @@ -3495,7 +3495,7 @@ struct ImDrawList struct ImDrawData { bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. - int FrameCount; // Frame counter of the emitter context. For debugging purpose. + int FrameCount; // Frame counter of the emitter context. Mostly for debugging purpose. int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size ImVector CmdLists; // Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here. diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 8598d7026..6ec7478b9 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2280,7 +2280,7 @@ void ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx) void ImDrawData::Clear() { Valid = false; - TotalIdxCount = TotalVtxCount = 0; + FrameCount = TotalIdxCount = TotalVtxCount = 0; CmdLists.resize(0); // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them. DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.0f, 0.0f); OwnerViewport = NULL; From d94d0f3649c99d9a026518214f796aaacde707fe Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 23 Jul 2026 18:56:53 +0200 Subject: [PATCH 10/47] Examples: SDL+WebGPU: removed stray glfw from cmakefiles. (#9387, #9428) --- examples/example_sdl2_wgpu/CMakeLists.txt | 2 +- examples/example_sdl3_wgpu/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/example_sdl2_wgpu/CMakeLists.txt b/examples/example_sdl2_wgpu/CMakeLists.txt index 8795070c3..aff4a184f 100644 --- a/examples/example_sdl2_wgpu/CMakeLists.txt +++ b/examples/example_sdl2_wgpu/CMakeLists.txt @@ -162,7 +162,7 @@ else() # Native/Desktop build endif() list(APPEND WGVK_PLATFORM_LIBS m dl pthread) endif() - set(LIBRARIES glfw Vulkan::Vulkan ${WGVK_PLATFORM_LIBS}) + set(LIBRARIES Vulkan::Vulkan ${WGVK_PLATFORM_LIBS}) endif() endif() diff --git a/examples/example_sdl3_wgpu/CMakeLists.txt b/examples/example_sdl3_wgpu/CMakeLists.txt index 21a72bd0e..b7b05881d 100644 --- a/examples/example_sdl3_wgpu/CMakeLists.txt +++ b/examples/example_sdl3_wgpu/CMakeLists.txt @@ -162,7 +162,7 @@ else() # Native/Desktop build endif() list(APPEND WGVK_PLATFORM_LIBS m dl pthread) endif() - set(LIBRARIES glfw Vulkan::Vulkan ${WGVK_PLATFORM_LIBS}) + set(LIBRARIES Vulkan::Vulkan ${WGVK_PLATFORM_LIBS}) endif() endif() From e7221655615f38bedd6651554edc75975aa0d1fb Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 25 Jul 2026 12:21:17 +0200 Subject: [PATCH 11/47] Docs: rework FAQ index, added multi-threading and string-view details. --- docs/BACKENDS.md | 2 +- docs/FAQ.md | 105 +++++++++++++++++++++++++++++++---------------- imgui.cpp | 5 ++- 3 files changed, 74 insertions(+), 38 deletions(-) diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index 2fb9cfa71..2f3a802d8 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -117,7 +117,7 @@ If you are not sure which backend to use, the recommended platform/frameworks fo | GLFW | https://github.com/glfw/glfw | imgui_impl_glfw.cpp | | | Sokol | https://github.com/floooh/sokol | [util/sokol_imgui.h](https://github.com/floooh/sokol/blob/master/util/sokol_imgui.h) | Lower-level than GLFW/SDL | -If your application runs on Windows or if you are using multi-viewport, the win32 backend handles some details a little better than other backends. +If your application runs on Windows or if you are using multi-viewports, the imgui_impl_win32 backend handles some details better than other backends. ## Using third-party Backends diff --git a/docs/FAQ.md b/docs/FAQ.md index e805566d8..d67616725 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -9,42 +9,58 @@ or view this file with any Markdown viewer. ## Index -| **Q&A: Basics** | -:---------------------------------------------------------- | -| [Where is the documentation?](#q-where-is-the-documentation) | -| [What is this library called?](#q-what-is-this-library-called) | -| [What is the difference between Dear ImGui and traditional UI toolkits?](#q-what-is-the-difference-between-dear-imgui-and-traditional-ui-toolkits) | -| [Which version should I get?](#q-which-version-should-i-get) | -| **Q&A: Integration** | -| **[How to get started?](#q-how-to-get-started)** | -| **[How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?](#q-how-can-i-tell-whether-to-dispatch-mousekeyboard-to-dear-imgui-or-my-application)** | -| [How can I enable keyboard or gamepad controls?](#q-how-can-i-enable-keyboard-or-gamepad-controls) | -| [How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)](#q-how-can-i-use-this-on-a-machine-without-mouse-keyboard-or-screen-input-share-remote-display) | -| [How can I create my own backend?](#q-how-can-i-create-my-own-backend) -| [I integrated Dear ImGui in my engine and little squares are showing instead of text...](#q-i-integrated-dear-imgui-in-my-engine-and-little-squares-are-showing-instead-of-text) | -| [I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-clipping-or-disappearing-when-i-move-windows-around) | -| [I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-displaying-outside-their-expected-windows-boundaries) | -| **Q&A: Usage** | -| **[About the ID Stack system...](#q-about-the-id-stack-system)**
**[How can I have multiple widgets with the same label?](#q-how-can-i-have-multiple-widgets-with-the-same-label) (using `##` or `PushID()`)**
**[How can I have widgets with an empty label?](#q-how-can-i-have-widgets-with-an-empty-label) (using `##`)**
**[How can I make a label dynamic?](#q-how-can-i-make-a-label-dynamic) (using `###`)**
**[General description of the label and ID Stack system.](#general-description-of-the-label-and-id-stack-system)** | -| [How can I display an image?](#q-how-can-i-display-an-image)
[What are ImTextureID/ImTextureRef?](#q-what-are-imtextureidimtextureref)| -| [How can I use maths operators with ImVec2?](#q-how-can-i-use-maths-operators-with-imvec2) | -| [How can I use my own maths types instead of ImVec2/ImVec4?](#q-how-can-i-use-my-own-maths-types-instead-of-imvec2imvec4) | -| [How can I interact with standard C++ types (such as std::string and std::vector)?](#q-how-can-i-interact-with-standard-c-types-such-as-stdstring-and-stdvector) | -| [How can I display custom shapes? (using low-level ImDrawList API)](#q-how-can-i-display-custom-shapes-using-low-level-imdrawlist-api) | -| **Q&A: Fonts, Text** | -| [How should I handle DPI in my application?](#q-how-should-i-handle-dpi-in-my-application) | -| [How can I load a different font than the default?](#q-how-can-i-load-a-different-font-than-the-default) | -| [How can I easily use icons in my application?](#q-how-can-i-easily-use-icons-in-my-application) | -| [How can I load multiple fonts?](#q-how-can-i-load-multiple-fonts) | -| [How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?](#q-how-can-i-display-and-input-non-latin-characters-such-as-chinese-japanese-korean-cyrillic) | -| **Q&A: Concerns** | -| [Who uses Dear ImGui?](#q-who-uses-dear-imgui) | -| [Can you create elaborate/serious tools with Dear ImGui?](#q-can-you-create-elaborateserious-tools-with-dear-imgui) | -| [Can you reskin the look of Dear ImGui?](#q-can-you-reskin-the-look-of-dear-imgui) | -| [Why using C++ (as opposed to C)?](#q-why-using-c-as-opposed-to-c) | -| **Q&A: Community** | -| [How can I help?](#q-how-can-i-help) | +### Basics +- [Where is the documentation?](#q-where-is-the-documentation) +- [What is this library called?](#q-what-is-this-library-called) +- [What is the difference between Dear ImGui and traditional UI toolkits?](#q-what-is-the-difference-between-dear-imgui-and-traditional-ui-toolkits) +- [Which version should I get?](#q-which-version-should-i-get) + +### Integration + +- **[How to get started?](#q-how-to-get-started)** +- **[How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?](#q-how-can-i-tell-whether-to-dispatch-mousekeyboard-to-dear-imgui-or-my-application)** +- [How can I enable keyboard or gamepad controls?](#q-how-can-i-enable-keyboard-or-gamepad-controls) +- [How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)](#q-how-can-i-use-this-on-a-machine-without-mouse-keyboard-or-screen-input-share-remote-display) +- [How can I create my own backend?](#q-how-can-i-create-my-own-backend) +- [I integrated Dear ImGui in my engine and little squares are showing instead of text...](#q-i-integrated-dear-imgui-in-my-engine-and-little-squares-are-showing-instead-of-text) +- [I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-clipping-or-disappearing-when-i-move-windows-around) +- [I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-displaying-outside-their-expected-windows-boundaries) + +### Usage + +- **[About the ID Stack system...](#q-about-the-id-stack-system)** + - **[How can I have multiple widgets with the same label?](#q-how-can-i-have-multiple-widgets-with-the-same-label) (using `##` or `PushID()`)** + - **[How can I have widgets with an empty label?](#q-how-can-i-have-widgets-with-an-empty-label) (using `##`)** + - **[How can I make a label dynamic?](#q-how-can-i-make-a-label-dynamic) (using `###`)** + - **[General description of the label and ID Stack system.](#general-description-of-the-label-and-id-stack-system)** +- [How can I display an image?](#q-how-can-i-display-an-image), [What are ImTextureID/ImTextureRef?](#q-what-are-imtextureidimtextureref) +- [How can I use maths operators with ImVec2?](#q-how-can-i-use-maths-operators-with-imvec2) +- [How can I use my own maths types instead of ImVec2/ImVec4?](#q-how-can-i-use-my-own-maths-types-instead-of-imvec2imvec4) +- [How can I interact with standard C++ types (such as std::string and std::vector)?](#q-how-can-i-interact-with-standard-c-types-such-as-stdstring-and-stdvector) +- [How can I display custom shapes? (using low-level ImDrawList API)](#q-how-can-i-display-custom-shapes-using-low-level-imdrawlist-api) +- [About Multi-Threading](#about-multi-threading) + +### Fonts, Text + +- [How should I handle DPI in my application?](#q-how-should-i-handle-dpi-in-my-application) +- [How can I load a different font than the default?](#q-how-can-i-load-a-different-font-than-the-default) +- [How can I easily use icons in my application?](#q-how-can-i-easily-use-icons-in-my-application) +- [How can I load multiple fonts?](#q-how-can-i-load-multiple-fonts) +- [How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?](#q-how-can-i-display-and-input-non-latin-characters-such-as-chinese-japanese-korean-cyrillic) + +### Concerns + +- [Who uses Dear ImGui?](#q-who-uses-dear-imgui) +- [Can you create elaborate/serious tools with Dear ImGui?](#q-can-you-create-elaborateserious-tools-with-dear-imgui) +- [Can you reskin the look of Dear ImGui?](#q-can-you-reskin-the-look-of-dear-imgui) +- [Why using C++ (as opposed to C)?](#q-why-using-c-as-opposed-to-c) + +### Community + +- [How can I help?](#q-how-can-i-help) + +-------- # Q&A: Basics @@ -667,6 +683,7 @@ This way you will be able to use your own types everywhere, e.g. passing `MyVect --- ### Q: How can I interact with standard C++ types (such as std::string and std::vector)? + - Being highly portable (backends/bindings for several languages, frameworks, programming styles, obscure or older platforms/compilers), and aiming for compatibility & performance suitable for every modern real-time game engine, Dear ImGui does not use any of std C++ types. We use raw types (e.g. char* instead of std::string) because they adapt to more use cases. - To use ImGui::InputText() with a std::string or any resizable string class, see [misc/cpp/imgui_stdlib.h](https://github.com/ocornut/imgui/blob/master/misc/cpp/imgui_stdlib.h). - To use combo boxes and list boxes with `std::vector` or any other data structure: the `BeginCombo()/EndCombo()` API @@ -686,6 +703,10 @@ One possible implementation of a helper to facilitate printf-style building of s This is a small helper where you can instance strings with configurable local buffers length. Many game engines will provide similar or better string helpers. +Using string views: + +- String view types such as `std::string_view` may be used with the [feature/string_view](https://github.com/ocornut/imgui/tree/features/string_view) branch. This branch is actively maintained and supported. Please provide feedback if you use it. We are waiting for a good moment to merge it. + ##### [Return to Index](#index) --- @@ -726,6 +747,20 @@ ImGui::End(); --- +### About Multi-Threading + +A same Dear ImGui context may be not used from multiple threads in parallel. + +If you want to use a same context within parallel tasks for occasional debug purpose, consider using a lock. + +If you want to submit contents from a main/update thread but render Dear ImGui output in a dedicated render thread, you'll need to stage `ImDrawData` and texture requests. See the `ImDrawDataSnapshot` and `ImTextureQueue` helpers in [imgui_threaded_rendering](https://github.com/ocornut/imgui_club#imgui_threaded_rendering). Also see topics with [label: multi-threading](https://github.com/ocornut/imgui/issues?q=label%3Amulti-threading). + +If you use multiple Dear ImGui contexts and want to use them from multiple threads, you need to `#define GImGui` to become a TLS variable (see details near the definition of `GImGui`. If you need to display multiple contexts simultaneously, consider using [imgui_multicontext_compositor](https://github.com/ocornut/imgui_club#imgui_multicontext_compositor). Also see topics with [label: multi-contexts](https://github.com/ocornut/imgui/issues?q=label%3Amulti-contexts). + +There is a script/patch to make the API take an explicit context pointer: [#5856](https://github.com/ocornut/imgui/pull/5856). It is presently unmaintained and likely to easy to update. It has been decided that the change is not currently worth applying to main-line but maybe will be in a future version. + +--- + # Q&A: Fonts, Text ### Q: How should I handle DPI in my application? diff --git a/imgui.cpp b/imgui.cpp index b93e48a66..2eab3aae3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -32,7 +32,7 @@ // See LICENSE.txt for copyright and licensing details (standard MIT License). // This library is free but needs your support to sustain development and maintenance. // Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts. -// PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Funding +// PLEASE reach out at omar AT discohello DOT com. See https://github.com/ocornut/imgui/wiki/Funding // Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine. // It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library. @@ -1198,6 +1198,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: Q: How can I use my own math types instead of ImVec2? Q: How can I interact with standard C++ types (such as std::string and std::vector)? Q: How can I display custom shapes? (using low-level ImDrawList API) + Q: About Multi-Threading >> See https://www.dearimgui.com/faq Q&A: Fonts, Text @@ -1223,7 +1224,7 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: ============== Q: How can I help? - A: - Businesses: please reach out to "omar AT dearimgui DOT com" if you work in a place using Dear ImGui! + A: - Businesses: please reach out to "omar AT discohello DOT com" if you work in a place using Dear ImGui! We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts. This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project. >>> See https://github.com/ocornut/imgui/wiki/Funding From 01380c579715e62fb9a8d6ec0502c4ea83bfde6e Mon Sep 17 00:00:00 2001 From: ocornut Date: Sat, 25 Jul 2026 12:47:56 +0200 Subject: [PATCH 12/47] Version 1.92.9 --- docs/CHANGELOG.txt | 87 ++++++++++++++++++++++++---------------------- imgui.cpp | 2 +- imgui.h | 6 ++-- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 54 insertions(+), 51 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0074103db..a3e036018 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -36,38 +36,32 @@ HOW TO UPDATE? - Please report any issue! ----------------------------------------------------------------------- - VERSION 1.92.9 WIP (In Progress) + VERSION 1.92.9 (Released 2026-07-25) ----------------------------------------------------------------------- +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.8 + Breaking Changes: -- TreeNode: commented out legacy name `ImGuiTreeNodeFlags_SpanTextWidth` which - was obsoleted in 1.90.7 (May 2024). Use `ImGuiTreeNodeFlags_SpanLabelWidth`. -- ColorEdit: obsoleted SetColorEditOptions() function added in 1.51 (June 2017) in - of directly poking to io.ConfigColorEditFlags. More consistent and easier to discover. -- Drag and Drop: commented out legacy name `ImGuiDragDropFlags_SourceAutoExpirePayload` - which obsoleted in 1.90.9 (July 2024). Use `ImGuiDragDropFlags_PayloadAutoExpire`. - DragXXX, SliderXXX, InputScalar: with `ImGuiItemFlags_LiveEditOnInputScalar` now defaulting to being disabled: inputting a value with the keyboard doesn't write intermediate values to the backing variable. (#9476) Before: DragFloat() with user typing "123" --> write back 1, then 12, then 123. After: DragFloat() with user typing "123" --> write back 123 when validated/unfocused. You can enable the flag back for a given scope if needed by specific widget/behavior. + Read below for details. +- TreeNode: commented out legacy name `ImGuiTreeNodeFlags_SpanTextWidth` which + was obsoleted in 1.90.7 (May 2024). Use `ImGuiTreeNodeFlags_SpanLabelWidth`. +- ColorEdit: obsoleted SetColorEditOptions() function added in 1.51 (June 2017) in favor + of directly poking to io.ConfigColorEditFlags. More consistent and easier to discover. +- Drag and Drop: commented out legacy name `ImGuiDragDropFlags_SourceAutoExpirePayload` + which obsoleted in 1.90.9 (July 2024). Use `ImGuiDragDropFlags_PayloadAutoExpire`. - ImDrawData: marked `CmdListsCount` as obsolete (technically was obsoleted in 1.89.8). Before: `draw_data->CmdListsCount`, After: `draw_data->CmdLists.Size`. Other Changes: -- Windows: - - Clicking on a window's empty-space to move/focus a window checks - for lack of mouse button ownership. This gives an additional opportunity - for user code to bypass it without using a clickable item. (#9382) - - Clicking on a window's empty-space to move/focus a window checks - for lack of queued focus request. (#9382) - - Fixed double-click collapse toggle not owning the mouse button. If a - `SetNextWindowPos()` with pivot was queued in the same frame, the second - click could trigger another item in the same window. (#9439) [@Cleroth] -- Added `ImGuiItemFlags_LiveEditOnInputText` and `ImGuiItemFlags_LiveEditOnInputScalar` +- LiveEdit on/off: added `ImGuiItemFlags_LiveEditOnInputText` and `ImGuiItemFlags_LiveEditOnInputScalar` flags to configure the timing of applying edits of backing variables when typing values using a keyboard. (#9476, #701, #3936, #3946, #5904, #6284, #8149, #8065, #8665, #9117, #9299, #700, #1351, @@ -106,13 +100,22 @@ Other Changes: - We intentionally are not adding `io.ConfigLiveEditXXX` fields to dictate the default value of each `ImGuiItemFlags_LiveEditXXX`, because this is not expected to be a user preference but a programmer/widget preferences. - Also, we strive to make the toolkit consistent. + And we strive to make the toolkit consistent. +- Windows: + - Clicking on a window's empty-space to move/focus a window checks + for lack of mouse button ownership. This gives an additional opportunity + for user code to bypass it without using a clickable item. (#9382) + - Clicking on a window's empty-space to move/focus a window checks + for lack of queued focus request. (#9382) + - Fixed double-click collapse toggle not owning the mouse button. If a + `SetNextWindowPos()` with pivot was queued in the same frame, the second + click could trigger another item in the same window. (#9439) [@Cleroth] - Popups: - Added bool return value to `OpenPopup()`, `OpenPopupOnItemClick()` functions to notify when the popup was just opened. (#9429) - InputText: - Added `style.InputTextCursorSize` to configure cursor/caret thickness. (#7031, #9409) - This is automatically scaled by `style.ScaleAllSizes()`. + It is automatically scaled by `style.ScaleAllSizes()`. - Reworked `io.ConfigInputTextEnterKeepActive` mode so that pressing Ctrl+Enter or Shift+Enter still allows to deactivate. (#9239) - Tables: @@ -135,9 +138,9 @@ Other Changes: even when no sort marker is displayed. Auto-fitting a column still accounts for the possible marker, so that sorting after an auto-fit doesn't clip the label. - Multi-Select: - - Reworked ImGuiMultiSelectFlags_NoAutoSelect as it carried side-effects that + - Reworked `ImGuiMultiSelectFlags_NoAutoSelect` as it carried side-effects that were hardcoded/designed to use multi-selection on checkboxes. (#9391) - Specifically removed those undocumented behaviors from _NoAutoSelect: + Specifically removed those undocumented behaviors from `_NoAutoSelect`: - Clicking a selected/checked item always unselect/uncheck. - Shift+Click inverts targets value and copy to range. - Shift+Keyboard copy selection source value to range. @@ -146,27 +149,27 @@ Other Changes: - Added `IMGUI_DISABLE_DEFAULT_FONT_BITMAP`/`IMGUI_DISABLE_DEFAULT_FONT_VECTOR` to disable embedding either fonts separately. (#9407) - Tweak `CalcTextSize()` awkward width rounding/ceiling code to reduce floating-point - imprecisions altering the result by 1 even at relatively small width. (#791) - - Better document the fact that ImFontAtlas::Clear()/ClearFonts() functions are + precision issues altering the result by 1 even at relatively small width. (#791) + - Better document the fact that `ImFontAtlas::Clear()`/`ClearFonts()` functions are unlikely to be useful nowadays. Better recover to an edge case of mistakenly - calling ClearFonts() during rendering. + calling `ClearFonts()` during rendering. - Fixed an issue where passing a manually created ImFontAtlas to CreateContext() would - incorrectly destroy it in DestroyContext() when ref-count gets back to zero. (#9426) - - Destroying an ImGui context using a ImFontAtlas checks that the later has no references. + incorrectly destroy it in `DestroyContext()` when ref-count gets back to zero. (#9426) + - Destroying an ImGui context using a `ImFontAtlas` checks that the later has no references. - Nav: - Fixed context menu activation with gamepad erroneously testing for _NavEnableKeyboard instead of _NavEnableGamepad. (#9454, #8803, #9270) [@Clownacy] - TreeNode: - Fixed nav cursor rendering with rounding even though tree nodes don't have it. (#7589) - Inputs: - - Added GetItemClickedCountWithSingleClickDelay() helper for easy disambiguation + - Added `GetItemClickedCountWithSingleClickDelay()` helper for easy disambiguation between single-click and double-click for actions that needs single-click to do something other than selection. (#8337) - Returns 1 on single-click but delayed by io.MouseSingleClickDelay. - - Returns 2 on double-click, and 2+ on subsequent repeated cicks. - - Added io.MouseSingleClickDelay to configure default delayed single click delay when - using GetItemClickedCountWithSingleClickDelay() or IsMouseReleasedWithDelay(). (#8337) - Note that io.MouseSingleClickDelay is always > io.MouseDoubleClickTime. + - Returns 2 on double-click, and 2+ on subsequent repeated clicks. + - Added `io.MouseSingleClickDelay` to configure default delayed single click delay when + using `GetItemClickedCountWithSingleClickDelay()` or `IsMouseReleasedWithDelay()`. (#8337) + Note that `io.MouseSingleClickDelay` is always `> io.MouseDoubleClickTime`. - ColorEdit: - Added `io.ConfigColorEditFlags` to read/modify current color edit/picker settings. - ColorPicker: added `ImGuiColorEditFlags_PickerNoRotate` to disable rotation of the @@ -182,13 +185,13 @@ Other Changes: - Settings: - Windows/Tables settings entries can now record the last used date in YYYYMMDD format, allowing tools to run to e.g. delete entries that haven't been used in X months. (#9460) - - Added bool io.ConfigIniSettingsSaveLastUsedDate to disable saving that info. (#9460) - - Added int io.ConfigIniSettingsAutoDiscardMonths to enable a mode where unused settings + - Added `bool io.ConfigIniSettingsSaveLastUsedDate` to disable saving that info. (#9460) + - Added `int io.ConfigIniSettingsAutoDiscardMonths` to enable a mode where unused settings are automatically discard after xx months. (#9460) - Added a trimming tool under Metrics->Settings, along with a yet-unexposed function. - - The current system date is fed through ImGuiPlatformIO::Platform_SessionDate, + - The current system date is fed through `ImGuiPlatformIO::Platform_SessionDate`, which is automatically set by a call to time() done during context creation. (#9460) - - Added IMGUI_DISABLE_TIME_FUNCTIONS to disable setting platform_io.Platform_SessionDate. + - Added `IMGUI_DISABLE_TIME_FUNCTIONS` to disable setting platform_io.Platform_SessionDate. A custom backend may still set it manually. (#9460) - DrawList: - Minor optimization to `AddLine()`, `AddLineH()`, `AddLineV()` functions. (#4091) @@ -197,24 +200,24 @@ Other Changes: - Demo: - Extract 'Widgets->Tree Nodes->Selectable Nodes' out of the 'Advanced' demo for clarity (manual reimplementation of basic selection). -- Misc: - - Added IM_DEBUG_BREAK() handler for GCC+AArch64/ARM64. [@tom-seddon] +- Debug Tools: + - Added `IM_DEBUG_BREAK()` handler for GCC+AArch64/ARM64. [@tom-seddon] - Backends: - Android: - - Clear mouse position on touch release (AMOTION_EVENT_ACTION_UP) to prevent + - Clear mouse position on touch release (`AMOTION_EVENT_ACTION_UP`) to prevent items from staying in hovered state. (#6627, #9474) [@Turtle-PB] - Metal4: - Added new Metal 4 backend (forked from Metal 3 backend). (#9458, #9451) [@AmelieHeinrich] - Added Metal-cpp support enabled with `IMGUI_IMPL_METAL_CPP` define. (#9461) [@MERL10N] - OpenGL2: - - Backup and restore GL_UNPACK_ROW_LENGTH and GL_UNPACK_ALIGNMENT when updating texture + - Backup and restore `GL_UNPACK_ROW_LENGTH` and `GL_UNPACK_ALIGNMENT` when updating texture to avoid altering caller GL state. (#8802, #9473) [@Turtle-PB] - OpenGL3: - GLSL version detection assume GLSL 410 when GL context is 4.1. Fixes an issue running on macOS with Wine. [#9427, #6577) [@perminovVS] - - Expose selected render state in ImGui_ImplOpenGL3_RenderState, allowing to - dynamically select between use of glBindSampler() and glTexParameter(). (#9378) - - Backup and restore GL_UNPACK_ROW_LENGTH and GL_UNPACK_ALIGNMENT when updating texture + - Expose selected render state in `ImGui_ImplOpenGL3_RenderState`, allowing to + dynamically select between use of `glBindSampler()` and `glTexParameter()`. (#9378) + - Backup and restore `GL_UNPACK_ROW_LENGTH` and `GL_UNPACK_ALIGNMENT` when updating texture to avoid altering caller GL state. (#8802, #9473) [@Turtle-PB] - SDL2: - Restore SDL_StartTextInput()/SDL_StopTextInput() in IME handler for on-screen keyboard diff --git a/imgui.cpp b/imgui.cpp index 2eab3aae3..2c536fa8f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9 WIP +// dear imgui, v1.92.9 // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index 918d253d2..da664cb8e 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9 WIP +// dear imgui, v1.92.9 // (headers) // Help: @@ -29,8 +29,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.92.9 WIP" -#define IMGUI_VERSION_NUM 19287 +#define IMGUI_VERSION "1.92.9" +#define IMGUI_VERSION_NUM 19290 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 27413b7aa..f9b3d0108 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9 WIP +// dear imgui, v1.92.9 // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 6ec7478b9..acf377c55 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9 WIP +// dear imgui, v1.92.9 // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index 584914fc4..1a9369aa0 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9 WIP +// dear imgui, v1.92.9 // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index efc03d7d9..43b5cd5a7 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9 WIP +// dear imgui, v1.92.9 // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index efd3a64ff..6c324e7b0 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9 WIP +// dear imgui, v1.92.9 // (widgets code) /* From a9e7a8c880cf4a7fbf142b13a41bcbb1f43d0938 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 26 Jul 2026 15:42:48 +0200 Subject: [PATCH 13/47] ImDrawData: fixed a regression in 1.92.9 where legacy `ImDrawData::CmdListsCount` was always 0. --- docs/CHANGELOG.txt | 17 +++++++++++++++-- imgui.cpp | 4 +++- imgui_draw.cpp | 3 --- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a3e036018..35a67649a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -35,11 +35,24 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.92.9b (In Progress) +----------------------------------------------------------------------- + +Breaking Changes: + +Other Changes: + +- ImDrawData: fixed a regression in 1.92.9 where legacy `ImDrawData::CmdListsCount` was always 0. + Prefer using `ImDrawData::CmdList.Size` anyway. + + + ----------------------------------------------------------------------- VERSION 1.92.9 (Released 2026-07-25) ----------------------------------------------------------------------- -Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.8 +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.9 Breaking Changes: @@ -1106,7 +1119,7 @@ Other Changes: not available. (#5924, #5562) - SDL3+DirectX11: Added SDL3+DirectX11 example. (#8956, #8957) [@tomaz82] - Win32+DirectX12: Rework synchronization logic. (#8961) [@RT2Code] - - Made examples's main.cpp consistent with returning 1 on error. + - Made examples' main.cpp consistent with returning 1 on error. ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 2c536fa8f..da2780fd9 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6243,7 +6243,9 @@ void ImGui::Render() ImDrawData* draw_data = &viewport->DrawDataP; for (ImDrawList* draw_list : draw_data->CmdLists) draw_list->_PopUnusedDrawCmd(); - +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + draw_data->CmdListsCount = draw_data->CmdLists.Size; +#endif g.IO.MetricsRenderVertices += draw_data->TotalVtxCount; g.IO.MetricsRenderIndices += draw_data->TotalIdxCount; } diff --git a/imgui_draw.cpp b/imgui_draw.cpp index acf377c55..d89e36a9a 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -2340,9 +2340,6 @@ void ImDrawData::AddDrawList(ImDrawList* draw_list) { draw_list->_PopUnusedDrawCmd(); ImGui::AddDrawListToDrawDataEx(this, &CmdLists, draw_list); -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - CmdListsCount = CmdLists.Size; -#endif } // For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! From ff135cf5e529e49519e8046381d9297454807c43 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 27 Jul 2026 21:49:46 +0200 Subject: [PATCH 14/47] Backends: DirectX11: removed obsolete comment that got left in this backend only. --- backends/imgui_impl_dx11.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp index 6573d2742..73d7d6b70 100644 --- a/backends/imgui_impl_dx11.cpp +++ b/backends/imgui_impl_dx11.cpp @@ -574,7 +574,6 @@ bool ImGui_ImplDX11_CreateDeviceObjects() } // Create texture sampler - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) { D3D11_SAMPLER_DESC desc; ZeroMemory(&desc, sizeof(desc)); From 8c131021b0680fbfa22610737033d2fe6c54277f Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 29 Jul 2026 15:26:51 +0200 Subject: [PATCH 15/47] Update pull_request_template.md --- .github/pull_request_template.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d40b14ccf..27cb3e1dd 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -6,5 +6,7 @@ 3. Consider running the [imgui_test_suite](https://github.com/ocornut/imgui_test_engine) or adding new tests to test for expected behaviors. -4. Clear this template before submitting your PR. +4. PLEASE DO NOT SUBMIT AI SLOP OR AUTOMATED REPORTS. Submissions that are lacking proper human review/judgement before being passed to us are not welcome. + +5. Clear this template before submitting your PR. From 950d27137e75663d7697cc21b7d566e67476ba28 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 29 Jul 2026 14:09:10 +0200 Subject: [PATCH 16/47] Tweaks. --- backends/imgui_impl_dx10.cpp | 10 +++++----- backends/imgui_impl_dx11.cpp | 10 +++++----- backends/imgui_impl_dx12.cpp | 8 ++++---- backends/imgui_impl_wgpu.cpp | 2 +- imgui.cpp | 18 +++++++++--------- imgui.h | 2 +- imgui_demo.cpp | 10 +++++----- imgui_tables.cpp | 11 ++++++----- 8 files changed, 36 insertions(+), 35 deletions(-) diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index 9858cbe94..9e570ef8c 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -148,7 +148,7 @@ static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* device->GSSetShader(nullptr); // Setup render state - const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; + const float blend_factor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; device->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff); device->OMSetDepthStencilState(bd->pDepthStencilState, 0); device->RSSetState(bd->pRasterizerState); @@ -446,7 +446,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() PS_INPUT main(VS_INPUT input)\ {\ PS_INPUT output;\ - output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\ + output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.0f, 1.0f));\ output.col = input.col;\ output.uv = input.uv;\ return output;\ @@ -566,10 +566,10 @@ bool ImGui_ImplDX10_CreateDeviceObjects() desc.AddressU = D3D10_TEXTURE_ADDRESS_CLAMP; desc.AddressV = D3D10_TEXTURE_ADDRESS_CLAMP; desc.AddressW = D3D10_TEXTURE_ADDRESS_CLAMP; - desc.MipLODBias = 0.f; + desc.MipLODBias = 0.0f; desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS; - desc.MinLOD = 0.f; - desc.MaxLOD = 0.f; + desc.MinLOD = 0.0f; + desc.MaxLOD = 0.0f; bd->pd3dDevice->CreateSamplerState(&desc, &bd->pTexSamplerLinear); desc.Filter = D3D10_FILTER_MIN_MAG_MIP_POINT; bd->pd3dDevice->CreateSamplerState(&desc, &bd->pTexSamplerNearest); diff --git a/backends/imgui_impl_dx11.cpp b/backends/imgui_impl_dx11.cpp index 73d7d6b70..73f722a08 100644 --- a/backends/imgui_impl_dx11.cpp +++ b/backends/imgui_impl_dx11.cpp @@ -155,7 +155,7 @@ static void ImGui_ImplDX11_SetupRenderState(const ImDrawData* draw_data, ID3D11D device_ctx->CSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used.. // Setup render state - const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; + const float blend_factor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; device_ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff); device_ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0); device_ctx->RSSetState(bd->pRasterizerState); @@ -462,7 +462,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects() PS_INPUT main(VS_INPUT input)\ {\ PS_INPUT output;\ - output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\ + output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.0f, 1.0f));\ output.col = input.col;\ output.uv = input.uv;\ return output;\ @@ -581,10 +581,10 @@ bool ImGui_ImplDX11_CreateDeviceObjects() desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; - desc.MipLODBias = 0.f; + desc.MipLODBias = 0.0f; desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; - desc.MinLOD = 0.f; - desc.MaxLOD = 0.f; + desc.MinLOD = 0.0f; + desc.MaxLOD = 0.0f; bd->pd3dDevice->CreateSamplerState(&desc, &bd->pTexSamplerLinear); desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; bd->pd3dDevice->CreateSamplerState(&desc, &bd->pTexSamplerNearest); diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 157384abf..53c7b3dd0 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -207,7 +207,7 @@ static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12Graphic command_list->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0); // Setup blend factor - const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; + const float blend_factor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; command_list->OMSetBlendFactor(blend_factor); } @@ -614,11 +614,11 @@ bool ImGui_ImplDX12_CreateDeviceObjects() staticSampler[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; staticSampler[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; staticSampler[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSampler[0].MipLODBias = 0.f; + staticSampler[0].MipLODBias = 0.0f; staticSampler[0].MaxAnisotropy = 0; staticSampler[0].ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; staticSampler[0].BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; - staticSampler[0].MinLOD = 0.f; + staticSampler[0].MinLOD = 0.0f; staticSampler[0].MaxLOD = D3D12_FLOAT32_MAX; staticSampler[0].ShaderRegister = 0; staticSampler[0].RegisterSpace = 0; @@ -721,7 +721,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects() PS_INPUT main(VS_INPUT input)\ {\ PS_INPUT output;\ - output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\ + output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.0f, 1.0f));\ output.col = input.col;\ output.uv = input.uv;\ return output;\ diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index cd952cdc2..9f0124f0c 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -449,7 +449,7 @@ static void ImGui_ImplWGPU_SetupRenderState(ImDrawData* draw_data, WGPURenderPas wgpuRenderPassEncoderSetBindGroup(ctx, 0, bd->renderResources.CommonBindGroupLinear, 0, nullptr); // Setup blend factor - WGPUColor blend_color = { 0.f, 0.f, 0.f, 0.f }; + WGPUColor blend_color = { 0.0f, 0.0f, 0.0f, 0.0f }; wgpuRenderPassEncoderSetBlendConstant(ctx, &blend_color); } diff --git a/imgui.cpp b/imgui.cpp index da2780fd9..b480b50ad 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2860,7 +2860,7 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE IMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b) { - float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f; + float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.0f; int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t); int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t); int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t); @@ -2891,20 +2891,20 @@ ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in) // Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) { - float K = 0.f; + float K = 0.0f; if (g < b) { ImSwap(g, b); - K = -1.f; + K = -1.0f; } if (r < g) { ImSwap(r, g); - K = -2.f / 6.f - K; + K = -2.0f / 6.0f - K; } const float chroma = r - (g < b ? g : b); - out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f)); + out_h = ImFabs(K + (g - b) / (6.0f * chroma + 1e-20f)); out_s = chroma / (r + 1e-20f); out_v = r; } @@ -7746,10 +7746,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (flags & ImGuiWindowFlags_AlwaysAutoResize) { if (!window_size_x_set_by_api) - window->Size.x = window->SizeFull.x = 0.f; + window->Size.x = window->SizeFull.x = 0.0f; if (!window_size_y_set_by_api) - window->Size.y = window->SizeFull.y = 0.f; - window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f); + window->Size.y = window->SizeFull.y = 0.0f; + window->ContentSize = window->ContentSizeIdeal = ImVec2(0.0f, 0.0f); } } @@ -18519,7 +18519,7 @@ void ImGui::ShowIDStackToolWindow(bool* p_open) Checkbox("Ctrl+C: copy path", &tool->OptCopyToClipboardOnCtrlC); PopStyleVar(); SameLine(); - TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), "*COPIED*"); + TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.0f, 1.0f, 0.3f, 1.0f) : ImVec4(), "*COPIED*"); if (tool->OptCopyToClipboardOnCtrlC && Shortcut(ImGuiMod_Ctrl | ImGuiKey_C, ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteOverFocused)) { tool->CopyToClipboardLastTime = (float)g.Time; diff --git a/imgui.h b/imgui.h index da664cb8e..c7f5c92ac 100644 --- a/imgui.h +++ b/imgui.h @@ -3298,7 +3298,7 @@ enum ImDrawFlags_ // Stroke options ImDrawFlags_Closed = 1 << 9, // PathStroke(), AddPolyline(): specify that shape should be closed. - //ImDrawFlags_Closed = 1 << 0, // Prior to 1.92.8 (May 2026), ImDrawFlags_Closed was guaranteed to be == 1<<0 == 1 for legacy compatibility reason. Hardcoded use of 1 or true should be replaced. + //ImDrawFlags_Closed = 1, // Prior to 1.92.8 (May 2026), ImDrawFlags_Closed was guaranteed to be == 1<<0 == 1 for legacy compatibility reason. Hardcoded use of 1 or true should be replaced. ImDrawFlags_InvalidMask_ = ~0x7FFFFFF0, // == 0x8000000F, }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index f9b3d0108..67aa1721a 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1747,7 +1747,7 @@ static void DemoWindowWidgetsDragAndDrop() if (ImGui::IsItemActive() && !ImGui::IsItemHovered()) { - int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.f ? -1 : 1); + int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.0f ? -1 : 1); if (n_next >= 0 && n_next < IM_COUNTOF(item_names)) { item_names[n] = item_names[n_next]; @@ -2201,7 +2201,7 @@ static void DemoWindowWidgetsProgressBars() char buf[32]; sprintf(buf, "%d/%d", (int)(progress * 1753), 1753); - ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf); + ImGui::ProgressBar(progress, ImVec2(0.0f, 0.0f), buf); // Pass an animated negative value, e.g. -1.0f * (float)ImGui::GetTime() is the recommended value. // Adjust the factor if you want to adjust the animation speed. @@ -9094,7 +9094,7 @@ struct ExampleAppConsole static int Stricmp(const char* s1, const char* s2) { int d; while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; } return d; } static int Strnicmp(const char* s1, const char* s2, int n) { int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; n--; } return d; } static char* Strdup(const char* s) { IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = ImGui::MemAlloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)s, len); } - static void Strtrim(char* s) { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; } + static void Strtrimblanks(char* s) { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; } void ClearLog() { @@ -9243,10 +9243,10 @@ struct ExampleAppConsole if (ImGui::InputText("Input", InputBuf, IM_COUNTOF(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this)) { char* s = InputBuf; - Strtrim(s); + Strtrimblanks(s); if (s[0]) ExecCommand(s); - strcpy(s, ""); + s[0] = 0; reclaim_focus = true; } diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 43b5cd5a7..4799d5648 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -490,17 +490,18 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize, // it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with // different x/y values to BeginChild(). + const float border_size = TABLE_BORDER_SIZE; if (flags & ImGuiTableFlags_BordersOuterV) { - table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x); + table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + border_size, table->HostClipRect.Max.x); if (inner_window->DecoOuterSizeX2 == 0.0f) - table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - TABLE_BORDER_SIZE, table->HostClipRect.Min.x); + table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - border_size, table->HostClipRect.Min.x); } if (flags & ImGuiTableFlags_BordersOuterH) { - table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y); + table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + border_size, table->HostClipRect.Max.y); if (inner_window->DecoOuterSizeY2 == 0.0f) - table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - TABLE_BORDER_SIZE, table->HostClipRect.Min.y); + table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - border_size, table->HostClipRect.Min.y); } } @@ -3626,7 +3627,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label if (pass == 1) { // Draw border - draw_list->AddLine(bg_shape[0], bg_shape[3], TableGetColumnBorderCol(table, order_n, column_n)); + draw_list->AddLine(bg_shape[0], bg_shape[3], TableGetColumnBorderCol(table, order_n, column_n), TABLE_BORDER_SIZE); } } PopClipRect(); From e47e6b0d9de3433dd9c3fee17e6e509163256fc9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 30 Jul 2026 16:03:42 +0200 Subject: [PATCH 17/47] Fonts: made GetDefaultCompressedFontDataProggyClean()/GetDefaultCompressedFontDataProggyForever() undocumented non-static. (#9493) --- imgui_draw.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index d89e36a9a..37ab7b4fc 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3159,10 +3159,10 @@ static void Decode85(const unsigned char* src, unsigned char* dst) } } #if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_BITMAP) -static const char* GetDefaultCompressedFontDataProggyClean(int* out_size); +const char* ImGui_GetDefaultCompressedFontDataProggyClean(int* out_size); #endif #if !defined(IMGUI_DISABLE_DEFAULT_FONT) && !defined(IMGUI_DISABLE_DEFAULT_FONT_VECTOR) -static const char* GetDefaultCompressedFontDataProggyForever(int* out_size); +const char* ImGui_GetDefaultCompressedFontDataProggyForever(int* out_size); #endif // This duplicates some of the logic in UpdateFontsNewFrame() which is a bit chicken-and-eggy/tricky to extract due to variety of codepaths and possible initialization ordering. @@ -3201,7 +3201,7 @@ ImFont* ImFontAtlas::AddFontDefaultBitmap(const ImFontConfig* font_cfg_template) font_cfg.GlyphOffset.y += 1.0f * (font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units int ttf_compressed_size = 0; - const char* ttf_compressed = GetDefaultCompressedFontDataProggyClean(&ttf_compressed_size); + const char* ttf_compressed = ImGui_GetDefaultCompressedFontDataProggyClean(&ttf_compressed_size); return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg); #else IM_ASSERT(0 && "Function is disabled in this build."); @@ -3229,7 +3229,7 @@ ImFont* ImFontAtlas::AddFontDefaultVector(const ImFontConfig* font_cfg_template) font_cfg.GlyphOffset.y += 0.5f * (font_cfg.SizePixels / 16.0f); // Closer match ProggyClean + avoid descenders going too high (with current code). int ttf_compressed_size = 0; - const char* ttf_compressed = GetDefaultCompressedFontDataProggyForever(&ttf_compressed_size); + const char* ttf_compressed = ImGui_GetDefaultCompressedFontDataProggyForever(&ttf_compressed_size); return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, font_cfg.SizePixels, &font_cfg); #else IM_ASSERT(0 && "Function is disabled in this build."); @@ -6536,7 +6536,7 @@ static const unsigned char proggy_clean_ttf_compressed_data[9583] = 239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,32,57,141,239,35,57,102,0,0,5,250,72,249,98,247, }; -static const char* GetDefaultCompressedFontDataProggyClean(int* out_size) +const char* ImGui_GetDefaultCompressedFontDataProggyClean(int* out_size) { *out_size = proggy_clean_ttf_compressed_size; return (const char*)proggy_clean_ttf_compressed_data; @@ -6801,7 +6801,7 @@ static const unsigned char proggy_forever_minimal_ttf_compressed_data[14562] = 3,36,0,229,13,183,147,132,7,42,175,187,66,0,0,0,0,229,178,59,232,5,250,48,120,202,241, }; -static const char* GetDefaultCompressedFontDataProggyForever(int* out_size) +const char* ImGui_GetDefaultCompressedFontDataProggyForever(int* out_size) { *out_size = proggy_forever_minimal_ttf_compressed_size; return (const char*)proggy_forever_minimal_ttf_compressed_data; From ffbf21af6b61157473a711738ad6da0641bb267d Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 30 Jul 2026 16:12:31 +0200 Subject: [PATCH 18/47] Demo: Custom Rendering: enable LiveEdit. Tweaks. --- imgui_demo.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 67aa1721a..85e1bc4b4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -10265,6 +10265,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) { IMGUI_DEMO_MARKER("Examples/Custom rendering/Primitives"); ImGui::PushItemWidth(-ImGui::GetFontSize() * 15); + ImGui::PushItemFlag(ImGuiItemFlags_LiveEditOnInput, true); ImDrawList* draw_list = ImGui::GetWindowDrawList(); // Draw gradients @@ -10291,7 +10292,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) // Draw a bunch of primitives ImGui::Text("All primitives"); - static float sz = 36.0f; + static float sz = 42.0f; static float thickness = 3.0f; static int ngon_sides = 6; static bool circle_segments_override = false; @@ -10385,6 +10386,7 @@ static void ShowExampleAppCustomRendering(bool* p_open) x += sz + spacing; ImGui::Dummy(ImVec2((sz + spacing) * 13.2f, (sz + spacing) * 3.0f)); + ImGui::PopItemFlag(); ImGui::PopItemWidth(); ImGui::EndTabItem(); } From ed252c55aadd46e4ef78d85f795d6e3c4191a911 Mon Sep 17 00:00:00 2001 From: thedmd Date: Thu, 30 Jul 2026 21:40:05 +0200 Subject: [PATCH 19/47] Tables: fixed Y position of lower horizontal border being off by one pixel when vertical ones are not also enabled. Attributed to thedmd who reported this to me. --- docs/CHANGELOG.txt | 2 ++ imgui_tables.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 35a67649a..fc3d69650 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,8 @@ Breaking Changes: Other Changes: +- Tables: fixed Y2 position of lower horizontal border (ImGuiTableFlags_BordersOuterH) + being off by one pixel when vertical ones are not also enabled. [@thedmd] - ImDrawData: fixed a regression in 1.92.9 where legacy `ImDrawData::CmdListsCount` was always 0. Prefer using `ImDrawData::CmdList.Size` anyway. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 4799d5648..a6185d85d 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -3026,7 +3026,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table) else if (table->Flags & ImGuiTableFlags_BordersOuterH) { inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Min.y, outer_col, border_size); - inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Max.y, outer_col, border_size); + inner_drawlist->AddLineH(outer_border.Min.x, outer_border.Max.x, outer_border.Max.y - border_size, outer_col, border_size); } } if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y) From b9adace7b575b401cef8cd77bfee1fed4706b913 Mon Sep 17 00:00:00 2001 From: thedmd Date: Thu, 30 Jul 2026 22:02:50 +0200 Subject: [PATCH 20/47] Tables: fixed X position of angled borders being off by half a pixel. --- docs/CHANGELOG.txt | 6 ++++-- imgui_tables.cpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fc3d69650..642dbded5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,8 +43,10 @@ Breaking Changes: Other Changes: -- Tables: fixed Y2 position of lower horizontal border (ImGuiTableFlags_BordersOuterH) - being off by one pixel when vertical ones are not also enabled. [@thedmd] +- Tables: + - Fixed Y2 position of lower horizontal border (ImGuiTableFlags_BordersOuterH) + being off by one pixel when vertical ones are not also enabled. [@thedmd] + - Fixed X position of angled borders being off by half a pixel. [@thedmd] - ImDrawData: fixed a regression in 1.92.9 where legacy `ImDrawData::CmdListsCount` was always 0. Prefer using `ImDrawData::CmdList.Size` anyway. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index a6185d85d..3e7065bf9 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -3627,7 +3627,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label if (pass == 1) { // Draw border - draw_list->AddLine(bg_shape[0], bg_shape[3], TableGetColumnBorderCol(table, order_n, column_n), TABLE_BORDER_SIZE); + draw_list->AddLine(bg_shape[0] - ImVec2(TABLE_BORDER_SIZE * 0.5f, 0.0f), bg_shape[3] - ImVec2(TABLE_BORDER_SIZE * 0.5f, 0.0f), TableGetColumnBorderCol(table, order_n, column_n), TABLE_BORDER_SIZE); } } PopClipRect(); From 448ef2baa994f24a15cbd6cadeea3f64323e2e4e Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 31 Jul 2026 13:09:40 +0200 Subject: [PATCH 21/47] DragXXX, SliderXXX: fixed a regression in 1.92.9 where clicking a previously Ctrl+Clicked field would flick it to text edit mode for a frame. (#9476, #701) --- docs/CHANGELOG.txt | 2 ++ imgui_internal.h | 1 + imgui_widgets.cpp | 5 +++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 642dbded5..2265df94d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,8 @@ Breaking Changes: Other Changes: +- DragXXX, SliderXXX: fixed a regression in 1.92.9 where clicking a previously + Ctrl+Clicked field would flick it to text edit mode for a frame. (#9476, #701) - Tables: - Fixed Y2 position of lower horizontal border (ImGuiTableFlags_BordersOuterH) being off by one pixel when vertical ones are not also enabled. [@thedmd] diff --git a/imgui_internal.h b/imgui_internal.h index 1a9369aa0..c7efaf44e 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1265,6 +1265,7 @@ struct IMGUI_API ImGuiInputTextState ImStbTexteditState* Stb; // State for stb_textedit.h ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set. ImGuiID ID; // widget id owning the text state + //int LastFrameActive; // Last used frame-count (unused) int TextLen; // UTF-8 length of the string in TextA (in bytes) const char* TextSrc; // == TextA.Data unless read-only, in which case == buf passed to InputText(). For _ReadOnly fields, pointer will be null outside the InputText() call. ImVector TextA; // main UTF8 buffer. TextA.Size is a buffer size! Should always be >= buf_size passed by user (and of course >= CurLenA + 1). diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 6c324e7b0..3c46db524 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4606,6 +4606,7 @@ void ImGui::InputTextDeactivateHook(ImGuiID id) if (!state->EditedBefore) return; //IMGUI_DEBUG_LOG_ACTIVEID("InputTextDeactivateHook() id = 0x%08X\n", id); + state->EditedBefore = false; g.InputTextDeactivatedState.ID = state->ID; g.InputTextDeactivatedState.ElapseFrame = g.FrameCount + 1; if (state->Flags & ImGuiInputTextFlags_ReadOnly) @@ -4997,6 +4998,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (state != NULL && state->ID == id) { state->Flags = flags; + //state->LastFrameActive = g.FrameCount; // Word-wrapping: attempt to keep cursor in view while resizing frame/parent (FIXME-WORDWRAP: would be better to preserve same relative offset) if (is_wordwrap && state->WrapWidth != wrap_width) @@ -5484,9 +5486,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Otherwise request text input ahead for next frame. if (g.ActiveId == id && clear_active_id) { - state->ID = 0; // To avoid InputTextDeactivateHook() unnecessarily running, which wouldn't be harmful but wasteful. + state->EditedBefore = false; // Data already applied: avoid InputTextDeactivateHook() taking a record now or later if same id is activated again without editing. ClearActiveID(); - state->ID = id; } // Render frame From f1cc2ae15e53a861a874c3034aae6798fde194ab Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 31 Jul 2026 15:51:26 +0200 Subject: [PATCH 22/47] Version 1.92.9b --- backends/imgui_impl_android.cpp | 2 +- backends/imgui_impl_android.h | 2 +- docs/CHANGELOG.txt | 7 +++---- imgui.cpp | 2 +- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 10 files changed, 14 insertions(+), 15 deletions(-) diff --git a/backends/imgui_impl_android.cpp b/backends/imgui_impl_android.cpp index 08b3de08a..21a7ae526 100644 --- a/backends/imgui_impl_android.cpp +++ b/backends/imgui_impl_android.cpp @@ -1,5 +1,5 @@ // dear imgui: Platform Binding for Android native app -// This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3) +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) // Implemented features: // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values are obsolete since 1.87 and not supported since 1.91.5] diff --git a/backends/imgui_impl_android.h b/backends/imgui_impl_android.h index f6e41039a..8f22e9b76 100644 --- a/backends/imgui_impl_android.h +++ b/backends/imgui_impl_android.h @@ -1,5 +1,5 @@ // dear imgui: Platform Binding for Android native app -// This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3) +// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) // Implemented features: // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values are obsolete since 1.87 and not supported since 1.91.5] diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2265df94d..fbfd2aa55 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -36,12 +36,12 @@ HOW TO UPDATE? - Please report any issue! ----------------------------------------------------------------------- - VERSION 1.92.9b (In Progress) + VERSION 1.92.9b (Released 2026-07-31) ----------------------------------------------------------------------- -Breaking Changes: +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.92.9b -Other Changes: +Changes: - DragXXX, SliderXXX: fixed a regression in 1.92.9 where clicking a previously Ctrl+Clicked field would flick it to text edit mode for a frame. (#9476, #701) @@ -53,7 +53,6 @@ Other Changes: Prefer using `ImDrawData::CmdList.Size` anyway. - ----------------------------------------------------------------------- VERSION 1.92.9 (Released 2026-07-25) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index b480b50ad..7fa2d49e8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9 +// dear imgui, v1.92.9b // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index c7f5c92ac..611c5f28d 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9 +// dear imgui, v1.92.9b // (headers) // Help: @@ -29,8 +29,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.92.9" -#define IMGUI_VERSION_NUM 19290 +#define IMGUI_VERSION "1.92.9b" +#define IMGUI_VERSION_NUM 19291 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 85e1bc4b4..9f841b3c4 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9 +// dear imgui, v1.92.9b // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 37ab7b4fc..e582f5cd8 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9 +// dear imgui, v1.92.9b // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index c7efaf44e..d2a8595a1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9 +// dear imgui, v1.92.9b // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 3e7065bf9..f8fab3778 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9 +// dear imgui, v1.92.9b // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 3c46db524..5ed7d6feb 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9 +// dear imgui, v1.92.9b // (widgets code) /* From 9b7699f32597e7c7a799f22b1860ac586c2857b9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 31 Jul 2026 22:07:01 +0200 Subject: [PATCH 23/47] Backends: amend comments to state that bilinear filtering is required ahead of 1.93. --- backends/imgui_impl_allegro5.cpp | 3 +-- backends/imgui_impl_dx10.cpp | 3 +-- backends/imgui_impl_dx12.cpp | 2 +- backends/imgui_impl_metal.mm | 2 +- backends/imgui_impl_opengl2.cpp | 3 +-- backends/imgui_impl_opengl3.cpp | 3 +-- backends/imgui_impl_sdlgpu3.cpp | 2 +- backends/imgui_impl_sdlrenderer2.cpp | 3 +-- backends/imgui_impl_sdlrenderer3.cpp | 3 +-- backends/imgui_impl_vulkan.cpp | 3 +-- backends/imgui_impl_wgpu.cpp | 3 +-- docs/BACKENDS.md | 4 +--- 12 files changed, 12 insertions(+), 22 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index 79b169da8..bd877f322 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -268,8 +268,7 @@ void ImGui_ImplAllegro5_UpdateTexture(ImTextureData* tex) IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); - // Create texture - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + // Create texture (bilinear sampling is required) const int new_bitmap_flags = al_get_new_bitmap_flags(); int new_bitmap_format = al_get_new_bitmap_format(); al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP | ALLEGRO_MIN_LINEAR | ALLEGRO_MAG_LINEAR); diff --git a/backends/imgui_impl_dx10.cpp b/backends/imgui_impl_dx10.cpp index 9e570ef8c..35378612d 100644 --- a/backends/imgui_impl_dx10.cpp +++ b/backends/imgui_impl_dx10.cpp @@ -557,8 +557,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects() bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState); } - // Create texture sampler - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + // Create texture sampler (bilinear sampling is required) { D3D10_SAMPLER_DESC desc; ZeroMemory(&desc, sizeof(desc)); diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 53c7b3dd0..034882856 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -608,7 +608,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects() param[1].DescriptorTable.pDescriptorRanges = &descRange; param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. + // Bilinear sampling is required D3D12_STATIC_SAMPLER_DESC staticSampler[1] = {}; staticSampler[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; staticSampler[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index 445c4b36c..1c0660628 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -584,7 +584,7 @@ void ImGui_ImplMetal_Shutdown() return [[MetalBuffer alloc] initWithBuffer:backing]; } -// Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. +// Bilinear sampling is required. - (id)renderPipelineStateForFramebufferDescriptor:(FramebufferDescriptor*)descriptor device:(id)device { NSError* error = nil; diff --git a/backends/imgui_impl_opengl2.cpp b/backends/imgui_impl_opengl2.cpp index 111f37413..e6d195533 100644 --- a/backends/imgui_impl_opengl2.cpp +++ b/backends/imgui_impl_opengl2.cpp @@ -276,8 +276,7 @@ void ImGui_ImplOpenGL2_UpdateTexture(ImTextureData* tex) const void* pixels = tex->GetPixels(); GLuint gl_texture_id = 0; - // Upload texture to graphics system - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + // Upload texture to graphics system (bilinear sampling is required). GLint last_texture; GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); GL_CALL(glGenTextures(1, &gl_texture_id)); diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index ff9980035..7be882e06 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -689,8 +689,7 @@ void ImGui_ImplOpenGL3_UpdateTexture(ImTextureData* tex) const void* pixels = tex->GetPixels(); GLuint gl_texture_id = 0; - // Upload texture to graphics system - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + // Upload texture to graphics system (bilinear sampling is required). GLint last_texture; GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); GL_CALL(glGenTextures(1, &gl_texture_id)); diff --git a/backends/imgui_impl_sdlgpu3.cpp b/backends/imgui_impl_sdlgpu3.cpp index 8f0408c82..9328b0a32 100644 --- a/backends/imgui_impl_sdlgpu3.cpp +++ b/backends/imgui_impl_sdlgpu3.cpp @@ -591,7 +591,7 @@ void ImGui_ImplSDLGPU3_CreateDeviceObjects() if (bd->TexSamplerLinear == nullptr) { - // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. + // Bilinear sampling is required. SDL_GPUSamplerCreateInfo sampler_info = {}; sampler_info.min_filter = SDL_GPU_FILTER_LINEAR; sampler_info.mag_filter = SDL_GPU_FILTER_LINEAR; diff --git a/backends/imgui_impl_sdlrenderer2.cpp b/backends/imgui_impl_sdlrenderer2.cpp index d9f329bd1..e295e8677 100644 --- a/backends/imgui_impl_sdlrenderer2.cpp +++ b/backends/imgui_impl_sdlrenderer2.cpp @@ -213,8 +213,7 @@ void ImGui_ImplSDLRenderer2_UpdateTexture(ImTextureData* tex) IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); - // Create texture - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + // Create texture (bilinear sampling is required) SDL_Texture* sdl_texture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, tex->Width, tex->Height); IM_ASSERT(sdl_texture != nullptr && "Backend failed to create texture!"); SDL_UpdateTexture(sdl_texture, nullptr, tex->GetPixels(), tex->GetPitch()); diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index d4c043cbc..cb975393e 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -242,8 +242,7 @@ void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex) IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); - // Create texture - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + // Create texture (bilinear sampling is required) SDL_Texture* sdl_texture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, tex->Width, tex->Height); IM_ASSERT(sdl_texture != nullptr && "Backend failed to create texture!"); SDL_UpdateTexture(sdl_texture, nullptr, tex->GetPixels(), tex->GetPitch()); diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index ef7dd625f..57ebdb064 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -1153,8 +1153,7 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() check_vk_result(err); } - // Create samplers - // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. + // Create samplers (bilinear sampling is required) if (!bd->SamplerLinear || !bd->SamplerNearest) { VkSamplerCreateInfo info = {}; diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 9f0124f0c..ce5974b41 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -844,8 +844,7 @@ bool ImGui_ImplWGPU_CreateDeviceObjects() ImGui_ImplWGPU_CreateUniformBuffer(); - // Create samplers (Linear/Nearest) - // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) + // Create samplers (Linear/Nearest) (bilinear sampling is required) WGPUSamplerDescriptor sampler_desc = {}; sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index 2f3a802d8..f72391f3e 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -234,9 +234,7 @@ void MyImGuiBackend_RenderDrawData(ImDrawData* draw_data) // - Scissor enabled MyEngineSetupenderState(); - // TODO: Setup texture sampling state - // - Sample with bilinear filtering (NOT point/nearest filtering). - // - Use 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines;' to allow point/nearest filtering. + // TODO: Setup texture sampler to bilinear (NOT point/nearest filtering). // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize From ebd752a0ece30054ba2e165ee06a4ef1a1500a97 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 3 Aug 2026 14:42:56 +0200 Subject: [PATCH 24/47] Version 1.93.0 WIP --- docs/CHANGELOG.txt | 9 +++++++++ imgui.cpp | 2 +- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fbfd2aa55..d9b60016d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -35,6 +35,15 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.93.0 WIP (In Progress) +----------------------------------------------------------------------- + +Breaking Changes: + +Other Changes: + + ----------------------------------------------------------------------- VERSION 1.92.9b (Released 2026-07-31) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 7fa2d49e8..fbad03746 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9b +// dear imgui, v1.93.0 WIP // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index 611c5f28d..7830580a7 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9b +// dear imgui, v1.93.0 WIP // (headers) // Help: @@ -29,8 +29,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.92.9b" -#define IMGUI_VERSION_NUM 19291 +#define IMGUI_VERSION "1.93.0 WIP" +#define IMGUI_VERSION_NUM 19292 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 9f841b3c4..e013f510c 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9b +// dear imgui, v1.93.0 WIP // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index e582f5cd8..fa8ea9cc0 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9b +// dear imgui, v1.93.0 WIP // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index d2a8595a1..208c04cb4 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9b +// dear imgui, v1.93.0 WIP // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index f8fab3778..19f7933ed 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9b +// dear imgui, v1.93.0 WIP // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5ed7d6feb..5bd6da9fa 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.9b +// dear imgui, v1.93.0 WIP // (widgets code) /* From b647af21008696a46c2aad2f444d6bd9b1798a2f Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 3 Aug 2026 15:00:05 +0200 Subject: [PATCH 25/47] Fonts: AddFontDefault() uses framebuffer scale. If running before DisplaySize is set, assume a different default on Apple. --- docs/CHANGELOG.txt | 5 +++++ imgui_draw.cpp | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d9b60016d..e854fbc69 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,11 @@ Breaking Changes: Other Changes: +- Fonts: + - Reworked `AddFontDefault()` to use `io.DisplayFrameBufferScale` as part of the heuristic + to select `AddFontDefaultVector()` by default, effectively using ProggyForever instead of + ProggyClean on most Mac setups by default. + ----------------------------------------------------------------------- VERSION 1.92.9b (Released 2026-07-31) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index fa8ea9cc0..814a09352 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3168,11 +3168,15 @@ const char* ImGui_GetDefaultCompressedFontDataProggyForever(int* out_size); // This duplicates some of the logic in UpdateFontsNewFrame() which is a bit chicken-and-eggy/tricky to extract due to variety of codepaths and possible initialization ordering. static float GetExpectedContextFontSize(ImGuiContext* ctx) { - return ((ctx->Style.FontSizeBase > 0.0f) ? ctx->Style.FontSizeBase : 13.0f) * ctx->Style.FontScaleMain * ctx->Style.FontScaleDpi; + float fb_scale = (ctx->IO.DisplayFramebufferScale.x != 0.0f) ? ctx->IO.DisplayFramebufferScale.x : 1.0f; + if (ctx->IO.DisplayFramebufferScale.x == 1.0f && ctx->IO.DisplaySize.x <= 0.0f && ctx->IO.ConfigMacOSXBehaviors) + fb_scale = 2.0f; // If called before backend had a chance to init DisplayFramebufferScale. default to expecting Retina. + float scale = ctx->Style.FontScaleMain * ctx->Style.FontScaleDpi * fb_scale; + return ((ctx->Style.FontSizeBase > 0.0f) ? ctx->Style.FontSizeBase : 13.0f) * scale; } // Legacy function with heuristic to select Pixel or Vector font. -// The selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold at the time of adding the default font. +// The selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi * io.DisplayFramebufferScale) reaching a small threshold at the time of adding the default font. // Prefer calling AddFontDefaultVector() or AddFontDefaultBitmap() based on your own logic. ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg) { From fdc11522811cac1e246ed59c11208af29ee08063 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 3 Aug 2026 16:45:56 +0200 Subject: [PATCH 26/47] (Breaking) Style: obsoleted style.CurveTessellationTol in favor of style.CurveTesselationMaxError. cc #3127, #3664, #3665 --- docs/CHANGELOG.txt | 5 +++++ imgui.cpp | 35 ++++++++++++++++++++++++----------- imgui.h | 5 ++++- imgui_demo.cpp | 5 +++-- imgui_draw.cpp | 26 ++++++++++++++------------ imgui_internal.h | 6 +++--- 6 files changed, 53 insertions(+), 29 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e854fbc69..69aa3477c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,11 @@ HOW TO UPDATE? Breaking Changes: +- Style: obsoleted `style.CurveTessellationTol (default 1.25)` which was in Pixels² unit in + favor of `style.CurveTesselationMaxError` (default 1.12)` which is in Pixels unit. It is + easier to tweak + this allows us to easily scale error threshold on high density screens. + - style.CurveTesselationMaxError == sqrf(style.CurveTessellationTol). + Other Changes: - Fonts: diff --git a/imgui.cpp b/imgui.cpp index fbad03746..b1791da0e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -395,6 +395,8 @@ IMPLEMENTING SUPPORT for ImGuiBackendFlags_RendererHasTextures: When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2026/08/03 (1.93.0) - Style: obsoleted `style.CurveTessellationTol (default 1.25)` which was in Pixels² unit in favor of `style.CurveTesselationMaxError` (default 1.12)` which is in Pixels unit. + - style.CurveTesselationMaxError == sqrf(style.CurveTessellationTol). - 2026/07/20 (1.92.9) - DragXXX, SliderXXX, InputScalar: with `ImGuiItemFlags_LiveEditOnInputScalar` now defaulting to being disabled: inputting a value with the keyboard doesn't write intermediate values to backing variable. (#9476) - Before: DragFloat() with user typing "123" --> write back 1, then 12, then 123. @@ -1570,10 +1572,12 @@ ImGuiStyle::ImGuiStyle() DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. + + // Rendering & Tesselation AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). AntiAliasedFill = true; // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.). - CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. + CurveTessellationMaxError = 1.12f; // Maximum error (in pixels) when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. // Behaviors @@ -1586,6 +1590,9 @@ ImGuiStyle::ImGuiStyle() // [Internal] _MainScale = 1.0f; _NextFrameFontSizeBase = 0.0f; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + CurveTessellationTol = 0.0f; // Old CurveTessellationTol = CurveTessellationMaxError*CurveTessellationMaxError. +#endif // Default theme ImGui::StyleColorsDark(this); @@ -2100,7 +2107,7 @@ ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec } // Closely mimics PathBezierToCasteljau() in imgui_draw.cpp -static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) +static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float max_error_sqr, int level) { float dx = x4 - x1; float dy = y4 - y1; @@ -2108,7 +2115,7 @@ static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_cl float d3 = ((x3 - x4) * dy - (y3 - y4) * dx); d2 = (d2 >= 0) ? d2 : -d2; d3 = (d3 >= 0) ? d3 : -d3; - if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy)) + if ((d2 + d3) * (d2 + d3) < max_error_sqr * (dx * dx + dy * dy)) { ImVec2 p_current(x4, y4); ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p); @@ -2128,20 +2135,21 @@ static void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_cl float x123 = (x12 + x23)*0.5f, y123 = (y12 + y23)*0.5f; float x234 = (x23 + x34)*0.5f, y234 = (y23 + y34)*0.5f; float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f; - ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); - ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); + ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, max_error_sqr, level + 1); + ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, max_error_sqr, level + 1); } } -// tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol +// tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationMaxError // Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically. -ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol) +ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float max_error) { - IM_ASSERT(tess_tol > 0.0f); + IM_ASSERT(max_error > 0.0f); ImVec2 p_last = p1; ImVec2 p_closest; float p_closest_dist2 = FLT_MAX; - ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0); + float max_error_sqr = max_error * max_error; + ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, max_error_sqr, 0); return p_closest; } @@ -5572,7 +5580,7 @@ static void SetupDrawListSharedData() for (ImGuiViewportP* viewport : g.Viewports) virtual_space.Add(viewport->GetMainRect()); g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4(); - g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; + g.DrawListSharedData.CurveTessellationMaxError = g.Style.CurveTessellationMaxError; g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError); g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; if (g.Style.AntiAliasedLines) @@ -11098,7 +11106,7 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!"); IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!"); - IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!"); + IM_ASSERT(g.Style.CurveTessellationMaxError > 0.0f && "Invalid style setting!"); IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f && "Invalid style setting!"); IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting!"); @@ -11138,6 +11146,11 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() g.PlatformIO.Platform_GetClipboardTextFn = [](ImGuiContext* ctx) { return ctx->IO.GetClipboardTextFn(ctx->IO.ClipboardUserData); }; if (g.IO.SetClipboardTextFn != NULL && (g.PlatformIO.Platform_SetClipboardTextFn == NULL || g.PlatformIO.Platform_SetClipboardTextFn == Platform_SetClipboardTextFn_DefaultImpl)) g.PlatformIO.Platform_SetClipboardTextFn = [](ImGuiContext* ctx, const char* text) { return ctx->IO.SetClipboardTextFn(ctx->IO.ClipboardUserData, text); }; + + // Remap legacy CurveTessellationTol into CurveTessellationMaxError (OBSOLETED in 1.93.0, August 2026) + if (g.Style.CurveTessellationTol != 0.0f) + g.Style.CurveTessellationMaxError = ImSqrt(g.Style.CurveTessellationTol); + g.Style.CurveTessellationTol = 0.0f; #endif } diff --git a/imgui.h b/imgui.h index 7830580a7..4bf5fe38b 100644 --- a/imgui.h +++ b/imgui.h @@ -2373,10 +2373,12 @@ struct ImGuiStyle ImVec2 DisplayWindowPadding; // Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen. ImVec2 DisplaySafeAreaPadding; // Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured). float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). We apply per-monitor DPI scaling over this scale. May be removed later. + + // Rendering & Tesselation bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). Latched at the beginning of the frame (copied to ImDrawList). bool AntiAliasedFill; // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). - float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. + float CurveTessellationMaxError; // Maximum error (in pixels) when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. float CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. // Colors @@ -2400,6 +2402,7 @@ struct ImGuiStyle // Obsolete names #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + float CurveTessellationTol; // [OBSOLETE] Old CurveTessellationTol = New CurveTessellationMaxError*CurveTessellationMaxError. // Changed in 1.93.0 // TabMinWidthForCloseButton = TabCloseButtonMinWidthUnselected // Renamed in 1.91.9. #endif }; diff --git a/imgui_demo.cpp b/imgui_demo.cpp index e013f510c..314488d94 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -8828,8 +8828,9 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) Checkbox("Anti-aliased fill", &style.AntiAliasedFill); PushItemWidth(GetFontSize() * 8); - DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, "%.2f"); - if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f; + DragFloat("Curve Tessellation Max Error", &style.CurveTessellationMaxError, 0.02f, 0.10f, 10.0f, "%.2f"); + if (style.CurveTessellationMaxError < 0.10f) + style.CurveTessellationMaxError = 0.10f; // When editing the "Circle Segment Max Error" value, draw a preview of its effect on auto-tessellated circles. DragFloat("Circle Tessellation Max Error", &style.CircleTessellationMaxError , 0.005f, 0.10f, 5.0f, "%.2f", ImGuiSliderFlags_AlwaysClamp); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 814a09352..a84fc3ef1 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1370,7 +1370,7 @@ ImVec2 ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p } // Closely mimics ImBezierCubicClosestPointCasteljau() in imgui.cpp -static void PathBezierCubicCurveToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) +static void PathBezierCubicCurveToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float max_error_sqr, int level) { float dx = x4 - x1; float dy = y4 - y1; @@ -1378,7 +1378,7 @@ static void PathBezierCubicCurveToCasteljau(ImVector* path, float x1, fl float d3 = (x3 - x4) * dy - (y3 - y4) * dx; d2 = (d2 >= 0) ? d2 : -d2; d3 = (d3 >= 0) ? d3 : -d3; - if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy)) + if ((d2 + d3) * (d2 + d3) < max_error_sqr * (dx * dx + dy * dy)) { path->push_back(ImVec2(x4, y4)); } @@ -1390,16 +1390,16 @@ static void PathBezierCubicCurveToCasteljau(ImVector* path, float x1, fl float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f; float x234 = (x23 + x34) * 0.5f, y234 = (y23 + y34) * 0.5f; float x1234 = (x123 + x234) * 0.5f, y1234 = (y123 + y234) * 0.5f; - PathBezierCubicCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1); - PathBezierCubicCurveToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1); + PathBezierCubicCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234, max_error_sqr, level + 1); + PathBezierCubicCurveToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4, max_error_sqr, level + 1); } } -static void PathBezierQuadraticCurveToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float tess_tol, int level) +static void PathBezierQuadraticCurveToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float max_error_sqr, int level) { float dx = x3 - x1, dy = y3 - y1; float det = (x2 - x3) * dy - (y2 - y3) * dx; - if (det * det * 4.0f < tess_tol * (dx * dx + dy * dy)) + if (det * det * 4.0f < max_error_sqr * (dx * dx + dy * dy)) { path->push_back(ImVec2(x3, y3)); } @@ -1408,8 +1408,8 @@ static void PathBezierQuadraticCurveToCasteljau(ImVector* path, float x1 float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f; float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f; float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f; - PathBezierQuadraticCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, tess_tol, level + 1); - PathBezierQuadraticCurveToCasteljau(path, x123, y123, x23, y23, x3, y3, tess_tol, level + 1); + PathBezierQuadraticCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, max_error_sqr, level + 1); + PathBezierQuadraticCurveToCasteljau(path, x123, y123, x23, y23, x3, y3, max_error_sqr, level + 1); } } @@ -1418,8 +1418,9 @@ void ImDrawList::PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, cons ImVec2 p1 = _Path.back(); if (num_segments == 0) { - IM_ASSERT(_Data->CurveTessellationTol > 0.0f); - PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); // Auto-tessellated + IM_ASSERT(_Data->CurveTessellationMaxError > 0.0f); + float max_error_sqr = _Data->CurveTessellationMaxError * _Data->CurveTessellationMaxError; + PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, max_error_sqr, 0); // Auto-tessellated } else { @@ -1434,8 +1435,9 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, ImVec2 p1 = _Path.back(); if (num_segments == 0) { - IM_ASSERT(_Data->CurveTessellationTol > 0.0f); - PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0);// Auto-tessellated + IM_ASSERT(_Data->CurveTessellationMaxError > 0.0f); + float max_error_sqr = _Data->CurveTessellationMaxError * _Data->CurveTessellationMaxError; + PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, max_error_sqr, 0);// Auto-tessellated } else { diff --git a/imgui_internal.h b/imgui_internal.h index 208c04cb4..a633c8cee 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -549,8 +549,8 @@ IM_MSVC_RUNTIME_CHECKS_RESTORE // Helpers: Geometry IMGUI_API ImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t); -IMGUI_API ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments); // For curves with explicit number of segments -IMGUI_API ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol);// For auto-tessellated curves you can use tess_tol = style.CurveTessellationTol +IMGUI_API ImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments); // For curves with explicit number of segments +IMGUI_API ImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float max_error);// For auto-tessellated curves you can use max_error = style.CurveTessellationMaxError IMGUI_API ImVec2 ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t); IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p); IMGUI_API bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); @@ -895,7 +895,7 @@ struct IMGUI_API ImDrawListSharedData ImFont* Font; // Current font (used for simplified AddText overload) float FontSize; // Current font size (used for for simplified AddText overload) float FontScale; // Current font scale (== FontSize / Font->FontSize) - float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() + float CurveTessellationMaxError; // Tessellation tolerance when using PathBezierCurveTo() float CircleTessellationMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc float InitialFringeScale; // Initial scale to apply to AA fringe ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards) From 5108603789af559bde144d0848da8297bbf39803 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 3 Aug 2026 19:11:05 +0200 Subject: [PATCH 27/47] DrawList: apply current pixel density to DrawList's fringe scale. Added ImDrawList::_SetPixelDensity(). Reword FontRasterizerDensity as CurrentPixelDensity. Amend 94a4322. # Conflicts: # imgui.cpp # imgui.h # imgui_draw.cpp # imgui_internal.h --- docs/CHANGELOG.txt | 6 +++++- imgui.cpp | 17 +++++++++-------- imgui.h | 4 +++- imgui_draw.cpp | 14 ++++++++++---- imgui_internal.h | 7 +++---- 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 69aa3477c..93dc4c9f7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -51,7 +51,11 @@ Other Changes: - Fonts: - Reworked `AddFontDefault()` to use `io.DisplayFrameBufferScale` as part of the heuristic to select `AddFontDefaultVector()` by default, effectively using ProggyForever instead of - ProggyClean on most Mac setups by default. + ProggyClean on most Apple/Retina setups by default. +- ImDrawList: + - Per-viewport FramebufferScale for Apple/Retina screen is applied to draw lists: + - AA Fringe is scaled accordingly. + - Circle and Curves tessellation error are scaled accordingly. ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index b1791da0e..2eee9d87f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4240,7 +4240,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) Font = NULL; FontBaked = NULL; FontSize = FontSizeBase = FontBakedScale = CurrentDpiScale = 0.0f; - FontRasterizerDensity = 1.0f; + CurrentPixelDensity = 1.0f; IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); if (shared_font_atlas == NULL) IO.Fonts->OwnerContext = this; @@ -4711,7 +4711,7 @@ static void SetCurrentWindow(ImGuiWindow* window) if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures) { ImGuiViewport* viewport = window->Viewport; - g.FontRasterizerDensity = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale.x : g.IO.DisplayFramebufferScale.x; // == SetFontRasterizerDensity() + g.CurrentPixelDensity = (viewport->FramebufferScale.x != 0.0f) ? viewport->FramebufferScale.x : g.IO.DisplayFramebufferScale.x; // == SetPixelDensity() } const bool backup_skip_items = window->SkipItems; window->SkipItems = false; @@ -5308,6 +5308,7 @@ static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t draw if (viewport->BgFgDrawListsLastTimeActive[drawlist_no] != (float)g.Time) { draw_list->_ResetForNewFrame(); + draw_list->_SetPixelDensity(viewport->FramebufferScale.x); draw_list->PushTexture(g.IO.Fonts->TexRef); draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); viewport->BgFgDrawListsLastTimeActive[drawlist_no] = (float)g.Time; @@ -5591,7 +5592,6 @@ static void SetupDrawListSharedData() g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill; if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; - g.DrawListSharedData.InitialFringeScale = 1.0f; // FIXME-DPI: Change this for some DPI scaling experiments. } void ImGui::NewFrame() @@ -8081,6 +8081,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Setup draw list and outer clipping rectangle IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0); + window->DrawList->_SetPixelDensity(window->Viewport->FramebufferScale.x); window->DrawList->PushTexture(g.Font->OwnerAtlas->TexRef); PushClipRect(host_rect.Min, host_rect.Max, false); @@ -9049,7 +9050,7 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) // - UnregisterFontAtlas() [Internal] // - SetCurrentFont() [Internal] // - UpdateCurrentFontSize() [Internal] -// - SetFontRasterizerDensity() [Internal] +// - SetPixelDensity() [Internal] // - PushFont() // - PopFont() //----------------------------------------------------------------------------- @@ -9242,7 +9243,7 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling) final_size = GetRoundedFontSize(final_size); final_size = ImClamp(final_size, 1.0f, IMGUI_FONT_SIZE_MAX); if (g.Font != NULL && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures)) - g.Font->CurrentRasterizerDensity = g.FontRasterizerDensity; + g.Font->CurrentRasterizerDensity = g.CurrentPixelDensity; g.FontSize = final_size; g.DrawListSharedData.FontSize = g.FontSize; @@ -9266,13 +9267,13 @@ void ImGui::UpdateCurrentFontSize(float restore_font_size_after_scaling) // Exposed in case user may want to override setting density. // IMPORTANT: Begin()/End() is overriding density. Be considerate of this you change it. -void ImGui::SetFontRasterizerDensity(float rasterizer_density) +void ImGui::SetPixelDensity(float pixel_density) { ImGuiContext& g = *GImGui; IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_RendererHasTextures); - if (g.FontRasterizerDensity == rasterizer_density) + if (g.CurrentPixelDensity == pixel_density) return; - g.FontRasterizerDensity = rasterizer_density; + g.CurrentPixelDensity = pixel_density; UpdateCurrentFontSize(0.0f); } diff --git a/imgui.h b/imgui.h index 4bf5fe38b..fea82515d 100644 --- a/imgui.h +++ b/imgui.h @@ -3346,7 +3346,8 @@ struct ImDrawList ImVector _ClipRectStack; // [Internal] ImVector _TextureStack; // [Internal] ImVector _CallbacksDataBuf; // [Internal] - float _FringeScale; // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content + float _FringeScale; // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content. + float _InvFringeScale; // [internal] 1.0 / _FringeScale // FIXME: Consider renaming to _PixelDensity. const char* _OwnerName; // Pointer to owner window's name for debugging // If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData(). @@ -3480,6 +3481,7 @@ struct ImDrawList // [Internal helpers] IMGUI_API void _SetDrawListSharedData(ImDrawListSharedData* data); IMGUI_API void _ResetForNewFrame(); + IMGUI_API void _SetPixelDensity(float pixel_density); IMGUI_API void _ClearFreeMemory(); IMGUI_API void _PopUnusedDrawCmd(); IMGUI_API void _TryMergeDrawCmds(); diff --git a/imgui_draw.cpp b/imgui_draw.cpp index a84fc3ef1..a9759c072 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -396,7 +396,6 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) ImDrawListSharedData::ImDrawListSharedData() { memset((void*)this, 0, sizeof(*this)); - InitialFringeScale = 1.0f; for (int i = 0; i < IM_COUNTOF(ArcFastVtx); i++) { const float a = ((float)i * 2 * IM_PI) / (float)IM_COUNTOF(ArcFastVtx); @@ -473,7 +472,7 @@ void ImDrawList::_ResetForNewFrame() _Path.resize(0); _Splitter.Clear(); CmdBuffer.push_back(ImDrawCmd()); - _FringeScale = _Data->InitialFringeScale; + // Caller needs to bet _SetPixelDensity() as well. } void ImDrawList::_ClearFreeMemory() @@ -655,6 +654,7 @@ void ImDrawList::_OnChangedVtxOffset() int ImDrawList::_CalcCircleAutoSegmentCount(float radius) const { // Automatic segment count + radius *= _InvFringeScale; const int radius_idx = (int)(radius + 0.999f); // ceil to never reduce accuracy if (radius_idx >= 0 && radius_idx < IM_COUNTOF(_Data->CircleSegmentCounts)) return _Data->CircleSegmentCounts[radius_idx]; // Use cached value @@ -720,6 +720,12 @@ void ImDrawList::_SetTexture(ImTextureRef tex_ref) _OnChangedTexture(); } +void ImDrawList::_SetPixelDensity(float pixel_density) +{ + _FringeScale = 1.0f / pixel_density; + _InvFringeScale = pixel_density; +} + // Reserve space for a number of vertices and indices. // You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or // submit the intermediate results. PrimUnreserve() can be used to release unused allocations. @@ -1419,7 +1425,7 @@ void ImDrawList::PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, cons if (num_segments == 0) { IM_ASSERT(_Data->CurveTessellationMaxError > 0.0f); - float max_error_sqr = _Data->CurveTessellationMaxError * _Data->CurveTessellationMaxError; + float max_error_sqr = (_Data->CurveTessellationMaxError * _FringeScale) * (_Data->CurveTessellationMaxError * _FringeScale); PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, max_error_sqr, 0); // Auto-tessellated } else @@ -1436,7 +1442,7 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, if (num_segments == 0) { IM_ASSERT(_Data->CurveTessellationMaxError > 0.0f); - float max_error_sqr = _Data->CurveTessellationMaxError * _Data->CurveTessellationMaxError; + float max_error_sqr = (_Data->CurveTessellationMaxError * _FringeScale) * (_Data->CurveTessellationMaxError * _FringeScale); PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, max_error_sqr, 0);// Auto-tessellated } else diff --git a/imgui_internal.h b/imgui_internal.h index a633c8cee..59f005508 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -897,7 +897,6 @@ struct IMGUI_API ImDrawListSharedData float FontScale; // Current font scale (== FontSize / Font->FontSize) float CurveTessellationMaxError; // Tessellation tolerance when using PathBezierCurveTo() float CircleTessellationMaxError; // Number of circle segments to use per pixel of radius for AddCircle() etc - float InitialFringeScale; // Initial scale to apply to AA fringe ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards) ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen() ImVector TempBuffer; // Temporary write buffer @@ -2267,7 +2266,7 @@ struct ImGuiContext float FontSize; // Currently bound font size == line height (== FontSizeBase + externals scales applied in the UpdateCurrentFontSize() function). float FontSizeBase; // Font size before scaling == style.FontSizeBase == value passed to PushFont() when specified. float FontBakedScale; // == FontBaked->Size / FontSize. Scale factor over baked size. Rarely used nowadays, very often == 1.0f. - float FontRasterizerDensity; // Current font density. Used by all calls to GetFontBaked(). + float CurrentPixelDensity; // Current font density. Used by all calls to GetFontBaked(). float CurrentDpiScale; // Current window/viewport DpiScale == CurrentViewport->DpiScale ImDrawListSharedData DrawListSharedData; ImGuiID WithinEndChildID; // Set within EndChild() @@ -3390,8 +3389,8 @@ namespace ImGui IMGUI_API void UnregisterFontAtlas(ImFontAtlas* atlas); IMGUI_API void SetCurrentFont(ImFont* font, float font_size_before_scaling, float font_size_after_scaling); IMGUI_API void UpdateCurrentFontSize(float restore_font_size_after_scaling); - IMGUI_API void SetFontRasterizerDensity(float rasterizer_density); - inline float GetFontRasterizerDensity() { return GImGui->FontRasterizerDensity; } + IMGUI_API void SetPixelDensity(float pixel_density); // was SetFontRasterizerDensity() + inline float GetPixelDensity() { return GImGui->CurrentPixelDensity; } inline float GetRoundedFontSize(float size) { return IM_ROUND(size); } IMGUI_API ImFont* GetDefaultFont(); IMGUI_API void PushPasswordFont(); From 40ec568762a2adc4a8680ebd5acd27be5fe92943 Mon Sep 17 00:00:00 2001 From: Mike Gorchak Date: Wed, 29 Jul 2026 10:58:34 -0400 Subject: [PATCH 28/47] Backends+Examples: QNX Screen: added initial QNX support to backends and examples. (#9492) --- backends/imgui_impl_qnx.cpp | 763 ++++++++++++++++++ backends/imgui_impl_qnx.h | 36 + docs/BACKENDS.md | 1 + docs/CHANGELOG.txt | 4 + docs/EXAMPLES.md | 10 + examples/example_qnx_opengl3/.gitignore | 19 + examples/example_qnx_opengl3/Makefile | 2 + examples/example_qnx_opengl3/README.md | 29 + examples/example_qnx_opengl3/common.mk | 31 + .../example_qnx_opengl3.use | 6 + examples/example_qnx_opengl3/main.cpp | 299 +++++++ examples/example_qnx_opengl3/nto/Makefile | 8 + .../example_qnx_opengl3/nto/aarch64/Makefile | 8 + .../nto/aarch64/o.le/Makefile | 1 + .../example_qnx_opengl3/nto/x86_64/Makefile | 8 + .../example_qnx_opengl3/nto/x86_64/o/Makefile | 1 + examples/example_qnx_vulkan/.gitignore | 19 + examples/example_qnx_vulkan/Makefile | 2 + examples/example_qnx_vulkan/README.md | 29 + examples/example_qnx_vulkan/common.mk | 31 + .../example_qnx_vulkan/example_qnx_vulkan.use | 7 + examples/example_qnx_vulkan/main.cpp | 626 ++++++++++++++ examples/example_qnx_vulkan/nto/Makefile | 8 + .../example_qnx_vulkan/nto/aarch64/Makefile | 8 + .../nto/aarch64/o.le/Makefile | 1 + .../example_qnx_vulkan/nto/x86_64/Makefile | 8 + .../example_qnx_vulkan/nto/x86_64/o/Makefile | 1 + 27 files changed, 1966 insertions(+) create mode 100644 backends/imgui_impl_qnx.cpp create mode 100644 backends/imgui_impl_qnx.h create mode 100644 examples/example_qnx_opengl3/.gitignore create mode 100644 examples/example_qnx_opengl3/Makefile create mode 100644 examples/example_qnx_opengl3/README.md create mode 100644 examples/example_qnx_opengl3/common.mk create mode 100644 examples/example_qnx_opengl3/example_qnx_opengl3.use create mode 100644 examples/example_qnx_opengl3/main.cpp create mode 100644 examples/example_qnx_opengl3/nto/Makefile create mode 100644 examples/example_qnx_opengl3/nto/aarch64/Makefile create mode 100644 examples/example_qnx_opengl3/nto/aarch64/o.le/Makefile create mode 100644 examples/example_qnx_opengl3/nto/x86_64/Makefile create mode 100644 examples/example_qnx_opengl3/nto/x86_64/o/Makefile create mode 100644 examples/example_qnx_vulkan/.gitignore create mode 100644 examples/example_qnx_vulkan/Makefile create mode 100644 examples/example_qnx_vulkan/README.md create mode 100644 examples/example_qnx_vulkan/common.mk create mode 100644 examples/example_qnx_vulkan/example_qnx_vulkan.use create mode 100644 examples/example_qnx_vulkan/main.cpp create mode 100644 examples/example_qnx_vulkan/nto/Makefile create mode 100644 examples/example_qnx_vulkan/nto/aarch64/Makefile create mode 100644 examples/example_qnx_vulkan/nto/aarch64/o.le/Makefile create mode 100644 examples/example_qnx_vulkan/nto/x86_64/Makefile create mode 100644 examples/example_qnx_vulkan/nto/x86_64/o/Makefile diff --git a/backends/imgui_impl_qnx.cpp b/backends/imgui_impl_qnx.cpp new file mode 100644 index 000000000..82c9cb376 --- /dev/null +++ b/backends/imgui_impl_qnx.cpp @@ -0,0 +1,763 @@ +// dear imgui: Platform Backend for QNX Screen +// This needs to be used along with a Renderer Backend (e.g. OpenGL3, Vulkan). + +// Implemented features: +// [X] Platform: Keyboard support. Uses io.AddKeyEvent() and native QNX key symbols. +// [X] Platform: Mouse support. Handles position, three buttons and horizontal/vertical wheels. +// [X] Platform: Touch support. Maps the first active touch contact to the primary mouse button. +// [X] Platform: Focus and window-size updates. +// Missing features or Issues: +// [ ] Platform: Clipboard support. +// [ ] Platform: Gamepad support. +// [ ] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). +// [ ] Platform: Multi-viewport support. + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2026-07-30: Renamed backend from imgui_impl_screen to imgui_impl_qnx and embedded a compact keysym table. +// 2026-07-29: Reworked keyboard handling from the proven qterm Screen path: authoritative modifier masks, +// complete keypad/Hyper mapping, and QNX/X11 keysym-to-Unicode conversion. +// 2026-07-29: Fixed keyboard handling: use KEY_CAP for physical keys, SYM for text, and honor Screen validity flags. +// 2026-07-28: Initial QNX Screen backend. + +#include "imgui.h" +#ifndef IMGUI_DISABLE +#include "imgui_impl_qnx.h" + +#include +#include +#include +#include +#include + +static unsigned int ImGui_ImplQNX_KeySymToUnicode(unsigned int keysym); + +struct ImGui_ImplQNX_Data +{ + screen_context_t Context; + screen_window_t Window; + uint64_t TimeNanoseconds; + int MouseButtons; + int ActiveTouchId; + bool LeftSuper; + bool RightSuper; + + ImGui_ImplQNX_Data() + { + memset(this, 0, sizeof(*this)); + ActiveTouchId = -1; + } +}; + +static ImGui_ImplQNX_Data* ImGui_ImplQNX_GetBackendData() +{ + return ImGui::GetCurrentContext() != nullptr ? (ImGui_ImplQNX_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; +} + +static uint64_t ImGui_ImplQNX_GetTimeNanoseconds() +{ + struct timespec timestamp = {}; + if (clock_gettime(CLOCK_MONOTONIC, ×tamp) != 0) + { + return 0; + } + return (uint64_t)timestamp.tv_sec * UINT64_C(1000000000) + (uint64_t)timestamp.tv_nsec; +} + +static ImGuiKey ImGui_ImplQNX_KeyCodeToImGuiKey(int key_code) +{ + switch (key_code) + { + case KEYCODE_TAB: return ImGuiKey_Tab; + case KEYCODE_LEFT: return ImGuiKey_LeftArrow; + case KEYCODE_RIGHT: return ImGuiKey_RightArrow; + case KEYCODE_UP: return ImGuiKey_UpArrow; + case KEYCODE_DOWN: return ImGuiKey_DownArrow; + case KEYCODE_PG_UP: return ImGuiKey_PageUp; + case KEYCODE_PG_DOWN: return ImGuiKey_PageDown; + case KEYCODE_HOME: return ImGuiKey_Home; + case KEYCODE_END: return ImGuiKey_End; + case KEYCODE_INSERT: return ImGuiKey_Insert; + case KEYCODE_DELETE: return ImGuiKey_Delete; + case KEYCODE_BACKSPACE: return ImGuiKey_Backspace; + case KEYCODE_SPACE: return ImGuiKey_Space; + case KEYCODE_RETURN: return ImGuiKey_Enter; + case KEYCODE_ESCAPE: return ImGuiKey_Escape; + case KEYCODE_APOSTROPHE: return ImGuiKey_Apostrophe; + case KEYCODE_COMMA: return ImGuiKey_Comma; + case KEYCODE_MINUS: return ImGuiKey_Minus; + case KEYCODE_PERIOD: return ImGuiKey_Period; + case KEYCODE_SLASH: return ImGuiKey_Slash; + case KEYCODE_SEMICOLON: return ImGuiKey_Semicolon; + case KEYCODE_EQUAL: return ImGuiKey_Equal; + case KEYCODE_LEFT_BRACKET: return ImGuiKey_LeftBracket; + case KEYCODE_BACK_SLASH: return ImGuiKey_Backslash; + case KEYCODE_RIGHT_BRACKET: return ImGuiKey_RightBracket; + case KEYCODE_GRAVE: return ImGuiKey_GraveAccent; + case KEYCODE_CAPS_LOCK: return ImGuiKey_CapsLock; + case KEYCODE_SCROLL_LOCK: return ImGuiKey_ScrollLock; + case KEYCODE_NUM_LOCK: return ImGuiKey_NumLock; + case KEYCODE_PRINT: return ImGuiKey_PrintScreen; + case KEYCODE_PAUSE: return ImGuiKey_Pause; + case KEYCODE_KP_DIVIDE: return ImGuiKey_KeypadDivide; + case KEYCODE_KP_MULTIPLY: return ImGuiKey_KeypadMultiply; + case KEYCODE_KP_MINUS: return ImGuiKey_KeypadSubtract; + case KEYCODE_KP_PLUS: return ImGuiKey_KeypadAdd; + case KEYCODE_KP_ENTER: return ImGuiKey_KeypadEnter; + // QNX reports keypad digits by their navigation key-cap names. + case KEYCODE_KP_INSERT: return ImGuiKey_Keypad0; + case KEYCODE_KP_END: return ImGuiKey_Keypad1; + case KEYCODE_KP_DOWN: return ImGuiKey_Keypad2; + case KEYCODE_KP_PG_DOWN: return ImGuiKey_Keypad3; + case KEYCODE_KP_LEFT: return ImGuiKey_Keypad4; + case KEYCODE_KP_FIVE: return ImGuiKey_Keypad5; + case KEYCODE_KP_RIGHT: return ImGuiKey_Keypad6; + case KEYCODE_KP_HOME: return ImGuiKey_Keypad7; + case KEYCODE_KP_UP: return ImGuiKey_Keypad8; + case KEYCODE_KP_PG_UP: return ImGuiKey_Keypad9; + case KEYCODE_KP_DELETE: return ImGuiKey_KeypadDecimal; + case KEYCODE_LEFT_CTRL: return ImGuiKey_LeftCtrl; + case KEYCODE_LEFT_SHIFT: return ImGuiKey_LeftShift; + case KEYCODE_LEFT_ALT: return ImGuiKey_LeftAlt; + case KEYCODE_RIGHT_CTRL: return ImGuiKey_RightCtrl; + case KEYCODE_RIGHT_SHIFT: return ImGuiKey_RightShift; + case KEYCODE_RIGHT_ALT: return ImGuiKey_RightAlt; + case KEYCODE_LEFT_HYPER: return ImGuiKey_LeftSuper; + case KEYCODE_RIGHT_HYPER: return ImGuiKey_RightSuper; + case KEYCODE_MENU: return ImGuiKey_Menu; + case KEYCODE_ZERO: return ImGuiKey_0; + case KEYCODE_ONE: return ImGuiKey_1; + case KEYCODE_TWO: return ImGuiKey_2; + case KEYCODE_THREE: return ImGuiKey_3; + case KEYCODE_FOUR: return ImGuiKey_4; + case KEYCODE_FIVE: return ImGuiKey_5; + case KEYCODE_SIX: return ImGuiKey_6; + case KEYCODE_SEVEN: return ImGuiKey_7; + case KEYCODE_EIGHT: return ImGuiKey_8; + case KEYCODE_NINE: return ImGuiKey_9; + case KEYCODE_A: return ImGuiKey_A; + case KEYCODE_B: return ImGuiKey_B; + case KEYCODE_C: return ImGuiKey_C; + case KEYCODE_D: return ImGuiKey_D; + case KEYCODE_E: return ImGuiKey_E; + case KEYCODE_F: return ImGuiKey_F; + case KEYCODE_G: return ImGuiKey_G; + case KEYCODE_H: return ImGuiKey_H; + case KEYCODE_I: return ImGuiKey_I; + case KEYCODE_J: return ImGuiKey_J; + case KEYCODE_K: return ImGuiKey_K; + case KEYCODE_L: return ImGuiKey_L; + case KEYCODE_M: return ImGuiKey_M; + case KEYCODE_N: return ImGuiKey_N; + case KEYCODE_O: return ImGuiKey_O; + case KEYCODE_P: return ImGuiKey_P; + case KEYCODE_Q: return ImGuiKey_Q; + case KEYCODE_R: return ImGuiKey_R; + case KEYCODE_S: return ImGuiKey_S; + case KEYCODE_T: return ImGuiKey_T; + case KEYCODE_U: return ImGuiKey_U; + case KEYCODE_V: return ImGuiKey_V; + case KEYCODE_W: return ImGuiKey_W; + case KEYCODE_X: return ImGuiKey_X; + case KEYCODE_Y: return ImGuiKey_Y; + case KEYCODE_Z: return ImGuiKey_Z; + case KEYCODE_F1: return ImGuiKey_F1; + case KEYCODE_F2: return ImGuiKey_F2; + case KEYCODE_F3: return ImGuiKey_F3; + case KEYCODE_F4: return ImGuiKey_F4; + case KEYCODE_F5: return ImGuiKey_F5; + case KEYCODE_F6: return ImGuiKey_F6; + case KEYCODE_F7: return ImGuiKey_F7; + case KEYCODE_F8: return ImGuiKey_F8; + case KEYCODE_F9: return ImGuiKey_F9; + case KEYCODE_F10: return ImGuiKey_F10; + case KEYCODE_F11: return ImGuiKey_F11; + case KEYCODE_F12: return ImGuiKey_F12; + default: return ImGuiKey_None; + } +} + +static void ImGui_ImplQNX_UpdateKeyModifiers(ImGuiIO& io, ImGui_ImplQNX_Data* bd, int modifiers, ImGuiKey key, bool down) +{ + // SCREEN_PROPERTY_MODIFIERS is the authoritative state for Shift/Ctrl/Alt. + // Tracking those keys locally loses sticky modifiers and can become stale if + // a key transition is consumed elsewhere. qterm uses this same Screen state. + if (key == ImGuiKey_LeftSuper) + { + bd->LeftSuper = down; + } + else if (key == ImGuiKey_RightSuper) + { + bd->RightSuper = down; + } + + io.AddKeyEvent(ImGuiMod_Ctrl, (modifiers & KEYMOD_CTRL) != 0); + io.AddKeyEvent(ImGuiMod_Shift, (modifiers & KEYMOD_SHIFT) != 0); + io.AddKeyEvent(ImGuiMod_Alt, (modifiers & KEYMOD_ALT) != 0); + io.AddKeyEvent(ImGuiMod_Super, bd->LeftSuper || bd->RightSuper); +} + +static void ImGui_ImplQNX_ClearModifierState(ImGuiIO& io, ImGui_ImplQNX_Data* bd) +{ + bd->LeftSuper = false; + bd->RightSuper = false; + io.AddKeyEvent(ImGuiMod_Ctrl, false); + io.AddKeyEvent(ImGuiMod_Shift, false); + io.AddKeyEvent(ImGuiMod_Alt, false); + io.AddKeyEvent(ImGuiMod_Super, false); +} + +static bool ImGui_ImplQNX_IsTextCodePoint(unsigned int code_point) +{ + // At this point the QNX keysym has already been converted to Unicode. + // Do not reject Unicode private-use characters: a user keymap may emit one. + const bool surrogate = code_point >= 0xd800u && code_point <= 0xdfffu; + const bool control = code_point < 0x20u || (code_point >= 0x7fu && code_point <= 0x9fu); + return code_point <= IM_UNICODE_CODEPOINT_MAX && !surrogate && !control; +} + +static bool ImGui_ImplQNX_ProcessKeyboardEvent(ImGui_ImplQNX_Data* bd, screen_event_t event) +{ + int flags = 0; + int modifiers = 0; + if (screen_get_event_property_iv(event, SCREEN_PROPERTY_FLAGS, &flags) != 0 || + screen_get_event_property_iv(event, SCREEN_PROPERTY_MODIFIERS, &modifiers) != 0) + { + return false; + } + + // KEY_CAP is the unmodified key identity used for ImGuiKey events. SYM is + // the layout/modifier-translated QNX keysym used for text input. + int key_cap = 0; + int key_sym = 0; + int scan_code = 0; + const bool cap_valid = (flags & SCREEN_FLAG_CAP_VALID) != 0; + const bool sym_valid = (flags & SCREEN_FLAG_SYM_VALID) != 0; + const bool scan_valid = (flags & SCREEN_FLAG_SCAN_VALID) != 0; + const bool down = (flags & (SCREEN_FLAG_KEY_DOWN | SCREEN_FLAG_KEY_REPEAT)) != 0; + + if (cap_valid && screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_CAP, &key_cap) != 0) + { + return false; + } + if (sym_valid && screen_get_event_property_iv(event, SCREEN_PROPERTY_SYM, &key_sym) != 0) + { + return false; + } + if (scan_valid && screen_get_event_property_iv(event, SCREEN_PROPERTY_SCAN, &scan_code) != 0) + { + scan_code = 0; + } + + const ImGuiKey key = cap_valid ? ImGui_ImplQNX_KeyCodeToImGuiKey(key_cap) : ImGuiKey_None; + ImGuiIO& io = ImGui::GetIO(); + + // Modifier events must precede the ordinary key event in Dear ImGui's queue. + // Unlike qterm, we still submit the individual modifier key because ImGui + // exposes Left/Right Ctrl, Shift, Alt and Super as named keys. + ImGui_ImplQNX_UpdateKeyModifiers(io, bd, modifiers, key, down); + + if (key != ImGuiKey_None) + { + io.AddKeyEvent(key, down); + io.SetKeyEventNativeData(key, key_cap, scan_code); + } + + bool text_added = false; + if (down && sym_valid && key_sym >= 0) + { + const unsigned int keysym = (unsigned int)key_sym; + // QNX special/function symbols begin at KEYCODE_PC_KEYS. Printable + // symbols below that boundary use X11 keysym encoding. Also accept the + // standard directly encoded UCS keysym form (0x01000000 | codepoint). + if (keysym < (unsigned int)KEYCODE_PC_KEYS || (keysym & 0xff000000u) == 0x01000000u) + { + const unsigned int code_point = ImGui_ImplQNX_KeySymToUnicode(keysym); + if (ImGui_ImplQNX_IsTextCodePoint(code_point)) + { + io.AddInputCharacter(code_point); + text_added = true; + } + } + } + + return key != ImGuiKey_None || text_added || cap_valid || sym_valid; +} + +static bool ImGui_ImplQNX_ProcessPointerEvent(ImGui_ImplQNX_Data* bd, screen_event_t event) +{ + int position[2] = {}; + int buttons = 0; + int wheel_vertical = 0; + int wheel_horizontal = 0; + + if (screen_get_event_property_iv(event, SCREEN_PROPERTY_SOURCE_POSITION, position) != 0 && + screen_get_event_property_iv(event, SCREEN_PROPERTY_POSITION, position) != 0) + { + return false; + } + if (screen_get_event_property_iv(event, SCREEN_PROPERTY_BUTTONS, &buttons) != 0) + { + buttons = bd->MouseButtons; + } + + ImGuiIO& io = ImGui::GetIO(); + io.AddMouseSourceEvent(ImGuiMouseSource_Mouse); + io.AddMousePosEvent((float)position[0], (float)position[1]); + + const int changed_buttons = buttons ^ bd->MouseButtons; + if ((changed_buttons & SCREEN_LEFT_MOUSE_BUTTON) != 0) + { + io.AddMouseButtonEvent(0, (buttons & SCREEN_LEFT_MOUSE_BUTTON) != 0); + } + if ((changed_buttons & SCREEN_RIGHT_MOUSE_BUTTON) != 0) + { + io.AddMouseButtonEvent(1, (buttons & SCREEN_RIGHT_MOUSE_BUTTON) != 0); + } + if ((changed_buttons & SCREEN_MIDDLE_MOUSE_BUTTON) != 0) + { + io.AddMouseButtonEvent(2, (buttons & SCREEN_MIDDLE_MOUSE_BUTTON) != 0); + } + bd->MouseButtons = buttons; + + if (screen_get_event_property_iv(event, SCREEN_PROPERTY_MOUSE_WHEEL, &wheel_vertical) != 0) + { + wheel_vertical = 0; + } + if (screen_get_event_property_iv(event, SCREEN_PROPERTY_MOUSE_HORIZONTAL_WHEEL, &wheel_horizontal) != 0) + { + wheel_horizontal = 0; + } + if (wheel_vertical != 0 || wheel_horizontal != 0) + { + // Screen: up/left are negative. Dear ImGui: up/left are positive. + io.AddMouseWheelEvent((float)-wheel_horizontal, (float)-wheel_vertical); + } + return true; +} + +static bool ImGui_ImplQNX_ProcessTouchEvent(ImGui_ImplQNX_Data* bd, screen_event_t event, int event_type) +{ + int touch_id = -1; + int position[2] = {}; + if (screen_get_event_property_iv(event, SCREEN_PROPERTY_TOUCH_ID, &touch_id) != 0) + { + return false; + } + + const bool is_release = event_type == SCREEN_EVENT_MTOUCH_RELEASE; + if (bd->ActiveTouchId != -1 && touch_id != bd->ActiveTouchId) + { + return false; + } + + ImGuiIO& io = ImGui::GetIO(); + io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen); + + if (!is_release) + { + if (screen_get_event_property_iv(event, SCREEN_PROPERTY_SOURCE_POSITION, position) != 0 && + screen_get_event_property_iv(event, SCREEN_PROPERTY_POSITION, position) != 0) + { + return false; + } + io.AddMousePosEvent((float)position[0], (float)position[1]); + } + + if (event_type == SCREEN_EVENT_MTOUCH_TOUCH) + { + bd->ActiveTouchId = touch_id; + io.AddMouseButtonEvent(0, true); + } + else if (is_release) + { + io.AddMouseButtonEvent(0, false); + io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); + bd->ActiveTouchId = -1; + } + return true; +} + +bool ImGui_ImplQNX_ProcessEvent(screen_event_t event) +{ + ImGui_ImplQNX_Data* bd = ImGui_ImplQNX_GetBackendData(); + IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplQNX_Init()?"); + if (bd == nullptr || event == nullptr) + { + return false; + } + + int event_type = SCREEN_EVENT_NONE; + if (screen_get_event_property_iv(event, SCREEN_PROPERTY_TYPE, &event_type) != 0 || event_type == SCREEN_EVENT_NONE) + { + return false; + } + switch (event_type) + { + case SCREEN_EVENT_KEYBOARD: + return ImGui_ImplQNX_ProcessKeyboardEvent(bd, event); + case SCREEN_EVENT_POINTER: + return ImGui_ImplQNX_ProcessPointerEvent(bd, event); + case SCREEN_EVENT_MTOUCH_TOUCH: + case SCREEN_EVENT_MTOUCH_MOVE: + case SCREEN_EVENT_MTOUCH_RELEASE: + return ImGui_ImplQNX_ProcessTouchEvent(bd, event, event_type); + case SCREEN_EVENT_PROPERTY: + { + int property = 0; + if (screen_get_event_property_iv(event, SCREEN_PROPERTY_NAME, &property) != 0) + { + return false; + } + if (property == SCREEN_PROPERTY_FOCUS) + { + int focused = 0; + if (screen_get_window_property_iv(bd->Window, SCREEN_PROPERTY_FOCUS, &focused) == 0) + { + ImGuiIO& io = ImGui::GetIO(); + io.AddFocusEvent(focused != 0); + if (focused == 0) + { + ImGui_ImplQNX_ClearModifierState(io, bd); + } + } + return true; + } + if (property == SCREEN_PROPERTY_SIZE || property == SCREEN_PROPERTY_BUFFER_SIZE || property == SCREEN_PROPERTY_VISIBLE) + { + return true; + } + return false; + } + case SCREEN_EVENT_CLOSE: + { + ImGuiIO& io = ImGui::GetIO(); + io.AddFocusEvent(false); + ImGui_ImplQNX_ClearModifierState(io, bd); + return true; + } + default: + return false; + } +} + +bool ImGui_ImplQNX_Init(screen_context_t context, screen_window_t window) +{ + ImGuiIO& io = ImGui::GetIO(); + IMGUI_CHECKVERSION(); + IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!"); + IM_ASSERT(context != nullptr && window != nullptr); + if (context == nullptr || window == nullptr) + { + return false; + } + + ImGui_ImplQNX_Data* bd = IM_NEW(ImGui_ImplQNX_Data)(); + io.BackendPlatformUserData = (void*)bd; + io.BackendPlatformName = "imgui_impl_qnx"; + + bd->Context = context; + bd->Window = window; + bd->TimeNanoseconds = ImGui_ImplQNX_GetTimeNanoseconds(); + + int focused = 0; + if (screen_get_window_property_iv(window, SCREEN_PROPERTY_FOCUS, &focused) == 0) + { + io.AddFocusEvent(focused != 0); + } + return true; +} + +void ImGui_ImplQNX_Shutdown() +{ + ImGui_ImplQNX_Data* bd = ImGui_ImplQNX_GetBackendData(); + IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); + if (bd == nullptr) + { + return; + } + + ImGuiIO& io = ImGui::GetIO(); + io.BackendPlatformName = nullptr; + io.BackendPlatformUserData = nullptr; + IM_DELETE(bd); +} + +void ImGui_ImplQNX_NewFrame() +{ + ImGui_ImplQNX_Data* bd = ImGui_ImplQNX_GetBackendData(); + IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplQNX_Init()?"); + if (bd == nullptr) + { + return; + } + + ImGuiIO& io = ImGui::GetIO(); + int size[2] = {}; + if (screen_get_window_property_iv(bd->Window, SCREEN_PROPERTY_BUFFER_SIZE, size) != 0 || + size[0] <= 0 || size[1] <= 0) + { + size[0] = 0; + size[1] = 0; + (void)screen_get_window_property_iv(bd->Window, SCREEN_PROPERTY_SIZE, size); + } + if (size[0] > 0 && size[1] > 0) + { + io.DisplaySize = ImVec2((float)size[0], (float)size[1]); + io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f); + } + + const uint64_t current_time = ImGui_ImplQNX_GetTimeNanoseconds(); + if (current_time > bd->TimeNanoseconds && bd->TimeNanoseconds != 0) + { + io.DeltaTime = (float)((double)(current_time - bd->TimeNanoseconds) / 1000000000.0); + } + else + { + io.DeltaTime = 1.0f / 60.0f; + } + bd->TimeNanoseconds = current_time; +} + +// Generated by Markus Kuhn's public-domain unicode/convmap.pl from X11 keysymdef.h and UnicodeData. +// Original source: https://www.cl.cam.ac.uk/~mgk25/ucs/keysym2ucs.c +// The table must remain sorted by keysym for the binary search below. +struct ImGui_ImplQNX_KeySymPair +{ + uint16_t KeySym; + uint16_t Unicode; +}; + +static const ImGui_ImplQNX_KeySymPair ImGui_ImplQNX_KeySymTable[] = +{ + { 0x01a1, 0x0104 }, { 0x01a2, 0x02d8 }, { 0x01a3, 0x0141 }, { 0x01a5, 0x013d }, + { 0x01a6, 0x015a }, { 0x01a9, 0x0160 }, { 0x01aa, 0x015e }, { 0x01ab, 0x0164 }, + { 0x01ac, 0x0179 }, { 0x01ae, 0x017d }, { 0x01af, 0x017b }, { 0x01b1, 0x0105 }, + { 0x01b2, 0x02db }, { 0x01b3, 0x0142 }, { 0x01b5, 0x013e }, { 0x01b6, 0x015b }, + { 0x01b7, 0x02c7 }, { 0x01b9, 0x0161 }, { 0x01ba, 0x015f }, { 0x01bb, 0x0165 }, + { 0x01bc, 0x017a }, { 0x01bd, 0x02dd }, { 0x01be, 0x017e }, { 0x01bf, 0x017c }, + { 0x01c0, 0x0154 }, { 0x01c3, 0x0102 }, { 0x01c5, 0x0139 }, { 0x01c6, 0x0106 }, + { 0x01c8, 0x010c }, { 0x01ca, 0x0118 }, { 0x01cc, 0x011a }, { 0x01cf, 0x010e }, + { 0x01d0, 0x0110 }, { 0x01d1, 0x0143 }, { 0x01d2, 0x0147 }, { 0x01d5, 0x0150 }, + { 0x01d8, 0x0158 }, { 0x01d9, 0x016e }, { 0x01db, 0x0170 }, { 0x01de, 0x0162 }, + { 0x01e0, 0x0155 }, { 0x01e3, 0x0103 }, { 0x01e5, 0x013a }, { 0x01e6, 0x0107 }, + { 0x01e8, 0x010d }, { 0x01ea, 0x0119 }, { 0x01ec, 0x011b }, { 0x01ef, 0x010f }, + { 0x01f0, 0x0111 }, { 0x01f1, 0x0144 }, { 0x01f2, 0x0148 }, { 0x01f5, 0x0151 }, + { 0x01f8, 0x0159 }, { 0x01f9, 0x016f }, { 0x01fb, 0x0171 }, { 0x01fe, 0x0163 }, + { 0x01ff, 0x02d9 }, { 0x02a1, 0x0126 }, { 0x02a6, 0x0124 }, { 0x02a9, 0x0130 }, + { 0x02ab, 0x011e }, { 0x02ac, 0x0134 }, { 0x02b1, 0x0127 }, { 0x02b6, 0x0125 }, + { 0x02b9, 0x0131 }, { 0x02bb, 0x011f }, { 0x02bc, 0x0135 }, { 0x02c5, 0x010a }, + { 0x02c6, 0x0108 }, { 0x02d5, 0x0120 }, { 0x02d8, 0x011c }, { 0x02dd, 0x016c }, + { 0x02de, 0x015c }, { 0x02e5, 0x010b }, { 0x02e6, 0x0109 }, { 0x02f5, 0x0121 }, + { 0x02f8, 0x011d }, { 0x02fd, 0x016d }, { 0x02fe, 0x015d }, { 0x03a2, 0x0138 }, + { 0x03a3, 0x0156 }, { 0x03a5, 0x0128 }, { 0x03a6, 0x013b }, { 0x03aa, 0x0112 }, + { 0x03ab, 0x0122 }, { 0x03ac, 0x0166 }, { 0x03b3, 0x0157 }, { 0x03b5, 0x0129 }, + { 0x03b6, 0x013c }, { 0x03ba, 0x0113 }, { 0x03bb, 0x0123 }, { 0x03bc, 0x0167 }, + { 0x03bd, 0x014a }, { 0x03bf, 0x014b }, { 0x03c0, 0x0100 }, { 0x03c7, 0x012e }, + { 0x03cc, 0x0116 }, { 0x03cf, 0x012a }, { 0x03d1, 0x0145 }, { 0x03d2, 0x014c }, + { 0x03d3, 0x0136 }, { 0x03d9, 0x0172 }, { 0x03dd, 0x0168 }, { 0x03de, 0x016a }, + { 0x03e0, 0x0101 }, { 0x03e7, 0x012f }, { 0x03ec, 0x0117 }, { 0x03ef, 0x012b }, + { 0x03f1, 0x0146 }, { 0x03f2, 0x014d }, { 0x03f3, 0x0137 }, { 0x03f9, 0x0173 }, + { 0x03fd, 0x0169 }, { 0x03fe, 0x016b }, { 0x047e, 0x203e }, { 0x04a1, 0x3002 }, + { 0x04a2, 0x300c }, { 0x04a3, 0x300d }, { 0x04a4, 0x3001 }, { 0x04a5, 0x30fb }, + { 0x04a6, 0x30f2 }, { 0x04a7, 0x30a1 }, { 0x04a8, 0x30a3 }, { 0x04a9, 0x30a5 }, + { 0x04aa, 0x30a7 }, { 0x04ab, 0x30a9 }, { 0x04ac, 0x30e3 }, { 0x04ad, 0x30e5 }, + { 0x04ae, 0x30e7 }, { 0x04af, 0x30c3 }, { 0x04b0, 0x30fc }, { 0x04b1, 0x30a2 }, + { 0x04b2, 0x30a4 }, { 0x04b3, 0x30a6 }, { 0x04b4, 0x30a8 }, { 0x04b5, 0x30aa }, + { 0x04b6, 0x30ab }, { 0x04b7, 0x30ad }, { 0x04b8, 0x30af }, { 0x04b9, 0x30b1 }, + { 0x04ba, 0x30b3 }, { 0x04bb, 0x30b5 }, { 0x04bc, 0x30b7 }, { 0x04bd, 0x30b9 }, + { 0x04be, 0x30bb }, { 0x04bf, 0x30bd }, { 0x04c0, 0x30bf }, { 0x04c1, 0x30c1 }, + { 0x04c2, 0x30c4 }, { 0x04c3, 0x30c6 }, { 0x04c4, 0x30c8 }, { 0x04c5, 0x30ca }, + { 0x04c6, 0x30cb }, { 0x04c7, 0x30cc }, { 0x04c8, 0x30cd }, { 0x04c9, 0x30ce }, + { 0x04ca, 0x30cf }, { 0x04cb, 0x30d2 }, { 0x04cc, 0x30d5 }, { 0x04cd, 0x30d8 }, + { 0x04ce, 0x30db }, { 0x04cf, 0x30de }, { 0x04d0, 0x30df }, { 0x04d1, 0x30e0 }, + { 0x04d2, 0x30e1 }, { 0x04d3, 0x30e2 }, { 0x04d4, 0x30e4 }, { 0x04d5, 0x30e6 }, + { 0x04d6, 0x30e8 }, { 0x04d7, 0x30e9 }, { 0x04d8, 0x30ea }, { 0x04d9, 0x30eb }, + { 0x04da, 0x30ec }, { 0x04db, 0x30ed }, { 0x04dc, 0x30ef }, { 0x04dd, 0x30f3 }, + { 0x04de, 0x309b }, { 0x04df, 0x309c }, { 0x05ac, 0x060c }, { 0x05bb, 0x061b }, + { 0x05bf, 0x061f }, { 0x05c1, 0x0621 }, { 0x05c2, 0x0622 }, { 0x05c3, 0x0623 }, + { 0x05c4, 0x0624 }, { 0x05c5, 0x0625 }, { 0x05c6, 0x0626 }, { 0x05c7, 0x0627 }, + { 0x05c8, 0x0628 }, { 0x05c9, 0x0629 }, { 0x05ca, 0x062a }, { 0x05cb, 0x062b }, + { 0x05cc, 0x062c }, { 0x05cd, 0x062d }, { 0x05ce, 0x062e }, { 0x05cf, 0x062f }, + { 0x05d0, 0x0630 }, { 0x05d1, 0x0631 }, { 0x05d2, 0x0632 }, { 0x05d3, 0x0633 }, + { 0x05d4, 0x0634 }, { 0x05d5, 0x0635 }, { 0x05d6, 0x0636 }, { 0x05d7, 0x0637 }, + { 0x05d8, 0x0638 }, { 0x05d9, 0x0639 }, { 0x05da, 0x063a }, { 0x05e0, 0x0640 }, + { 0x05e1, 0x0641 }, { 0x05e2, 0x0642 }, { 0x05e3, 0x0643 }, { 0x05e4, 0x0644 }, + { 0x05e5, 0x0645 }, { 0x05e6, 0x0646 }, { 0x05e7, 0x0647 }, { 0x05e8, 0x0648 }, + { 0x05e9, 0x0649 }, { 0x05ea, 0x064a }, { 0x05eb, 0x064b }, { 0x05ec, 0x064c }, + { 0x05ed, 0x064d }, { 0x05ee, 0x064e }, { 0x05ef, 0x064f }, { 0x05f0, 0x0650 }, + { 0x05f1, 0x0651 }, { 0x05f2, 0x0652 }, { 0x06a1, 0x0452 }, { 0x06a2, 0x0453 }, + { 0x06a3, 0x0451 }, { 0x06a4, 0x0454 }, { 0x06a5, 0x0455 }, { 0x06a6, 0x0456 }, + { 0x06a7, 0x0457 }, { 0x06a8, 0x0458 }, { 0x06a9, 0x0459 }, { 0x06aa, 0x045a }, + { 0x06ab, 0x045b }, { 0x06ac, 0x045c }, { 0x06ae, 0x045e }, { 0x06af, 0x045f }, + { 0x06b0, 0x2116 }, { 0x06b1, 0x0402 }, { 0x06b2, 0x0403 }, { 0x06b3, 0x0401 }, + { 0x06b4, 0x0404 }, { 0x06b5, 0x0405 }, { 0x06b6, 0x0406 }, { 0x06b7, 0x0407 }, + { 0x06b8, 0x0408 }, { 0x06b9, 0x0409 }, { 0x06ba, 0x040a }, { 0x06bb, 0x040b }, + { 0x06bc, 0x040c }, { 0x06be, 0x040e }, { 0x06bf, 0x040f }, { 0x06c0, 0x044e }, + { 0x06c1, 0x0430 }, { 0x06c2, 0x0431 }, { 0x06c3, 0x0446 }, { 0x06c4, 0x0434 }, + { 0x06c5, 0x0435 }, { 0x06c6, 0x0444 }, { 0x06c7, 0x0433 }, { 0x06c8, 0x0445 }, + { 0x06c9, 0x0438 }, { 0x06ca, 0x0439 }, { 0x06cb, 0x043a }, { 0x06cc, 0x043b }, + { 0x06cd, 0x043c }, { 0x06ce, 0x043d }, { 0x06cf, 0x043e }, { 0x06d0, 0x043f }, + { 0x06d1, 0x044f }, { 0x06d2, 0x0440 }, { 0x06d3, 0x0441 }, { 0x06d4, 0x0442 }, + { 0x06d5, 0x0443 }, { 0x06d6, 0x0436 }, { 0x06d7, 0x0432 }, { 0x06d8, 0x044c }, + { 0x06d9, 0x044b }, { 0x06da, 0x0437 }, { 0x06db, 0x0448 }, { 0x06dc, 0x044d }, + { 0x06dd, 0x0449 }, { 0x06de, 0x0447 }, { 0x06df, 0x044a }, { 0x06e0, 0x042e }, + { 0x06e1, 0x0410 }, { 0x06e2, 0x0411 }, { 0x06e3, 0x0426 }, { 0x06e4, 0x0414 }, + { 0x06e5, 0x0415 }, { 0x06e6, 0x0424 }, { 0x06e7, 0x0413 }, { 0x06e8, 0x0425 }, + { 0x06e9, 0x0418 }, { 0x06ea, 0x0419 }, { 0x06eb, 0x041a }, { 0x06ec, 0x041b }, + { 0x06ed, 0x041c }, { 0x06ee, 0x041d }, { 0x06ef, 0x041e }, { 0x06f0, 0x041f }, + { 0x06f1, 0x042f }, { 0x06f2, 0x0420 }, { 0x06f3, 0x0421 }, { 0x06f4, 0x0422 }, + { 0x06f5, 0x0423 }, { 0x06f6, 0x0416 }, { 0x06f7, 0x0412 }, { 0x06f8, 0x042c }, + { 0x06f9, 0x042b }, { 0x06fa, 0x0417 }, { 0x06fb, 0x0428 }, { 0x06fc, 0x042d }, + { 0x06fd, 0x0429 }, { 0x06fe, 0x0427 }, { 0x06ff, 0x042a }, { 0x07a1, 0x0386 }, + { 0x07a2, 0x0388 }, { 0x07a3, 0x0389 }, { 0x07a4, 0x038a }, { 0x07a5, 0x03aa }, + { 0x07a7, 0x038c }, { 0x07a8, 0x038e }, { 0x07a9, 0x03ab }, { 0x07ab, 0x038f }, + { 0x07ae, 0x0385 }, { 0x07af, 0x2015 }, { 0x07b1, 0x03ac }, { 0x07b2, 0x03ad }, + { 0x07b3, 0x03ae }, { 0x07b4, 0x03af }, { 0x07b5, 0x03ca }, { 0x07b6, 0x0390 }, + { 0x07b7, 0x03cc }, { 0x07b8, 0x03cd }, { 0x07b9, 0x03cb }, { 0x07ba, 0x03b0 }, + { 0x07bb, 0x03ce }, { 0x07c1, 0x0391 }, { 0x07c2, 0x0392 }, { 0x07c3, 0x0393 }, + { 0x07c4, 0x0394 }, { 0x07c5, 0x0395 }, { 0x07c6, 0x0396 }, { 0x07c7, 0x0397 }, + { 0x07c8, 0x0398 }, { 0x07c9, 0x0399 }, { 0x07ca, 0x039a }, { 0x07cb, 0x039b }, + { 0x07cc, 0x039c }, { 0x07cd, 0x039d }, { 0x07ce, 0x039e }, { 0x07cf, 0x039f }, + { 0x07d0, 0x03a0 }, { 0x07d1, 0x03a1 }, { 0x07d2, 0x03a3 }, { 0x07d4, 0x03a4 }, + { 0x07d5, 0x03a5 }, { 0x07d6, 0x03a6 }, { 0x07d7, 0x03a7 }, { 0x07d8, 0x03a8 }, + { 0x07d9, 0x03a9 }, { 0x07e1, 0x03b1 }, { 0x07e2, 0x03b2 }, { 0x07e3, 0x03b3 }, + { 0x07e4, 0x03b4 }, { 0x07e5, 0x03b5 }, { 0x07e6, 0x03b6 }, { 0x07e7, 0x03b7 }, + { 0x07e8, 0x03b8 }, { 0x07e9, 0x03b9 }, { 0x07ea, 0x03ba }, { 0x07eb, 0x03bb }, + { 0x07ec, 0x03bc }, { 0x07ed, 0x03bd }, { 0x07ee, 0x03be }, { 0x07ef, 0x03bf }, + { 0x07f0, 0x03c0 }, { 0x07f1, 0x03c1 }, { 0x07f2, 0x03c3 }, { 0x07f3, 0x03c2 }, + { 0x07f4, 0x03c4 }, { 0x07f5, 0x03c5 }, { 0x07f6, 0x03c6 }, { 0x07f7, 0x03c7 }, + { 0x07f8, 0x03c8 }, { 0x07f9, 0x03c9 }, { 0x08a1, 0x23b7 }, { 0x08a2, 0x250c }, + { 0x08a3, 0x2500 }, { 0x08a4, 0x2320 }, { 0x08a5, 0x2321 }, { 0x08a6, 0x2502 }, + { 0x08a7, 0x23a1 }, { 0x08a8, 0x23a3 }, { 0x08a9, 0x23a4 }, { 0x08aa, 0x23a6 }, + { 0x08ab, 0x239b }, { 0x08ac, 0x239d }, { 0x08ad, 0x239e }, { 0x08ae, 0x23a0 }, + { 0x08af, 0x23a8 }, { 0x08b0, 0x23ac }, { 0x08bc, 0x2264 }, { 0x08bd, 0x2260 }, + { 0x08be, 0x2265 }, { 0x08bf, 0x222b }, { 0x08c0, 0x2234 }, { 0x08c1, 0x221d }, + { 0x08c2, 0x221e }, { 0x08c5, 0x2207 }, { 0x08c8, 0x223c }, { 0x08c9, 0x2243 }, + { 0x08cd, 0x21d4 }, { 0x08ce, 0x21d2 }, { 0x08cf, 0x2261 }, { 0x08d6, 0x221a }, + { 0x08da, 0x2282 }, { 0x08db, 0x2283 }, { 0x08dc, 0x2229 }, { 0x08dd, 0x222a }, + { 0x08de, 0x2227 }, { 0x08df, 0x2228 }, { 0x08ef, 0x2202 }, { 0x08f6, 0x0192 }, + { 0x08fb, 0x2190 }, { 0x08fc, 0x2191 }, { 0x08fd, 0x2192 }, { 0x08fe, 0x2193 }, + { 0x09e0, 0x25c6 }, { 0x09e1, 0x2592 }, { 0x09e2, 0x2409 }, { 0x09e3, 0x240c }, + { 0x09e4, 0x240d }, { 0x09e5, 0x240a }, { 0x09e8, 0x2424 }, { 0x09e9, 0x240b }, + { 0x09ea, 0x2518 }, { 0x09eb, 0x2510 }, { 0x09ec, 0x250c }, { 0x09ed, 0x2514 }, + { 0x09ee, 0x253c }, { 0x09ef, 0x23ba }, { 0x09f0, 0x23bb }, { 0x09f1, 0x2500 }, + { 0x09f2, 0x23bc }, { 0x09f3, 0x23bd }, { 0x09f4, 0x251c }, { 0x09f5, 0x2524 }, + { 0x09f6, 0x2534 }, { 0x09f7, 0x252c }, { 0x09f8, 0x2502 }, { 0x0aa1, 0x2003 }, + { 0x0aa2, 0x2002 }, { 0x0aa3, 0x2004 }, { 0x0aa4, 0x2005 }, { 0x0aa5, 0x2007 }, + { 0x0aa6, 0x2008 }, { 0x0aa7, 0x2009 }, { 0x0aa8, 0x200a }, { 0x0aa9, 0x2014 }, + { 0x0aaa, 0x2013 }, { 0x0aae, 0x2026 }, { 0x0aaf, 0x2025 }, { 0x0ab0, 0x2153 }, + { 0x0ab1, 0x2154 }, { 0x0ab2, 0x2155 }, { 0x0ab3, 0x2156 }, { 0x0ab4, 0x2157 }, + { 0x0ab5, 0x2158 }, { 0x0ab6, 0x2159 }, { 0x0ab7, 0x215a }, { 0x0ab8, 0x2105 }, + { 0x0abb, 0x2012 }, { 0x0abc, 0x2329 }, { 0x0abe, 0x232a }, { 0x0ac3, 0x215b }, + { 0x0ac4, 0x215c }, { 0x0ac5, 0x215d }, { 0x0ac6, 0x215e }, { 0x0ac9, 0x2122 }, + { 0x0aca, 0x2613 }, { 0x0acc, 0x25c1 }, { 0x0acd, 0x25b7 }, { 0x0ace, 0x25cb }, + { 0x0acf, 0x25af }, { 0x0ad0, 0x2018 }, { 0x0ad1, 0x2019 }, { 0x0ad2, 0x201c }, + { 0x0ad3, 0x201d }, { 0x0ad4, 0x211e }, { 0x0ad6, 0x2032 }, { 0x0ad7, 0x2033 }, + { 0x0ad9, 0x271d }, { 0x0adb, 0x25ac }, { 0x0adc, 0x25c0 }, { 0x0add, 0x25b6 }, + { 0x0ade, 0x25cf }, { 0x0adf, 0x25ae }, { 0x0ae0, 0x25e6 }, { 0x0ae1, 0x25ab }, + { 0x0ae2, 0x25ad }, { 0x0ae3, 0x25b3 }, { 0x0ae4, 0x25bd }, { 0x0ae5, 0x2606 }, + { 0x0ae6, 0x2022 }, { 0x0ae7, 0x25aa }, { 0x0ae8, 0x25b2 }, { 0x0ae9, 0x25bc }, + { 0x0aea, 0x261c }, { 0x0aeb, 0x261e }, { 0x0aec, 0x2663 }, { 0x0aed, 0x2666 }, + { 0x0aee, 0x2665 }, { 0x0af0, 0x2720 }, { 0x0af1, 0x2020 }, { 0x0af2, 0x2021 }, + { 0x0af3, 0x2713 }, { 0x0af4, 0x2717 }, { 0x0af5, 0x266f }, { 0x0af6, 0x266d }, + { 0x0af7, 0x2642 }, { 0x0af8, 0x2640 }, { 0x0af9, 0x260e }, { 0x0afa, 0x2315 }, + { 0x0afb, 0x2117 }, { 0x0afc, 0x2038 }, { 0x0afd, 0x201a }, { 0x0afe, 0x201e }, + { 0x0ba3, 0x003c }, { 0x0ba6, 0x003e }, { 0x0ba8, 0x2228 }, { 0x0ba9, 0x2227 }, + { 0x0bc0, 0x00af }, { 0x0bc2, 0x22a5 }, { 0x0bc3, 0x2229 }, { 0x0bc4, 0x230a }, + { 0x0bc6, 0x005f }, { 0x0bca, 0x2218 }, { 0x0bcc, 0x2395 }, { 0x0bce, 0x22a4 }, + { 0x0bcf, 0x25cb }, { 0x0bd3, 0x2308 }, { 0x0bd6, 0x222a }, { 0x0bd8, 0x2283 }, + { 0x0bda, 0x2282 }, { 0x0bdc, 0x22a2 }, { 0x0bfc, 0x22a3 }, { 0x0cdf, 0x2017 }, + { 0x0ce0, 0x05d0 }, { 0x0ce1, 0x05d1 }, { 0x0ce2, 0x05d2 }, { 0x0ce3, 0x05d3 }, + { 0x0ce4, 0x05d4 }, { 0x0ce5, 0x05d5 }, { 0x0ce6, 0x05d6 }, { 0x0ce7, 0x05d7 }, + { 0x0ce8, 0x05d8 }, { 0x0ce9, 0x05d9 }, { 0x0cea, 0x05da }, { 0x0ceb, 0x05db }, + { 0x0cec, 0x05dc }, { 0x0ced, 0x05dd }, { 0x0cee, 0x05de }, { 0x0cef, 0x05df }, + { 0x0cf0, 0x05e0 }, { 0x0cf1, 0x05e1 }, { 0x0cf2, 0x05e2 }, { 0x0cf3, 0x05e3 }, + { 0x0cf4, 0x05e4 }, { 0x0cf5, 0x05e5 }, { 0x0cf6, 0x05e6 }, { 0x0cf7, 0x05e7 }, + { 0x0cf8, 0x05e8 }, { 0x0cf9, 0x05e9 }, { 0x0cfa, 0x05ea }, { 0x0da1, 0x0e01 }, + { 0x0da2, 0x0e02 }, { 0x0da3, 0x0e03 }, { 0x0da4, 0x0e04 }, { 0x0da5, 0x0e05 }, + { 0x0da6, 0x0e06 }, { 0x0da7, 0x0e07 }, { 0x0da8, 0x0e08 }, { 0x0da9, 0x0e09 }, + { 0x0daa, 0x0e0a }, { 0x0dab, 0x0e0b }, { 0x0dac, 0x0e0c }, { 0x0dad, 0x0e0d }, + { 0x0dae, 0x0e0e }, { 0x0daf, 0x0e0f }, { 0x0db0, 0x0e10 }, { 0x0db1, 0x0e11 }, + { 0x0db2, 0x0e12 }, { 0x0db3, 0x0e13 }, { 0x0db4, 0x0e14 }, { 0x0db5, 0x0e15 }, + { 0x0db6, 0x0e16 }, { 0x0db7, 0x0e17 }, { 0x0db8, 0x0e18 }, { 0x0db9, 0x0e19 }, + { 0x0dba, 0x0e1a }, { 0x0dbb, 0x0e1b }, { 0x0dbc, 0x0e1c }, { 0x0dbd, 0x0e1d }, + { 0x0dbe, 0x0e1e }, { 0x0dbf, 0x0e1f }, { 0x0dc0, 0x0e20 }, { 0x0dc1, 0x0e21 }, + { 0x0dc2, 0x0e22 }, { 0x0dc3, 0x0e23 }, { 0x0dc4, 0x0e24 }, { 0x0dc5, 0x0e25 }, + { 0x0dc6, 0x0e26 }, { 0x0dc7, 0x0e27 }, { 0x0dc8, 0x0e28 }, { 0x0dc9, 0x0e29 }, + { 0x0dca, 0x0e2a }, { 0x0dcb, 0x0e2b }, { 0x0dcc, 0x0e2c }, { 0x0dcd, 0x0e2d }, + { 0x0dce, 0x0e2e }, { 0x0dcf, 0x0e2f }, { 0x0dd0, 0x0e30 }, { 0x0dd1, 0x0e31 }, + { 0x0dd2, 0x0e32 }, { 0x0dd3, 0x0e33 }, { 0x0dd4, 0x0e34 }, { 0x0dd5, 0x0e35 }, + { 0x0dd6, 0x0e36 }, { 0x0dd7, 0x0e37 }, { 0x0dd8, 0x0e38 }, { 0x0dd9, 0x0e39 }, + { 0x0dda, 0x0e3a }, { 0x0ddf, 0x0e3f }, { 0x0de0, 0x0e40 }, { 0x0de1, 0x0e41 }, + { 0x0de2, 0x0e42 }, { 0x0de3, 0x0e43 }, { 0x0de4, 0x0e44 }, { 0x0de5, 0x0e45 }, + { 0x0de6, 0x0e46 }, { 0x0de7, 0x0e47 }, { 0x0de8, 0x0e48 }, { 0x0de9, 0x0e49 }, + { 0x0dea, 0x0e4a }, { 0x0deb, 0x0e4b }, { 0x0dec, 0x0e4c }, { 0x0ded, 0x0e4d }, + { 0x0df0, 0x0e50 }, { 0x0df1, 0x0e51 }, { 0x0df2, 0x0e52 }, { 0x0df3, 0x0e53 }, + { 0x0df4, 0x0e54 }, { 0x0df5, 0x0e55 }, { 0x0df6, 0x0e56 }, { 0x0df7, 0x0e57 }, + { 0x0df8, 0x0e58 }, { 0x0df9, 0x0e59 }, { 0x0ea1, 0x3131 }, { 0x0ea2, 0x3132 }, + { 0x0ea3, 0x3133 }, { 0x0ea4, 0x3134 }, { 0x0ea5, 0x3135 }, { 0x0ea6, 0x3136 }, + { 0x0ea7, 0x3137 }, { 0x0ea8, 0x3138 }, { 0x0ea9, 0x3139 }, { 0x0eaa, 0x313a }, + { 0x0eab, 0x313b }, { 0x0eac, 0x313c }, { 0x0ead, 0x313d }, { 0x0eae, 0x313e }, + { 0x0eaf, 0x313f }, { 0x0eb0, 0x3140 }, { 0x0eb1, 0x3141 }, { 0x0eb2, 0x3142 }, + { 0x0eb3, 0x3143 }, { 0x0eb4, 0x3144 }, { 0x0eb5, 0x3145 }, { 0x0eb6, 0x3146 }, + { 0x0eb7, 0x3147 }, { 0x0eb8, 0x3148 }, { 0x0eb9, 0x3149 }, { 0x0eba, 0x314a }, + { 0x0ebb, 0x314b }, { 0x0ebc, 0x314c }, { 0x0ebd, 0x314d }, { 0x0ebe, 0x314e }, + { 0x0ebf, 0x314f }, { 0x0ec0, 0x3150 }, { 0x0ec1, 0x3151 }, { 0x0ec2, 0x3152 }, + { 0x0ec3, 0x3153 }, { 0x0ec4, 0x3154 }, { 0x0ec5, 0x3155 }, { 0x0ec6, 0x3156 }, + { 0x0ec7, 0x3157 }, { 0x0ec8, 0x3158 }, { 0x0ec9, 0x3159 }, { 0x0eca, 0x315a }, + { 0x0ecb, 0x315b }, { 0x0ecc, 0x315c }, { 0x0ecd, 0x315d }, { 0x0ece, 0x315e }, + { 0x0ecf, 0x315f }, { 0x0ed0, 0x3160 }, { 0x0ed1, 0x3161 }, { 0x0ed2, 0x3162 }, + { 0x0ed3, 0x3163 }, { 0x0ed4, 0x11a8 }, { 0x0ed5, 0x11a9 }, { 0x0ed6, 0x11aa }, + { 0x0ed7, 0x11ab }, { 0x0ed8, 0x11ac }, { 0x0ed9, 0x11ad }, { 0x0eda, 0x11ae }, + { 0x0edb, 0x11af }, { 0x0edc, 0x11b0 }, { 0x0edd, 0x11b1 }, { 0x0ede, 0x11b2 }, + { 0x0edf, 0x11b3 }, { 0x0ee0, 0x11b4 }, { 0x0ee1, 0x11b5 }, { 0x0ee2, 0x11b6 }, + { 0x0ee3, 0x11b7 }, { 0x0ee4, 0x11b8 }, { 0x0ee5, 0x11b9 }, { 0x0ee6, 0x11ba }, + { 0x0ee7, 0x11bb }, { 0x0ee8, 0x11bc }, { 0x0ee9, 0x11bd }, { 0x0eea, 0x11be }, + { 0x0eeb, 0x11bf }, { 0x0eec, 0x11c0 }, { 0x0eed, 0x11c1 }, { 0x0eee, 0x11c2 }, + { 0x0eef, 0x316d }, { 0x0ef0, 0x3171 }, { 0x0ef1, 0x3178 }, { 0x0ef2, 0x317f }, + { 0x0ef3, 0x3181 }, { 0x0ef4, 0x3184 }, { 0x0ef5, 0x3186 }, { 0x0ef6, 0x318d }, + { 0x0ef7, 0x318e }, { 0x0ef8, 0x11eb }, { 0x0ef9, 0x11f0 }, { 0x0efa, 0x11f9 }, + { 0x0eff, 0x20a9 }, { 0x13a4, 0x20ac }, { 0x13bc, 0x0152 }, { 0x13bd, 0x0153 }, + { 0x13be, 0x0178 }, { 0x20ac, 0x20ac }, +}; + +static unsigned int ImGui_ImplQNX_KeySymToUnicode(unsigned int keysym) +{ + // Latin-1 keysyms map directly to Unicode. + if ((keysym >= 0x0020u && keysym <= 0x007eu) || + (keysym >= 0x00a0u && keysym <= 0x00ffu)) + { + return keysym; + } + + // X11-style directly encoded UCS keysyms use 0x01000000 | codepoint. + if ((keysym & 0xff000000u) == 0x01000000u) + { + const unsigned int codepoint = keysym & 0x00ffffffu; + return codepoint <= IM_UNICODE_CODEPOINT_MAX ? codepoint : 0u; + } + + int min = 0; + int max = (int)(sizeof(ImGui_ImplQNX_KeySymTable) / sizeof(ImGui_ImplQNX_KeySymTable[0])) - 1; + while (max >= min) + { + const int mid = min + (max - min) / 2; + if (ImGui_ImplQNX_KeySymTable[mid].KeySym < keysym) + { + min = mid + 1; + } + else if (ImGui_ImplQNX_KeySymTable[mid].KeySym > keysym) + { + max = mid - 1; + } + else + { + return ImGui_ImplQNX_KeySymTable[mid].Unicode; + } + } + + return 0u; +} + +#endif // #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_qnx.h b/backends/imgui_impl_qnx.h new file mode 100644 index 000000000..6436dfea9 --- /dev/null +++ b/backends/imgui_impl_qnx.h @@ -0,0 +1,36 @@ +// dear imgui: Platform Backend for QNX Screen +// This needs to be used along with a Renderer Backend (e.g. OpenGL3, Vulkan). + +// Implemented features: +// [X] Platform: Keyboard support. Uses io.AddKeyEvent() and native QNX key symbols. +// [X] Platform: Mouse support. Handles position, three buttons and horizontal/vertical wheels. +// [X] Platform: Touch support. Maps the first active touch contact to the primary mouse button. +// [X] Platform: Focus and window-size updates. +// Missing features or Issues: +// [ ] Platform: Clipboard support. +// [ ] Platform: Gamepad support. +// [ ] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). +// [ ] Platform: Multi-viewport support. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +#pragma once +#include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE + +#include + +// The application owns the Screen context, window and event object. +// Poll events with screen_get_event() and pass each non-NONE event to ProcessEvent(). +IMGUI_IMPL_API bool ImGui_ImplQNX_Init(screen_context_t context, screen_window_t window); +IMGUI_IMPL_API void ImGui_ImplQNX_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplQNX_NewFrame(); +IMGUI_IMPL_API bool ImGui_ImplQNX_ProcessEvent(screen_event_t event); + +#endif // #ifndef IMGUI_DISABLE diff --git a/docs/BACKENDS.md b/docs/BACKENDS.md index f72391f3e..bab76fb60 100644 --- a/docs/BACKENDS.md +++ b/docs/BACKENDS.md @@ -79,6 +79,7 @@ List of Platforms Backends: imgui_impl_osx.mm ; macOS native API (not as feature complete as glfw/sdl backends) imgui_impl_sdl2.cpp ; SDL2 (Windows, macOS, Linux, iOS, Android) https://www.libsdl.org imgui_impl_sdl3.cpp ; SDL3 (Windows, macOS, Linux, iOS, Android) https://www.libsdl.org + imgui_impl_qnx.cpp ; QNX Screen imgui_impl_win32.cpp ; Win32 native API (Windows) imgui_impl_glut.cpp ; GLUT/FreeGLUT (this is prehistoric software and absolutely not recommended today!) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 93dc4c9f7..3197f28e6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -56,6 +56,10 @@ Other Changes: - Per-viewport FramebufferScale for Apple/Retina screen is applied to draw lists: - AA Fringe is scaled accordingly. - Circle and Curves tessellation error are scaled accordingly. +- Backends: + - QNX: added QNX Screen backend. (#9492) [@mgorchak-blackberry] +- Examples: + - QNX+OpenGL3, QNX+Vulkan: added QNX Screen examples. (#9492) [@mgorchak-blackberry] ----------------------------------------------------------------------- diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md index 6315a3a5c..89e9bd0df 100644 --- a/docs/EXAMPLES.md +++ b/docs/EXAMPLES.md @@ -117,6 +117,16 @@ Because this application doesn't create a window nor a graphic context, there's Please note that imgui_impl_null itself is a rather empty backend. We provide it for consistency but it is similarly easy to create a skeleton application without the null backend. +[example_qnx_opengl3/](https://github.com/ocornut/imgui/blob/master/examples/example_qnx_opengl3/)
+QNX Screen + OpenGL ES 3 example.
+= main.cpp + imgui_impl_qnx.cpp + imgui_impl_opengl3.cpp
+Uses the QNX recursive make build system and compiles the OpenGL3 renderer with `IMGUI_IMPL_OPENGL_ES3`. + +[example_qnx_vulkan/](https://github.com/ocornut/imgui/blob/master/examples/example_qnx_vulkan/)
+QNX Screen + Vulkan example using `VK_QNX_screen_surface`.
+= main.cpp + imgui_impl_qnx.cpp + imgui_impl_vulkan.cpp
+Uses the QNX recursive make build system and the Vulkan backend's example helper structures. + [example_sdl2_directx11/](https://github.com/ocornut/imgui/blob/master/examples/example_sdl2_directx11/)
SDL2 + DirectX11 example, Windows only.
= main.cpp + imgui_impl_sdl2.cpp + imgui_impl_dx11.cpp
diff --git a/examples/example_qnx_opengl3/.gitignore b/examples/example_qnx_opengl3/.gitignore new file mode 100644 index 000000000..12b50871c --- /dev/null +++ b/examples/example_qnx_opengl3/.gitignore @@ -0,0 +1,19 @@ +# ignore all files +* + +# unignore all dirs +!/**/ + +# unignore all with extensions +!*.* + +# unignore makefiles +!Makefile + +# re-ignore qnx build artefacts +*.o +*.so* +*.a +*.pinfo +*.dep +*.gcno diff --git a/examples/example_qnx_opengl3/Makefile b/examples/example_qnx_opengl3/Makefile new file mode 100644 index 000000000..3beecfb7e --- /dev/null +++ b/examples/example_qnx_opengl3/Makefile @@ -0,0 +1,2 @@ +LIST=OS +include recurse.mk diff --git a/examples/example_qnx_opengl3/README.md b/examples/example_qnx_opengl3/README.md new file mode 100644 index 000000000..2fd97d6d3 --- /dev/null +++ b/examples/example_qnx_opengl3/README.md @@ -0,0 +1,29 @@ +# Dear ImGui QNX Screen + OpenGL ES 3 example + +This example uses: + +- `imgui_impl_qnx.cpp` as the QNX Screen platform backend +- `imgui_impl_opengl3.cpp` as the renderer backend +- EGL to create an OpenGL ES 3 context and Screen window surface + +`imgui_impl_opengl3.cpp` supports desktop OpenGL and OpenGL ES. On QNX this +example compiles it with `IMGUI_IMPL_OPENGL_ES3` and uses `#version 300 es`. +No separate Screen/OpenGL renderer backend is required. + +## Build + +Load the QNX SDP environment, then run: + +```sh +cd examples/example_qnx_opengl3 +make +``` + +The QNX recursive make files build for the CPUs enabled in your SDP +configuration and install the program as `example_qnx_opengl3`. + +## Input + +The application polls its `screen_context_t` event queue and forwards every +non-`SCREEN_EVENT_NONE` event to `ImGui_ImplQNX_ProcessEvent()`. +Escape exits the example. diff --git a/examples/example_qnx_opengl3/common.mk b/examples/example_qnx_opengl3/common.mk new file mode 100644 index 000000000..d162d684e --- /dev/null +++ b/examples/example_qnx_opengl3/common.mk @@ -0,0 +1,31 @@ +ifndef QCONFIG +QCONFIG=qconfig.mk +endif +include $(QCONFIG) + +NAME=example_qnx_opengl3 + +SRCS = \ + main.cpp \ + imgui.cpp \ + imgui_demo.cpp \ + imgui_draw.cpp \ + imgui_tables.cpp \ + imgui_widgets.cpp \ + imgui_impl_qnx.cpp \ + imgui_impl_opengl3.cpp + +EXTRA_SRCVPATH += $(PROJECT_ROOT)/../.. $(PROJECT_ROOT)/../../backends +EXTRA_INCVPATH += $(PROJECT_ROOT)/../.. $(PROJECT_ROOT)/../../backends + +CXXFLAGS += -std=gnu++11 -DIMGUI_IMPL_OPENGL_ES3 +LIBS += EGL GLESv2 screen m + +INSTALLDIR=$(firstword $(INSTALLDIR_$(OS)) usr/bin) + +define PINFO +PINFO DESCRIPTION=Dear ImGui QNX Screen and OpenGL ES 3 example +endef + +include $(MKFILES_ROOT)/qmacros.mk +include $(MKFILES_ROOT)/qtargets.mk diff --git a/examples/example_qnx_opengl3/example_qnx_opengl3.use b/examples/example_qnx_opengl3/example_qnx_opengl3.use new file mode 100644 index 000000000..6361d35cc --- /dev/null +++ b/examples/example_qnx_opengl3/example_qnx_opengl3.use @@ -0,0 +1,6 @@ +Dear ImGui QNX Screen + OpenGL ES 3 example + +Usage: example_qnx_opengl3 + +The example uses the fullscreen size reported by QNX Screen, displays the Dear ImGui demo UI, +and exits when Escape is pressed or the Screen window receives a close event. diff --git a/examples/example_qnx_opengl3/main.cpp b/examples/example_qnx_opengl3/main.cpp new file mode 100644 index 000000000..76cc3a866 --- /dev/null +++ b/examples/example_qnx_opengl3/main.cpp @@ -0,0 +1,299 @@ +// Dear ImGui: standalone example application for QNX Screen + OpenGL ES 3 + +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +#include "imgui.h" +#include "imgui_impl_qnx.h" +#include "imgui_impl_opengl3.h" +#include // printf, fprintf +#include +#include +#include +#include +#include +#include + +#ifndef EGL_OPENGL_ES3_BIT +#ifdef EGL_OPENGL_ES3_BIT_KHR +#define EGL_OPENGL_ES3_BIT EGL_OPENGL_ES3_BIT_KHR +#else +#define EGL_OPENGL_ES3_BIT 0x0040 +#endif +#endif + +struct QNXWindow +{ + screen_context_t Context = nullptr; + screen_window_t Window = nullptr; + screen_event_t Event = nullptr; + EGLDisplay Display = EGL_NO_DISPLAY; + EGLContext GLContext = EGL_NO_CONTEXT; + EGLSurface GLSurface = EGL_NO_SURFACE; + int Size[2] = {}; +}; + +static bool CreateQNXWindow(QNXWindow* window) +{ + if (screen_create_context(&window->Context, 0) != 0 || + screen_create_window(&window->Window, window->Context) != 0 || + screen_create_event(&window->Event) != 0) + { + fprintf(stderr, "QNX Screen initialization failed: %s\n", strerror(errno)); + return false; + } + + // A newly created QNX Screen window reports the fullscreen dimensions. + if (screen_get_window_property_iv(window->Window, SCREEN_PROPERTY_SIZE, window->Size) != 0 || window->Size[0] <= 0 || window->Size[1] <= 0) + { + fprintf(stderr, "Failed to obtain QNX Screen window size: %s\n", strerror(errno)); + return false; + } + + const int usage = SCREEN_USAGE_OPENGL_ES2 | SCREEN_USAGE_OPENGL_ES3 | SCREEN_USAGE_NATIVE; + const int transparency = SCREEN_TRANSPARENCY_NONE; + const int swap_interval = 1; + if (screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_USAGE, &usage) != 0 || + screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_SIZE, window->Size) != 0 || + screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_TRANSPARENCY, &transparency) != 0 || + screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_SWAP_INTERVAL, &swap_interval) != 0) + { + fprintf(stderr, "Failed to configure QNX Screen window: %s\n", strerror(errno)); + return false; + } + + window->Display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (window->Display == EGL_NO_DISPLAY || eglInitialize(window->Display, nullptr, nullptr) != EGL_TRUE || eglBindAPI(EGL_OPENGL_ES_API) != EGL_TRUE) + { + fprintf(stderr, "EGL initialization failed: 0x%04x\n", eglGetError()); + return false; + } + + const EGLint config_attributes[] = + { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 24, + EGL_STENCIL_SIZE, 8, + EGL_NONE + }; + EGLConfig config = nullptr; + EGLint config_count = 0; + if (eglChooseConfig(window->Display, config_attributes, &config, 1, &config_count) != EGL_TRUE || config_count == 0) + { + fprintf(stderr, "eglChooseConfig() failed: 0x%04x\n", eglGetError()); + return false; + } + + EGLint native_visual = 0; + if (eglGetConfigAttrib(window->Display, config, EGL_NATIVE_VISUAL_ID, &native_visual) != EGL_TRUE || + screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_FORMAT, &native_visual) != 0 || + screen_create_window_buffers(window->Window, 2) != 0) + { + fprintf(stderr, "Failed to create QNX Screen buffers: %s\n", strerror(errno)); + return false; + } + + const EGLint context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE }; + window->GLContext = eglCreateContext(window->Display, config, EGL_NO_CONTEXT, context_attributes); + window->GLSurface = eglCreateWindowSurface(window->Display, config, window->Window, nullptr); + if (window->GLContext == EGL_NO_CONTEXT || window->GLSurface == EGL_NO_SURFACE || + eglMakeCurrent(window->Display, window->GLSurface, window->GLSurface, window->GLContext) != EGL_TRUE || + eglSwapInterval(window->Display, 1) != EGL_TRUE) + { + fprintf(stderr, "EGL context creation failed: 0x%04x\n", eglGetError()); + return false; + } + + const int visible = 1; + if (screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_VISIBLE, &visible) != 0) + { + fprintf(stderr, "Failed to show QNX Screen window: %s\n", strerror(errno)); + return false; + } + return true; +} + +static void DestroyQNXWindow(QNXWindow* window) +{ + if (window->Display != EGL_NO_DISPLAY) + { + eglMakeCurrent(window->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (window->GLSurface != EGL_NO_SURFACE) + eglDestroySurface(window->Display, window->GLSurface); + if (window->GLContext != EGL_NO_CONTEXT) + eglDestroyContext(window->Display, window->GLContext); + eglTerminate(window->Display); + } + if (window->Event != nullptr) + screen_destroy_event(window->Event); + if (window->Window != nullptr) + screen_destroy_window(window->Window); + if (window->Context != nullptr) + screen_destroy_context(window->Context); +} + +static bool ProcessQNXEvents(QNXWindow* window) +{ + for (;;) + { + if (screen_get_event(window->Context, window->Event, 0) != 0) + return false; + + int type = SCREEN_EVENT_NONE; + if (screen_get_event_property_iv(window->Event, SCREEN_PROPERTY_TYPE, &type) != 0) + return false; + if (type == SCREEN_EVENT_NONE) + return true; + + ImGui_ImplQNX_ProcessEvent(window->Event); + if (type == SCREEN_EVENT_CLOSE) + return false; + if (type == SCREEN_EVENT_KEYBOARD) + { + int flags = 0; + int key_cap = 0; + if (screen_get_event_property_iv(window->Event, SCREEN_PROPERTY_FLAGS, &flags) == 0 && + screen_get_event_property_iv(window->Event, SCREEN_PROPERTY_KEY_CAP, &key_cap) == 0 && + (flags & SCREEN_FLAG_KEY_DOWN) != 0 && key_cap == KEYCODE_ESCAPE) + return false; + } + } +} + +// Main code +int main(int, char**) +{ + // Setup QNX Screen and EGL + QNXWindow window; + if (!CreateQNXWindow(&window)) + { + DestroyQNXWindow(&window); + return 1; + } + + // GL ES 3.0 + GLSL 300 es + const char* glsl_version = "#version 300 es"; + float main_scale = 1.0f; + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + + // Setup Platform/Renderer backends + ImGui_ImplQNX_Init(window.Context, window.Window); + ImGui_ImplOpenGL3_Init(glsl_version); + + // Load Fonts + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). + // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); + //IM_ASSERT(font != nullptr); + + // Our state + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // Main loop + bool done = false; + while (!done) + { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + if (!ProcessQNXEvents(&window)) + done = true; + + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplQNX_NewFrame(); + ImGui::NewFrame(); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::End(); + } + + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // Rendering + ImGui::Render(); + glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); + glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + eglSwapBuffers(window.Display, window.GLSurface); + } + + // Cleanup + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplQNX_Shutdown(); + ImGui::DestroyContext(); + + DestroyQNXWindow(&window); + + return 0; +} diff --git a/examples/example_qnx_opengl3/nto/Makefile b/examples/example_qnx_opengl3/nto/Makefile new file mode 100644 index 000000000..0cc5eae2d --- /dev/null +++ b/examples/example_qnx_opengl3/nto/Makefile @@ -0,0 +1,8 @@ +LIST=CPU +ifndef QRECURSE +QRECURSE=recurse.mk +ifdef QCONFIG +QRDIR=$(dir $(QCONFIG)) +endif +endif +include $(QRDIR)$(QRECURSE) diff --git a/examples/example_qnx_opengl3/nto/aarch64/Makefile b/examples/example_qnx_opengl3/nto/aarch64/Makefile new file mode 100644 index 000000000..0e22650c0 --- /dev/null +++ b/examples/example_qnx_opengl3/nto/aarch64/Makefile @@ -0,0 +1,8 @@ +LIST=VARIANT +ifndef QRECURSE +QRECURSE=recurse.mk +ifdef QCONFIG +QRDIR=$(dir $(QCONFIG)) +endif +endif +include $(QRDIR)$(QRECURSE) diff --git a/examples/example_qnx_opengl3/nto/aarch64/o.le/Makefile b/examples/example_qnx_opengl3/nto/aarch64/o.le/Makefile new file mode 100644 index 000000000..0f9d7b9a9 --- /dev/null +++ b/examples/example_qnx_opengl3/nto/aarch64/o.le/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/examples/example_qnx_opengl3/nto/x86_64/Makefile b/examples/example_qnx_opengl3/nto/x86_64/Makefile new file mode 100644 index 000000000..0e22650c0 --- /dev/null +++ b/examples/example_qnx_opengl3/nto/x86_64/Makefile @@ -0,0 +1,8 @@ +LIST=VARIANT +ifndef QRECURSE +QRECURSE=recurse.mk +ifdef QCONFIG +QRDIR=$(dir $(QCONFIG)) +endif +endif +include $(QRDIR)$(QRECURSE) diff --git a/examples/example_qnx_opengl3/nto/x86_64/o/Makefile b/examples/example_qnx_opengl3/nto/x86_64/o/Makefile new file mode 100644 index 000000000..0f9d7b9a9 --- /dev/null +++ b/examples/example_qnx_opengl3/nto/x86_64/o/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/examples/example_qnx_vulkan/.gitignore b/examples/example_qnx_vulkan/.gitignore new file mode 100644 index 000000000..12b50871c --- /dev/null +++ b/examples/example_qnx_vulkan/.gitignore @@ -0,0 +1,19 @@ +# ignore all files +* + +# unignore all dirs +!/**/ + +# unignore all with extensions +!*.* + +# unignore makefiles +!Makefile + +# re-ignore qnx build artefacts +*.o +*.so* +*.a +*.pinfo +*.dep +*.gcno diff --git a/examples/example_qnx_vulkan/Makefile b/examples/example_qnx_vulkan/Makefile new file mode 100644 index 000000000..3beecfb7e --- /dev/null +++ b/examples/example_qnx_vulkan/Makefile @@ -0,0 +1,2 @@ +LIST=OS +include recurse.mk diff --git a/examples/example_qnx_vulkan/README.md b/examples/example_qnx_vulkan/README.md new file mode 100644 index 000000000..13eba6f6f --- /dev/null +++ b/examples/example_qnx_vulkan/README.md @@ -0,0 +1,29 @@ +# Dear ImGui QNX Screen + Vulkan example + +This example uses: + +- `imgui_impl_qnx.cpp` as the QNX Screen platform backend +- the unmodified `imgui_impl_vulkan.cpp` renderer backend +- `vkCreateScreenSurfaceQNX()` and `VK_QNX_screen_surface` + +No separate Screen/Vulkan renderer backend is required. The QNX-specific work +is limited to the Screen platform backend, native window setup, and Vulkan WSI +surface creation. + +## Build + +Load the QNX SDP environment, then run: + +```sh +cd examples/example_qnx_vulkan +make +``` + +The build defines `VK_USE_PLATFORM_SCREEN_QNX` and links `libvulkan` and +`libscreen`. + +## Input and resize + +The application forwards Screen input events to `ImGui_ImplQNX_ProcessEvent()`. +It recreates the Vulkan swapchain when the Screen window size changes or Vulkan +reports `VK_ERROR_OUT_OF_DATE_KHR` / `VK_SUBOPTIMAL_KHR`. Escape exits. diff --git a/examples/example_qnx_vulkan/common.mk b/examples/example_qnx_vulkan/common.mk new file mode 100644 index 000000000..7d99539aa --- /dev/null +++ b/examples/example_qnx_vulkan/common.mk @@ -0,0 +1,31 @@ +ifndef QCONFIG +QCONFIG=qconfig.mk +endif +include $(QCONFIG) + +NAME=example_qnx_vulkan + +SRCS = \ + main.cpp \ + imgui.cpp \ + imgui_demo.cpp \ + imgui_draw.cpp \ + imgui_tables.cpp \ + imgui_widgets.cpp \ + imgui_impl_qnx.cpp \ + imgui_impl_vulkan.cpp + +EXTRA_SRCVPATH += $(PROJECT_ROOT)/../.. $(PROJECT_ROOT)/../../backends +EXTRA_INCVPATH += $(PROJECT_ROOT)/../.. $(PROJECT_ROOT)/../../backends + +CXXFLAGS += -std=gnu++11 -DVK_USE_PLATFORM_SCREEN_QNX=1 +LIBS += vulkan screen m + +INSTALLDIR=$(firstword $(INSTALLDIR_$(OS)) usr/bin) + +define PINFO +PINFO DESCRIPTION=Dear ImGui QNX Screen and Vulkan example +endef + +include $(MKFILES_ROOT)/qmacros.mk +include $(MKFILES_ROOT)/qtargets.mk diff --git a/examples/example_qnx_vulkan/example_qnx_vulkan.use b/examples/example_qnx_vulkan/example_qnx_vulkan.use new file mode 100644 index 000000000..893eb5c41 --- /dev/null +++ b/examples/example_qnx_vulkan/example_qnx_vulkan.use @@ -0,0 +1,7 @@ +Dear ImGui QNX Screen + Vulkan example + +Usage: example_qnx_vulkan + +The example uses the fullscreen size reported by QNX Screen, creates a VK_QNX_screen_surface +swapchain, displays the Dear ImGui demo UI, and exits when Escape is pressed +or the Screen window receives a close event. diff --git a/examples/example_qnx_vulkan/main.cpp b/examples/example_qnx_vulkan/main.cpp new file mode 100644 index 000000000..ae309ad15 --- /dev/null +++ b/examples/example_qnx_vulkan/main.cpp @@ -0,0 +1,626 @@ +// Dear ImGui: standalone example application for QNX Screen + Vulkan + +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app. +// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h. +// You will use those if you want to use this rendering backend in your engine/app. +// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by +// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. +// Read comments in imgui_impl_vulkan.h. + +#ifndef VK_USE_PLATFORM_SCREEN_QNX +#define VK_USE_PLATFORM_SCREEN_QNX +#endif +#include "imgui.h" +#include "imgui_impl_qnx.h" +#include "imgui_impl_vulkan.h" +#include // printf, fprintf +#include // abort +#include +#include +#include +#include +#include + +// Volk headers +#ifdef IMGUI_IMPL_VULKAN_USE_VOLK +#define VOLK_IMPLEMENTATION +#include +#endif + +//#define APP_USE_UNLIMITED_FRAME_RATE +#ifdef _DEBUG +#define APP_USE_VULKAN_DEBUG_REPORT +static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; +#endif + +// Data +static VkAllocationCallbacks* g_Allocator = nullptr; +static VkInstance g_Instance = VK_NULL_HANDLE; +static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; +static VkDevice g_Device = VK_NULL_HANDLE; +static uint32_t g_QueueFamily = (uint32_t)-1; +static VkQueue g_Queue = VK_NULL_HANDLE; +static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; +static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; + +static ImGui_ImplVulkanH_Window g_MainWindowData; +static uint32_t g_MinImageCount = 2; +static bool g_SwapChainRebuild = false; + +static void check_vk_result(VkResult err) +{ + if (err == VK_SUCCESS) + return; + fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); + if (err < 0) + abort(); +} + +#ifdef APP_USE_VULKAN_DEBUG_REPORT +static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) +{ + (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments + fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); + return VK_FALSE; +} +#endif // APP_USE_VULKAN_DEBUG_REPORT + +static bool IsExtensionAvailable(const ImVector& properties, const char* extension) +{ + for (const VkExtensionProperties& p : properties) + if (strcmp(p.extensionName, extension) == 0) + return true; + return false; +} + +static void SetupVulkan(ImVector instance_extensions) +{ + VkResult err; +#ifdef IMGUI_IMPL_VULKAN_USE_VOLK + volkInitialize(); +#endif + + // Create Vulkan Instance + { + VkInstanceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + + // Enumerate available extensions + uint32_t properties_count; + ImVector properties; + vkEnumerateInstanceExtensionProperties(nullptr, &properties_count, nullptr); + properties.resize(properties_count); + err = vkEnumerateInstanceExtensionProperties(nullptr, &properties_count, properties.Data); + check_vk_result(err); + + // Enable required extensions + if (IsExtensionAvailable(properties, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) + instance_extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); +#ifdef VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME + if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) + { + instance_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + create_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; + } +#endif + + // Enabling validation layers +#ifdef APP_USE_VULKAN_DEBUG_REPORT + const char* layers[] = { "VK_LAYER_KHRONOS_validation" }; + create_info.enabledLayerCount = 1; + create_info.ppEnabledLayerNames = layers; + instance_extensions.push_back("VK_EXT_debug_report"); +#endif + + // Create Vulkan Instance + create_info.enabledExtensionCount = (uint32_t)instance_extensions.Size; + create_info.ppEnabledExtensionNames = instance_extensions.Data; + err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); + check_vk_result(err); +#ifdef IMGUI_IMPL_VULKAN_USE_VOLK + volkLoadInstance(g_Instance); +#endif + + // Setup the debug report callback +#ifdef APP_USE_VULKAN_DEBUG_REPORT + auto f_vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); + IM_ASSERT(f_vkCreateDebugReportCallbackEXT != nullptr); + VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; + debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; + debug_report_ci.pfnCallback = debug_report; + debug_report_ci.pUserData = nullptr; + err = f_vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); + check_vk_result(err); +#endif + } + + // Select Physical Device (GPU) + g_PhysicalDevice = ImGui_ImplVulkanH_SelectPhysicalDevice(g_Instance); + IM_ASSERT(g_PhysicalDevice != VK_NULL_HANDLE); + + // Select graphics queue family + g_QueueFamily = ImGui_ImplVulkanH_SelectQueueFamilyIndex(g_PhysicalDevice); + IM_ASSERT(g_QueueFamily != (uint32_t)-1); + + // Create Logical Device (with 1 queue) + { + ImVector device_extensions; + device_extensions.push_back("VK_KHR_swapchain"); + + // Enumerate physical device extension + uint32_t properties_count; + ImVector properties; + vkEnumerateDeviceExtensionProperties(g_PhysicalDevice, nullptr, &properties_count, nullptr); + properties.resize(properties_count); + vkEnumerateDeviceExtensionProperties(g_PhysicalDevice, nullptr, &properties_count, properties.Data); +#ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME + if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) + device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); +#endif + + const float queue_priority[] = { 1.0f }; + VkDeviceQueueCreateInfo queue_info[1] = {}; + queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info[0].queueFamilyIndex = g_QueueFamily; + queue_info[0].queueCount = 1; + queue_info[0].pQueuePriorities = queue_priority; + VkDeviceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]); + create_info.pQueueCreateInfos = queue_info; + create_info.enabledExtensionCount = (uint32_t)device_extensions.Size; + create_info.ppEnabledExtensionNames = device_extensions.Data; + err = vkCreateDevice(g_PhysicalDevice, &create_info, g_Allocator, &g_Device); + check_vk_result(err); + vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue); + } + + // Create Descriptor Pool + // If you wish to load e.g. additional textures you may need to alter pools sizes and maxSets. + { + VkDescriptorPoolSize pool_sizes[] = + { + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE }, + { VK_DESCRIPTOR_TYPE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE }, + }; + VkDescriptorPoolCreateInfo pool_info = {}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; + pool_info.maxSets = 0; + for (VkDescriptorPoolSize& pool_size : pool_sizes) + pool_info.maxSets += pool_size.descriptorCount; + pool_info.poolSizeCount = (uint32_t)IM_COUNTOF(pool_sizes); + pool_info.pPoolSizes = pool_sizes; + err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); + check_vk_result(err); + } +} + +// All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo. +// Your real engine/app may not use them. +static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) +{ + // Check for WSI support + VkBool32 res; + vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, surface, &res); + if (res != VK_TRUE) + { + fprintf(stderr, "Error no WSI support on physical device 0\n"); + exit(-1); + } + + // Select Surface Format + const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; + const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + wd->Surface = surface; + wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_COUNTOF(requestSurfaceImageFormat), requestSurfaceColorSpace); + + // Select Present Mode +#ifdef APP_USE_UNLIMITED_FRAME_RATE + VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; +#else + VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR }; +#endif + wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_COUNTOF(present_modes)); + //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode); + + // Create SwapChain, RenderPass, Framebuffer, etc. + IM_ASSERT(g_MinImageCount >= 2); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, 0); +} + +static void CleanupVulkan() +{ + vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); + +#ifdef APP_USE_VULKAN_DEBUG_REPORT + // Remove the debug report callback + auto f_vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT"); + f_vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator); +#endif // APP_USE_VULKAN_DEBUG_REPORT + + vkDestroyDevice(g_Device, g_Allocator); + vkDestroyInstance(g_Instance, g_Allocator); +} + +static void CleanupVulkanWindow(ImGui_ImplVulkanH_Window* wd) +{ + ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, wd, g_Allocator); + vkDestroySurfaceKHR(g_Instance, wd->Surface, g_Allocator); +} + +static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) +{ + VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; + VkResult err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) + g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) + return; + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); + + ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; + { + err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking + check_vk_result(err); + + err = vkResetFences(g_Device, 1, &fd->Fence); + check_vk_result(err); + } + { + err = vkResetCommandPool(g_Device, fd->CommandPool, 0); + check_vk_result(err); + VkCommandBufferBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + err = vkBeginCommandBuffer(fd->CommandBuffer, &info); + check_vk_result(err); + } + { + VkRenderPassBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + info.renderPass = wd->RenderPass; + info.framebuffer = fd->Framebuffer; + info.renderArea.extent.width = wd->Width; + info.renderArea.extent.height = wd->Height; + info.clearValueCount = 1; + info.pClearValues = &wd->ClearValue; + vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); + } + + // Record dear imgui primitives into command buffer + ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer); + + // Submit command buffer + vkCmdEndRenderPass(fd->CommandBuffer); + { + VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &image_acquired_semaphore; + info.pWaitDstStageMask = &wait_stage; + info.commandBufferCount = 1; + info.pCommandBuffers = &fd->CommandBuffer; + info.signalSemaphoreCount = 1; + info.pSignalSemaphores = &render_complete_semaphore; + + err = vkEndCommandBuffer(fd->CommandBuffer); + check_vk_result(err); + err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence); + check_vk_result(err); + } +} + +static void FramePresent(ImGui_ImplVulkanH_Window* wd) +{ + if (g_SwapChainRebuild) + return; + VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; + VkPresentInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &render_complete_semaphore; + info.swapchainCount = 1; + info.pSwapchains = &wd->Swapchain; + info.pImageIndices = &wd->FrameIndex; + VkResult err = vkQueuePresentKHR(g_Queue, &info); + if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) + g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) + return; + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); + wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores +} + + +struct QNXWindow +{ + screen_context_t Context = nullptr; + screen_window_t Window = nullptr; + screen_event_t Event = nullptr; + int Size[2] = {}; +}; + +static bool CreateQNXWindow(QNXWindow* window) +{ + if (screen_create_context(&window->Context, 0) != 0 || + screen_create_window(&window->Window, window->Context) != 0 || + screen_create_event(&window->Event) != 0) + { + fprintf(stderr, "QNX Screen initialization failed: %s\n", strerror(errno)); + return false; + } + + // A newly created QNX Screen window reports the fullscreen dimensions. + if (screen_get_window_property_iv(window->Window, SCREEN_PROPERTY_SIZE, window->Size) != 0 || window->Size[0] <= 0 || window->Size[1] <= 0) + { + fprintf(stderr, "Failed to obtain QNX Screen window size: %s\n", strerror(errno)); + return false; + } + + const int usage = SCREEN_USAGE_VULKAN | SCREEN_USAGE_NATIVE; + const int transparency = SCREEN_TRANSPARENCY_NONE; + if (screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_USAGE, &usage) != 0 || + screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_SIZE, window->Size) != 0 || + screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_TRANSPARENCY, &transparency) != 0) + { + fprintf(stderr, "Failed to configure QNX Screen window: %s\n", strerror(errno)); + return false; + } + return true; +} + +static void DestroyQNXWindow(QNXWindow* window) +{ + if (window->Event != nullptr) + screen_destroy_event(window->Event); + if (window->Window != nullptr) + screen_destroy_window(window->Window); + if (window->Context != nullptr) + screen_destroy_context(window->Context); +} + +static bool ProcessQNXEvents(QNXWindow* window) +{ + for (;;) + { + if (screen_get_event(window->Context, window->Event, 0) != 0) + return false; + + int type = SCREEN_EVENT_NONE; + if (screen_get_event_property_iv(window->Event, SCREEN_PROPERTY_TYPE, &type) != 0) + return false; + if (type == SCREEN_EVENT_NONE) + return true; + + ImGui_ImplQNX_ProcessEvent(window->Event); + if (type == SCREEN_EVENT_CLOSE) + return false; + if (type == SCREEN_EVENT_KEYBOARD) + { + int flags = 0; + int key_cap = 0; + if (screen_get_event_property_iv(window->Event, SCREEN_PROPERTY_FLAGS, &flags) == 0 && + screen_get_event_property_iv(window->Event, SCREEN_PROPERTY_KEY_CAP, &key_cap) == 0 && + (flags & SCREEN_FLAG_KEY_DOWN) != 0 && key_cap == KEYCODE_ESCAPE) + return false; + } + } +} + +static void QNXSleep(int milliseconds) +{ + struct timespec request = { milliseconds / 1000, (milliseconds % 1000) * 1000000L }; + nanosleep(&request, nullptr); +} + +// Main code +int main(int, char**) +{ + // Create window with Vulkan graphics context + QNXWindow window; + if (!CreateQNXWindow(&window)) + { + DestroyQNXWindow(&window); + return 1; + } + float main_scale = 1.0f; + + ImVector extensions; + extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); + extensions.push_back(VK_QNX_SCREEN_SURFACE_EXTENSION_NAME); + SetupVulkan(extensions); + + // Create Window Surface + VkScreenSurfaceCreateInfoQNX surface_create_info = {}; + surface_create_info.sType = VK_STRUCTURE_TYPE_SCREEN_SURFACE_CREATE_INFO_QNX; + surface_create_info.context = window.Context; + surface_create_info.window = window.Window; + VkSurfaceKHR surface; + VkResult err = vkCreateScreenSurfaceQNX(g_Instance, &surface_create_info, g_Allocator, &surface); + check_vk_result(err); + + // Create Framebuffers + int w = window.Size[0]; + int h = window.Size[1]; + ImGui_ImplVulkanH_Window* wd = &g_MainWindowData; + SetupVulkanWindow(wd, surface, w, h); + + const int visible = 1; + if (screen_set_window_property_iv(window.Window, SCREEN_PROPERTY_VISIBLE, &visible) != 0) + { + fprintf(stderr, "Failed to show QNX Screen window: %s\n", strerror(errno)); + return 1; + } + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + + // Setup Platform/Renderer backends + ImGui_ImplQNX_Init(window.Context, window.Window); + ImGui_ImplVulkan_InitInfo init_info = {}; + //init_info.ApiVersion = VK_API_VERSION_1_3; // Pass in your value of VkApplicationInfo::apiVersion, otherwise will default to header version. + init_info.Instance = g_Instance; + init_info.PhysicalDevice = g_PhysicalDevice; + init_info.Device = g_Device; + init_info.QueueFamily = g_QueueFamily; + init_info.Queue = g_Queue; + init_info.PipelineCache = g_PipelineCache; + init_info.DescriptorPool = g_DescriptorPool; + init_info.MinImageCount = g_MinImageCount; + init_info.ImageCount = wd->ImageCount; + init_info.Allocator = g_Allocator; + init_info.PipelineInfoMain.RenderPass = wd->RenderPass; + init_info.PipelineInfoMain.Subpass = 0; + init_info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + init_info.CheckVkResultFn = check_vk_result; + ImGui_ImplVulkan_Init(&init_info); + + // Load Fonts + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). + // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); + //IM_ASSERT(font != nullptr); + + // Our state + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // Main loop + bool done = false; + while (!done) + { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + if (!ProcessQNXEvents(&window)) + done = true; + + // Resize swap chain? + int framebuffer_size[2] = {}; + if (screen_get_window_property_iv(window.Window, SCREEN_PROPERTY_SIZE, framebuffer_size) != 0) + done = true; + int fb_width = framebuffer_size[0]; + int fb_height = framebuffer_size[1]; + if (fb_width <= 0 || fb_height <= 0) + { + QNXSleep(10); + continue; + } + if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height)) + { + ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount, 0); + g_MainWindowData.FrameIndex = 0; + g_SwapChainRebuild = false; + } + + // Start the Dear ImGui frame + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplQNX_NewFrame(); + ImGui::NewFrame(); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::End(); + } + + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // Rendering + ImGui::Render(); + ImDrawData* draw_data = ImGui::GetDrawData(); + const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f); + if (!is_minimized) + { + wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w; + wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w; + wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w; + wd->ClearValue.color.float32[3] = clear_color.w; + FrameRender(wd, draw_data); + FramePresent(wd); + } + } + + // Cleanup + err = vkDeviceWaitIdle(g_Device); + check_vk_result(err); + ImGui_ImplVulkan_Shutdown(); + ImGui_ImplQNX_Shutdown(); + ImGui::DestroyContext(); + + CleanupVulkanWindow(&g_MainWindowData); + CleanupVulkan(); + + DestroyQNXWindow(&window); + + return 0; +} diff --git a/examples/example_qnx_vulkan/nto/Makefile b/examples/example_qnx_vulkan/nto/Makefile new file mode 100644 index 000000000..0cc5eae2d --- /dev/null +++ b/examples/example_qnx_vulkan/nto/Makefile @@ -0,0 +1,8 @@ +LIST=CPU +ifndef QRECURSE +QRECURSE=recurse.mk +ifdef QCONFIG +QRDIR=$(dir $(QCONFIG)) +endif +endif +include $(QRDIR)$(QRECURSE) diff --git a/examples/example_qnx_vulkan/nto/aarch64/Makefile b/examples/example_qnx_vulkan/nto/aarch64/Makefile new file mode 100644 index 000000000..0e22650c0 --- /dev/null +++ b/examples/example_qnx_vulkan/nto/aarch64/Makefile @@ -0,0 +1,8 @@ +LIST=VARIANT +ifndef QRECURSE +QRECURSE=recurse.mk +ifdef QCONFIG +QRDIR=$(dir $(QCONFIG)) +endif +endif +include $(QRDIR)$(QRECURSE) diff --git a/examples/example_qnx_vulkan/nto/aarch64/o.le/Makefile b/examples/example_qnx_vulkan/nto/aarch64/o.le/Makefile new file mode 100644 index 000000000..0f9d7b9a9 --- /dev/null +++ b/examples/example_qnx_vulkan/nto/aarch64/o.le/Makefile @@ -0,0 +1 @@ +include ../../../common.mk diff --git a/examples/example_qnx_vulkan/nto/x86_64/Makefile b/examples/example_qnx_vulkan/nto/x86_64/Makefile new file mode 100644 index 000000000..0e22650c0 --- /dev/null +++ b/examples/example_qnx_vulkan/nto/x86_64/Makefile @@ -0,0 +1,8 @@ +LIST=VARIANT +ifndef QRECURSE +QRECURSE=recurse.mk +ifdef QCONFIG +QRDIR=$(dir $(QCONFIG)) +endif +endif +include $(QRDIR)$(QRECURSE) diff --git a/examples/example_qnx_vulkan/nto/x86_64/o/Makefile b/examples/example_qnx_vulkan/nto/x86_64/o/Makefile new file mode 100644 index 000000000..0f9d7b9a9 --- /dev/null +++ b/examples/example_qnx_vulkan/nto/x86_64/o/Makefile @@ -0,0 +1 @@ +include ../../../common.mk From 7c9f0c148c00874d03688eeba8099f45d4b4e8a7 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 4 Aug 2026 14:11:12 +0200 Subject: [PATCH 29/47] Backends, Examples: QNX: amends. Moved ignore list to main file. Removed .use files. (#9492) Moved functions in example_qnx_opengl3/main.cpp to match structure of other examples. --- .gitignore | 7 + backends/imgui_impl_qnx.cpp | 6 +- backends/imgui_impl_qnx.h | 1 - examples/example_qnx_opengl3/.gitignore | 19 -- .../example_qnx_opengl3.use | 6 - examples/example_qnx_opengl3/main.cpp | 270 +++++++++--------- examples/example_qnx_vulkan/.gitignore | 19 -- .../example_qnx_vulkan/example_qnx_vulkan.use | 7 - 8 files changed, 147 insertions(+), 188 deletions(-) delete mode 100644 examples/example_qnx_opengl3/.gitignore delete mode 100644 examples/example_qnx_opengl3/example_qnx_opengl3.use delete mode 100644 examples/example_qnx_vulkan/.gitignore delete mode 100644 examples/example_qnx_vulkan/example_qnx_vulkan.use diff --git a/.gitignore b/.gitignore index 83c0ab423..250e7eed5 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ imgui*.ini ## General build artifacts *.o +*.so* +*.a *.obj *.exe examples/*/Debug/* @@ -57,6 +59,11 @@ examples/example_sdl3_wgpu/web/* .idea cmake-build-* +## QNX artifacts +*.pinfo +*.dep +*.gcno + ## Unix executables from our example Makefiles examples/example_apple_metal/example_apple_metal examples/example_apple_opengl2/example_apple_opengl2 diff --git a/backends/imgui_impl_qnx.cpp b/backends/imgui_impl_qnx.cpp index 82c9cb376..3730f54d2 100644 --- a/backends/imgui_impl_qnx.cpp +++ b/backends/imgui_impl_qnx.cpp @@ -14,11 +14,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2026-07-30: Renamed backend from imgui_impl_screen to imgui_impl_qnx and embedded a compact keysym table. -// 2026-07-29: Reworked keyboard handling from the proven qterm Screen path: authoritative modifier masks, -// complete keypad/Hyper mapping, and QNX/X11 keysym-to-Unicode conversion. -// 2026-07-29: Fixed keyboard handling: use KEY_CAP for physical keys, SYM for text, and honor Screen validity flags. -// 2026-07-28: Initial QNX Screen backend. +// 2026-08-04: Initial QNX Screen backend. (#9492) #include "imgui.h" #ifndef IMGUI_DISABLE diff --git a/backends/imgui_impl_qnx.h b/backends/imgui_impl_qnx.h index 6436dfea9..7bd86f439 100644 --- a/backends/imgui_impl_qnx.h +++ b/backends/imgui_impl_qnx.h @@ -10,7 +10,6 @@ // [ ] Platform: Clipboard support. // [ ] Platform: Gamepad support. // [ ] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). -// [ ] Platform: Multi-viewport support. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/examples/example_qnx_opengl3/.gitignore b/examples/example_qnx_opengl3/.gitignore deleted file mode 100644 index 12b50871c..000000000 --- a/examples/example_qnx_opengl3/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -# ignore all files -* - -# unignore all dirs -!/**/ - -# unignore all with extensions -!*.* - -# unignore makefiles -!Makefile - -# re-ignore qnx build artefacts -*.o -*.so* -*.a -*.pinfo -*.dep -*.gcno diff --git a/examples/example_qnx_opengl3/example_qnx_opengl3.use b/examples/example_qnx_opengl3/example_qnx_opengl3.use deleted file mode 100644 index 6361d35cc..000000000 --- a/examples/example_qnx_opengl3/example_qnx_opengl3.use +++ /dev/null @@ -1,6 +0,0 @@ -Dear ImGui QNX Screen + OpenGL ES 3 example - -Usage: example_qnx_opengl3 - -The example uses the fullscreen size reported by QNX Screen, displays the Dear ImGui demo UI, -and exits when Escape is pressed or the Screen window receives a close event. diff --git a/examples/example_qnx_opengl3/main.cpp b/examples/example_qnx_opengl3/main.cpp index 76cc3a866..d78bee90c 100644 --- a/examples/example_qnx_opengl3/main.cpp +++ b/examples/example_qnx_opengl3/main.cpp @@ -36,137 +36,10 @@ struct QNXWindow int Size[2] = {}; }; -static bool CreateQNXWindow(QNXWindow* window) -{ - if (screen_create_context(&window->Context, 0) != 0 || - screen_create_window(&window->Window, window->Context) != 0 || - screen_create_event(&window->Event) != 0) - { - fprintf(stderr, "QNX Screen initialization failed: %s\n", strerror(errno)); - return false; - } - - // A newly created QNX Screen window reports the fullscreen dimensions. - if (screen_get_window_property_iv(window->Window, SCREEN_PROPERTY_SIZE, window->Size) != 0 || window->Size[0] <= 0 || window->Size[1] <= 0) - { - fprintf(stderr, "Failed to obtain QNX Screen window size: %s\n", strerror(errno)); - return false; - } - - const int usage = SCREEN_USAGE_OPENGL_ES2 | SCREEN_USAGE_OPENGL_ES3 | SCREEN_USAGE_NATIVE; - const int transparency = SCREEN_TRANSPARENCY_NONE; - const int swap_interval = 1; - if (screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_USAGE, &usage) != 0 || - screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_SIZE, window->Size) != 0 || - screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_TRANSPARENCY, &transparency) != 0 || - screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_SWAP_INTERVAL, &swap_interval) != 0) - { - fprintf(stderr, "Failed to configure QNX Screen window: %s\n", strerror(errno)); - return false; - } - - window->Display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (window->Display == EGL_NO_DISPLAY || eglInitialize(window->Display, nullptr, nullptr) != EGL_TRUE || eglBindAPI(EGL_OPENGL_ES_API) != EGL_TRUE) - { - fprintf(stderr, "EGL initialization failed: 0x%04x\n", eglGetError()); - return false; - } - - const EGLint config_attributes[] = - { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_DEPTH_SIZE, 24, - EGL_STENCIL_SIZE, 8, - EGL_NONE - }; - EGLConfig config = nullptr; - EGLint config_count = 0; - if (eglChooseConfig(window->Display, config_attributes, &config, 1, &config_count) != EGL_TRUE || config_count == 0) - { - fprintf(stderr, "eglChooseConfig() failed: 0x%04x\n", eglGetError()); - return false; - } - - EGLint native_visual = 0; - if (eglGetConfigAttrib(window->Display, config, EGL_NATIVE_VISUAL_ID, &native_visual) != EGL_TRUE || - screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_FORMAT, &native_visual) != 0 || - screen_create_window_buffers(window->Window, 2) != 0) - { - fprintf(stderr, "Failed to create QNX Screen buffers: %s\n", strerror(errno)); - return false; - } - - const EGLint context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE }; - window->GLContext = eglCreateContext(window->Display, config, EGL_NO_CONTEXT, context_attributes); - window->GLSurface = eglCreateWindowSurface(window->Display, config, window->Window, nullptr); - if (window->GLContext == EGL_NO_CONTEXT || window->GLSurface == EGL_NO_SURFACE || - eglMakeCurrent(window->Display, window->GLSurface, window->GLSurface, window->GLContext) != EGL_TRUE || - eglSwapInterval(window->Display, 1) != EGL_TRUE) - { - fprintf(stderr, "EGL context creation failed: 0x%04x\n", eglGetError()); - return false; - } - - const int visible = 1; - if (screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_VISIBLE, &visible) != 0) - { - fprintf(stderr, "Failed to show QNX Screen window: %s\n", strerror(errno)); - return false; - } - return true; -} - -static void DestroyQNXWindow(QNXWindow* window) -{ - if (window->Display != EGL_NO_DISPLAY) - { - eglMakeCurrent(window->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (window->GLSurface != EGL_NO_SURFACE) - eglDestroySurface(window->Display, window->GLSurface); - if (window->GLContext != EGL_NO_CONTEXT) - eglDestroyContext(window->Display, window->GLContext); - eglTerminate(window->Display); - } - if (window->Event != nullptr) - screen_destroy_event(window->Event); - if (window->Window != nullptr) - screen_destroy_window(window->Window); - if (window->Context != nullptr) - screen_destroy_context(window->Context); -} - -static bool ProcessQNXEvents(QNXWindow* window) -{ - for (;;) - { - if (screen_get_event(window->Context, window->Event, 0) != 0) - return false; - - int type = SCREEN_EVENT_NONE; - if (screen_get_event_property_iv(window->Event, SCREEN_PROPERTY_TYPE, &type) != 0) - return false; - if (type == SCREEN_EVENT_NONE) - return true; - - ImGui_ImplQNX_ProcessEvent(window->Event); - if (type == SCREEN_EVENT_CLOSE) - return false; - if (type == SCREEN_EVENT_KEYBOARD) - { - int flags = 0; - int key_cap = 0; - if (screen_get_event_property_iv(window->Event, SCREEN_PROPERTY_FLAGS, &flags) == 0 && - screen_get_event_property_iv(window->Event, SCREEN_PROPERTY_KEY_CAP, &key_cap) == 0 && - (flags & SCREEN_FLAG_KEY_DOWN) != 0 && key_cap == KEYCODE_ESCAPE) - return false; - } - } -} +// Forward declarations of helper functions +bool CreateQNXWindow(QNXWindow* window); +void DestroyQNXWindow(QNXWindow* window); +bool ProcessQNXEvents(QNXWindow* window); // Main code int main(int, char**) @@ -297,3 +170,138 @@ int main(int, char**) return 0; } + +// Helper functions + +bool CreateQNXWindow(QNXWindow* window) +{ + if (screen_create_context(&window->Context, 0) != 0 || + screen_create_window(&window->Window, window->Context) != 0 || + screen_create_event(&window->Event) != 0) + { + fprintf(stderr, "QNX Screen initialization failed: %s\n", strerror(errno)); + return false; + } + + // A newly created QNX Screen window reports the fullscreen dimensions. + if (screen_get_window_property_iv(window->Window, SCREEN_PROPERTY_SIZE, window->Size) != 0 || window->Size[0] <= 0 || window->Size[1] <= 0) + { + fprintf(stderr, "Failed to obtain QNX Screen window size: %s\n", strerror(errno)); + return false; + } + + const int usage = SCREEN_USAGE_OPENGL_ES2 | SCREEN_USAGE_OPENGL_ES3 | SCREEN_USAGE_NATIVE; + const int transparency = SCREEN_TRANSPARENCY_NONE; + const int swap_interval = 1; + if (screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_USAGE, &usage) != 0 || + screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_SIZE, window->Size) != 0 || + screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_TRANSPARENCY, &transparency) != 0 || + screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_SWAP_INTERVAL, &swap_interval) != 0) + { + fprintf(stderr, "Failed to configure QNX Screen window: %s\n", strerror(errno)); + return false; + } + + window->Display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (window->Display == EGL_NO_DISPLAY || eglInitialize(window->Display, nullptr, nullptr) != EGL_TRUE || eglBindAPI(EGL_OPENGL_ES_API) != EGL_TRUE) + { + fprintf(stderr, "EGL initialization failed: 0x%04x\n", eglGetError()); + return false; + } + + const EGLint config_attributes[] = + { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 24, + EGL_STENCIL_SIZE, 8, + EGL_NONE + }; + EGLConfig config = nullptr; + EGLint config_count = 0; + if (eglChooseConfig(window->Display, config_attributes, &config, 1, &config_count) != EGL_TRUE || config_count == 0) + { + fprintf(stderr, "eglChooseConfig() failed: 0x%04x\n", eglGetError()); + return false; + } + + EGLint native_visual = 0; + if (eglGetConfigAttrib(window->Display, config, EGL_NATIVE_VISUAL_ID, &native_visual) != EGL_TRUE || + screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_FORMAT, &native_visual) != 0 || + screen_create_window_buffers(window->Window, 2) != 0) + { + fprintf(stderr, "Failed to create QNX Screen buffers: %s\n", strerror(errno)); + return false; + } + + const EGLint context_attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE }; + window->GLContext = eglCreateContext(window->Display, config, EGL_NO_CONTEXT, context_attributes); + window->GLSurface = eglCreateWindowSurface(window->Display, config, window->Window, nullptr); + if (window->GLContext == EGL_NO_CONTEXT || window->GLSurface == EGL_NO_SURFACE || + eglMakeCurrent(window->Display, window->GLSurface, window->GLSurface, window->GLContext) != EGL_TRUE || + eglSwapInterval(window->Display, 1) != EGL_TRUE) + { + fprintf(stderr, "EGL context creation failed: 0x%04x\n", eglGetError()); + return false; + } + + const int visible = 1; + if (screen_set_window_property_iv(window->Window, SCREEN_PROPERTY_VISIBLE, &visible) != 0) + { + fprintf(stderr, "Failed to show QNX Screen window: %s\n", strerror(errno)); + return false; + } + return true; +} + +void DestroyQNXWindow(QNXWindow* window) +{ + if (window->Display != EGL_NO_DISPLAY) + { + eglMakeCurrent(window->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (window->GLSurface != EGL_NO_SURFACE) + eglDestroySurface(window->Display, window->GLSurface); + if (window->GLContext != EGL_NO_CONTEXT) + eglDestroyContext(window->Display, window->GLContext); + eglTerminate(window->Display); + } + if (window->Event != nullptr) + screen_destroy_event(window->Event); + if (window->Window != nullptr) + screen_destroy_window(window->Window); + if (window->Context != nullptr) + screen_destroy_context(window->Context); +} + +bool ProcessQNXEvents(QNXWindow* window) +{ + for (;;) + { + if (screen_get_event(window->Context, window->Event, 0) != 0) + return false; + + int type = SCREEN_EVENT_NONE; + if (screen_get_event_property_iv(window->Event, SCREEN_PROPERTY_TYPE, &type) != 0) + return false; + if (type == SCREEN_EVENT_NONE) + return true; + + ImGui_ImplQNX_ProcessEvent(window->Event); + if (type == SCREEN_EVENT_CLOSE) + return false; + if (type == SCREEN_EVENT_KEYBOARD) + { + int flags = 0; + int key_cap = 0; + if (screen_get_event_property_iv(window->Event, SCREEN_PROPERTY_FLAGS, &flags) == 0 && + screen_get_event_property_iv(window->Event, SCREEN_PROPERTY_KEY_CAP, &key_cap) == 0 && + (flags & SCREEN_FLAG_KEY_DOWN) != 0 && key_cap == KEYCODE_ESCAPE) + return false; + } + } +} + diff --git a/examples/example_qnx_vulkan/.gitignore b/examples/example_qnx_vulkan/.gitignore deleted file mode 100644 index 12b50871c..000000000 --- a/examples/example_qnx_vulkan/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -# ignore all files -* - -# unignore all dirs -!/**/ - -# unignore all with extensions -!*.* - -# unignore makefiles -!Makefile - -# re-ignore qnx build artefacts -*.o -*.so* -*.a -*.pinfo -*.dep -*.gcno diff --git a/examples/example_qnx_vulkan/example_qnx_vulkan.use b/examples/example_qnx_vulkan/example_qnx_vulkan.use deleted file mode 100644 index 893eb5c41..000000000 --- a/examples/example_qnx_vulkan/example_qnx_vulkan.use +++ /dev/null @@ -1,7 +0,0 @@ -Dear ImGui QNX Screen + Vulkan example - -Usage: example_qnx_vulkan - -The example uses the fullscreen size reported by QNX Screen, creates a VK_QNX_screen_surface -swapchain, displays the Dear ImGui demo UI, and exits when Escape is pressed -or the Screen window receives a close event. From 2de0a07cd4b59416d0318a5952f5f5594955dd10 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 4 Aug 2026 14:33:31 +0200 Subject: [PATCH 30/47] Fixed PVS Studio false positive. --- imgui.cpp | 2 +- imgui_draw.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 2eee9d87f..e8b16a7fa 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8228,7 +8228,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Set default BgClickFlags // This is set at the end of this function, so UpdateWindowManualResize()/ClampWindowPos() may use last-frame value if overridden by user code. // FIXME: The general intent is that we will later expose config options to default to enable scrolling + select scrolling mouse button. - window->BgClickFlags = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->BgClickFlags : (g.IO.ConfigWindowsMoveFromTitleBarOnly ? ImGuiWindowBgClickFlags_None : ImGuiWindowBgClickFlags_Move); + window->BgClickFlags = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->BgClickFlags : (g.IO.ConfigWindowsMoveFromTitleBarOnly ? ImGuiWindowBgClickFlags_None : ImGuiWindowBgClickFlags_Move); // -V1004 // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). // This is useful to allow creating context menus on title bar only, etc. diff --git a/imgui_draw.cpp b/imgui_draw.cpp index a9759c072..35ac29f47 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -722,6 +722,7 @@ void ImDrawList::_SetTexture(ImTextureRef tex_ref) void ImDrawList::_SetPixelDensity(float pixel_density) { + IM_ASSERT(pixel_density > 0.0f); _FringeScale = 1.0f / pixel_density; _InvFringeScale = pixel_density; } From a5a3e1bd0490d854ad1244bdc41152a488eafb4a Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 5 Aug 2026 14:58:55 +0200 Subject: [PATCH 31/47] (Breaking) Backends: DX12: removed support for legacy `ImGui_ImplDX12_Init()` signature obsoleted in 1.91.6. (#9487) Amend 40b2286d1, 142827f7, eefe5d5aa. --- backends/imgui_impl_dx12.cpp | 32 +------------------------------- backends/imgui_impl_dx12.h | 15 ++++++++------- docs/CHANGELOG.txt | 4 ++++ 3 files changed, 13 insertions(+), 38 deletions(-) diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 034882856..04516eefd 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-08-05: DirectX12: *BREAKING CHANGE* Removed support for legacy `ImGui_ImplDX12_Init()` signature obsoleted in 1.91.6 (2024-11-15) because it needed to forcefully disable support for ImGuiBackendFlags_RendererHasTextures. (#9487) // 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) // 2025-10-11: DirectX12: Reuse texture upload buffer and grow it only when necessary. (#9002) // 2025-09-29: DirectX12: Rework synchronization logic. (#8961) @@ -956,37 +957,6 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info) return true; } -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -// Legacy initialization API Obsoleted in 1.91.5 -// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture, they must be in 'srv_descriptor_heap' -bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* srv_descriptor_heap, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle) -{ - ImGui_ImplDX12_InitInfo init_info; - init_info.Device = device; - init_info.NumFramesInFlight = num_frames_in_flight; - init_info.RTVFormat = rtv_format; - init_info.SrvDescriptorHeap = srv_descriptor_heap; - init_info.LegacySingleSrvCpuDescriptor = font_srv_cpu_desc_handle; - init_info.LegacySingleSrvGpuDescriptor = font_srv_gpu_desc_handle; - - D3D12_COMMAND_QUEUE_DESC queueDesc = {}; - queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; - queueDesc.NodeMask = 1; - HRESULT hr = device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&init_info.CommandQueue)); - IM_ASSERT(SUCCEEDED(hr)); - - bool ret = ImGui_ImplDX12_Init(&init_info); - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - bd->commandQueueOwned = true; - - ImGuiIO& io = ImGui::GetIO(); - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasTextures; // Using legacy ImGui_ImplDX12_Init() call with 1 SRV descriptor we cannot support multiple textures. - - return ret; -} -#endif - void ImGui_ImplDX12_Shutdown() { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); diff --git a/backends/imgui_impl_dx12.h b/backends/imgui_impl_dx12.h index b8be8db22..dfb327fe4 100644 --- a/backends/imgui_impl_dx12.h +++ b/backends/imgui_impl_dx12.h @@ -59,13 +59,6 @@ IMGUI_IMPL_API void ImGui_ImplDX12_Shutdown(); IMGUI_IMPL_API void ImGui_ImplDX12_NewFrame(); IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* graphics_command_list); -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -// Legacy initialization API Obsoleted in 1.91.5 -// - font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture, they must be in 'srv_descriptor_heap' -// - When we introduced the ImGui_ImplDX12_InitInfo struct we also added a 'ID3D12CommandQueue* CommandQueue' field. -IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* srv_descriptor_heap, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle); -#endif - // Use if you want to reset your rendering device without losing Dear ImGui state. IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects(); IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects(); @@ -82,6 +75,14 @@ struct ImGui_ImplDX12_RenderState ID3D12GraphicsCommandList* CommandList; }; +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +// Legacy initialization API Obsoleted in 1.91.5 +// - font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture, they must be in 'srv_descriptor_heap' +// - When we introduced the ImGui_ImplDX12_InitInfo struct we also added a 'ID3D12CommandQueue* CommandQueue' field. +// - DOES NOT SUPPORT ImGuiBackendFlags_RendererHasTextures. +//IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* srv_descriptor_heap, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle); +#endif + #if defined(__clang__) #pragma clang diagnostic pop #endif diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3197f28e6..a4ed837f5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -45,6 +45,10 @@ Breaking Changes: favor of `style.CurveTesselationMaxError` (default 1.12)` which is in Pixels unit. It is easier to tweak + this allows us to easily scale error threshold on high density screens. - style.CurveTesselationMaxError == sqrf(style.CurveTessellationTol). +- Backends: + - DirectX12: Removed support for legacy `ImGui_ImplDX12_Init()` signature, which was + obsoleted in 1.91.6 (December 2014), because it needed to forcefully disable support + for `ImGuiBackendFlags_RendererHasTextures`. Use the new signature! (#9487) Other Changes: From 6a6d7bc3ca5f6a88517177988f4b8b8caf3b3822 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 5 Aug 2026 20:38:18 +0200 Subject: [PATCH 32/47] Internals: added SetNextWindowFlags(), SetNextWindowChildFlags() helpers + made BeginChildEx() itself read from them. (#9503, #8280, #9412, #9499, #9393, #8488) As a result BeginTable() doesn't need to read itself and pass that to BeginChildEx(). --- imgui.cpp | 44 +++++++++++++++++++++++++++++--------------- imgui.h | 2 +- imgui_internal.h | 8 ++++++-- imgui_tables.cpp | 7 ++----- 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e8b16a7fa..d157353cf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6537,6 +6537,9 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I ImGuiWindow* parent_window = g.CurrentWindow; IM_ASSERT(id != 0); + if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) + child_flags = (child_flags | g.NextWindowData.ChildFlagsSet) & ~g.NextWindowData.ChildFlagsClear; + // Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument. const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Borders | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle | ImGuiChildFlags_NavFlattened; IM_UNUSED(ImGuiChildFlags_SupportedMask_); @@ -6599,13 +6602,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I } } SetNextWindowSize(size); - - // Forward child flags (we allow prior settings to merge but it'll only work for adding flags) - if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) - g.NextWindowData.ChildFlags |= child_flags; - else - g.NextWindowData.ChildFlags = child_flags; - g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasChildFlags; + SetNextWindowChildFlags(child_flags, true); // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value. // FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround. @@ -7550,6 +7547,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (g.DebugBreakInWindow == window->ID) IM_DEBUG_BREAK(); + if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasWindowFlags) + flags = (flags | g.NextWindowData.WindowFlagsSet) & ~g.NextWindowData.WindowFlagsClear; + // Automatically disable manual moving/resizing when NoInputs is set if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; @@ -7575,7 +7575,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { UpdateWindowInFocusOrderList(window, window_just_created, flags); window->Flags = (ImGuiWindowFlags)flags; - window->ChildFlags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0; + window->ChildFlags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlagsSet : 0; window->LastFrameActive = current_frame; window->LastTimeActive = (float)g.Time; window->BeginOrderWithinParent = 0; @@ -8834,6 +8834,26 @@ void ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags) g.NextWindowData.RefreshFlagsVal = flags; } +void ImGui::SetNextWindowFlags(ImGuiWindowFlags flags, bool enabled) +{ + ImGuiContext& g = *GImGui; + if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasWindowFlags) == 0) + g.NextWindowData.WindowFlagsSet = g.NextWindowData.WindowFlagsClear = ImGuiChildFlags_None; + g.NextWindowData.WindowFlagsSet = enabled ? (g.NextWindowData.WindowFlagsSet | flags) : (g.NextWindowData.WindowFlagsSet & ~flags); + g.NextWindowData.WindowFlagsClear = !enabled ? (g.NextWindowData.WindowFlagsClear | flags) : (g.NextWindowData.WindowFlagsClear & ~flags); + g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasWindowFlags; +} + +void ImGui::SetNextWindowChildFlags(ImGuiChildFlags flags, bool enabled) +{ + ImGuiContext& g = *GImGui; + if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) == 0) + g.NextWindowData.ChildFlagsSet = g.NextWindowData.ChildFlagsClear = ImGuiChildFlags_None; + g.NextWindowData.ChildFlagsSet = enabled ? (g.NextWindowData.ChildFlagsSet | flags) : (g.NextWindowData.ChildFlagsSet & ~flags); + g.NextWindowData.ChildFlagsClear = !enabled ? (g.NextWindowData.ChildFlagsClear | flags) : (g.NextWindowData.ChildFlagsClear & ~flags); + g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasChildFlags; +} + ImDrawList* ImGui::GetWindowDrawList() { ImGuiWindow* window = GetCurrentWindow(); @@ -12674,13 +12694,7 @@ bool ImGui::BeginPopupMenuEx(ImGuiID id, const char* label, ImGuiWindowFlags ext // As we bypass BeginChild(), set ImGuiChildFlags_AlwaysAutoResize as it is checked independently from ImGuiWindowFlags_AlwaysAutoResize for now (see #9355) // Ideally we should remove setting ImGuiWindowFlags_AlwaysAutoResize in BeginChild(). if ((extra_window_flags & ImGuiWindowFlags_ChildWindow) && (extra_window_flags & ImGuiWindowFlags_AlwaysAutoResize)) - { - if (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) - g.NextWindowData.ChildFlags |= ImGuiChildFlags_AlwaysAutoResize; - else - g.NextWindowData.ChildFlags = ImGuiChildFlags_AlwaysAutoResize; - g.NextWindowData.HasFlags |= ImGuiNextWindowDataFlags_HasChildFlags; - } + SetNextWindowChildFlags(ImGuiChildFlags_AlwaysAutoResize, true); char name[128]; IM_ASSERT(extra_window_flags & ImGuiWindowFlags_ChildMenu); diff --git a/imgui.h b/imgui.h index fea82515d..43f297d44 100644 --- a/imgui.h +++ b/imgui.h @@ -30,7 +30,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.93.0 WIP" -#define IMGUI_VERSION_NUM 19292 +#define IMGUI_VERSION_NUM 19293 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 diff --git a/imgui_internal.h b/imgui_internal.h index 59f005508..d837a6962 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1360,8 +1360,10 @@ struct ImGuiNextWindowData ImVec2 SizeVal; ImVec2 ContentSizeVal; ImVec2 ScrollVal; - ImGuiWindowFlags WindowFlags; // Only honored by BeginTable() - ImGuiChildFlags ChildFlags; + ImGuiWindowFlags WindowFlagsSet; // Flags to set. Use SetNextWindowFlags()! + ImGuiWindowFlags WindowFlagsClear; + ImGuiChildFlags ChildFlagsSet; // Flags to set. Use SetNextWindowChildFlags(). + ImGuiChildFlags ChildFlagsClear; bool CollapsedVal; ImRect SizeConstraintRect; ImGuiSizeCallback SizeCallback; @@ -3368,6 +3370,8 @@ namespace ImGui inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } inline ImVec2 WindowPosAbsToRel(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x - off.x, p.y - off.y); } inline ImVec2 WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); } + IMGUI_API void SetNextWindowFlags(ImGuiWindowFlags flags, bool enabled); + IMGUI_API void SetNextWindowChildFlags(ImGuiChildFlags flags, bool enabled); // Windows: Display Order and Focus Order IMGUI_API void FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags = 0); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 19f7933ed..8f260c873 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -428,11 +428,8 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG SetNextWindowScroll(ImVec2(0.0f, 0.0f)); // Create scrolling region (without border and zero window padding) - ImGuiChildFlags child_child_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : ImGuiChildFlags_None; - ImGuiWindowFlags child_window_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasWindowFlags) ? g.NextWindowData.WindowFlags : ImGuiWindowFlags_None; - if (flags & ImGuiTableFlags_ScrollX) - child_window_flags |= ImGuiWindowFlags_HorizontalScrollbar; - BeginChildEx(name, instance_id, outer_rect.GetSize(), child_child_flags, child_window_flags); + ImGuiWindowFlags child_window_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None; + BeginChildEx(name, instance_id, outer_rect.GetSize(), ImGuiChildFlags_None, child_window_flags); table->InnerWindow = g.CurrentWindow; table->WorkRect = table->InnerWindow->WorkRect; table->OuterRect = table->InnerWindow->Rect(); From 724c69b182f1804dfeb651d2d176b284d77067fb Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 6 Aug 2026 18:28:23 +0200 Subject: [PATCH 33/47] Backends: SDL2: fixed querying framebuffer scale/density when using Metal. (#5592) --- backends/imgui_impl_sdl2.cpp | 13 +++++++++++++ docs/CHANGELOG.txt | 1 + examples/example_sdl2_metal/main.mm | 8 +++++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index eec4e0075..a93bcc29e 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -21,6 +21,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-08-06: Fixed querying framebuffer scale when using Metal. (#5592) // 2026-07-15: Inputs: restore SDL_StartTextInput()/SDL_StopTextInput() in IME handler for on-screen keyboard support on Android/mobile. (#7636, #9474) // 2026-04-16: Made ImGui_ImplSDL2_GetContentScaleForWindow(), ImGui_ImplSDL2_GetContentScaleForDisplay() helpers return a minimum of 1.0f, as some Linux setup seems to report <1.0f value and this breaks scaling border size. (#9369) // 2026-02-13: Inputs: systems other than X11 are back to starting mouse capture on mouse down (reverts 2025-02-26 change). Only X11 requires waiting for a drag by default (not ideal, but a better default for X11 users). Added ImGui_ImplSDL2_SetMouseCaptureMode() for X11 debugger users. (#3650, #6410, #9235) @@ -135,10 +136,18 @@ #endif #define SDL_HAS_PER_MONITOR_DPI SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) +#if defined(__APPLE__) +#define SDL_HAS_METAL SDL_VERSION_ATLEAST(2,0,14) +#else +#define SDL_HAS_METAL 0 +#endif #define SDL_HAS_OPEN_URL SDL_VERSION_ATLEAST(2,0,14) #if SDL_HAS_VULKAN #include #endif +#if SDL_HAS_METAL +#include +#endif // SDL Data struct ImGui_ImplSDL2_Data @@ -899,6 +908,10 @@ static void ImGui_ImplSDL2_GetWindowSizeAndFramebufferScale(SDL_Window* window, #if SDL_HAS_VULKAN else if (SDL_GetWindowFlags(window) & SDL_WINDOW_VULKAN) SDL_Vulkan_GetDrawableSize(window, &display_w, &display_h); +#endif +#if SDL_HAS_METAL + else if (SDL_GetWindowFlags(window) & SDL_WINDOW_METAL) + SDL_Metal_GetDrawableSize(window, &display_w, &display_h); #endif else SDL_GL_GetDrawableSize(window, &display_w, &display_h); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index a4ed837f5..994b0c4a2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -62,6 +62,7 @@ Other Changes: - Circle and Curves tessellation error are scaled accordingly. - Backends: - QNX: added QNX Screen backend. (#9492) [@mgorchak-blackberry] + - SDL2: fixed querying framebuffer scale/density when using Metal. (#5592) [@lailoken] - Examples: - QNX+OpenGL3, QNX+Vulkan: added QNX Screen examples. (#9492) [@mgorchak-blackberry] diff --git a/examples/example_sdl2_metal/main.mm b/examples/example_sdl2_metal/main.mm index ea2afdab3..22d0e0fc8 100644 --- a/examples/example_sdl2_metal/main.mm +++ b/examples/example_sdl2_metal/main.mm @@ -56,13 +56,15 @@ int main(int, char**) return 1; } - // Inform SDL that we will be using metal for rendering. Without this hint initialization of metal renderer may fail. + // Inform SDL that we will be using metal for rendering. Without this hint initialization of Metal renderer may fail. SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); - // Enable native IME. + // From 2.0.18: Enable native IME. +#ifdef SDL_HINT_IME_SHOW_UI SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); +#endif - SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL+Metal example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 800, SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+Metal example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 800, SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); if (window == nullptr) { printf("Error creating window: %s\n", SDL_GetError()); From 730f099239a15e48caf3efedb3070d0e001c9946 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 6 Aug 2026 19:01:49 +0200 Subject: [PATCH 34/47] Backends: OpenGL3: fixed unused variable warning. (#9507) --- backends/imgui_impl_opengl3.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 7be882e06..e917fd36b 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -385,6 +385,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, ImGui_Impl glUniform1i(bd->AttribLocationTex, 0); glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); + (void)render_state; #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER if (render_state->UseBindSampler) { From 98f3efcf07929569ee73e142c4ac32e77614fa17 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 6 Aug 2026 21:17:55 +0200 Subject: [PATCH 35/47] Examples: SDL2+Metal: fixed mismatches with other examples. --- examples/example_sdl2_metal/main.mm | 102 +++++++++++++++------------- 1 file changed, 56 insertions(+), 46 deletions(-) diff --git a/examples/example_sdl2_metal/main.mm b/examples/example_sdl2_metal/main.mm index 22d0e0fc8..bae566daf 100644 --- a/examples/example_sdl2_metal/main.mm +++ b/examples/example_sdl2_metal/main.mm @@ -10,25 +10,74 @@ #include "imgui.h" #include "imgui_impl_sdl2.h" #include "imgui_impl_metal.h" -#include +#include // printf, fprintf #include #import #import +// Main code int main(int, char**) { + // Setup SDL + // (Some versions of SDL before <2.0.10 appears to have performance/stalling issues on a minority of Windows systems, + // depending on whether SDL_INIT_GAMECONTROLLER is enabled or disabled.. updating to latest version of SDL is recommended!) + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) + { + printf("Error: SDL_Init(): %s\n", SDL_GetError()); + return 1; + } + + // Inform SDL that we will be using metal for rendering. Without this hint initialization of Metal renderer may fail. + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); + + // From 2.0.18: Enable native IME. +#ifdef SDL_HINT_IME_SHOW_UI + SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); +#endif + + // Create SDL window graphics context + float main_scale = ImGui_ImplSDL2_GetContentScaleForDisplay(0); + int window_flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; + SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+Metal example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, (int)(1280 * main_scale), (int)(800 * main_scale), window_flags); + if (window == nullptr) + { + printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError()); + return 1; + } + + SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + if (renderer == nullptr) + { + printf("Error creating renderer: %s\n", SDL_GetError()); + return -3; + } + CAMetalLayer* layer = (__bridge CAMetalLayer*)SDL_RenderGetMetalLayer(renderer); + layer.pixelFormat = MTLPixelFormatBGRA8Unorm; + + id commandQueue = [layer.device newCommandQueue]; + MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor new]; + // Setup Dear ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls - // Setup style + // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // Setup scaling + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) + style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) + + // Setup Platform/Renderer backends + ImGui_ImplMetal_Init(layer.device); + ImGui_ImplSDL2_InitForMetal(window); + // Load Fonts // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. @@ -47,50 +96,10 @@ int main(int, char**) //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); - // Setup SDL - // (Some versions of SDL before <2.0.10 appears to have performance/stalling issues on a minority of Windows systems, - // depending on whether SDL_INIT_GAMECONTROLLER is enabled or disabled.. updating to latest version of SDL is recommended!) - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) - { - printf("Error: %s\n", SDL_GetError()); - return 1; - } - - // Inform SDL that we will be using metal for rendering. Without this hint initialization of Metal renderer may fail. - SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); - - // From 2.0.18: Enable native IME. -#ifdef SDL_HINT_IME_SHOW_UI - SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); -#endif - - SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+Metal example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 800, SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - if (window == nullptr) - { - printf("Error creating window: %s\n", SDL_GetError()); - return -2; - } - - SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (renderer == nullptr) - { - printf("Error creating renderer: %s\n", SDL_GetError()); - return -3; - } - - // Setup Platform/Renderer backends - CAMetalLayer* layer = (__bridge CAMetalLayer*)SDL_RenderGetMetalLayer(renderer); - layer.pixelFormat = MTLPixelFormatBGRA8Unorm; - ImGui_ImplMetal_Init(layer.device); - ImGui_ImplSDL2_InitForMetal(window); - - id commandQueue = [layer.device newCommandQueue]; - MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor new]; - // Our state bool show_demo_window = true; bool show_another_window = false; - float clear_color[4] = {0.45f, 0.55f, 0.60f, 1.00f}; + float clear_color[4] = { 0.45f, 0.55f, 0.60f, 1.00f }; // Main loop bool done = false; @@ -170,7 +179,8 @@ int main(int, char**) // Rendering ImGui::Render(); - ImGui_ImplMetal_RenderDrawData(ImGui::GetDrawData(), commandBuffer, renderEncoder); + ImDrawData* draw_data = ImGui::GetDrawData(); + ImGui_ImplMetal_RenderDrawData(draw_data, commandBuffer, renderEncoder); [renderEncoder popDebugGroup]; [renderEncoder endEncoding]; From 2ddb5fc0956e6376ccdbb0a522a5a3f0d8b42696 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 6 Aug 2026 21:23:28 +0200 Subject: [PATCH 36/47] Examples: SDL2+Metal: stop using SDL_CreateRenderer(). (#5592) --- docs/CHANGELOG.txt | 4 +++- examples/example_sdl2_metal/main.mm | 22 ++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 994b0c4a2..2101df990 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -62,8 +62,10 @@ Other Changes: - Circle and Curves tessellation error are scaled accordingly. - Backends: - QNX: added QNX Screen backend. (#9492) [@mgorchak-blackberry] - - SDL2: fixed querying framebuffer scale/density when using Metal. (#5592) [@lailoken] + - SDL2: fixed querying framebuffer scale/density when using Metal without + going through a SDL_Renderer. (#5592) [@lailoken] - Examples: + - SDL2+Metal: create Metal context without using a SDL_Renderer. - QNX+OpenGL3, QNX+Vulkan: added QNX Screen examples. (#9492) [@mgorchak-blackberry] diff --git a/examples/example_sdl2_metal/main.mm b/examples/example_sdl2_metal/main.mm index bae566daf..edd436aca 100644 --- a/examples/example_sdl2_metal/main.mm +++ b/examples/example_sdl2_metal/main.mm @@ -28,9 +28,6 @@ int main(int, char**) return 1; } - // Inform SDL that we will be using metal for rendering. Without this hint initialization of Metal renderer may fail. - SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); - // From 2.0.18: Enable native IME. #ifdef SDL_HINT_IME_SHOW_UI SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); @@ -46,13 +43,18 @@ int main(int, char**) return 1; } - SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (renderer == nullptr) + // Create Metal device _before_ creating the view/layer + id metalDevice = MTLCreateSystemDefaultDevice(); + if (!metalDevice) { - printf("Error creating renderer: %s\n", SDL_GetError()); - return -3; + printf("Error: failed to create Metal device.\n"); + SDL_DestroyWindow(window); + SDL_Quit(); + return 1; } - CAMetalLayer* layer = (__bridge CAMetalLayer*)SDL_RenderGetMetalLayer(renderer); + SDL_MetalView view = SDL_Metal_CreateView(window); + CAMetalLayer* layer = (__bridge CAMetalLayer*)SDL_Metal_GetLayer(view); + layer.device = metalDevice; layer.pixelFormat = MTLPixelFormatBGRA8Unorm; id commandQueue = [layer.device newCommandQueue]; @@ -123,7 +125,8 @@ int main(int, char**) } int width, height; - SDL_GetRendererOutputSize(renderer, &width, &height); + SDL_GetWindowSizeInPixels(window, &width, &height); + layer.drawableSize = CGSizeMake(width, height); id drawable = [layer nextDrawable]; @@ -195,7 +198,6 @@ int main(int, char**) ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); - SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); From 70e60a3c4c1698e699cebbd1bb92404b21233cd3 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 6 Aug 2026 23:01:56 +0200 Subject: [PATCH 37/47] Examples: Apple+OpenGL2: silence warnings. Example is precisely to demonstrate OpenGL. --- examples/example_apple_opengl2/main.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/example_apple_opengl2/main.mm b/examples/example_apple_opengl2/main.mm index 1cfa882c8..65c169089 100644 --- a/examples/example_apple_opengl2/main.mm +++ b/examples/example_apple_opengl2/main.mm @@ -6,6 +6,7 @@ // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). // - Introduction, links and more at the top of imgui.cpp +#define GL_SILENCE_DEPRECATION #import #import #import @@ -32,7 +33,7 @@ #ifndef DEBUG GLint swapInterval = 1; - [[self openGLContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; + [[self openGLContext] setValues:&swapInterval forParameter:NSOpenGLContextParameterSwapInterval]; if (swapInterval == 0) NSLog(@"Error: Cannot set swap interval."); #endif From 5b6e19bb50359f7b0c62d2081f2ea024bd9fd5a1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 6 Aug 2026 23:10:56 +0200 Subject: [PATCH 38/47] Examples: Apple+OpenGL3: added new example. --- .github/workflows/build.yml | 4 + .gitignore | 1 + docs/CHANGELOG.txt | 1 + docs/EXAMPLES.md | 6 +- examples/example_apple_opengl3/Makefile | 21 ++ .../project.pbxproj | 334 ++++++++++++++++++ examples/example_apple_opengl3/main.mm | 260 ++++++++++++++ 7 files changed, 625 insertions(+), 2 deletions(-) create mode 100644 examples/example_apple_opengl3/Makefile create mode 100644 examples/example_apple_opengl3/example_apple_opengl3.xcodeproj/project.pbxproj create mode 100644 examples/example_apple_opengl3/main.mm diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5bbaefd1a..4e276e1de 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -601,6 +601,10 @@ jobs: run: xcodebuild -project examples/example_apple_opengl2/example_apple_opengl2.xcodeproj -target example_osx_opengl2 if: github.event_name == 'workflow_run' + - name: Build macOS example_apple_opengl3 + run: xcodebuild -project examples/example_apple_opengl3/example_apple_opengl3.xcodeproj -target example_osx_opengl3 + if: github.event_name == 'workflow_run' + Build-iOS: runs-on: macos-14 name: Build - iOS diff --git a/.gitignore b/.gitignore index 250e7eed5..009e61ede 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,7 @@ cmake-build-* ## Unix executables from our example Makefiles examples/example_apple_metal/example_apple_metal examples/example_apple_opengl2/example_apple_opengl2 +examples/example_apple_opengl3/example_apple_opengl3 examples/example_glfw_metal/example_glfw_metal examples/example_glfw_opengl2/example_glfw_opengl2 examples/example_glfw_opengl3/example_glfw_opengl3 diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2101df990..716cd3fc7 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -67,6 +67,7 @@ Other Changes: - Examples: - SDL2+Metal: create Metal context without using a SDL_Renderer. - QNX+OpenGL3, QNX+Vulkan: added QNX Screen examples. (#9492) [@mgorchak-blackberry] + - OSX+OpenGL3: added example_apple_opengl3/ (OSX/Cocoa + OpenGL 3.2 Core profile). ----------------------------------------------------------------------- diff --git a/docs/EXAMPLES.md b/docs/EXAMPLES.md index 89e9bd0df..231fe7f93 100644 --- a/docs/EXAMPLES.md +++ b/docs/EXAMPLES.md @@ -70,8 +70,10 @@ OSX & iOS + Metal4 example, Mac only.
[example_apple_opengl2/](https://github.com/ocornut/imgui/blob/master/examples/example_apple_opengl2/)
OSX + OpenGL2 example.
= main.mm + imgui_impl_osx.mm + imgui_impl_opengl2.cpp
-(NB: imgui_impl_osx.mm is currently not as feature complete as other platforms backends. - You may prefer to use the GLFW Or SDL backends, which will also support Windows and Linux.) + +[example_apple_opengl3/](https://github.com/ocornut/imgui/blob/master/examples/example_apple_opengl3/)
+OSX + OpenGL3 example.
+= main.mm + imgui_impl_osx.mm + imgui_impl_opengl3.cpp
[example_glfw_wgpu/](https://github.com/ocornut/imgui/blob/master/examples/example_glfw_wgpu/)
GLFW + WebGPU example. Supports Emscripten (web), Dawn (native), WGPU (native).
diff --git a/examples/example_apple_opengl3/Makefile b/examples/example_apple_opengl3/Makefile new file mode 100644 index 000000000..c9f9d44d3 --- /dev/null +++ b/examples/example_apple_opengl3/Makefile @@ -0,0 +1,21 @@ +# Makefile for example_apple_opengl3, for macOS only (**not iOS**) +CXX = clang++ +EXE = example_apple_opengl3 +IMGUI_DIR = ../../ +SOURCES = main.mm +SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp +SOURCES += $(IMGUI_DIR)/backends/imgui_impl_osx.mm $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp + +CXXFLAGS = -std=c++11 -ObjC++ -fobjc-arc -Wall -Wextra -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends +FRAMEWORKS = -framework Cocoa -framework OpenGL -framework GameController + +all: $(EXE) + +$(EXE): $(SOURCES) + $(CXX) $(CXXFLAGS) $(SOURCES) -o $(EXE) $(FRAMEWORKS) + +run: all + ./$(EXE) + +clean: + rm -f $(EXE) *.o diff --git a/examples/example_apple_opengl3/example_apple_opengl3.xcodeproj/project.pbxproj b/examples/example_apple_opengl3/example_apple_opengl3.xcodeproj/project.pbxproj new file mode 100644 index 000000000..50ee35fd9 --- /dev/null +++ b/examples/example_apple_opengl3/example_apple_opengl3.xcodeproj/project.pbxproj @@ -0,0 +1,334 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 48; + objects = { + +/* Begin PBXBuildFile section */ + 05E31B59274EF0700083FCB6 /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05E31B57274EF0360083FCB6 /* GameController.framework */; }; + 07A82EDB213941D00078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82EDA213941D00078D120 /* imgui_widgets.cpp */; }; + 4080A99820B02D340036BA46 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4080A98A20B02CD90036BA46 /* main.mm */; }; + 4080A9A220B034280036BA46 /* imgui_impl_opengl3.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4080A99E20B034280036BA46 /* imgui_impl_opengl3.cpp */; }; + 4080A9AD20B0343C0036BA46 /* imgui_demo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4080A9A620B0343C0036BA46 /* imgui_demo.cpp */; }; + 4080A9AE20B0343C0036BA46 /* imgui.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4080A9A720B0343C0036BA46 /* imgui.cpp */; }; + 4080A9AF20B0343C0036BA46 /* imgui_draw.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4080A9AA20B0343C0036BA46 /* imgui_draw.cpp */; }; + 4080A9B020B0347A0036BA46 /* imgui_impl_osx.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4080A99F20B034280036BA46 /* imgui_impl_osx.mm */; }; + 4080A9B320B034E40036BA46 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4080A9B220B034E40036BA46 /* Cocoa.framework */; }; + 4080A9B520B034EA0036BA46 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4080A9B420B034EA0036BA46 /* OpenGL.framework */; }; + 50798230257677FD0038A28D /* imgui_tables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5079822F257677FC0038A28D /* imgui_tables.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 4080A96920B029B00036BA46 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 05E31B57274EF0360083FCB6 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; }; + 07A82EDA213941D00078D120 /* imgui_widgets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_widgets.cpp; path = ../../imgui_widgets.cpp; sourceTree = ""; }; + 4080A96B20B029B00036BA46 /* example_osx_opengl3 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = example_osx_opengl3; sourceTree = BUILT_PRODUCTS_DIR; }; + 4080A98A20B02CD90036BA46 /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = SOURCE_ROOT; }; + 4080A99E20B034280036BA46 /* imgui_impl_opengl3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_impl_opengl3.cpp; path = ../../backends/imgui_impl_opengl3.cpp; sourceTree = ""; }; + 4080A99F20B034280036BA46 /* imgui_impl_osx.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = imgui_impl_osx.mm; path = ../../backends/imgui_impl_osx.mm; sourceTree = ""; }; + 4080A9A020B034280036BA46 /* imgui_impl_opengl3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui_impl_opengl3.h; path = ../../backends/imgui_impl_opengl3.h; sourceTree = ""; }; + 4080A9A120B034280036BA46 /* imgui_impl_osx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui_impl_osx.h; path = ../../backends/imgui_impl_osx.h; sourceTree = ""; }; + 4080A9A320B034280036BA46 /* imgui_impl_opengl3_loader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui_impl_opengl3_loader.h; path = ../../backends/imgui_impl_opengl3_loader.h; sourceTree = ""; }; + 4080A9A520B0343C0036BA46 /* imgui_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui_internal.h; path = ../../imgui_internal.h; sourceTree = ""; }; + 4080A9A620B0343C0036BA46 /* imgui_demo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_demo.cpp; path = ../../imgui_demo.cpp; sourceTree = ""; }; + 4080A9A720B0343C0036BA46 /* imgui.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui.cpp; path = ../../imgui.cpp; sourceTree = ""; }; + 4080A9A820B0343C0036BA46 /* imgui.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui.h; path = ../../imgui.h; sourceTree = ""; }; + 4080A9AA20B0343C0036BA46 /* imgui_draw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_draw.cpp; path = ../../imgui_draw.cpp; sourceTree = ""; }; + 4080A9AC20B0343C0036BA46 /* imconfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imconfig.h; path = ../../imconfig.h; sourceTree = ""; }; + 4080A9B220B034E40036BA46 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 4080A9B420B034EA0036BA46 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; + 5079822F257677FC0038A28D /* imgui_tables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_tables.cpp; path = ../../imgui_tables.cpp; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4080A96820B029B00036BA46 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4080A9B520B034EA0036BA46 /* OpenGL.framework in Frameworks */, + 4080A9B320B034E40036BA46 /* Cocoa.framework in Frameworks */, + 05E31B59274EF0700083FCB6 /* GameController.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4080A96220B029B00036BA46 = { + isa = PBXGroup; + children = ( + 5079822F257677FC0038A28D /* imgui_tables.cpp */, + 4080A9AC20B0343C0036BA46 /* imconfig.h */, + 4080A9A720B0343C0036BA46 /* imgui.cpp */, + 4080A9A820B0343C0036BA46 /* imgui.h */, + 07A82EDA213941D00078D120 /* imgui_widgets.cpp */, + 4080A9A620B0343C0036BA46 /* imgui_demo.cpp */, + 4080A9AA20B0343C0036BA46 /* imgui_draw.cpp */, + 4080A9A520B0343C0036BA46 /* imgui_internal.h */, + 4080A99E20B034280036BA46 /* imgui_impl_opengl3.cpp */, + 4080A9A020B034280036BA46 /* imgui_impl_opengl3.h */, + 4080A9A320B034280036BA46 /* imgui_impl_opengl3_loader.h */, + 4080A9A120B034280036BA46 /* imgui_impl_osx.h */, + 4080A99F20B034280036BA46 /* imgui_impl_osx.mm */, + 4080A98A20B02CD90036BA46 /* main.mm */, + 4080A96C20B029B00036BA46 /* Products */, + 4080A9B120B034E40036BA46 /* Frameworks */, + ); + sourceTree = ""; + }; + 4080A96C20B029B00036BA46 /* Products */ = { + isa = PBXGroup; + children = ( + 4080A96B20B029B00036BA46 /* example_osx_opengl3 */, + ); + name = Products; + sourceTree = ""; + }; + 4080A9B120B034E40036BA46 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 05E31B57274EF0360083FCB6 /* GameController.framework */, + 4080A9B420B034EA0036BA46 /* OpenGL.framework */, + 4080A9B220B034E40036BA46 /* Cocoa.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4080A96A20B029B00036BA46 /* example_osx_opengl3 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4080A97220B029B00036BA46 /* Build configuration list for PBXNativeTarget "example_osx_opengl3" */; + buildPhases = ( + 4080A96720B029B00036BA46 /* Sources */, + 4080A96820B029B00036BA46 /* Frameworks */, + 4080A96920B029B00036BA46 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = example_osx_opengl3; + productName = example_osx_opengl3; + productReference = 4080A96B20B029B00036BA46 /* example_osx_opengl3 */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4080A96320B029B00036BA46 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0930; + ORGANIZATIONNAME = ImGui; + TargetAttributes = { + 4080A96A20B029B00036BA46 = { + CreatedOnToolsVersion = 9.3.1; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 4080A96620B029B00036BA46 /* Build configuration list for PBXProject "example_apple_opengl3" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 4080A96220B029B00036BA46; + productRefGroup = 4080A96C20B029B00036BA46 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4080A96A20B029B00036BA46 /* example_osx_opengl3 */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 4080A96720B029B00036BA46 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4080A99820B02D340036BA46 /* main.mm in Sources */, + 4080A9AD20B0343C0036BA46 /* imgui_demo.cpp in Sources */, + 4080A9AF20B0343C0036BA46 /* imgui_draw.cpp in Sources */, + 4080A9A220B034280036BA46 /* imgui_impl_opengl3.cpp in Sources */, + 4080A9B020B0347A0036BA46 /* imgui_impl_osx.mm in Sources */, + 4080A9AE20B0343C0036BA46 /* imgui.cpp in Sources */, + 50798230257677FD0038A28D /* imgui_tables.cpp in Sources */, + 07A82EDB213941D00078D120 /* imgui_widgets.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 4080A97020B029B00036BA46 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 4080A97120B029B00036BA46 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 4080A97320B029B00036BA46 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + MACOSX_DEPLOYMENT_TARGET = 10.13; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = ../..; + }; + name = Debug; + }; + 4080A97420B029B00036BA46 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + MACOSX_DEPLOYMENT_TARGET = 10.13; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = ../..; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4080A96620B029B00036BA46 /* Build configuration list for PBXProject "example_apple_opengl3" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4080A97020B029B00036BA46 /* Debug */, + 4080A97120B029B00036BA46 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4080A97220B029B00036BA46 /* Build configuration list for PBXNativeTarget "example_osx_opengl3" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4080A97320B029B00036BA46 /* Debug */, + 4080A97420B029B00036BA46 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4080A96320B029B00036BA46 /* Project object */; +} diff --git a/examples/example_apple_opengl3/main.mm b/examples/example_apple_opengl3/main.mm new file mode 100644 index 000000000..1541d5d07 --- /dev/null +++ b/examples/example_apple_opengl3/main.mm @@ -0,0 +1,260 @@ +// Dear ImGui: standalone example application for OSX + OpenGL3 + +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +#define GL_SILENCE_DEPRECATION +#import +#import + +#include "imgui.h" +#include "imgui_impl_opengl3.h" +#include "imgui_impl_osx.h" + +//----------------------------------------------------------------------------------- +// AppView +//----------------------------------------------------------------------------------- + +@interface AppView : NSOpenGLView +{ + NSTimer* animationTimer; +} +@end + +@implementation AppView + +-(void)prepareOpenGL +{ + [super prepareOpenGL]; + +#ifndef DEBUG + GLint swapInterval = 1; + [[self openGLContext] setValues:&swapInterval forParameter:NSOpenGLContextParameterSwapInterval]; + if (swapInterval == 0) + NSLog(@"Error: Cannot set swap interval."); +#endif +} + +-(void)initialize +{ + // Setup Dear ImGui context + // FIXME: This example doesn't have proper cleanup... + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + + // Setup Platform/Renderer backends + [[self openGLContext] makeCurrentContext]; // ImGui_ImplOpenGL3_Init() requires a current GL context. + ImGui_ImplOSX_Init(self); + ImGui_ImplOpenGL3_Init(); + + // Load Fonts + // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). + // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. + // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. + // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). + // - Read 'docs/FONTS.md' for more instructions and details. + // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. + // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! + //style.FontSizeBase = 20.0f; + //io.Fonts->AddFontDefaultVector(); + //io.Fonts->AddFontDefaultBitmap(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); + //IM_ASSERT(font != nullptr); +} + +-(void)updateAndDrawDemoView +{ + // Start the Dear ImGui frame + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplOSX_NewFrame(self); + ImGui::NewFrame(); + + // Our state (make them static = more or less global) as a convenience to keep the example terse. + static bool show_demo_window = true; + static bool show_another_window = false; + static ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). + if (show_demo_window) + ImGui::ShowDemoWindow(&show_demo_window); + + // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. + { + static float f = 0.0f; + static int counter = 0; + + ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. + + ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) + ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state + ImGui::Checkbox("Another Window", &show_another_window); + + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f + ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + + if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::End(); + } + + // 3. Show another simple window. + if (show_another_window) + { + ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) + ImGui::Text("Hello from another window!"); + if (ImGui::Button("Close Me")) + show_another_window = false; + ImGui::End(); + } + + // Rendering + ImGui::Render(); + ImDrawData* draw_data = ImGui::GetDrawData(); + + [[self openGLContext] makeCurrentContext]; + GLsizei width = (GLsizei)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); + GLsizei height = (GLsizei)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); + glViewport(0, 0, width, height); + glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + + ImGui_ImplOpenGL3_RenderDrawData(draw_data); + + // Present + [[self openGLContext] flushBuffer]; + + if (!animationTimer) + animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.017 target:self selector:@selector(animationTimerFired:) userInfo:nil repeats:YES]; +} + +-(void)reshape { [super reshape]; [[self openGLContext] update]; [self updateAndDrawDemoView]; } +-(void)drawRect:(NSRect)bounds { [self updateAndDrawDemoView]; } +-(void)animationTimerFired:(NSTimer*)timer { [self setNeedsDisplay:YES]; } +-(void)dealloc { animationTimer = nil; } + +@end + +//----------------------------------------------------------------------------------- +// AppDelegate +//----------------------------------------------------------------------------------- + +@interface AppDelegate : NSObject +@property (nonatomic, readonly) NSWindow* window; +@end + +@implementation AppDelegate +@synthesize window = _window; + +-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication +{ + return YES; +} + +-(NSWindow*)window +{ + if (_window != nil) + return _window; + + NSRect viewRect = NSMakeRect(100.0, 100.0, 100.0 + 1280.0, 100 + 800.0); + + _window = [[NSWindow alloc] initWithContentRect:viewRect styleMask:NSWindowStyleMaskTitled|NSWindowStyleMaskMiniaturizable|NSWindowStyleMaskResizable|NSWindowStyleMaskClosable backing:NSBackingStoreBuffered defer:YES]; + [_window setTitle:@"Dear ImGui OSX+OpenGL3 Example"]; + [_window setAcceptsMouseMovedEvents:YES]; + [_window setOpaque:YES]; + [_window makeKeyAndOrderFront:NSApp]; + + return _window; +} + +-(void)setupMenu +{ + NSMenu* mainMenuBar = [[NSMenu alloc] init]; + NSMenu* appMenu; + NSMenuItem* menuItem; + + appMenu = [[NSMenu alloc] initWithTitle:@"Dear ImGui OSX+OpenGL3 Example"]; + menuItem = [appMenu addItemWithTitle:@"Quit Dear ImGui OSX+OpenGL3 Example" action:@selector(terminate:) keyEquivalent:@"q"]; + [menuItem setKeyEquivalentModifierMask:NSEventModifierFlagCommand]; + + menuItem = [[NSMenuItem alloc] init]; + [menuItem setSubmenu:appMenu]; + + [mainMenuBar addItem:menuItem]; + + appMenu = nil; + [NSApp setMainMenu:mainMenuBar]; +} + +-(void)dealloc +{ + _window = nil; +} + +-(void)applicationDidFinishLaunching:(NSNotification *)aNotification +{ + // Make the application a foreground application (else it won't receive keyboard events) + ProcessSerialNumber psn = {0, kCurrentProcess}; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + + // Menu + [self setupMenu]; + + NSOpenGLPixelFormatAttribute attrs[] = + { + NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFADepthSize, 32, + 0 + }; + + NSOpenGLPixelFormat* format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; + AppView* view = [[AppView alloc] initWithFrame:self.window.frame pixelFormat:format]; + format = nil; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6) + [view setWantsBestResolutionOpenGLSurface:YES]; +#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + [self.window setContentView:view]; + + if ([view openGLContext] == nil) + NSLog(@"No OpenGL Context!"); + + [view initialize]; +} + +@end + +//----------------------------------------------------------------------------------- +// Application main() function +//----------------------------------------------------------------------------------- + +int main(int argc, const char* argv[]) +{ + @autoreleasepool + { + NSApp = [NSApplication sharedApplication]; + AppDelegate* delegate = [[AppDelegate alloc] init]; + [[NSApplication sharedApplication] setDelegate:delegate]; + [NSApp run]; + } + return NSApplicationMain(argc, argv); +} From 49fa47fc19ce650731a38a426a0da2f5f24317e3 Mon Sep 17 00:00:00 2001 From: "Daniel K. O. (dkosmari)" Date: Thu, 25 Jun 2026 21:00:14 -0300 Subject: [PATCH 39/47] Added IM_FMTARGS/IM_FMTLIST to TextAligned() functions. (#1980, #9455) --- imgui_internal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index d837a6962..42c6f4d3b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3743,8 +3743,8 @@ namespace ImGui // Widgets: Text IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); - IMGUI_API void TextAligned(float align_x, float size_x, const char* fmt, ...); // FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024) - IMGUI_API void TextAlignedV(float align_x, float size_x, const char* fmt, va_list args); + IMGUI_API void TextAligned(float align_x, float width, const char* fmt, ...) IM_FMTARGS(3); // FIXME-WIP: Works but API is likely to be reworked. + IMGUI_API void TextAlignedV(float align_x, float width, const char* fmt, va_list args) IM_FMTLIST(3); // Widgets IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); From 5de45fb1d9811e31e393c2da90d6b432fd14c4b5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 7 Aug 2026 15:42:27 +0200 Subject: [PATCH 40/47] Trim trailing white spaces. Changes some NULL to nullptr. --- docs/FAQ.md | 8 ++++---- docs/FONTS.md | 2 +- examples/example_glfw_wgpu/main.cpp | 2 +- examples/example_sdl3_wgpu/main.cpp | 12 ++++++------ imgui.h | 8 ++++---- imgui_draw.cpp | 2 +- imgui_internal.h | 4 ++-- imgui_tables.cpp | 2 +- misc/freetype/imgui_freetype.cpp | 4 ++-- 9 files changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/FAQ.md b/docs/FAQ.md index d67616725..5e3bd1e83 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -570,8 +570,8 @@ typedef ImU64 ImTextureID; // Default: store up to 64-bits (any pointer or inte // Store a ImTextureID _or_ a ImTextureData*. struct ImTextureRef { - ImTextureRef() { _TexData = NULL; _TexID = ImTextureID_Invalid; } - ImTextureRef(ImTextureID tex_id) { _TexData = NULL; _TexID = tex_id; } + ImTextureRef() { _TexData = nullptr; _TexID = ImTextureID_Invalid; } + ImTextureRef(ImTextureID tex_id) { _TexData = nullptr; _TexID = tex_id; } inline ImTextureID GetTexID() const { return _TexData ? _TexData->TexID : _TexID; } // Members (either are set, never both!) @@ -589,7 +589,7 @@ struct ImTextureRef We intentionally do not provide an `ImTextureRef` constructor for this: we don't expect this to be frequently useful to the end-user, and it would be erroneously called by many legacy code. - There is no constructor to create a `ImTextureRef` from a `ImTextureData*` as we don't expect this to be useful to the end-user, and it would be erroneously called by many legacy code. - If you want to bind the current atlas when using custom rectangles, you can use `io.Fonts->TexRef`. - - Binding generators for languages such as C (which don't have constructors), should provide a helper, e.g. `inline ImTextureRef ImTextureRefFromID(ImTextureID tex_id) { ImTextureRef tex_ref = { ._TexData = NULL, .TexID = tex_id }; return tex_ref; }` + - Binding generators for languages such as C (which don't have constructors), should provide a helper, e.g. `inline ImTextureRef ImTextureRefFromID(ImTextureID tex_id) { ImTextureRef tex_ref = { ._TexData = nullptr, .TexID = tex_id }; return tex_ref; }` **Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward.** @@ -780,7 +780,7 @@ style.FontScaleDpi = 2.0f; To change font size: ```cpp -ImGui::PushFont(NULL, 42.0f); // This will be multiplied by style.FontScaleDpi +ImGui::PushFont(nullptr, 42.0f); // This will be multiplied by style.FontScaleDpi ``` To change font and font size: ```cpp diff --git a/docs/FONTS.md b/docs/FONTS.md index ac93bca0c..3f1e085fe 100644 --- a/docs/FONTS.md +++ b/docs/FONTS.md @@ -96,7 +96,7 @@ atlas->AddFont(...); v1.92 introduces a newer, dynamic font system. It requires backend to support the `ImGuiBackendFlags_HasTextures` feature: - Users of icons, Asian and non-English languages do not need to pre-build all glyphs ahead of time. Saving on loading time, memory, and also reducing issues with missing glyphs. Specifying glyph ranges is not needed anymore. -- `PushFont(NULL, new_size)` may be used anytime to change font size. +- `PushFont(nullptr, new_size)` may be used anytime to change font size. - Packing custom rectangles is more convenient as pixels may be written to immediately. - Any update to fonts previously required backend specific calls to re-upload the texture, and said calls were not portable across backends. It is now possible to scale fonts etc. in a way that doesn't require you to make backend-specific calls. - It is possible to plug a custom loader/backend to any font source. diff --git a/examples/example_glfw_wgpu/main.cpp b/examples/example_glfw_wgpu/main.cpp index 9d48258ba..dc6a7d155 100644 --- a/examples/example_glfw_wgpu/main.cpp +++ b/examples/example_glfw_wgpu/main.cpp @@ -548,7 +548,7 @@ WGPUSurface CreateWGPUSurface(const WGPUInstance& instance, GLFWwindow* window) { create_info.System = "win32"; create_info.RawWindow = (void*)glfwGetWin32Window(window); - create_info.RawInstance = (void*)::GetModuleHandle(NULL); + create_info.RawInstance = (void*)::GetModuleHandle(nullptr); return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); } #else diff --git a/examples/example_sdl3_wgpu/main.cpp b/examples/example_sdl3_wgpu/main.cpp index bc1787829..6b9a24e11 100644 --- a/examples/example_sdl3_wgpu/main.cpp +++ b/examples/example_sdl3_wgpu/main.cpp @@ -505,29 +505,29 @@ WGPUSurface CreateWGPUSurface(const WGPUInstance& instance, SDL_Window* window) #if defined(SDL_PLATFORM_MACOS) { create_info.System = "cocoa"; - create_info.RawWindow = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, NULL); + create_info.RawWindow = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr); return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); } #elif defined(SDL_PLATFORM_LINUX) if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0) { create_info.System = "wayland"; - create_info.RawDisplay = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, NULL); - create_info.RawSurface = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, NULL); + create_info.RawDisplay = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, nullptr); + create_info.RawSurface = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr); return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); } else if (!SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11")) { create_info.System = "x11"; create_info.RawWindow = (void*)SDL_GetNumberProperty(propertiesID, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0); - create_info.RawDisplay = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL); + create_info.RawDisplay = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, nullptr); return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); } #elif defined(SDL_PLATFORM_WIN32) { create_info.System = "win32"; - create_info.RawWindow = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); - create_info.RawInstance = (void*)::GetModuleHandle(NULL); + create_info.RawWindow = (void*)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); + create_info.RawInstance = (void*)::GetModuleHandle(nullptr); return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info); } #else diff --git a/imgui.h b/imgui.h index 43f297d44..54eea3035 100644 --- a/imgui.h +++ b/imgui.h @@ -1100,7 +1100,7 @@ namespace ImGui // - Reminder ImGuiKey enum include access to mouse buttons and gamepad, so key ownership can apply to them. // - The return value of SetItemKeyOwner() says if ownership has been requested for the item, which is a shortcut to calling yet non-public TestKeyOwner() function. // - Many related features are still in imgui_internal.h. For instance, most IsKeyXXX()/IsMouseXXX() functions have an owner-id-aware version. - IMGUI_API bool SetItemKeyOwner(ImGuiKey key); // Set key owner to last item ID if it is hovered or active. Return true when ownership has been set. Roughly equivalent to 'if (TestKeyOwner(key, GetItemID()) && (IsItemHovered() || IsItemActive())) { SetKeyOwner(key, GetItemID());'. + IMGUI_API bool SetItemKeyOwner(ImGuiKey key); // Set key owner to last item ID if it is hovered or active. Return true when ownership has been set. Roughly equivalent to 'if (TestKeyOwner(key, GetItemID()) && (IsItemHovered() || IsItemActive())) { SetKeyOwner(key, GetItemID());'. // Inputs Utilities: Mouse // - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right. @@ -2356,7 +2356,7 @@ struct ImGuiStyle ImGuiTreeNodeFlags TreeLinesFlags; // Default way to draw lines connecting TreeNode hierarchy. ImGuiTreeNodeFlags_DrawLinesNone or ImGuiTreeNodeFlags_DrawLinesFull or ImGuiTreeNodeFlags_DrawLinesToNodes. float TreeLinesSize; // Thickness of outlines when using ImGuiTreeNodeFlags_DrawLines. float TreeLinesRounding; // Radius of lines connecting child nodes to the vertical line. - float MenuItemRounding; // Radius of MenuItem, BeginMenu rounding. + float MenuItemRounding; // Radius of MenuItem, BeginMenu rounding. float SelectableRounding; // Radius of Selectable rounding. MODIFYING THIS IS DISCOURAGED. CONTIGUOUS SELECTIONS WILL NOT LOOK RIGHT. (#7589) float DragDropTargetRounding; // Radius of the drag and drop target frame. When <0.0f: use FrameRounding. float DragDropTargetBorderSize; // Thickness of the drag and drop target border. @@ -3746,8 +3746,8 @@ struct ImFontAtlas IMGUI_API void CompactCache(); // Compact cached glyphs and texture. IMGUI_API void SetFontLoader(const ImFontLoader* font_loader); // Change font loader at runtime. - // Clearing the atlas/fonts has little use nowadays, unless you want to batch remove all fonts. - // - Since 1.92, you can call ClearFonts() mid-frame, if you load new fonts afterwards. + // Clearing the atlas/fonts has little use nowadays, unless you want to batch remove all fonts. + // - Since 1.92, you can call ClearFonts() mid-frame, if you load new fonts afterwards. // - As we are transitioning toward our new font system the semantic for those functions gets increasingly misleading and are often a source of issues. // TL;DR; most likely, don't use any of those functions. We expect to obsolete/rework them. IMGUI_API void Clear(); // Clear everything (fonts + textures). Don't call mid-frame! diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 35ac29f47..b4739b8be 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -105,7 +105,7 @@ namespace IMGUI_STB_NAMESPACE #pragma warning (push) #pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration #pragma warning (disable: 6011) // (stb_rectpack) Dereferencing NULL pointer 'cur->next'. -#pragma warning (disable: 5262) // (stb_truetype) implicit fall-through occurs here; are you missing a break statement? +#pragma warning (disable: 5262) // (stb_truetype) implicit fall-through occurs here; are you missing a break statement? #pragma warning (disable: 6385) // (stb_truetype) Reading invalid data from 'buffer': the readable size is '_Old_3`kernel_width' bytes, but '3' bytes may be read. #pragma warning (disable: 28182) // (stb_rectpack) Dereferencing NULL pointer. 'cur' contains the same NULL value as 'cur->next' did. #endif diff --git a/imgui_internal.h b/imgui_internal.h index 42c6f4d3b..ef5bf9c4b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -284,7 +284,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 #define IM_TRUNC(_VAL) ((float)(int)(_VAL)) // Positive values only! ImTrunc() is not inlined in MSVC debug builds -#define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // Positive values only! +#define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // Positive values only! //#define IM_FLOOR IM_TRUNC // [OBSOLETE] Renamed in 1.90.0 (Sept 2023) // Hint for branch prediction @@ -3253,7 +3253,7 @@ struct ImGuiTableSettings }; namespace ImGui -{ +{ // Tables: Candidates for public API IMGUI_API void TableOpenContextMenu(int column_n = -1); IMGUI_API void TableSetColumnWidth(int column_n, float width); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 8f260c873..bdaeeaad3 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -631,7 +631,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG // At this point the ->NameOffset field of each column will be invalid until TableUpdateLayout() or the first call to TableSetupColumn() if (table->ColumnsNames.Buf.Size > 0) table->ColumnsNames.Buf.resize(0); - + return true; } diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 3c817d20f..79dff8bfb 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -508,9 +508,9 @@ static bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConf // Load metrics only mode const float advance_x = (slot->advance.x / FT_SCALEFACTOR) / rasterizer_density; - if (out_advance_x != NULL) + if (out_advance_x != nullptr) { - IM_ASSERT(out_glyph == NULL); + IM_ASSERT(out_glyph == nullptr); *out_advance_x = advance_x; return true; } From 551a0a6bc052a585ebbe5b3ae7a64696048c04f2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 7 Aug 2026 17:03:56 +0200 Subject: [PATCH 41/47] TextAligned, TextAlignedV: rework to always use provided width. (#7024, #9455, #1980) --- imgui.h | 2 +- imgui_widgets.cpp | 32 +++++++++++++++++--------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/imgui.h b/imgui.h index 54eea3035..07175b032 100644 --- a/imgui.h +++ b/imgui.h @@ -30,7 +30,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.93.0 WIP" -#define IMGUI_VERSION_NUM 19293 +#define IMGUI_VERSION_NUM 19294 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5bd6da9fa..2fe200f5e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -342,18 +342,18 @@ void ImGui::TextWrappedV(const char* fmt, va_list args) PopTextWrapPos(); } -void ImGui::TextAligned(float align_x, float size_x, const char* fmt, ...) +void ImGui::TextAligned(float align_x, float width, const char* fmt, ...) { va_list args; va_start(args, fmt); - TextAlignedV(align_x, size_x, fmt, args); + TextAlignedV(align_x, width, fmt, args); va_end(args); } // align_x: 0.0f = left, 0.5f = center, 1.0f = right. -// size_x : 0.0f = shortcut for GetContentRegionAvail().x -// FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024) -void ImGui::TextAlignedV(float align_x, float size_x, const char* fmt, va_list args) +// width < 0.0f (e.g. -FLT_MIN): shortcut for GetContentRegionAvail().x +// FIXME-WIP: Works but API is likely to be reworked. This is designed for 1 item on the line. (#7024, #9455) +void ImGui::TextAlignedV(float align_x, float width, const char* fmt, va_list args) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -362,23 +362,25 @@ void ImGui::TextAlignedV(float align_x, float size_x, const char* fmt, va_list a const char* text, *text_end; ImFormatStringToTempBufferV(&text, &text_end, fmt, args); const ImVec2 text_size = CalcTextSize(text, text_end); - size_x = CalcItemSize(ImVec2(size_x, 0.0f), 0.0f, text_size.y).x; + const ImVec2 size = CalcItemSize(ImVec2(width, 0.0f), 0.0f, text_size.y); ImVec2 pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset); - ImVec2 pos_max(pos.x + size_x, window->ClipRect.Max.y); - ImVec2 size(ImMin(size_x, text_size.x), text_size.y); + ImVec2 pos_max(pos.x + size.x, window->ClipRect.Max.y); + window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, pos.x + text_size.x); window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, pos.x + text_size.x); - if (align_x > 0.0f && text_size.x < size_x) - pos.x += ImTrunc((size_x - text_size.x) * align_x); - RenderTextEllipsis(window->DrawList, pos, pos_max, pos_max.x, text, text_end, &text_size); - const ImVec2 backup_max_pos = window->DC.CursorMaxPos; ItemSize(size); - ItemAdd(ImRect(pos, pos + size), 0); - window->DC.CursorMaxPos.x = backup_max_pos.x; // Cancel out extending content size because right-aligned text would otherwise mess it up. + const bool is_visible = ItemAdd(ImRect(pos, pos + size), 0); + window->DC.CursorMaxPos.x = backup_max_pos.x; // Cancel out extending content to 'size' to use 'text_size' allowing auto-resize to function with align_x > 0.0f + if (!is_visible) + return; - if (size_x < text_size.x && IsItemHovered(ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_ForTooltip)) + if (align_x > 0.0f && text_size.x < size.x) + pos.x += ImTrunc((size.x - text_size.x) * align_x); + RenderTextEllipsis(window->DrawList, pos, pos_max, pos_max.x, text, text_end, &text_size); + + if (size.x < text_size.x && IsItemHovered(ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_ForTooltip)) SetTooltip("%.*s", (int)(text_end - text), text); } From 46d39d56febc2a00bdd2270dc88c8a13f2a0441a Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 7 Aug 2026 18:42:39 +0200 Subject: [PATCH 42/47] Fixed GCC false positive. (#9508) --- imgui_widgets.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 2fe200f5e..062e26d73 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3635,6 +3635,7 @@ const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_ const char* fmt_end = ImParseFormatFindEnd(fmt_start); if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data. return fmt_start; + buf[0] = 0; // Fix false positive with caller assuming that buf could be non-initialized (#9508) ImStrncpy(buf, fmt_start, ImMin((size_t)(fmt_end - fmt_start) + 1, buf_size)); return buf; } From 3e9d0f7e8227d0eaa64b8ea9771073e7249292fb Mon Sep 17 00:00:00 2001 From: rubbededo Date: Sun, 16 Aug 2026 01:40:58 +0000 Subject: [PATCH 43/47] Backends: WebGPU: fixed passing non-integer sizes to wgpuRenderPassEncoderSetViewport(). (#9515, #8628) --- backends/imgui_impl_wgpu.cpp | 5 ++++- docs/CHANGELOG.txt | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index ce5974b41..43a7fbba7 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -20,6 +20,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-08-18: Fixed passing non-integer sizes to `wgpuRenderPassEncoderSetViewport() when FramebufferScale is >1.0f. (#9515, #8628) // 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378) // 2026-03-25: Added support for WGVK native backend via IMGUI_IMPL_WEBGPU_BACKEND_WGVK define, with SPIRV shaders if WGSL is not available. (#9316, #9246, #9257) // 2026-03-09: Removed support for Emscripten < 4.0.10. (#9281) @@ -440,7 +441,9 @@ static void ImGui_ImplWGPU_SetupRenderState(ImDrawData* draw_data, WGPURenderPas } // Setup viewport - wgpuRenderPassEncoderSetViewport(ctx, 0, 0, draw_data->FramebufferScale.x * draw_data->DisplaySize.x, draw_data->FramebufferScale.y * draw_data->DisplaySize.y, 0, 1); + int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); + wgpuRenderPassEncoderSetViewport(ctx, 0, 0, fb_width, fb_height, 0, 1); // Bind shader and vertex buffers wgpuRenderPassEncoderSetVertexBuffer(ctx, 0, fr->VertexBuffer, 0, fr->VertexBufferSize * sizeof(ImDrawVert)); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 716cd3fc7..14978c699 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -64,6 +64,8 @@ Other Changes: - QNX: added QNX Screen backend. (#9492) [@mgorchak-blackberry] - SDL2: fixed querying framebuffer scale/density when using Metal without going through a SDL_Renderer. (#5592) [@lailoken] + - WebGPU: fixed passing non-integer sizes to `wgpuRenderPassEncoderSetViewport()` + when when `FramebufferScale` is >1.0f. (#9515, #8628) [@WayU] - Examples: - SDL2+Metal: create Metal context without using a SDL_Renderer. - QNX+OpenGL3, QNX+Vulkan: added QNX Screen examples. (#9492) [@mgorchak-blackberry] From 1d7b37dc9b825a8aa49412e7f9a5546be0006ce4 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 19 Aug 2026 13:27:31 +0200 Subject: [PATCH 44/47] Tables: fix/workaround for losing display order. (#9519, #9108) --- imgui_tables.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index bdaeeaad3..fb1b998d0 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -3974,6 +3974,8 @@ void ImGui::TableSaveSettings(ImGuiTable* table) if (column->IsUserEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0)) settings->SaveFlags |= ImGuiTableFlags_Hideable; } + if (table->Flags & ImGuiTableFlags_Sortable) + settings->SaveFlags |= ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable; settings->SaveFlags &= table->Flags; settings->RefScale = save_ref_scale ? table->RefScale : 0.0f; From 0af8f475d1cf8b98d603702059c0c73deeb81ac2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 23 Aug 2026 13:05:33 +0200 Subject: [PATCH 45/47] Fixed `GetBackgroundDrawList()`/`GetForegroundDrawList()` not being properly reset every frame when cumulated session time is very large. (#9521) Using a FrameCount for compare. Kept time as float to be consistent with other GC-related timestamps: if there is an actual need to use double we'll switch everywhere. --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 7 ++++--- imgui_internal.h | 5 +++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 14978c699..21f452486 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -56,6 +56,9 @@ Other Changes: - Reworked `AddFontDefault()` to use `io.DisplayFrameBufferScale` as part of the heuristic to select `AddFontDefaultVector()` by default, effectively using ProggyForever instead of ProggyClean on most Apple/Retina setups by default. +- Misc: + - Fixed `GetBackgroundDrawList()`/`GetForegroundDrawList()` not being properly reset + every frame when cumulated session time is very large (e.g. a few days). [@lailoken] - ImDrawList: - Per-viewport FramebufferScale for Apple/Retina screen is applied to draw lists: - AA Fringe is scaled accordingly. diff --git a/imgui.cpp b/imgui.cpp index d157353cf..c4ac84770 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5305,12 +5305,13 @@ static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t draw } // Our ImDrawList system requires that there is always a command - if (viewport->BgFgDrawListsLastTimeActive[drawlist_no] != (float)g.Time) + if (viewport->BgFgDrawListsLastFrameActive[drawlist_no] != g.FrameCount) { draw_list->_ResetForNewFrame(); draw_list->_SetPixelDensity(viewport->FramebufferScale.x); draw_list->PushTexture(g.IO.Fonts->TexRef); draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); + viewport->BgFgDrawListsLastFrameActive[drawlist_no] = g.FrameCount; viewport->BgFgDrawListsLastTimeActive[drawlist_no] = (float)g.Time; } return draw_list; @@ -6212,7 +6213,7 @@ void ImGui::Render() for (ImGuiViewportP* viewport : g.Viewports) { InitViewportDrawData(viewport); - if (viewport->BgFgDrawLists[0] != NULL && viewport->BgFgDrawListsLastTimeActive[0] == (float)g.Time) + if (viewport->BgFgDrawLists[0] != NULL && viewport->BgFgDrawListsLastFrameActive[0] == g.FrameCount) AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); } @@ -6244,7 +6245,7 @@ void ImGui::Render() FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder); // Add foreground ImDrawList (for each active viewport) - if (viewport->BgFgDrawLists[1] != NULL && viewport->BgFgDrawListsLastTimeActive[1] == (float)g.Time) + if (viewport->BgFgDrawLists[1] != NULL && viewport->BgFgDrawListsLastFrameActive[1] == g.FrameCount) AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch). diff --git a/imgui_internal.h b/imgui_internal.h index ef5bf9c4b..631216426 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1999,8 +1999,9 @@ struct IMGUI_API ImGuiMultiSelectState // Every instance of ImGuiViewport is in fact a ImGuiViewportP. struct ImGuiViewportP : public ImGuiViewport { - float BgFgDrawListsLastTimeActive[2]; // Last frame number the background (0) and foreground (1) draw lists were used ImDrawList* BgFgDrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. + int BgFgDrawListsLastFrameActive[2]; // Last frame the background (0) and foreground (1) draw lists were used. + float BgFgDrawListsLastTimeActive[2]; // Timestamps for Gc. ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; // Temporary data while building final ImDrawData @@ -2013,7 +2014,7 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 BuildWorkInsetMin; // Work Area inset accumulator for current frame, to become next frame's WorkInset ImVec2 BuildWorkInsetMax; // " - ImGuiViewportP() { BgFgDrawListsLastTimeActive[0] = BgFgDrawListsLastTimeActive[1] = -1.0f; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; } + ImGuiViewportP() { BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; BgFgDrawListsLastFrameActive[0] = BgFgDrawListsLastFrameActive[1] = -1; BgFgDrawListsLastTimeActive[0] = BgFgDrawListsLastTimeActive[1] = -1.0; } ~ImGuiViewportP() { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); } // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect) From 1e51b6ffdc24f06a6e3cd3adaa7fc154af701d2f Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 28 Aug 2026 12:40:14 +0200 Subject: [PATCH 46/47] Comments/fixes about using IMGUI_DEFINE_MATH_OPERATORS in imconfig.h (#9524) --- imconfig.h | 5 ++++- misc/single_file/imgui_single_file.h | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/imconfig.h b/imconfig.h index b40db3898..fd1ed81f3 100644 --- a/imconfig.h +++ b/imconfig.h @@ -114,8 +114,11 @@ constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \ operator MyVec4() const { return MyVec4(x,y,z,w); } */ -//---- ...Or use Dear ImGui's own very basic math operators. +//---- ...Or use Dear ImGui's own basic math operators in every file including imgui.h: +//#ifndef IMGUI_DEFINE_MATH_OPERATORS //#define IMGUI_DEFINE_MATH_OPERATORS +//#endif +//---- ...Or manually '#define IMGUI_DEFINE_MATH_OPERATORS' before '#include "imgui.h"' to use from one file. //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). diff --git a/misc/single_file/imgui_single_file.h b/misc/single_file/imgui_single_file.h index 7ca31e0fe..bea1cb6b6 100644 --- a/misc/single_file/imgui_single_file.h +++ b/misc/single_file/imgui_single_file.h @@ -1,14 +1,14 @@ // dear imgui: single-file wrapper include // We use this to validate compiling all *.cpp files in a same compilation unit. -// Users of that technique (also called "Unity builds") can generally provide this themselves, -// so we don't really recommend you use this in your projects. +// Users of that technique (also called "Unity builds") can generally provide this themselves easily. // Do this: // #define IMGUI_IMPLEMENTATION -// Before you include this file in *one* C++ file to create the implementation. +// #include "imgui_single_file.h" +// In *one* C++ file to create the implementation. // Using this in your project will leak the contents of imgui_internal.h and ImVec2 operators in this compilation unit. -#ifdef IMGUI_IMPLEMENTATION +#if defined(IMGUI_IMPLEMENTATION) && !defined(IMGUI_DEFINE_MATH_OPERATORS) #define IMGUI_DEFINE_MATH_OPERATORS #endif From d3c5ff395ca10735456331c993e0789c2ccec355 Mon Sep 17 00:00:00 2001 From: ocornut Date: Sun, 30 Aug 2026 19:44:15 +0200 Subject: [PATCH 47/47] Drags, Sliders: fixed an overflow using `ImGuiSliderFlags_Logarithmic` with S32/S64 types. (#9526, #3361, #1823, #1316, #642) --- docs/CHANGELOG.txt | 3 +++ imgui_widgets.cpp | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 21f452486..2e010adeb 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -52,6 +52,9 @@ Breaking Changes: Other Changes: +- Drags, Sliders: + - Fixed an overflow using `ImGuiSliderFlags_Logarithmic` with S32/S64 types which + leads to clamped interactions. (#9526, #3361, #1823, #1316, #642) [@lailoken] - Fonts: - Reworked `AddFontDefault()` to use `io.DisplayFrameBufferScale` as part of the heuristic to select `AddFontDefaultVector()` by default, effectively using ProggyForever instead of diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 062e26d73..e2a6771bd 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -3006,7 +3006,7 @@ float ImGui::ScaleRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, T result = 0.0f; // Workaround for values that are in-range but below our fudge else if (v_clamped >= v_max_fudged) result = 1.0f; // Workaround for values that are in-range but above our fudge - else if ((v_min * v_max) < 0.0f) // Range crosses zero, so split into two portions + else if (((FLOATTYPE)v_min * (FLOATTYPE)v_max) < 0.0f) // Range crosses zero, so split into two portions { float zero_point_center = (-(float)v_min) / ((float)v_max - (float)v_min); // The zero point in parametric space. There's an argument we should take the logarithmic nature into account when calculating this, but for now this should do (and the most common case of a symmetrical range works fine) float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize; @@ -3060,7 +3060,7 @@ TYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type, float t, TYPE v_min, T float t_with_flip = flipped ? (1.0f - t) : t; // t, but flipped if necessary to account for us flipping the range - if ((v_min * v_max) < 0.0f) // Range crosses zero, so we have to do this in two parts + if (((FLOATTYPE)v_min * (FLOATTYPE)v_max) < 0.0f) // Range crosses zero, so we have to do this in two parts { float zero_point_center = (-(float)ImMin(v_min, v_max)) / ImAbs((float)v_max - (float)v_min); // The zero point in parametric space float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize;