From ee2c8dd9ccb726b2d6753446d0a0e6cb3e80d24f Mon Sep 17 00:00:00 2001 From: Nathan C <39100023+nathanctech@users.noreply.github.com> Date: Thu, 13 Mar 2025 17:01:39 -0400 Subject: [PATCH 1/3] Build adjustments for code annotation (#2155) * Drop special formatting, allowing annotations to work properly. * Suppress duplicate warnings, adding a prefix to prevent annotation * Tweak message, don't rebuild on test * Move testing into same job step * Only run PR build on newly opened PRs * flip build order, derp * Test suppressing summary for annotations... * Get the build order right, testing without conditionals... * Run tests after compile, suppress warnings from test * Reverted previous change to `main.yml`. * Drop special formatting, allowing annotations to work properly. * Suppress duplicate warnings, adding a prefix to prevent annotation * Tweak message, don't rebuild on test * Move testing into same job step * Only run PR build on newly opened PRs * flip build order, derp * Test suppressing summary for annotations... * Get the build order right, testing without conditionals... * Run tests after compile, suppress warnings from test * Reverted previous change to `main.yml`. * Drop special formatting, allowing annotations to work properly. * Suppress duplicate warnings, adding a prefix to prevent annotation * Tweak message, don't rebuild on test * Move testing into same job step * Only run PR build on newly opened PRs * flip build order, derp * Test suppressing summary for annotations... * Get the build order right, testing without conditionals... * Run tests after compile, suppress warnings from test * Reverted previous change to `main.yml`. * Add conditional for CI builds, add --skip-tests to make up for the combined build/test step. * Behavior change, now requires arg `ci` to be passed to trigger tests. Tests can also be manually triggered with `test`. --- .github/workflows/main.yml | 27 +++++++++++++------------ .nuke/build.schema.json | 4 ++++ build/DalamudBuild.cs | 40 +++++++++++++++++++++++++++++++------- 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 930adf8ed..f1267baca 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,5 +1,6 @@ name: Build Dalamud on: [push, pull_request, workflow_dispatch] + concurrency: group: build_dalamud_${{ github.ref_name }} cancel-in-progress: true @@ -32,10 +33,8 @@ jobs: ($env:REPO_NAME) >> VERSION ($env:BRANCH) >> VERSION ($env:COMMIT) >> VERSION - - name: Build Dalamud - run: .\build.ps1 compile - - name: Test Dalamud - run: .\build.ps1 test + - name: Build and Test Dalamud + run: .\build.ps1 ci - name: Sign Dalamud if: ${{ github.repository_owner == 'goatcorp' && github.event_name == 'push' }} env: @@ -87,9 +86,9 @@ jobs: - name: "Verify Compatibility" run: | $FILES_TO_VALIDATE = "Dalamud.dll","FFXIVClientStructs.dll","Lumina.dll","Lumina.Excel.dll" - + $retcode = 0 - + foreach ($file in $FILES_TO_VALIDATE) { $testout = "" Write-Output "::group::=== API COMPATIBILITY CHECK: ${file} ===" @@ -100,7 +99,7 @@ jobs: $retcode = 1 } } - + exit $retcode deploy_stg: @@ -129,18 +128,18 @@ jobs: GH_BRANCH: ${{ steps.extract_branch.outputs.branch }} run: | Compress-Archive .\scratch\* .\canary.zip # Recreate the release zip - + $branchName = $env:GH_BRANCH - + if ($branchName -eq "master") { $branchName = "stg" } - + $newVersion = [System.IO.File]::ReadAllText("$(Get-Location)\scratch\TEMP_gitver.txt") $revision = [System.IO.File]::ReadAllText("$(Get-Location)\scratch\revision.txt") $commitHash = [System.IO.File]::ReadAllText("$(Get-Location)\scratch\commit_hash.txt") Remove-Item -Force -Recurse .\scratch - + if (Test-Path -Path $branchName) { $versionData = Get-Content ".\${branchName}\version" | ConvertFrom-Json $oldVersion = $versionData.AssemblyVersion @@ -159,7 +158,7 @@ jobs: Write-Host "Deployment folder doesn't exist. Not doing anything." Remove-Item .\canary.zip } - + - name: Commit changes shell: bash env: @@ -167,8 +166,8 @@ jobs: run: | git config --global user.name "Actions User" git config --global user.email "actions@github.com" - + git add . git commit -m "[CI] Update staging for ${DVER} on ${GH_BRANCH}" || true - + git push origin main || true diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index e7e1a446a..8331affcc 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -76,6 +76,7 @@ "items": { "type": "string", "enum": [ + "CI", "Clean", "Compile", "CompileCImGui", @@ -88,6 +89,7 @@ "CompileInjector", "CompileInjectorBoot", "Restore", + "SetCILogging", "Test" ] } @@ -102,6 +104,7 @@ "items": { "type": "string", "enum": [ + "CI", "Clean", "Compile", "CompileCImGui", @@ -114,6 +117,7 @@ "CompileInjector", "CompileInjectorBoot", "Restore", + "SetCILogging", "Test" ] } diff --git a/build/DalamudBuild.cs b/build/DalamudBuild.cs index 67a812916..d374c79f8 100644 --- a/build/DalamudBuild.cs +++ b/build/DalamudBuild.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using Nuke.Common; @@ -5,6 +6,7 @@ using Nuke.Common.Execution; using Nuke.Common.Git; using Nuke.Common.IO; using Nuke.Common.ProjectModel; +using Nuke.Common.Tooling; using Nuke.Common.Tools.DotNet; using Nuke.Common.Tools.MSBuild; using Serilog; @@ -61,6 +63,9 @@ public class DalamudBuild : NukeBuild private static Dictionary EnvironmentVariables => new(EnvironmentInfo.Variables); + private static string ConsoleTemplate => "{Message:l}{NewLine}{Exception}"; + private static bool IsCIBuild => Environment.GetEnvironmentVariable("CI") == "true"; + Target Restore => _ => _ .Executes(() => { @@ -123,16 +128,20 @@ public class DalamudBuild : NukeBuild .SetProjectFile(DalamudProjectFile) .SetConfiguration(Configuration) .EnableNoRestore(); - + if (IsCIBuild) + { + s = s + .SetProcessArgumentConfigurator(a => a.Add("/clp:NoSummary")); // Disable MSBuild summary on CI builds + } // We need to emit compiler generated files for the docs build, since docfx can't run generators directly // TODO: This fails every build after this because of redefinitions... + // if (IsDocsBuild) // { // Log.Warning("Building for documentation, emitting compiler generated files. This can cause issues on Windows due to path-length limitations"); // s = s // .SetProperty("IsDocsBuild", "true"); // } - return s; }); }); @@ -171,12 +180,28 @@ public class DalamudBuild : NukeBuild .SetConfiguration(Configuration)); }); + Target SetCILogging => _ => _ + .DependentFor(Compile) + .OnlyWhenStatic(() => IsCIBuild) + .Executes(() => + { + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.Console(outputTemplate: ConsoleTemplate) + .CreateLogger(); + }); + Target Compile => _ => _ - .DependsOn(CompileDalamud) - .DependsOn(CompileDalamudBoot) - .DependsOn(CompileDalamudCrashHandler) - .DependsOn(CompileInjector) - .DependsOn(CompileInjectorBoot); + .DependsOn(CompileDalamud) + .DependsOn(CompileDalamudBoot) + .DependsOn(CompileDalamudCrashHandler) + .DependsOn(CompileInjector) + .DependsOn(CompileInjectorBoot) + ; + + Target CI => _ => _ + .DependsOn(Compile) + .Triggers(Test); Target Test => _ => _ .DependsOn(Compile) @@ -185,6 +210,7 @@ public class DalamudBuild : NukeBuild DotNetTasks.DotNetTest(s => s .SetProjectFile(TestProjectFile) .SetConfiguration(Configuration) + .AddProperty("WarningLevel", "0") .EnableNoRestore()); }); From 577977350f1a2350ba4d4768061a1985f29c5252 Mon Sep 17 00:00:00 2001 From: KazWolfe Date: Thu, 13 Mar 2025 14:16:28 -0700 Subject: [PATCH 2/3] fix: Attempt to better handle hook disposal (#1803) - Use a Weak Concurrent Collection to track scoped hooks - Make `Hook`s remove themselves from the Tracked Hook list. --- Dalamud/Hooking/AsmHook.cs | 8 +++- Dalamud/Hooking/Hook.cs | 5 +++ .../Internal/FunctionPointerVariableHook.cs | 4 +- .../GameInteropProviderPluginScoped.cs | 6 +-- Dalamud/Hooking/Internal/MinHookHook.cs | 4 +- Dalamud/Hooking/Internal/ReloadedHook.cs | 4 +- .../Internal/Windows/PluginStatWindow.cs | 13 ------ Dalamud/Utility/WeakConcurrentCollection.cs | 44 +++++++++++++++++++ 8 files changed, 67 insertions(+), 21 deletions(-) create mode 100644 Dalamud/Utility/WeakConcurrentCollection.cs diff --git a/Dalamud/Hooking/AsmHook.cs b/Dalamud/Hooking/AsmHook.cs index 4ee96bb15..f1ed7fd11 100644 --- a/Dalamud/Hooking/AsmHook.cs +++ b/Dalamud/Hooking/AsmHook.cs @@ -20,6 +20,8 @@ public sealed class AsmHook : IDisposable, IDalamudHook private bool isEnabled = false; private DynamicMethod statsMethod; + + private Guid hookId = Guid.NewGuid(); /// /// Initializes a new instance of the class. @@ -44,7 +46,7 @@ public sealed class AsmHook : IDisposable, IDalamudHook this.statsMethod.GetILGenerator().Emit(OpCodes.Ret); var dele = this.statsMethod.CreateDelegate(typeof(Action)); - HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, dele, Assembly.GetCallingAssembly())); + HookManager.TrackedHooks.TryAdd(this.hookId, new HookInfo(this, dele, Assembly.GetCallingAssembly())); } /// @@ -70,7 +72,7 @@ public sealed class AsmHook : IDisposable, IDalamudHook this.statsMethod.GetILGenerator().Emit(OpCodes.Ret); var dele = this.statsMethod.CreateDelegate(typeof(Action)); - HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, dele, Assembly.GetCallingAssembly())); + HookManager.TrackedHooks.TryAdd(this.hookId, new HookInfo(this, dele, Assembly.GetCallingAssembly())); } /// @@ -116,6 +118,8 @@ public sealed class AsmHook : IDisposable, IDalamudHook this.IsDisposed = true; + HookManager.TrackedHooks.TryRemove(this.hookId, out _); + if (this.isEnabled) { this.isEnabled = false; diff --git a/Dalamud/Hooking/Hook.cs b/Dalamud/Hooking/Hook.cs index da65fedc7..20796bbf0 100644 --- a/Dalamud/Hooking/Hook.cs +++ b/Dalamud/Hooking/Hook.cs @@ -71,6 +71,11 @@ public abstract class Hook : IDalamudHook where T : Delegate /// public virtual string BackendName => throw new NotImplementedException(); + /// + /// Gets the unique GUID for this hook. + /// + protected Guid HookId { get; } = Guid.NewGuid(); + /// /// Remove a hook from the current process. /// diff --git a/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs b/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs index 7a177206f..40a33fc1b 100644 --- a/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs +++ b/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs @@ -100,7 +100,7 @@ internal class FunctionPointerVariableHook : Hook unhooker.TrimAfterHook(); - HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly)); + HookManager.TrackedHooks.TryAdd(this.HookId, new HookInfo(this, detour, callingAssembly)); } } @@ -137,6 +137,8 @@ internal class FunctionPointerVariableHook : Hook this.Disable(); + HookManager.TrackedHooks.TryRemove(this.HookId, out _); + var index = HookManager.MultiHookTracker[this.Address].IndexOf(this); HookManager.MultiHookTracker[this.Address][index] = null; diff --git a/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs b/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs index f7c4db00a..52d266335 100644 --- a/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs +++ b/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs @@ -1,5 +1,4 @@ -using System.Collections.Concurrent; -using System.Diagnostics; +using System.Diagnostics; using System.Linq; using Dalamud.Game; @@ -7,6 +6,7 @@ using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Services; +using Dalamud.Utility; using Dalamud.Utility.Signatures; using Serilog; @@ -25,7 +25,7 @@ internal class GameInteropProviderPluginScoped : IGameInteropProvider, IInternal private readonly LocalPlugin plugin; private readonly SigScanner scanner; - private readonly ConcurrentBag trackedHooks = new(); + private readonly WeakConcurrentCollection trackedHooks = new(); /// /// Initializes a new instance of the class. diff --git a/Dalamud/Hooking/Internal/MinHookHook.cs b/Dalamud/Hooking/Internal/MinHookHook.cs index 4cebca0d0..0305f3c84 100644 --- a/Dalamud/Hooking/Internal/MinHookHook.cs +++ b/Dalamud/Hooking/Internal/MinHookHook.cs @@ -35,7 +35,7 @@ internal class MinHookHook : Hook where T : Delegate unhooker.TrimAfterHook(); - HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly)); + HookManager.TrackedHooks.TryAdd(this.HookId, new HookInfo(this, detour, callingAssembly)); } } @@ -76,6 +76,8 @@ internal class MinHookHook : Hook where T : Delegate HookManager.MultiHookTracker[this.Address][index] = null; } + HookManager.TrackedHooks.TryRemove(this.HookId, out _); + base.Dispose(); } diff --git a/Dalamud/Hooking/Internal/ReloadedHook.cs b/Dalamud/Hooking/Internal/ReloadedHook.cs index bf441a86d..2b0a4e9ce 100644 --- a/Dalamud/Hooking/Internal/ReloadedHook.cs +++ b/Dalamud/Hooking/Internal/ReloadedHook.cs @@ -30,7 +30,7 @@ internal class ReloadedHook : Hook where T : Delegate unhooker.TrimAfterHook(); - HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly)); + HookManager.TrackedHooks.TryAdd(this.HookId, new HookInfo(this, detour, callingAssembly)); } } @@ -63,6 +63,8 @@ internal class ReloadedHook : Hook where T : Delegate if (this.IsDisposed) return; + HookManager.TrackedHooks.TryRemove(this.HookId, out _); + this.Disable(); base.Dispose(); diff --git a/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs b/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs index 314f023da..eeafa98e7 100644 --- a/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs @@ -258,8 +258,6 @@ internal class PluginStatWindow : Window ImGui.EndTabItem(); } - var toRemove = new List(); - if (ImGui.BeginTabItem("Hooks")) { ImGui.Checkbox("Show Dalamud Hooks", ref this.showDalamudHooks); @@ -291,9 +289,6 @@ internal class PluginStatWindow : Window { try { - if (trackedHook.Hook.IsDisposed) - toRemove.Add(guid); - if (!this.showDalamudHooks && trackedHook.Assembly == Assembly.GetExecutingAssembly()) continue; @@ -355,14 +350,6 @@ internal class PluginStatWindow : Window } } - if (ImGui.IsWindowAppearing()) - { - foreach (var guid in toRemove) - { - HookManager.TrackedHooks.TryRemove(guid, out _); - } - } - ImGui.EndTabBar(); } } diff --git a/Dalamud/Utility/WeakConcurrentCollection.cs b/Dalamud/Utility/WeakConcurrentCollection.cs new file mode 100644 index 000000000..a3bc04651 --- /dev/null +++ b/Dalamud/Utility/WeakConcurrentCollection.cs @@ -0,0 +1,44 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Dalamud.Utility; + +/// +/// An implementation of a weak concurrent set based on a . +/// +/// The type of object that we're tracking. +public class WeakConcurrentCollection : ICollection where T : class +{ + private readonly ConditionalWeakTable cwt = new(); + + /// + public int Count => this.cwt.Count(); + + /// + public bool IsReadOnly => false; + + private IEnumerable Keys => this.cwt.Select(pair => pair.Key); + + /// + public IEnumerator GetEnumerator() => this.cwt.Select(pair => pair.Key).GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() => this.cwt.Select(pair => pair.Key).GetEnumerator(); + + /// + public void Add(T item) => this.cwt.AddOrUpdate(item, null); + + /// + public void Clear() => this.cwt.Clear(); + + /// + public bool Contains(T item) => this.Keys.Contains(item); + + /// + public void CopyTo(T[] array, int arrayIndex) => this.Keys.ToArray().CopyTo(array, arrayIndex); + + /// + public bool Remove(T item) => this.cwt.Remove(item); +} From 19ba6a961fed05453dfa3eca4f25372d118d5811 Mon Sep 17 00:00:00 2001 From: Blair Date: Thu, 27 Mar 2025 05:36:18 +1000 Subject: [PATCH 3/3] Switch INotificationManager and ITitleScreenMenu to use ISharedImmediateTexture (#1879) * Switch INotificationManager and ITitleScreenMenu to use ISharedImmediateTexture #1879 * Remove SetIconTexture Remove some remarks that no longer apply * Cleanup StyleCop warnings --- .../ImGuiNotification/IActiveNotification.cs | 61 --------------- .../ImGuiNotification/INotification.cs | 35 +-------- .../Internal/ActiveNotification.ImGui.cs | 4 +- .../Internal/ActiveNotification.cs | 78 ++----------------- .../ImGuiNotification/Notification.cs | 13 +--- .../NotificationUtilities.cs | 24 +++--- .../Interface/Internal/DalamudInterface.cs | 9 ++- .../Windows/Data/Widgets/ImGuiWidget.cs | 61 +++++++-------- .../Internal/Windows/TitleScreenMenuWindow.cs | 44 ++++++++++- .../ForwardingSharedImmediateTexture.cs | 50 ++++++++++++ .../TitleScreenMenu/TitleScreenMenu.cs | 49 ++++-------- .../TitleScreenMenu/TitleScreenMenuEntry.cs | 11 ++- Dalamud/Plugin/Services/ITitleScreenMenu.cs | 11 ++- 13 files changed, 176 insertions(+), 274 deletions(-) create mode 100644 Dalamud/Interface/Textures/ForwardingSharedImmediateTexture.cs diff --git a/Dalamud/Interface/ImGuiNotification/IActiveNotification.cs b/Dalamud/Interface/ImGuiNotification/IActiveNotification.cs index 332516315..d39fe6bee 100644 --- a/Dalamud/Interface/ImGuiNotification/IActiveNotification.cs +++ b/Dalamud/Interface/ImGuiNotification/IActiveNotification.cs @@ -1,9 +1,6 @@ using System.Threading; -using System.Threading.Tasks; using Dalamud.Interface.ImGuiNotification.EventArgs; -using Dalamud.Interface.Internal; -using Dalamud.Interface.Textures.TextureWraps; namespace Dalamud.Interface.ImGuiNotification; @@ -52,64 +49,6 @@ public interface IActiveNotification : INotification /// This does not override . void ExtendBy(TimeSpan extension); - /// Sets the icon from , overriding the icon. - /// The new texture wrap to use, or null to clear and revert back to the icon specified - /// from . - /// - /// The texture passed will be disposed when the notification is dismissed or a new different texture is set - /// via another call to this function or overwriting the property. You do not have to dispose it yourself. - /// If is not null, then calling this function will simply dispose the - /// passed without actually updating the icon. - /// - void SetIconTexture(IDalamudTextureWrap? textureWrap); - - /// Sets the icon from , overriding the icon, once the given task - /// completes. - /// The task that will result in a new texture wrap to use, or null to clear and - /// revert back to the icon specified from . - /// - /// The texture resulted from the passed will be disposed when the notification - /// is dismissed or a new different texture is set via another call to this function over overwriting the property. - /// You do not have to dispose the resulted instance of yourself. - /// If the task fails for any reason, the exception will be silently ignored and the icon specified from - /// will be used instead. - /// If is not null, then calling this function will simply dispose the - /// result of the passed without actually updating the icon. - /// - void SetIconTexture(Task? textureWrapTask); - - /// Sets the icon from , overriding the icon. - /// The new texture wrap to use, or null to clear and revert back to the icon specified - /// from . - /// Whether to keep the passed not disposed. - /// - /// If is false, the texture passed will be disposed when the - /// notification is dismissed or a new different texture is set via another call to this function. You do not have - /// to dispose it yourself. - /// If is not null and is false, then - /// calling this function will simply dispose the passed without actually updating - /// the icon. - /// - void SetIconTexture(IDalamudTextureWrap? textureWrap, bool leaveOpen); - - /// Sets the icon from , overriding the icon, once the given task - /// completes. - /// The task that will result in a new texture wrap to use, or null to clear and - /// revert back to the icon specified from . - /// Whether to keep the result from the passed not - /// disposed. - /// - /// If is false, the texture resulted from the passed - /// will be disposed when the notification is dismissed or a new different texture is - /// set via another call to this function. You do not have to dispose the resulted instance of - /// yourself. - /// If the task fails for any reason, the exception will be silently ignored and the icon specified from - /// will be used instead. - /// If is not null, then calling this function will simply dispose the - /// result of the passed without actually updating the icon. - /// - void SetIconTexture(Task? textureWrapTask, bool leaveOpen); - /// Generates a new value to use for . /// The new value. internal static long CreateNewId() => Interlocked.Increment(ref idCounter); diff --git a/Dalamud/Interface/ImGuiNotification/INotification.cs b/Dalamud/Interface/ImGuiNotification/INotification.cs index eab0fd131..0b15d2398 100644 --- a/Dalamud/Interface/ImGuiNotification/INotification.cs +++ b/Dalamud/Interface/ImGuiNotification/INotification.cs @@ -1,8 +1,4 @@ -using System.Threading.Tasks; - -using Dalamud.Interface.Internal; -using Dalamud.Interface.Textures.TextureWraps; -using Dalamud.Plugin.Services; +using Dalamud.Interface.Textures; namespace Dalamud.Interface.ImGuiNotification; @@ -22,35 +18,12 @@ public interface INotification /// Gets or sets the type of the notification. NotificationType Type { get; set; } - /// Gets or sets the icon source, in case is not set or the task has faulted. + /// Gets or sets the icon source, in case is not set. /// INotificationIcon? Icon { get; set; } - /// Gets or sets a texture wrap that will be used in place of if set. - /// - /// A texture wrap set via this property will NOT be disposed when the notification is dismissed. - /// Use or - /// to use a texture, after calling - /// . Call either of those functions with null to revert - /// the effective icon back to this property. - /// This property and are bound together. If the task is not null but - /// is false (because the task is still in progress or faulted,) - /// the property will return null. Setting this property will set to a new - /// completed with the new value as its result. - /// - public IDalamudTextureWrap? IconTexture { get; set; } - - /// Gets or sets a task that results in a texture wrap that will be used in place of if - /// available. - /// - /// A texture wrap set via this property will NOT be disposed when the notification is dismissed. - /// Use or - /// to use a texture, after calling - /// . Call either of those functions with null to revert - /// the effective icon back to this property. - /// This property and are bound together. - /// - Task? IconTextureTask { get; set; } + /// Gets or sets a texture that will be used in place of if set. + public ISharedImmediateTexture? IconTexture { get; set; } /// Gets or sets the hard expiry. /// diff --git a/Dalamud/Interface/ImGuiNotification/Internal/ActiveNotification.ImGui.cs b/Dalamud/Interface/ImGuiNotification/Internal/ActiveNotification.ImGui.cs index 16d58bea5..e8d6c5cc0 100644 --- a/Dalamud/Interface/ImGuiNotification/Internal/ActiveNotification.ImGui.cs +++ b/Dalamud/Interface/ImGuiNotification/Internal/ActiveNotification.ImGui.cs @@ -404,7 +404,7 @@ internal sealed partial class ActiveNotification var maxCoord = minCoord + size; var iconColor = this.Type.ToColor(); - if (NotificationUtilities.DrawIconFrom(minCoord, maxCoord, this.IconTextureTask)) + if (NotificationUtilities.DrawIconFrom(minCoord, maxCoord, this.IconTexture)) return; if (this.Icon?.DrawIcon(minCoord, maxCoord, iconColor) is true) @@ -499,7 +499,7 @@ internal sealed partial class ActiveNotification if (fillStartCw == 0 && fillEndCw == 0) return; - + var radius = Math.Min(size.X, size.Y) / 3f; var ifrom = fillStartCw * MathF.PI * 2; var ito = fillEndCw * MathF.PI * 2; diff --git a/Dalamud/Interface/ImGuiNotification/Internal/ActiveNotification.cs b/Dalamud/Interface/ImGuiNotification/Internal/ActiveNotification.cs index 607c7c49d..6587b6c32 100644 --- a/Dalamud/Interface/ImGuiNotification/Internal/ActiveNotification.cs +++ b/Dalamud/Interface/ImGuiNotification/Internal/ActiveNotification.cs @@ -1,11 +1,9 @@ using System.Runtime.Loader; -using System.Threading.Tasks; using Dalamud.Configuration.Internal; using Dalamud.Interface.Animation; using Dalamud.Interface.Animation.EasingFunctions; -using Dalamud.Interface.Internal; -using Dalamud.Interface.Textures.TextureWraps; +using Dalamud.Interface.Textures; using Dalamud.Plugin.Internal.Types; using Dalamud.Utility; @@ -23,9 +21,6 @@ internal sealed partial class ActiveNotification : IActiveNotification private readonly Easing progressEasing; private readonly Easing expandoEasing; - /// Whether to call on . - private bool hasIconTextureOwnership; - /// Gets the time of starting to count the timer for the expiration. private DateTime lastInterestTime; @@ -119,31 +114,10 @@ internal sealed partial class ActiveNotification : IActiveNotification } /// - public IDalamudTextureWrap? IconTexture + public ISharedImmediateTexture? IconTexture { get => this.underlyingNotification.IconTexture; - set => this.IconTextureTask = value is null ? null : Task.FromResult(value); - } - - /// - public Task? IconTextureTask - { - get => this.underlyingNotification.IconTextureTask; - set - { - // Do nothing if the value did not change. - if (this.underlyingNotification.IconTextureTask == value) - return; - - if (this.hasIconTextureOwnership) - { - _ = this.underlyingNotification.IconTextureTask?.ToContentDisposedTask(true); - this.underlyingNotification.IconTextureTask = null; - this.hasIconTextureOwnership = false; - } - - this.underlyingNotification.IconTextureTask = value; - } + set => this.underlyingNotification.IconTexture = value; } /// @@ -265,39 +239,6 @@ internal sealed partial class ActiveNotification : IActiveNotification this.extendedExpiry = newExpiry; } - /// - public void SetIconTexture(IDalamudTextureWrap? textureWrap) => - this.SetIconTexture(textureWrap, false); - - /// - public void SetIconTexture(IDalamudTextureWrap? textureWrap, bool leaveOpen) => - this.SetIconTexture(textureWrap is null ? null : Task.FromResult(textureWrap), leaveOpen); - - /// - public void SetIconTexture(Task? textureWrapTask) => - this.SetIconTexture(textureWrapTask, false); - - /// - public void SetIconTexture(Task? textureWrapTask, bool leaveOpen) - { - // If we're requested to replace the texture with the same texture, do nothing. - if (this.underlyingNotification.IconTextureTask == textureWrapTask) - return; - - if (this.DismissReason is not null) - { - if (!leaveOpen) - textureWrapTask?.ToContentDisposedTask(true); - return; - } - - if (this.hasIconTextureOwnership) - _ = this.underlyingNotification.IconTextureTask?.ToContentDisposedTask(true); - - this.hasIconTextureOwnership = !leaveOpen; - this.underlyingNotification.IconTextureTask = textureWrapTask; - } - /// Removes non-Dalamud invocation targets from events. /// /// This is done to prevent references of plugins being unloaded from outliving the plugin itself. @@ -317,10 +258,8 @@ internal sealed partial class ActiveNotification : IActiveNotification if (this.Icon is { } previousIcon && !IsOwnedByDalamud(previousIcon.GetType())) this.Icon = null; - // Clear the texture if we don't have the ownership. - // The texture probably was owned by the plugin being unloaded in such case. - if (!this.hasIconTextureOwnership) - this.IconTextureTask = null; + if (this.IconTexture is { } previousTexture && !IsOwnedByDalamud(previousTexture.GetType())) + this.IconTexture = null; this.isInitiatorUnloaded = true; this.UserDismissable = true; @@ -400,13 +339,6 @@ internal sealed partial class ActiveNotification : IActiveNotification /// Clears the resources associated with this instance of . internal void DisposeInternal() { - if (this.hasIconTextureOwnership) - { - _ = this.underlyingNotification.IconTextureTask?.ToContentDisposedTask(true); - this.underlyingNotification.IconTextureTask = null; - this.hasIconTextureOwnership = false; - } - this.Dismiss = null; this.Click = null; this.DrawActions = null; diff --git a/Dalamud/Interface/ImGuiNotification/Notification.cs b/Dalamud/Interface/ImGuiNotification/Notification.cs index 927dd5ba9..4dcb10c17 100644 --- a/Dalamud/Interface/ImGuiNotification/Notification.cs +++ b/Dalamud/Interface/ImGuiNotification/Notification.cs @@ -1,11 +1,11 @@ using System.Threading.Tasks; using Dalamud.Interface.ImGuiNotification.Internal; -using Dalamud.Interface.Internal; +using Dalamud.Interface.Textures; using Dalamud.Interface.Textures.TextureWraps; +using Serilog; namespace Dalamud.Interface.ImGuiNotification; - /// Represents a blueprint for a notification. public sealed record Notification : INotification { @@ -30,14 +30,7 @@ public sealed record Notification : INotification public INotificationIcon? Icon { get; set; } /// - public IDalamudTextureWrap? IconTexture - { - get => this.IconTextureTask?.IsCompletedSuccessfully is true ? this.IconTextureTask.Result : null; - set => this.IconTextureTask = value is null ? null : Task.FromResult(value); - } - - /// - public Task? IconTextureTask { get; set; } + public ISharedImmediateTexture? IconTexture { get; set; } /// public DateTime HardExpiry { get; set; } = DateTime.MaxValue; diff --git a/Dalamud/Interface/ImGuiNotification/NotificationUtilities.cs b/Dalamud/Interface/ImGuiNotification/NotificationUtilities.cs index 2172663e8..b31321d8b 100644 --- a/Dalamud/Interface/ImGuiNotification/NotificationUtilities.cs +++ b/Dalamud/Interface/ImGuiNotification/NotificationUtilities.cs @@ -7,6 +7,7 @@ using Dalamud.Game.Text; using Dalamud.Interface.Internal; using Dalamud.Interface.Internal.Windows; using Dalamud.Interface.ManagedFontAtlas; +using Dalamud.Interface.Textures; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Utility; using Dalamud.Plugin.Internal.Types; @@ -78,6 +79,19 @@ public static class NotificationUtilities return true; } + /// Draws an icon from an instance of . + /// The coordinates of the top left of the icon area. + /// The coordinates of the bottom right of the icon area. + /// The texture. + /// true if anything has been drawn. + internal static bool DrawIconFrom(Vector2 minCoord, Vector2 maxCoord, ISharedImmediateTexture? texture) + { + if (texture is null) + return false; + + return DrawIconFrom(minCoord, maxCoord, texture.GetWrapOrEmpty()); + } + /// Draws an icon from an instance of . /// The coordinates of the top left of the icon area. /// The coordinates of the bottom right of the icon area. @@ -105,16 +119,6 @@ public static class NotificationUtilities } } - /// Draws an icon from an instance of that results in an - /// . - /// The coordinates of the top left of the icon area. - /// The coordinates of the bottom right of the icon area. - /// The task that results in a texture. - /// true if anything has been drawn. - /// Exceptions from the task will be treated as if no texture is provided. - internal static bool DrawIconFrom(Vector2 minCoord, Vector2 maxCoord, Task? textureTask) => - textureTask?.IsCompletedSuccessfully is true && DrawIconFrom(minCoord, maxCoord, textureTask.Result); - /// Draws an icon from an instance of . /// The coordinates of the top left of the icon area. /// The coordinates of the bottom right of the icon area. diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs index f3f2564af..7d798c541 100644 --- a/Dalamud/Interface/Internal/DalamudInterface.cs +++ b/Dalamud/Interface/Internal/DalamudInterface.cs @@ -26,6 +26,7 @@ using Dalamud.Interface.Internal.Windows.Settings; using Dalamud.Interface.Internal.Windows.StyleEditor; using Dalamud.Interface.ManagedFontAtlas.Internals; using Dalamud.Interface.Style; +using Dalamud.Interface.Textures; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; using Dalamud.Interface.Windowing; @@ -169,16 +170,16 @@ internal class DalamudInterface : IInternalDisposableService { titleScreenMenu.AddEntryCore( Loc.Localize("TSMDalamudPlugins", "Plugin Installer"), - dalamudAssetManager.GetDalamudTextureWrap(DalamudAsset.LogoSmall), + new ForwardingSharedImmediateTexture(dalamudAssetManager.GetDalamudTextureWrap(DalamudAsset.LogoSmall)), this.OpenPluginInstaller); titleScreenMenu.AddEntryCore( Loc.Localize("TSMDalamudSettings", "Dalamud Settings"), - dalamudAssetManager.GetDalamudTextureWrap(DalamudAsset.LogoSmall), + new ForwardingSharedImmediateTexture(dalamudAssetManager.GetDalamudTextureWrap(DalamudAsset.LogoSmall)), this.OpenSettings); titleScreenMenu.AddEntryCore( "Toggle Dev Menu", - dalamudAssetManager.GetDalamudTextureWrap(DalamudAsset.LogoSmall), + new ForwardingSharedImmediateTexture(dalamudAssetManager.GetDalamudTextureWrap(DalamudAsset.LogoSmall)), () => Service.GetNullable()?.ToggleDevMenu(), VirtualKey.SHIFT); @@ -186,7 +187,7 @@ internal class DalamudInterface : IInternalDisposableService { titleScreenMenu.AddEntryCore( Loc.Localize("TSMDalamudDevMenu", "Developer Menu"), - dalamudAssetManager.GetDalamudTextureWrap(DalamudAsset.LogoSmall), + new ForwardingSharedImmediateTexture(dalamudAssetManager.GetDalamudTextureWrap(DalamudAsset.LogoSmall)), () => this.isImGuiDrawDevMenu = true); } }); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs index 1476ce2e6..08d29398b 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ImGuiWidget.cs @@ -5,11 +5,11 @@ using System.Threading.Tasks; using Dalamud.Game.Text; using Dalamud.Interface.ImGuiNotification; using Dalamud.Interface.ImGuiNotification.Internal; +using Dalamud.Interface.Textures; using Dalamud.Interface.Textures.Internal; using Dalamud.Interface.Textures.TextureWraps; using Dalamud.Interface.Windowing; using Dalamud.Storage.Assets; -using Dalamud.Utility; using ImGuiNET; @@ -144,8 +144,6 @@ internal class ImGuiWidget : IDataWindowWidget "Action Bar (always on if not user dismissable for the example)", ref this.notificationTemplate.ActionBar); - ImGui.Checkbox("Leave Textures Open", ref this.notificationTemplate.LeaveTexturesOpen); - if (ImGui.Button("Add notification")) { var text = @@ -212,35 +210,34 @@ internal class ImGuiWidget : IDataWindowWidget switch (this.notificationTemplate.IconInt) { case 5: - n.SetIconTexture( - DisposeLoggingTextureWrap.Wrap( - dam.GetDalamudTextureWrap( - Enum.Parse( - NotificationTemplate.AssetSources[this.notificationTemplate.IconAssetInt]))), - this.notificationTemplate.LeaveTexturesOpen); - break; - case 6: - n.SetIconTexture( - dam.GetDalamudTextureWrapAsync( - Enum.Parse( - NotificationTemplate.AssetSources[this.notificationTemplate.IconAssetInt])) - .ContinueWith( - r => r.IsCompletedSuccessfully - ? Task.FromResult(DisposeLoggingTextureWrap.Wrap(r.Result)) - : r).Unwrap(), - this.notificationTemplate.LeaveTexturesOpen); + var textureWrap = DisposeLoggingTextureWrap.Wrap( + dam.GetDalamudTextureWrap( + Enum.Parse( + NotificationTemplate.AssetSources[this.notificationTemplate.IconAssetInt]))); + + if (textureWrap != null) + { + n.IconTexture = new ForwardingSharedImmediateTexture(textureWrap); + } + break; case 7: - n.SetIconTexture( - DisposeLoggingTextureWrap.Wrap( - tm.Shared.GetFromGame(this.notificationTemplate.IconText).GetWrapOrDefault()), - this.notificationTemplate.LeaveTexturesOpen); + var textureWrap2 = DisposeLoggingTextureWrap.Wrap( + tm.Shared.GetFromGame(this.notificationTemplate.IconText).GetWrapOrDefault()); + if (textureWrap2 != null) + { + n.IconTexture = new ForwardingSharedImmediateTexture(textureWrap2); + } + break; case 8: - n.SetIconTexture( - DisposeLoggingTextureWrap.Wrap( - tm.Shared.GetFromFile(this.notificationTemplate.IconText).GetWrapOrDefault()), - this.notificationTemplate.LeaveTexturesOpen); + var textureWrap3 = DisposeLoggingTextureWrap.Wrap( + tm.Shared.GetFromFile(this.notificationTemplate.IconText).GetWrapOrDefault()); + if (textureWrap3 != null) + { + n.IconTexture = new ForwardingSharedImmediateTexture(textureWrap3); + } + break; } @@ -303,15 +300,15 @@ internal class ImGuiWidget : IDataWindowWidget }; } } - + ImGui.SameLine(); if (ImGui.Button("Replace images using setter")) { foreach (var n in this.notifications) { var i = (uint)Random.Shared.NextInt64(0, 200000); - n.IconTexture = DisposeLoggingTextureWrap.Wrap( - Service.Get().Shared.GetFromGameIcon(new(i)).GetWrapOrDefault()); + + n.IconTexture = Service.Get().Shared.GetFromGameIcon(new(i, false, false)); } } } @@ -428,7 +425,6 @@ internal class ImGuiWidget : IDataWindowWidget public bool Minimized; public bool UserDismissable; public bool ActionBar; - public bool LeaveTexturesOpen; public int ProgressMode; public void Reset() @@ -450,7 +446,6 @@ internal class ImGuiWidget : IDataWindowWidget this.Minimized = true; this.UserDismissable = true; this.ActionBar = true; - this.LeaveTexturesOpen = true; this.ProgressMode = 0; this.RespectUiHidden = true; } diff --git a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs index ce8c192a4..c9ca65e0c 100644 --- a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs +++ b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs @@ -28,6 +28,8 @@ using ImGuiNET; using Lumina.Text.ReadOnly; +using Serilog; + using LSeStringBuilder = Lumina.Text.SeStringBuilder; namespace Dalamud.Interface.Internal.Windows; @@ -185,6 +187,23 @@ internal class TitleScreenMenuWindow : Window, IDisposable if (!entry.IsShowConditionSatisfied()) continue; + if (entry.Texture.TryGetWrap(out var textureWrap, out var exception)) + { + if (textureWrap.Width != 64 && textureWrap.Height != 64) + { + Log.Error("Texture provided for ITitleScreenMenuEntry must be 64x64. Entry will be removed."); + this.titleScreenMenu.RemoveEntry(entry); + continue; + } + } + + if (exception != null) + { + Log.Error(exception, "An exception occurred while attempting to get the texture wrap for a ITitleScreenMenuEntry. Entry will be removed."); + this.titleScreenMenu.RemoveEntry(entry); + continue; + } + if (!this.moveEasings.TryGetValue(entry.Id, out var moveEasing)) { moveEasing = new InOutQuint(TimeSpan.FromMilliseconds(400)); @@ -259,6 +278,23 @@ internal class TitleScreenMenuWindow : Window, IDisposable if (!entry.IsShowConditionSatisfied()) continue; + if (entry.Texture.TryGetWrap(out var textureWrap, out var exception)) + { + if (textureWrap.Width != 64 && textureWrap.Height != 64) + { + Log.Error($"Texture provided for ITitleScreenMenuEntry {entry.Name} must be 64x64. Entry will be removed."); + this.titleScreenMenu.RemoveEntry(entry); + continue; + } + } + + if (exception != null) + { + Log.Error(exception, $"An exception occurred while attempting to get the texture wrap for ITitleScreenMenuEntry {entry.Name}. Entry will be removed."); + this.titleScreenMenu.RemoveEntry(entry); + continue; + } + var finalPos = (i + 1) * this.shadeTexture.Value.Height * scale; this.DrawEntry(entry, i != 0, true, i == 0, false, false); @@ -374,7 +410,9 @@ internal class TitleScreenMenuWindow : Window, IDisposable ImGui.PushStyleVar(ImGuiStyleVar.Alpha, 1f); } - ImGui.Image(entry.Texture.ImGuiHandle, new Vector2(TitleScreenMenu.TextureSize * scale)); + // Wrap should always be valid at this point due to us checking the validity of the image each frame + var dalamudTextureWrap = entry.Texture.GetWrapOrEmpty(); + ImGui.Image(dalamudTextureWrap.ImGuiHandle, new Vector2(TitleScreenMenu.TextureSize * scale)); if (overrideAlpha || isFirst) { ImGui.PopStyleVar(); @@ -388,7 +426,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable var textHeight = ImGui.GetTextLineHeightWithSpacing(); var cursor = ImGui.GetCursorPos(); - cursor.Y += (entry.Texture.Height * scale / 2) - (textHeight / 2); + cursor.Y += (dalamudTextureWrap.Height * scale / 2) - (textHeight / 2); if (overrideAlpha) { @@ -411,7 +449,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable ImGui.PopStyleVar(); } - initialCursor.Y += entry.Texture.Height * scale; + initialCursor.Y += dalamudTextureWrap.Height * scale; ImGui.SetCursorPos(initialCursor); return isHover; diff --git a/Dalamud/Interface/Textures/ForwardingSharedImmediateTexture.cs b/Dalamud/Interface/Textures/ForwardingSharedImmediateTexture.cs new file mode 100644 index 000000000..12e312b3e --- /dev/null +++ b/Dalamud/Interface/Textures/ForwardingSharedImmediateTexture.cs @@ -0,0 +1,50 @@ +using System.Threading; +using System.Threading.Tasks; + +using Dalamud.Interface.Textures.TextureWraps; +using Dalamud.Storage.Assets; + +namespace Dalamud.Interface.Textures; + +/// +/// Wraps a dalamud texture allowing interoperability with certain services. Only use this if you need to provide a texture that has been created or rented as a ISharedImmediateTexture. +/// +public class ForwardingSharedImmediateTexture : ISharedImmediateTexture +{ + private readonly IDalamudTextureWrap textureWrap; + + /// + /// Initializes a new instance of the class. + /// + /// A textureWrap that has been created or provided by RentAsync. + public ForwardingSharedImmediateTexture(IDalamudTextureWrap textureWrap) + { + this.textureWrap = textureWrap; + } + + /// + public IDalamudTextureWrap GetWrapOrEmpty() + { + return this.textureWrap; + } + + /// + public IDalamudTextureWrap? GetWrapOrDefault(IDalamudTextureWrap? defaultWrap = null) + { + return this.textureWrap; + } + + /// + public bool TryGetWrap(out IDalamudTextureWrap? texture, out Exception? exception) + { + texture = this.textureWrap; + exception = null; + return true; + } + + /// + public Task RentAsync(CancellationToken cancellationToken = default) + { + return Task.FromResult(this.textureWrap); + } +} diff --git a/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs b/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs index 6f98b9757..586d65559 100644 --- a/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs +++ b/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs @@ -3,8 +3,7 @@ using System.Linq; using System.Reflection; using Dalamud.Game.ClientState.Keys; -using Dalamud.Interface.Internal; -using Dalamud.Interface.Textures.TextureWraps; +using Dalamud.Interface.Textures; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; @@ -67,7 +66,7 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu } } } - + /// /// Adds a new entry to the title screen menu. /// @@ -76,13 +75,8 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu /// The action to execute when the option is selected. /// A object that can be used to manage the entry. /// Thrown when the texture provided does not match the required resolution(64x64). - public ITitleScreenMenuEntry AddPluginEntry(string text, IDalamudTextureWrap texture, Action onTriggered) + public ITitleScreenMenuEntry AddPluginEntry(string text, ISharedImmediateTexture texture, Action onTriggered) { - if (texture.Height != TextureSize || texture.Width != TextureSize) - { - throw new ArgumentException("Texture must be 64x64"); - } - TitleScreenMenuEntry entry; lock (this.entries) { @@ -103,13 +97,13 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu } /// - public IReadOnlyTitleScreenMenuEntry AddEntry(string text, IDalamudTextureWrap texture, Action onTriggered) + public IReadOnlyTitleScreenMenuEntry AddEntry(string text, ISharedImmediateTexture texture, Action onTriggered) { return this.AddPluginEntry(text, texture, onTriggered); } /// - public IReadOnlyTitleScreenMenuEntry AddEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered) + public IReadOnlyTitleScreenMenuEntry AddEntry(ulong priority, string text, ISharedImmediateTexture texture, Action onTriggered) { return this.AddPluginEntry(priority, text, texture, onTriggered); } @@ -123,13 +117,8 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu /// The action to execute when the option is selected. /// A object that can be used to manage the entry. /// Thrown when the texture provided does not match the required resolution(64x64). - public ITitleScreenMenuEntry AddPluginEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered) + public ITitleScreenMenuEntry AddPluginEntry(ulong priority, string text, ISharedImmediateTexture texture, Action onTriggered) { - if (texture.Height != TextureSize || texture.Width != TextureSize) - { - throw new ArgumentException("Texture must be 64x64"); - } - TitleScreenMenuEntry entry; lock (this.entries) { @@ -166,13 +155,8 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu /// The action to execute when the option is selected. /// A object that can be used to manage the entry. /// Thrown when the texture provided does not match the required resolution(64x64). - internal TitleScreenMenuEntry AddEntryCore(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered) + internal TitleScreenMenuEntry AddEntryCore(ulong priority, string text, ISharedImmediateTexture texture, Action onTriggered) { - if (texture.Height != TextureSize || texture.Width != TextureSize) - { - throw new ArgumentException("Texture must be 64x64"); - } - TitleScreenMenuEntry entry; lock (this.entries) { @@ -199,15 +183,10 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu /// Thrown when the texture provided does not match the required resolution(64x64). internal TitleScreenMenuEntry AddEntryCore( string text, - IDalamudTextureWrap texture, + ISharedImmediateTexture texture, Action onTriggered, params VirtualKey[] showConditionKeys) { - if (texture.Height != TextureSize || texture.Width != TextureSize) - { - throw new ArgumentException("Texture must be 64x64"); - } - TitleScreenMenuEntry entry; lock (this.entries) { @@ -240,7 +219,7 @@ internal class TitleScreenMenuPluginScoped : IInternalDisposableService, ITitleS { [ServiceManager.ServiceDependency] private readonly TitleScreenMenu titleScreenMenuService = Service.Get(); - + private readonly List pluginEntries = new(); /// @@ -254,25 +233,25 @@ internal class TitleScreenMenuPluginScoped : IInternalDisposableService, ITitleS this.titleScreenMenuService.RemoveEntry(entry); } } - + /// - public IReadOnlyTitleScreenMenuEntry AddEntry(string text, IDalamudTextureWrap texture, Action onTriggered) + public IReadOnlyTitleScreenMenuEntry AddEntry(string text, ISharedImmediateTexture texture, Action onTriggered) { var entry = this.titleScreenMenuService.AddPluginEntry(text, texture, onTriggered); this.pluginEntries.Add(entry); return entry; } - + /// - public IReadOnlyTitleScreenMenuEntry AddEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered) + public IReadOnlyTitleScreenMenuEntry AddEntry(ulong priority, string text, ISharedImmediateTexture texture, Action onTriggered) { var entry = this.titleScreenMenuService.AddPluginEntry(priority, text, texture, onTriggered); this.pluginEntries.Add(entry); return entry; } - + /// public void RemoveEntry(IReadOnlyTitleScreenMenuEntry entry) { diff --git a/Dalamud/Interface/TitleScreenMenu/TitleScreenMenuEntry.cs b/Dalamud/Interface/TitleScreenMenu/TitleScreenMenuEntry.cs index 2acb275ad..a98d32770 100644 --- a/Dalamud/Interface/TitleScreenMenu/TitleScreenMenuEntry.cs +++ b/Dalamud/Interface/TitleScreenMenu/TitleScreenMenuEntry.cs @@ -4,8 +4,7 @@ using System.Linq; using System.Reflection; using Dalamud.Game.ClientState.Keys; -using Dalamud.Interface.Internal; -using Dalamud.Interface.Textures.TextureWraps; +using Dalamud.Interface.Textures; namespace Dalamud.Interface; @@ -64,7 +63,7 @@ public interface IReadOnlyTitleScreenMenuEntry /// /// Gets the texture of this entry. /// - IDalamudTextureWrap Texture { get; } + ISharedImmediateTexture Texture { get; } } /// @@ -87,7 +86,7 @@ public class TitleScreenMenuEntry : ITitleScreenMenuEntry Assembly? callingAssembly, ulong priority, string text, - IDalamudTextureWrap texture, + ISharedImmediateTexture texture, Action onTriggered, IEnumerable? showConditionKeys = null) { @@ -106,8 +105,8 @@ public class TitleScreenMenuEntry : ITitleScreenMenuEntry public string Name { get; set; } /// - public IDalamudTextureWrap Texture { get; set; } - + public ISharedImmediateTexture Texture { get; set; } + /// public bool IsInternal { get; set; } diff --git a/Dalamud/Plugin/Services/ITitleScreenMenu.cs b/Dalamud/Plugin/Services/ITitleScreenMenu.cs index 5ebd80017..9f7b17ea3 100644 --- a/Dalamud/Plugin/Services/ITitleScreenMenu.cs +++ b/Dalamud/Plugin/Services/ITitleScreenMenu.cs @@ -1,8 +1,7 @@ using System.Collections.Generic; using Dalamud.Interface; -using Dalamud.Interface.Internal; -using Dalamud.Interface.Textures.TextureWraps; +using Dalamud.Interface.Textures; namespace Dalamud.Plugin.Services; @@ -20,22 +19,22 @@ public interface ITitleScreenMenu /// Adds a new entry to the title screen menu. /// /// The text to show. - /// The texture to show. + /// The texture to show. The texture must be 64x64 or the entry will be removed and an error will be logged. /// The action to execute when the option is selected. /// A object that can be reference the entry. /// Thrown when the texture provided does not match the required resolution(64x64). - public IReadOnlyTitleScreenMenuEntry AddEntry(string text, IDalamudTextureWrap texture, Action onTriggered); + public IReadOnlyTitleScreenMenuEntry AddEntry(string text, ISharedImmediateTexture texture, Action onTriggered); /// /// Adds a new entry to the title screen menu. /// /// Priority of the entry. /// The text to show. - /// The texture to show. + /// The texture to show. The texture must be 64x64 or the entry will be removed and an error will be logged. /// The action to execute when the option is selected. /// A object that can be used to reference the entry. /// Thrown when the texture provided does not match the required resolution(64x64). - public IReadOnlyTitleScreenMenuEntry AddEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered); + public IReadOnlyTitleScreenMenuEntry AddEntry(ulong priority, string text, ISharedImmediateTexture texture, Action onTriggered); /// /// Remove an entry from the title screen menu.