From cf3091b4099b9ca365360088c8409c93a71a2361 Mon Sep 17 00:00:00 2001 From: Soreepeong Date: Wed, 14 Feb 2024 13:05:45 +0900 Subject: [PATCH] If docked, force default font on window decoration ImGui docking functions are called outside our drawing context (from ImGui::NewFrame), which includes most of dock-related drawing calls. However, ImGui::RenderWindowDecoration is called from ImGui::Begin, which may be under the effect of other pushed font. As IG::RWD references to the ImDrawList irrelevant to the global shared state, it was trying to draw a rectangle referring to a pixel that is not guaranteed to be a white pixel. This commit fixes that by forcing the use of the default font for IG::RWD when the window is docked. --- .../ImGuiClipboardFunctionProvider.cs | 1 - .../Internals/ImGuiDockNodeUpdateForceFont.cs | 78 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 Dalamud/Interface/ManagedFontAtlas/Internals/ImGuiDockNodeUpdateForceFont.cs diff --git a/Dalamud/Interface/Internal/ImGuiClipboardFunctionProvider.cs b/Dalamud/Interface/Internal/ImGuiClipboardFunctionProvider.cs index 1746fb1c4..bbf665405 100644 --- a/Dalamud/Interface/Internal/ImGuiClipboardFunctionProvider.cs +++ b/Dalamud/Interface/Internal/ImGuiClipboardFunctionProvider.cs @@ -52,7 +52,6 @@ internal sealed unsafe class ImGuiClipboardFunctionProvider : IServiceType, IDis private ImGuiClipboardFunctionProvider(InterfaceManager.InterfaceManagerWithScene imws) { // Effectively waiting for ImGui to become available. - _ = imws; Debug.Assert(ImGuiHelpers.IsImGuiInitialized, "IMWS initialized but IsImGuiInitialized is false?"); var io = ImGui.GetIO(); diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/ImGuiDockNodeUpdateForceFont.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/ImGuiDockNodeUpdateForceFont.cs new file mode 100644 index 000000000..a2a30429a --- /dev/null +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/ImGuiDockNodeUpdateForceFont.cs @@ -0,0 +1,78 @@ +using System.Diagnostics; +using System.Linq; + +using Dalamud.Hooking; +using Dalamud.Interface.Internal; +using Dalamud.Interface.Utility; + +namespace Dalamud.Interface.ManagedFontAtlas.Internals; + +/// +/// Forces ImGui::RenderWindowDecorations to use the default font. +/// Fixes dock node draw using shared data across different draw lists. +/// TODO: figure out how to synchronize ImDrawList::_Data and ImDrawList::Push/PopTextureID across different instances. +/// It might be better to just special-case that particular function, +/// as no other code touches ImDrawList that is irrelevant to the global shared state, +/// with the exception of Dock... functions which are called from ImGui::NewFrame, +/// which are guaranteed to use the global default font. +/// +[ServiceManager.EarlyLoadedService] +internal class ImGuiRenderWindowDecorationsForceFont : IServiceType, IDisposable +{ + private const int CImGuiRenderWindowDecorationsOffset = 0x461B0; + private const int CImGuiWindowDockIsActiveOffset = 0x401; + + private readonly Hook hook; + + [ServiceManager.ServiceConstructor] + private ImGuiRenderWindowDecorationsForceFont(InterfaceManager.InterfaceManagerWithScene imws) + { + // Effectively waiting for ImGui to become available. + Debug.Assert(ImGuiHelpers.IsImGuiInitialized, "IMWS initialized but IsImGuiInitialized is false?"); + + var cimgui = Process.GetCurrentProcess().Modules.Cast() + .First(x => x.ModuleName == "cimgui.dll") + .BaseAddress; + this.hook = Hook.FromAddress( + cimgui + CImGuiRenderWindowDecorationsOffset, + this.ImGuiRenderWindowDecorationsDetour); + this.hook.Enable(); + } + + private delegate void ImGuiRenderWindowDecorationsDelegate( + nint window, + nint titleBarRectPtr, + byte titleBarIsHighlight, + byte handleBordersAndResizeGrips, + int resizeGripCount, + nint resizeGripColPtr, + float resizeGripDrawSize); + + /// + public void Dispose() => this.hook.Dispose(); + + private unsafe void ImGuiRenderWindowDecorationsDetour( + nint window, + nint titleBarRectPtr, + byte titleBarIsHighlight, + byte handleBordersAndResizeGrips, + int resizeGripCount, + nint resizeGripColPtr, + float resizeGripDrawSize) + { + using ( + ((byte*)window)![CImGuiWindowDockIsActiveOffset] != 0 + ? Service.Get().DefaultFontHandle?.Push() + : null) + { + this.hook.Original( + window, + titleBarRectPtr, + titleBarIsHighlight, + handleBordersAndResizeGrips, + resizeGripCount, + resizeGripColPtr, + resizeGripDrawSize); + } + } +}