diff --git a/Dalamud/Game/Gui/Internal/DalamudIME.cs b/Dalamud/Game/Gui/Internal/DalamudIME.cs
index 4aafa5f3b..e955997fd 100644
--- a/Dalamud/Game/Gui/Internal/DalamudIME.cs
+++ b/Dalamud/Game/Gui/Internal/DalamudIME.cs
@@ -1,8 +1,12 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
+using Dalamud.Hooking;
using Dalamud.Interface.Internal;
using Dalamud.Logging.Internal;
using ImGuiNET;
@@ -14,7 +18,7 @@ namespace Dalamud.Game.Gui.Internal
///
/// This class handles IME for non-English users.
///
- internal class DalamudIME : IDisposable
+ internal unsafe class DalamudIME : IDisposable
{
private static readonly ModuleLog Log = new("IME");
@@ -22,6 +26,8 @@ namespace Dalamud.Game.Gui.Internal
private IntPtr wndProcPtr;
private IntPtr oldWndProcPtr;
private WndProcDelegate wndProcDelegate;
+ private AsmHook imguiTextInputCursorHook;
+ private Vector2* cursorPos;
///
/// Initializes a new instance of the class.
@@ -60,6 +66,18 @@ namespace Dalamud.Game.Gui.Internal
SetWindowLongPtrW(this.interfaceHandle, WindowLongType.WndProc, this.oldWndProcPtr);
this.oldWndProcPtr = IntPtr.Zero;
}
+
+ this.imguiTextInputCursorHook?.Dispose();
+ Marshal.FreeHGlobal((IntPtr)this.cursorPos);
+ }
+
+ ///
+ /// Get the position of the cursor.
+ ///
+ /// The position of the cursor.
+ internal Vector2 GetCursorPos()
+ {
+ return new Vector2(this.cursorPos->X, this.cursorPos->Y);
}
///
@@ -73,6 +91,31 @@ namespace Dalamud.Game.Gui.Internal
this.interfaceHandle = Service.Get().WindowHandlePtr;
this.wndProcPtr = Marshal.GetFunctionPointerForDelegate(this.wndProcDelegate);
this.oldWndProcPtr = SetWindowLongPtrW(this.interfaceHandle, WindowLongType.WndProc, this.wndProcPtr);
+
+ var module = Process.GetCurrentProcess().Modules.Cast().First(m => m.ModuleName == "cimgui.dll");
+ var scanner = new SigScanner(module);
+ var cursorDrawingPtr = scanner.ScanModule("F3 0F 11 75 ?? 0F 28 CF");
+ Log.Debug($"Found cursorDrawingPtr at {cursorDrawingPtr:X}");
+
+ this.cursorPos = (Vector2*)Marshal.AllocHGlobal(sizeof(Vector2));
+ this.cursorPos->X = 0f;
+ this.cursorPos->Y = 0f;
+
+ var asm = new[]
+ {
+ "use64",
+ $"push rax",
+ $"mov rax, {(IntPtr)this.cursorPos + sizeof(float)}",
+ $"movss [rax],xmm7",
+ $"mov rax, {(IntPtr)this.cursorPos}",
+ $"movss [rax],xmm6",
+ $"pop rax",
+ };
+
+ Log.Debug($"Asm Code:\n{string.Join("\n", asm)}");
+ this.imguiTextInputCursorHook = new AsmHook(cursorDrawingPtr, asm, "ImguiTextInputCursorHook");
+ this.imguiTextInputCursorHook?.Enable();
+
this.IsEnabled = true;
Log.Information("Enabled!");
}
diff --git a/Dalamud/Interface/GameFonts/GameFontManager.cs b/Dalamud/Interface/GameFonts/GameFontManager.cs
index 933558c09..af4e06755 100644
--- a/Dalamud/Interface/GameFonts/GameFontManager.cs
+++ b/Dalamud/Interface/GameFonts/GameFontManager.cs
@@ -39,7 +39,7 @@ namespace Dalamud.Interface.GameFonts
private readonly Dictionary fontUseCounter = new();
private readonly Dictionary>> glyphRectIds = new();
- private bool isBetweenBuildFontsAndAfterBuildFonts = false;
+ private bool isBetweenBuildFontsAndRightAfterImGuiIoFontsBuild = false;
private bool isBuildingAsFallbackFontMode = false;
///
@@ -174,7 +174,7 @@ namespace Dalamud.Interface.GameFonts
needRebuild = !this.fonts.ContainsKey(style);
if (needRebuild)
{
- if (Service.Get().IsBuildingFontsBeforeAtlasBuild && this.isBetweenBuildFontsAndAfterBuildFonts)
+ if (Service.Get().IsBuildingFontsBeforeAtlasBuild && this.isBetweenBuildFontsAndRightAfterImGuiIoFontsBuild)
{
Log.Information("[GameFontManager] NewFontRef: Building {0} right now, as it is called while BuildFonts is already in progress yet atlas build has not been called yet.", style.ToString());
this.EnsureFont(style);
@@ -246,7 +246,7 @@ namespace Dalamud.Interface.GameFonts
public void BuildFonts(bool forceMinSize)
{
this.isBuildingAsFallbackFontMode = forceMinSize;
- this.isBetweenBuildFontsAndAfterBuildFonts = true;
+ this.isBetweenBuildFontsAndRightAfterImGuiIoFontsBuild = true;
this.glyphRectIds.Clear();
this.fonts.Clear();
@@ -255,6 +255,21 @@ namespace Dalamud.Interface.GameFonts
this.EnsureFont(style);
}
+ ///
+ /// Record that ImGui.GetIO().Fonts.Build() has been called.
+ ///
+ public void AfterIoFontsBuild()
+ {
+ this.isBetweenBuildFontsAndRightAfterImGuiIoFontsBuild = false;
+ }
+
+ ///
+ /// Checks whether GameFontMamager owns an ImFont.
+ ///
+ /// ImFontPtr to check.
+ /// Whether it owns.
+ public bool OwnsFont(ImFontPtr fontPtr) => this.fonts.ContainsValue(fontPtr);
+
///
/// Post-build fonts before plugins do something more. To be called from InterfaceManager.
///
@@ -270,6 +285,9 @@ namespace Dalamud.Interface.GameFonts
var fdt = this.fdts[(int)(this.isBuildingAsFallbackFontMode ? style.FamilyWithMinimumSize : style.FamilyAndSize)];
var scale = style.SizePt / fdt.FontHeader.Size;
var fontPtr = font.NativePtr;
+
+ Log.Verbose("[GameFontManager] AfterBuildFonts: Scaling {0} from {1}pt to {2}pt (scale: {3})", style.ToString(), fdt.FontHeader.Size, style.SizePt, scale);
+
fontPtr->FontSize = fdt.FontHeader.Size * 4 / 3;
if (fontPtr->ConfigData != null)
fontPtr->ConfigData->SizePixels = fontPtr->FontSize;
@@ -364,12 +382,8 @@ namespace Dalamud.Interface.GameFonts
}
}
- ImGuiHelpers.CopyGlyphsAcrossFonts(InterfaceManager.DefaultFont, font, true, false);
UnscaleFont(font, 1 / scale, false);
- font.BuildLookupTable();
}
-
- this.isBetweenBuildFontsAndAfterBuildFonts = false;
}
///
diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs
index fda86ac43..af096cc8a 100644
--- a/Dalamud/Interface/Internal/DalamudInterface.cs
+++ b/Dalamud/Interface/Internal/DalamudInterface.cs
@@ -67,6 +67,7 @@ namespace Dalamud.Interface.Internal
#endif
private bool isImGuiDrawDemoWindow = false;
+ private bool isImGuiTestWindowsInMonospace = false;
private bool isImGuiDrawMetricsWindow = false;
///
@@ -363,12 +364,18 @@ namespace Dalamud.Interface.Internal
this.WindowSystem.Draw();
+ if (this.isImGuiTestWindowsInMonospace)
+ ImGui.PushFont(InterfaceManager.MonoFont);
+
if (this.isImGuiDrawDemoWindow)
ImGui.ShowDemoWindow(ref this.isImGuiDrawDemoWindow);
if (this.isImGuiDrawMetricsWindow)
ImGui.ShowMetricsWindow(ref this.isImGuiDrawMetricsWindow);
+ if (this.isImGuiTestWindowsInMonospace)
+ ImGui.PopFont();
+
// Release focus of any ImGui window if we click into the game.
var io = ImGui.GetIO();
if (!io.WantCaptureMouse && (User32.GetKeyState((int)User32.VirtualKey.VK_LBUTTON) & 0x8000) != 0)
@@ -566,8 +573,8 @@ namespace Dalamud.Interface.Internal
if (ImGui.BeginMenu("GUI"))
{
+ ImGui.MenuItem("Use Monospace font for following windows", string.Empty, ref this.isImGuiTestWindowsInMonospace);
ImGui.MenuItem("Draw ImGui demo", string.Empty, ref this.isImGuiDrawDemoWindow);
-
ImGui.MenuItem("Draw metrics", string.Empty, ref this.isImGuiDrawMetricsWindow);
ImGui.Separator();
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index 5d4e1c5ef..a768dd2df 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -524,8 +524,6 @@ namespace Dalamud.Interface.Internal
this.scene.OnBuildUI += this.Display;
this.scene.OnNewInputFrame += this.OnNewInputFrame;
- this.SetupFonts();
-
StyleModel.TransferOldModels();
if (configuration.SavedStyles == null || configuration.SavedStyles.All(x => x.Name != StyleModelV1.DalamudStandard.Name))
@@ -557,6 +555,8 @@ namespace Dalamud.Interface.Internal
ImGui.GetIO().FontGlobalScale = configuration.GlobalUiScale;
+ this.SetupFonts();
+
if (!configuration.IsDocking)
{
ImGui.GetIO().ConfigFlags &= ~ImGuiConfigFlags.DockingEnable;
@@ -833,10 +833,15 @@ namespace Dalamud.Interface.Internal
for (int i = customFontFirstConfigIndex, i_ = ioFonts.ConfigData.Size; i < i_; i++)
{
var config = ioFonts.ConfigData[i];
+ if (gameFontManager.OwnsFont(config.DstFont))
+ continue;
+
config.OversampleH = 1;
config.OversampleV = 1;
var name = Encoding.UTF8.GetString((byte*)config.Name.Data, config.Name.Count).TrimEnd('\0');
+ if (name.IsNullOrEmpty())
+ name = $"{config.SizePixels}px";
// ImFont information is reflected only if corresponding ImFontConfig has MergeMode not set.
if (config.MergeMode)
@@ -874,6 +879,7 @@ namespace Dalamud.Interface.Internal
Log.Verbose("[FONT] ImGui.IO.Build will be called.");
ioFonts.Build();
+ gameFontManager.AfterIoFontsBuild();
Log.Verbose("[FONT] ImGui.IO.Build OK!");
if (ioFonts.TexHeight > maxTexDimension)
@@ -891,6 +897,7 @@ namespace Dalamud.Interface.Internal
if (possibilityForScaling && !disableBigFonts)
{
Log.Information("[FONT] Atlas size is {0}x{1} which is bigger than allowed {2}x{3}. Retrying with minimized font sizes.", ioFonts.TexWidth, ioFonts.TexHeight, maxTexDimension, maxTexDimension);
+ this.IsFallbackFontMode = true;
this.SetupFonts(true);
return;
}
@@ -900,7 +907,8 @@ namespace Dalamud.Interface.Internal
}
}
- this.IsFallbackFontMode = disableBigFonts;
+ if (!disableBigFonts)
+ this.IsFallbackFontMode = false;
if (Math.Abs(fontGamma - 1.0f) >= 0.001)
{
@@ -934,9 +942,10 @@ namespace Dalamud.Interface.Internal
if (mod.Axis == TargetFontModification.AxisMode.Overwrite)
{
Log.Verbose("[FONT] {0}: Overwrite from AXIS of size {1}px (was {2}px)", mod.Name, mod.SourceAxis.ImFont.FontSize, font.FontSize);
- font.FontSize = mod.SourceAxis.ImFont.FontSize;
- font.Ascent = mod.SourceAxis.ImFont.Ascent;
- font.Descent = mod.SourceAxis.ImFont.Descent;
+ GameFontManager.UnscaleFont(font, font.FontSize / mod.SourceAxis.ImFont.FontSize, false);
+ var ascentDiff = mod.SourceAxis.ImFont.Ascent - font.Ascent;
+ font.Ascent += ascentDiff;
+ font.Descent = ascentDiff;
font.FallbackChar = mod.SourceAxis.ImFont.FallbackChar;
font.EllipsisChar = mod.SourceAxis.ImFont.EllipsisChar;
ImGuiHelpers.CopyGlyphsAcrossFonts(mod.SourceAxis.ImFont, font, false, false);
@@ -969,6 +978,9 @@ namespace Dalamud.Interface.Internal
this.AfterBuildFonts?.Invoke();
Log.Verbose("[FONT] OnAfterBuildFonts OK!");
+ if (ioFonts.Fonts[0].NativePtr != DefaultFont.NativePtr)
+ Log.Warning("[FONT] First font is not DefaultFont");
+
Log.Verbose("[FONT] Fonts built!");
this.fontBuildSignal.Set();
@@ -1000,7 +1012,36 @@ namespace Dalamud.Interface.Internal
Log.Verbose("[FONT] RebuildFontsInternal() detaching");
this.scene.OnNewRenderFrame -= this.RebuildFontsInternal;
- this.scene.InvalidateFonts();
+
+ Log.Verbose("[FONT] Calling InvalidateFonts");
+ try
+ {
+ this.scene.InvalidateFonts();
+ }
+ catch (Exception ex)
+ {
+ if (this.FontResolutionLevel > 2)
+ {
+ Log.Error(ex, "[FONT] Failed to create font textures; setting font resolution level to 2 and retrying");
+ this.FontResolutionLevelOverride = 2;
+ this.SetupFonts();
+ }
+ else
+ {
+ Log.Error(ex, "[FONT] Failed to create font textures; forcing fallback font mode");
+ this.SetupFonts(true);
+ }
+
+ Log.Verbose("[FONT] Calling InvalidateFonts again");
+ try
+ {
+ this.scene.InvalidateFonts();
+ }
+ catch (Exception ex2)
+ {
+ Log.Error(ex2, "[FONT] Giving up");
+ }
+ }
Log.Verbose("[FONT] Font Rebuild OK!");
diff --git a/Dalamud/Interface/Internal/Windows/IMEWindow.cs b/Dalamud/Interface/Internal/Windows/IMEWindow.cs
index 5c936d4e9..e987047a1 100644
--- a/Dalamud/Interface/Internal/Windows/IMEWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/IMEWindow.cs
@@ -1,5 +1,5 @@
using System.Numerics;
-
+using Dalamud.Game.ClientState.Keys;
using Dalamud.Game.Gui.Internal;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Windowing;
@@ -10,7 +10,7 @@ namespace Dalamud.Interface.Internal.Windows
///
/// A window for displaying IME details.
///
- internal class IMEWindow : Window
+ internal unsafe class IMEWindow : Window
{
private const int ImePageSize = 9;
@@ -18,7 +18,7 @@ namespace Dalamud.Interface.Internal.Windows
/// Initializes a new instance of the class.
///
public IMEWindow()
- : base("Dalamud IME", ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.AlwaysAutoResize)
+ : base("Dalamud IME", ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoBackground)
{
this.Size = new Vector2(100, 200);
this.SizeCondition = ImGuiCond.FirstUseEver;
@@ -29,6 +29,7 @@ namespace Dalamud.Interface.Internal.Windows
///
public override void Draw()
{
+ if (this.IsOpen && Service.Get()[VirtualKey.SHIFT]) Service.Get().CloseIMEWindow();
var ime = Service.GetNullable();
if (ime == null || !ime.IsEnabled)
@@ -36,34 +37,70 @@ namespace Dalamud.Interface.Internal.Windows
ImGui.Text("IME is unavailable.");
return;
}
+ }
- ImGui.Text(ime.ImmComp);
+ ///
+ public override void PostDraw()
+ {
+ if (this.IsOpen && Service.Get()[VirtualKey.SHIFT]) Service.Get().CloseIMEWindow();
+ var ime = Service.GetNullable();
- ImGui.Separator();
+ if (ime == null || !ime.IsEnabled)
+ return;
+
+ var cursorPos = ime.GetCursorPos();
+
+ var nextDrawPosY = cursorPos.Y;
+ var maxTextWidth = 0f;
+ var textHeight = ImGui.CalcTextSize(ime.ImmComp).Y;
+ var drawAreaPosX = cursorPos.X + ImGui.GetStyle().WindowPadding.X;
var native = ime.ImmCandNative;
- for (var i = 0; i < ime.ImmCand.Count; i++)
- {
- var selected = i == (native.Selection % ImePageSize);
-
- if (selected)
- ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.HealerGreen);
-
- ImGui.Text($"{i + 1}. {ime.ImmCand[i]}");
-
- if (selected)
- ImGui.PopStyleColor();
- }
-
var totalIndex = native.Selection + 1;
var totalSize = native.Count;
var pageStart = native.PageStart;
var pageIndex = (pageStart / ImePageSize) + 1;
var pageCount = (totalSize / ImePageSize) + 1;
+ var pageInfo = $"{totalIndex}/{totalSize} ({pageIndex}/{pageCount})";
- ImGui.Separator();
- ImGui.Text($"{totalIndex}/{totalSize} ({pageIndex}/{pageCount})");
+ // Calc the window size
+ for (var i = 0; i < ime.ImmCand.Count; i++)
+ {
+ var textSize = ImGui.CalcTextSize($"{i + 1}. {ime.ImmCand[i]}");
+ maxTextWidth = maxTextWidth > textSize.X ? maxTextWidth : textSize.X;
+ }
+
+ maxTextWidth = maxTextWidth > ImGui.CalcTextSize(pageInfo).X ? maxTextWidth : ImGui.CalcTextSize(pageInfo).X;
+ maxTextWidth = maxTextWidth > ImGui.CalcTextSize(ime.ImmComp).X ? maxTextWidth : ImGui.CalcTextSize(ime.ImmComp).X;
+
+ var imeWindowMinPos = new Vector2(cursorPos.X, cursorPos.Y);
+ var imeWindowMaxPos = new Vector2(cursorPos.X + maxTextWidth + (2 * ImGui.GetStyle().WindowPadding.X), cursorPos.Y + (textHeight * (ime.ImmCand.Count + 2)) + (5 * (ime.ImmCand.Count - 1)) + (2 * ImGui.GetStyle().WindowPadding.Y));
+
+ var drawList = ImGui.GetForegroundDrawList();
+ // Draw the background rect
+ drawList.AddRectFilled(imeWindowMinPos, imeWindowMaxPos, ImGui.GetColorU32(ImGuiCol.WindowBg), ImGui.GetStyle().WindowRounding);
+ // Add component text
+ drawList.AddText(new Vector2(drawAreaPosX, nextDrawPosY), ImGui.GetColorU32(ImGuiCol.Text), ime.ImmComp);
+ nextDrawPosY += textHeight + ImGui.GetStyle().ItemSpacing.Y;
+ // Add separator
+ drawList.AddLine(new Vector2(drawAreaPosX, nextDrawPosY), new Vector2(drawAreaPosX + maxTextWidth, nextDrawPosY), ImGui.GetColorU32(ImGuiCol.Separator));
+ // Add candidate words
+ for (var i = 0; i < ime.ImmCand.Count; i++)
+ {
+ var selected = i == (native.Selection % ImePageSize);
+ var color = ImGui.GetColorU32(ImGuiCol.Text);
+ if (selected)
+ color = ImGui.GetColorU32(ImGuiCol.NavHighlight);
+
+ drawList.AddText(new Vector2(drawAreaPosX, nextDrawPosY), color, $"{i + 1}. {ime.ImmCand[i]}");
+ nextDrawPosY += textHeight + ImGui.GetStyle().ItemSpacing.Y;
+ }
+
+ // Add separator
+ drawList.AddLine(new Vector2(drawAreaPosX, nextDrawPosY), new Vector2(drawAreaPosX + maxTextWidth, nextDrawPosY), ImGui.GetColorU32(ImGuiCol.Separator));
+ // Add pages infomation
+ drawList.AddText(new Vector2(drawAreaPosX, nextDrawPosY), ImGui.GetColorU32(ImGuiCol.Text), pageInfo);
}
}
}
diff --git a/Dalamud/Interface/Internal/Windows/SettingsWindow.cs b/Dalamud/Interface/Internal/Windows/SettingsWindow.cs
index 72f699840..8f86752fa 100644
--- a/Dalamud/Interface/Internal/Windows/SettingsWindow.cs
+++ b/Dalamud/Interface/Internal/Windows/SettingsWindow.cs
@@ -367,6 +367,8 @@ namespace Dalamud.Interface.Internal.Windows
ImGuiHelpers.ScaledDummy(3);
ImGui.Text(Loc.Localize("DalamudSettingsFontResolutionLevel", "Font resolution level"));
+ if (interfaceManager.FontResolutionLevelOverride != null)
+ this.fontResolutionLevel = interfaceManager.FontResolutionLevelOverride.Value;
if (ImGui.Combo("##DalamudSettingsFontResolutionLevelCombo", ref this.fontResolutionLevel, this.fontResolutionLevelStrings, this.fontResolutionLevelStrings.Length))
{
interfaceManager.FontResolutionLevelOverride = this.fontResolutionLevel;
@@ -375,7 +377,9 @@ namespace Dalamud.Interface.Internal.Windows
ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey);
ImGui.TextWrapped(string.Format(
- Loc.Localize("DalamudSettingsFontResolutionLevelHint", "This option allows Dalamud fonts to look better. If your game crashes when changing this option, your PC does not support high font resolutions in Dalamud - you will have to use a lower one.\nCurrent font atlas size is {0}px * {1}px."),
+ Loc.Localize(
+ "DalamudSettingsFontResolutionLevelHint",
+ "This option allows Dalamud fonts to look better.\n* If your game crashes right away, or the option reverts, when changing this option, your PC does not support high font resolutions in Dalamud - you will have to use a lower one.\n* If it doesn't crash or revert immediately, then you can keep the new choice indefinitely as it's not going to crash your game once it worked.\n* Either choose the 3rd or 5th option. Use other options only when neither works well.\n* Current font atlas size is {0}px * {1}px."),
ImGui.GetIO().Fonts.TexWidth,
ImGui.GetIO().Fonts.TexHeight));
ImGui.PopStyleColor();