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);
+ }
}
}