diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index 7ea4f2caa..3502d1bc0 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -6,7 +6,7 @@ XIV Launcher addon framework - 13.0.0.13 + 13.0.0.16 $(DalamudVersion) $(DalamudVersion) $(DalamudVersion) diff --git a/Dalamud/Data/DataManager.cs b/Dalamud/Data/DataManager.cs index 559bd84dc..f53195a2d 100644 --- a/Dalamud/Data/DataManager.cs +++ b/Dalamud/Data/DataManager.cs @@ -84,8 +84,11 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager dalamud.StartInfo.TroubleshootingPackData); // Don't fail for IndexIntegrityResult.Exception, since the check during launch has a very small timeout - this.HasModifiedGameDataFiles = - tsInfo?.IndexIntegrity is LauncherTroubleshootingInfo.IndexIntegrityResult.Failed; + // this.HasModifiedGameDataFiles = + // tsInfo?.IndexIntegrity is LauncherTroubleshootingInfo.IndexIntegrityResult.Failed; + + // TODO: Put above back when check in XL is fixed + this.HasModifiedGameDataFiles = false; if (this.HasModifiedGameDataFiles) Log.Verbose("Game data integrity check failed!\n{TsData}", dalamud.StartInfo.TroubleshootingPackData); diff --git a/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs b/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs index a9e9b5a0f..ea60be98d 100644 --- a/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs +++ b/Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using System.Text; using Dalamud.Bindings.ImGui; +using Dalamud.Console; using Dalamud.Memory; using Dalamud.Utility; @@ -37,6 +38,8 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler private readonly WndProcDelegate wndProcDelegate; private readonly nint platformNamePtr; + private readonly IConsoleVariable cvLogMouseEvents; + private ViewportHandler viewportHandler; private int mouseButtonsDown; @@ -87,6 +90,11 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler this.cursors[(int)ImGuiMouseCursor.ResizeNwse] = LoadCursorW(default, IDC.IDC_SIZENWSE); this.cursors[(int)ImGuiMouseCursor.Hand] = LoadCursorW(default, IDC.IDC_HAND); this.cursors[(int)ImGuiMouseCursor.NotAllowed] = LoadCursorW(default, IDC.IDC_NO); + + this.cvLogMouseEvents = Service.Get().AddVariable( + "imgui.log_mouse_events", + "Log mouse events to console for debugging", + false); } /// @@ -267,11 +275,23 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler case WM.WM_XBUTTONDOWN: case WM.WM_XBUTTONDBLCLK: { + if (this.cvLogMouseEvents.Value) + { + Log.Verbose( + "Handle MouseDown {Btn} WantCaptureMouse: {Want} mouseButtonsDown: {Down}", + GetButton(msg, wParam), + io.WantCaptureMouse, + this.mouseButtonsDown); + } + var button = GetButton(msg, wParam); if (io.WantCaptureMouse) { if (this.mouseButtonsDown == 0 && GetCapture() == nint.Zero) + { SetCapture(hWndCurrent); + } + this.mouseButtonsDown |= 1 << button; io.AddMouseButtonEvent(button, true); return default(LRESULT); @@ -288,12 +308,28 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler case WM.WM_MBUTTONUP: case WM.WM_XBUTTONUP: { + if (this.cvLogMouseEvents.Value) + { + Log.Verbose( + "Handle MouseUp {Btn} WantCaptureMouse: {Want} mouseButtonsDown: {Down}", + GetButton(msg, wParam), + io.WantCaptureMouse, + this.mouseButtonsDown); + } + var button = GetButton(msg, wParam); - if (io.WantCaptureMouse) + + // Need to check if we captured the button event away from the game here, otherwise the game might get + // a down event but no up event, causing the cursor to get stuck. + // Can happen if WantCaptureMouse becomes true in between down and up + if (io.WantCaptureMouse && (this.mouseButtonsDown & (1 << button)) != 0) { this.mouseButtonsDown &= ~(1 << button); if (this.mouseButtonsDown == 0 && GetCapture() == hWndCurrent) + { ReleaseCapture(); + } + io.AddMouseButtonEvent(button, false); return default(LRESULT); } @@ -458,7 +494,12 @@ internal sealed unsafe partial class Win32InputHandler : IImGuiInputHandler // (This is the position you can get with ::GetCursorPos() or WM_MOUSEMOVE + ::ClientToScreen(). In theory adding viewport->Pos to a client position would also be the same.) var mousePos = mouseScreenPos; if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) == 0) - ClientToScreen(focusedWindow, &mousePos); + { + // Use game window, otherwise, positions are calculated based on the focused window which might not be the game. + // Leads to offsets. + ClientToScreen(this.hWnd, &mousePos); + } + io.AddMousePosEvent(mousePos.x, mousePos.y); } diff --git a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs index 397502b30..4a8e6517e 100644 --- a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs +++ b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs @@ -170,7 +170,7 @@ internal class SeStringRenderer : IServiceType // This also does argument validation for drawParams. Do it here. // `using var` makes a struct read-only, but we do want to modify it. - var stateStorage = new SeStringDrawState( + using var stateStorage = new SeStringDrawState( sss, drawParams, ThreadSafety.IsMainThread ? this.colorStackSetMainThread : new(this.colorStackSetMainThread.ColorTypes), diff --git a/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawState.cs b/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawState.cs index c5aba26c1..5edf60e9d 100644 --- a/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawState.cs +++ b/Dalamud/Interface/ImGuiSeStringRenderer/SeStringDrawState.cs @@ -10,6 +10,7 @@ using Dalamud.Interface.Utility; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Component.GUI; + using Lumina.Text.Payloads; using Lumina.Text.ReadOnly; @@ -17,7 +18,7 @@ namespace Dalamud.Interface.ImGuiSeStringRenderer; /// Calculated values from using ImGui styles. [StructLayout(LayoutKind.Sequential)] -public unsafe ref struct SeStringDrawState +public unsafe ref struct SeStringDrawState : IDisposable { private static readonly int ChannelCount = Enum.GetValues().Length; @@ -194,6 +195,9 @@ public unsafe ref struct SeStringDrawState /// Gets the text fragments. internal List Fragments { get; } + /// + public void Dispose() => this.splitter.ClearFreeMemory(); + /// Sets the current channel in the ImGui draw list splitter. /// Channel to switch to. [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs index bf55a5486..b0fbeb6c5 100644 --- a/Dalamud/Interface/Internal/DalamudInterface.cs +++ b/Dalamud/Interface/Internal/DalamudInterface.cs @@ -669,6 +669,8 @@ internal class DalamudInterface : IInternalDisposableService { using var barColor = ImRaii.PushColor(ImGuiCol.WindowBg, new Vector4(0.060f, 0.060f, 0.060f, 0.773f)); barColor.Push(ImGuiCol.MenuBarBg, Vector4.Zero); + barColor.Push(ImGuiCol.Border, Vector4.Zero); + barColor.Push(ImGuiCol.BorderShadow, Vector4.Zero); if (ImGui.BeginMainMenuBar()) { var pluginManager = Service.Get(); diff --git a/Dalamud/Interface/Windowing/Persistence/PresetModel.cs b/Dalamud/Interface/Windowing/Persistence/PresetModel.cs index f7910e0b2..4ddf55e51 100644 --- a/Dalamud/Interface/Windowing/Persistence/PresetModel.cs +++ b/Dalamud/Interface/Windowing/Persistence/PresetModel.cs @@ -53,6 +53,7 @@ internal class PresetModel /// /// Gets a value indicating whether this preset is in the default state. /// + [JsonIgnore] public bool IsDefault => !this.IsPinned && !this.IsClickThrough && diff --git a/Dalamud/Interface/Windowing/Window.cs b/Dalamud/Interface/Windowing/Window.cs index 48352daa2..5a79a017a 100644 --- a/Dalamud/Interface/Windowing/Window.cs +++ b/Dalamud/Interface/Windowing/Window.cs @@ -84,7 +84,7 @@ public abstract class Window Click = _ => { this.internalIsClickthrough = false; - this.presetDirty = false; + this.presetDirty = true; ImGui.OpenPopup(AdditionsPopupName); }, Priority = int.MinValue, @@ -672,16 +672,13 @@ public abstract class Window Task.FromResult(tex)); } - if (!this.hasError) + if (isErrorStylePushed) { - this.PostDraw(); + Style.StyleModelV1.DalamudStandard.Pop(); } else { - if (isErrorStylePushed) - { - Style.StyleModelV1.DalamudStandard.Pop(); - } + this.PostDraw(); } this.PostHandlePreset(persistence); @@ -864,7 +861,7 @@ public abstract class Window foreach (var button in this.allButtons) { if (this.internalIsClickthrough && !button.AvailableClickthrough) - return; + continue; Vector2 position = new(titleBarRect.Max.X - padR - buttonSize, titleBarRect.Min.Y + style.FramePadding.Y); padR += buttonSize + style.ItemInnerSpacing.X;