diff --git a/StArray.ModManager.Windows/UI/D3D11Renderer.cs b/StArray.ModManager.Windows/UI/D3D11Renderer.cs
index 1ca7e21..286d43a 100644
--- a/StArray.ModManager.Windows/UI/D3D11Renderer.cs
+++ b/StArray.ModManager.Windows/UI/D3D11Renderer.cs
@@ -132,8 +132,7 @@ void Init(nint sc)
_origWnd = Win32Native.SetWindowLongPtrW(_hwnd, -4, Marshal.GetFunctionPointerForDelegate(_wndDel));
// ImGui
- ImGui.CreateContext();
- ImGui.GetIO().ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.DockingEnable;
+ ((IImGuiRenderer)this).InitImGui();
ImGui_ImplWin32_Init(_hwnd);
ImGui_ImplDX11_Init(_dev, _ctx);
_inited = true; Logger.Info(nameof(D3D11Renderer), "ImGui ready");
diff --git a/StArray.ModManager/Resources/NotoSans-Regular.ttf b/StArray.ModManager/Resources/NotoSans-Regular.ttf
new file mode 100644
index 0000000..d552209
Binary files /dev/null and b/StArray.ModManager/Resources/NotoSans-Regular.ttf differ
diff --git a/StArray.ModManager/Resources/NotoSansSymbols2-Regular.ttf b/StArray.ModManager/Resources/NotoSansSymbols2-Regular.ttf
new file mode 100644
index 0000000..d786ee8
Binary files /dev/null and b/StArray.ModManager/Resources/NotoSansSymbols2-Regular.ttf differ
diff --git a/StArray.ModManager/Resources/OpenMoji-black-glyf.ttf b/StArray.ModManager/Resources/OpenMoji-black-glyf.ttf
new file mode 100644
index 0000000..1ff6d3c
Binary files /dev/null and b/StArray.ModManager/Resources/OpenMoji-black-glyf.ttf differ
diff --git a/StArray.ModManager/RuntimeAbstractions/RuntimeManager.cs b/StArray.ModManager/RuntimeAbstractions/RuntimeManager.cs
index 8c9e57c..a2788cb 100644
--- a/StArray.ModManager/RuntimeAbstractions/RuntimeManager.cs
+++ b/StArray.ModManager/RuntimeAbstractions/RuntimeManager.cs
@@ -148,11 +148,7 @@ private static extern uint GetModuleFileNameW(IntPtr hModule,
internal const int RtldNoLoad = 0x0004;
private static bool IsUnixLibraryLoaded(string filename)
- => ProbeUnixLibrary(
- filename,
- (name, flags) => DL.Open(
- name, (DL.RTLDFlags)(flags)), // ProbeUnixLibrary 只传 NOW|NOLOAD,直接透传
- handle => _ = DL.Close(handle));
+ => DL.GetBaseAddress(filename) != IntPtr.Zero;
internal static bool ProbeUnixLibrary(
string filename,
diff --git a/StArray.ModManager/StArray.ModManager.csproj b/StArray.ModManager/StArray.ModManager.csproj
index d3c3a9a..3f4cf47 100644
--- a/StArray.ModManager/StArray.ModManager.csproj
+++ b/StArray.ModManager/StArray.ModManager.csproj
@@ -24,7 +24,10 @@
+
+
+
diff --git a/StArray.ModManager/UI/IImGuiRenderer.cs b/StArray.ModManager/UI/IImGuiRenderer.cs
index 0287db1..075ba16 100644
--- a/StArray.ModManager/UI/IImGuiRenderer.cs
+++ b/StArray.ModManager/UI/IImGuiRenderer.cs
@@ -27,72 +27,162 @@ void InitImGui()
ImGui.SetCurrentContext(ImGui.CreateContext());
var io = ImGui.GetIO();
io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.DockingEnable;
- LoadIconFont(io);
- LoadEmbeddedFont(io);
- // 注意:AddFontFromMemoryTTF 只存指针,Build() 时才真正读取数据
- io.Fonts.Build();
- // Build() 之后才能安全释放字体内存
- FreeFontMemory();
+
+ try
+ {
+ var hasBaseFont = LoadEmbeddedFont(
+ io,
+ "StArray.ModManager.Resources.NotoSansCJK-Regular.otf",
+ ref _cjkFontPtr,
+ merge: false,
+ GetAllUnicodeRanges());
+
+ var latinFontIsBase = false;
+ if (!hasBaseFont)
+ {
+ latinFontIsBase = LoadEmbeddedFont(
+ io,
+ "StArray.ModManager.Resources.NotoSans-Regular.ttf",
+ ref _latinFontPtr,
+ merge: false,
+ GetAllUnicodeRanges());
+ hasBaseFont = latinFontIsBase;
+ }
+
+ if (!hasBaseFont)
+ {
+ io.Fonts.AddFontDefault();
+ hasBaseFont = true;
+ }
+
+ if (!latinFontIsBase)
+ {
+ LoadEmbeddedFont(
+ io,
+ "StArray.ModManager.Resources.NotoSans-Regular.ttf",
+ ref _latinFontPtr,
+ merge: true,
+ GetAllUnicodeRanges());
+ }
+ LoadEmbeddedFont(
+ io,
+ "StArray.ModManager.Resources.NotoSansSymbols2-Regular.ttf",
+ ref _symbolsFontPtr,
+ merge: true,
+ GetAllUnicodeRanges());
+ LoadEmbeddedFont(
+ io,
+ "StArray.ModManager.Resources.OpenMoji-black-glyf.ttf",
+ ref _emojiFontPtr,
+ merge: true,
+ GetAllUnicodeRanges());
+ LoadEmbeddedFont(
+ io,
+ "StArray.ModManager.Resources.fa-solid-900.ttf",
+ ref _iconFontPtr,
+ merge: true,
+ GetIconRanges());
+
+ // AddFontFromMemoryTTF 只保存指针,Build() 时才真正读取数据。
+ io.Fonts.Build();
+ }
+ finally
+ {
+ FreeFontMemory();
+ }
}
- private static nint _fontPtr1, _fontPtr2;
+ private static nint _cjkFontPtr;
+ private static nint _latinFontPtr;
+ private static nint _symbolsFontPtr;
+ private static nint _emojiFontPtr;
+ private static nint _iconFontPtr;
+ private static nint _allUnicodeRangesPtr;
+ private static nint _iconRangesPtr;
private static void FreeFontMemory()
{
- if (_fontPtr1 != 0) { Marshal.FreeHGlobal(_fontPtr1); _fontPtr1 = 0; }
- if (_fontPtr2 != 0) { Marshal.FreeHGlobal(_fontPtr2); _fontPtr2 = 0; }
+ Free(ref _cjkFontPtr);
+ Free(ref _latinFontPtr);
+ Free(ref _symbolsFontPtr);
+ Free(ref _emojiFontPtr);
+ Free(ref _iconFontPtr);
+ Free(ref _allUnicodeRangesPtr);
+ Free(ref _iconRangesPtr);
}
- private static unsafe void LoadEmbeddedFont(ImGuiIOPtr io)
+ private static void Free(ref nint ptr)
{
- try
- {
- var asm = typeof(IImGuiRenderer).Assembly;
- using var stream = asm.GetManifestResourceStream(
- "StArray.ModManager.Resources.NotoSansCJK-Regular.otf");
- if (stream == null) return;
+ if (ptr == 0) return;
+ Marshal.FreeHGlobal(ptr);
+ ptr = 0;
+ }
- var ttf = new byte[stream.Length];
- stream.ReadExactly(ttf);
- _fontPtr1 = Marshal.AllocHGlobal(ttf.Length);
- Marshal.Copy(ttf, 0, _fontPtr1, ttf.Length);
+ private static nint GetAllUnicodeRanges()
+ {
+ if (_allUnicodeRangesPtr != 0) return _allUnicodeRangesPtr;
+ _allUnicodeRangesPtr = AllocateRanges(0x0001, 0xFFFF);
+ return _allUnicodeRangesPtr;
+ }
- // MergeMode: 中文字形合并到图标基础字体
- var cfg = ImGuiNative.ImFontConfig_ImFontConfig();
- cfg->MergeMode = 1;
- cfg->FontDataOwnedByAtlas = 0; // 自己管理内存,Build() 后释放
+ private static nint GetIconRanges()
+ {
+ if (_iconRangesPtr != 0) return _iconRangesPtr;
+ _iconRangesPtr = AllocateRanges(0xE000, 0xF8FF);
+ return _iconRangesPtr;
+ }
- var glyphRanges = io.Fonts.GetGlyphRangesChineseSimplifiedCommon();
- io.Fonts.AddFontFromMemoryTTF(_fontPtr1, ttf.Length, 16f, cfg, glyphRanges);
- ImGuiNative.ImFontConfig_destroy(cfg);
- }
- catch { /* 静默跳过 */ }
+ private static nint AllocateRanges(ushort first, ushort last)
+ {
+ var ptr = Marshal.AllocHGlobal(sizeof(ushort) * 3);
+ Marshal.WriteInt16(ptr, 0, (short)first);
+ Marshal.WriteInt16(ptr, sizeof(ushort), (short)last);
+ Marshal.WriteInt16(ptr, sizeof(ushort) * 2, 0);
+ return ptr;
}
- private static unsafe void LoadIconFont(ImGuiIOPtr io)
+ private static unsafe bool LoadEmbeddedFont(
+ ImGuiIOPtr io,
+ string resourceName,
+ ref nint fontPtr,
+ bool merge,
+ nint glyphRanges)
{
try
{
var asm = typeof(IImGuiRenderer).Assembly;
- using var stream = asm.GetManifestResourceStream(
- "StArray.ModManager.Resources.fa-solid-900.ttf");
- if (stream == null) return;
+ using var stream = asm.GetManifestResourceStream(resourceName);
+ if (stream == null) return false;
- var ttf = new byte[stream.Length];
- stream.ReadExactly(ttf);
- _fontPtr2 = Marshal.AllocHGlobal(ttf.Length);
- Marshal.Copy(ttf, 0, _fontPtr2, ttf.Length);
+ var fontData = new byte[checked((int)stream.Length)];
+ stream.ReadExactly(fontData);
+ fontPtr = Marshal.AllocHGlobal(fontData.Length);
+ Marshal.Copy(fontData, 0, fontPtr, fontData.Length);
- // 基础字体:FontAwesome 7 图标
var cfg = ImGuiNative.ImFontConfig_ImFontConfig();
- cfg->FontDataOwnedByAtlas = 0; // 自己管理内存
+ try
+ {
+ cfg->MergeMode = merge ? (byte)1 : (byte)0;
+ cfg->FontDataOwnedByAtlas = 0;
+ io.Fonts.AddFontFromMemoryTTF(
+ fontPtr,
+ fontData.Length,
+ 16f,
+ cfg,
+ glyphRanges);
+ }
+ finally
+ {
+ ImGuiNative.ImFontConfig_destroy(cfg);
+ }
- ushort[] iconRange = [0xe005, 0xf8ff, 0];
- fixed (ushort* r = iconRange)
- io.Fonts.AddFontFromMemoryTTF(_fontPtr2, ttf.Length, 16f, cfg, (IntPtr)r);
- ImGuiNative.ImFontConfig_destroy(cfg);
+ return true;
+ }
+ catch
+ {
+ Free(ref fontPtr);
+ return false;
}
- catch { /* 静默跳过 */ }
}
///