From 5845af4fe3aab91d3dead1ca7d112943615ddd30 Mon Sep 17 00:00:00 2001 From: Anna Clemens Date: Tue, 6 Apr 2021 06:23:13 -0400 Subject: [PATCH] feat: add toast queue and event --- Dalamud/Game/Internal/Framework.cs | 1 + Dalamud/Game/Internal/Gui/GameGui.cs | 2 + Dalamud/Game/Internal/Gui/ToastGui.cs | 99 +++++++++++++++++++++++---- 3 files changed, 90 insertions(+), 12 deletions(-) diff --git a/Dalamud/Game/Internal/Framework.cs b/Dalamud/Game/Internal/Framework.cs index cf314ba70..7deb56211 100644 --- a/Dalamud/Game/Internal/Framework.cs +++ b/Dalamud/Game/Internal/Framework.cs @@ -120,6 +120,7 @@ namespace Dalamud.Game.Internal { private bool HandleFrameworkUpdate(IntPtr framework) { try { Gui.Chat.UpdateQueue(this); + Gui.Toast.UpdateQueue(); Network.UpdateQueue(this); } catch (Exception ex) { Log.Error(ex, "Exception while handling Framework::Update hook."); diff --git a/Dalamud/Game/Internal/Gui/GameGui.cs b/Dalamud/Game/Internal/Gui/GameGui.cs index 70ba8f8cd..cc8cb5b64 100644 --- a/Dalamud/Game/Internal/Gui/GameGui.cs +++ b/Dalamud/Game/Internal/Gui/GameGui.cs @@ -452,6 +452,7 @@ namespace Dalamud.Game.Internal.Gui { public void Enable() { Chat.Enable(); + Toast.Enable(); PartyFinder.Enable(); this.setGlobalBgmHook.Enable(); this.handleItemHoverHook.Enable(); @@ -463,6 +464,7 @@ namespace Dalamud.Game.Internal.Gui { public void Dispose() { Chat.Dispose(); + Toast.Dispose(); PartyFinder.Dispose(); this.setGlobalBgmHook.Dispose(); this.handleItemHoverHook.Dispose(); diff --git a/Dalamud/Game/Internal/Gui/ToastGui.cs b/Dalamud/Game/Internal/Gui/ToastGui.cs index 89b166c11..fefce42b7 100755 --- a/Dalamud/Game/Internal/Gui/ToastGui.cs +++ b/Dalamud/Game/Internal/Gui/ToastGui.cs @@ -1,28 +1,60 @@ using System; +using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; + using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Hooking; namespace Dalamud.Game.Internal.Gui { - public class ToastGui + public class ToastGui : IDisposable { + #region Events + + public delegate void OnToastDelegate(ref SeString message, ref bool isHandled); + + /// + /// Event that will be fired when a toast is sent by the game or a plugin. + /// + public event OnToastDelegate OnToast; + + #endregion + + #region Hooks + + private readonly Hook showToastHook; + + #endregion + + private delegate IntPtr ShowToastDelegate(IntPtr manager, IntPtr text, int layer, byte bool1, byte bool2, int logMessageId); + private Dalamud Dalamud { get; } private ToastGuiAddressResolver Address { get; } - private delegate IntPtr ShowToastDelegate(IntPtr manager, IntPtr text, int layer, byte bool1, byte bool2, int logMessageId); + private Queue ToastQueue { get; } = new Queue(); - private readonly ShowToastDelegate showToast; public ToastGui(SigScanner scanner, Dalamud dalamud) { - Dalamud = dalamud; + this.Dalamud = dalamud; - Address = new ToastGuiAddressResolver(); - Address.Setup(scanner); + this.Address = new ToastGuiAddressResolver(); + this.Address.Setup(scanner); - this.showToast = Marshal.GetDelegateForFunctionPointer(Address.ShowToast); + Marshal.GetDelegateForFunctionPointer(this.Address.ShowToast); + this.showToastHook = new Hook(this.Address.ShowToast, new ShowToastDelegate(this.HandleToastDetour)); + } + + public void Enable() + { + this.showToastHook.Enable(); + } + + public void Dispose() + { + this.showToastHook.Dispose(); } /// @@ -31,7 +63,7 @@ namespace Dalamud.Game.Internal.Gui /// The message to be shown public void Show(string message) { - this.Show(Encoding.UTF8.GetBytes(message)); + this.ToastQueue.Enqueue(Encoding.UTF8.GetBytes(message)); } /// @@ -40,20 +72,63 @@ namespace Dalamud.Game.Internal.Gui /// The message to be shown public void Show(SeString message) { - this.Show(message.Encode()); + this.ToastQueue.Enqueue(message.Encode()); + } + + /// + /// Process the toast queue. + /// + internal void UpdateQueue() + { + while (this.ToastQueue.Count > 0) + { + var message = this.ToastQueue.Dequeue(); + this.Show(message); + } } private void Show(byte[] bytes) { - var manager = Dalamud.Framework.Gui.GetUIModule(); + var manager = this.Dalamud.Framework.Gui.GetUIModule(); + + // terminate the string + var terminated = new byte[bytes.Length + 1]; + Array.Copy(bytes, 0, terminated, 0, bytes.Length); + terminated[^1] = 0; unsafe { - fixed (byte* ptr = bytes) + fixed (byte* ptr = terminated) { - this.showToast(manager, (IntPtr) ptr, 5, 0, 1, 0); + this.HandleToastDetour(manager, (IntPtr)ptr, 5, 0, 1, 0); } } } + + private IntPtr HandleToastDetour(IntPtr manager, IntPtr text, int layer, byte bool1, byte bool2, int logMessageId) + { + // get the message as an sestring + var bytes = new List(); + unsafe + { + var ptr = (byte*)text; + while (*ptr != 0) + { + bytes.Add(*ptr); + ptr += 1; + } + } + + // call events + var isHandled = false; + var str = this.Dalamud.SeStringManager.Parse(bytes.ToArray()); + + this.OnToast?.Invoke(ref str, ref isHandled); + + // do nothing if handled or show the toast + return isHandled + ? IntPtr.Zero + : this.showToastHook.Original(manager, text, layer, bool1, bool2, logMessageId); + } } }