diff --git a/Dalamud/Interface/Internal/ImGuiClipboardConfig.cs b/Dalamud/Interface/Internal/ImGuiClipboardConfig.cs
new file mode 100644
index 000000000..b3302add4
--- /dev/null
+++ b/Dalamud/Interface/Internal/ImGuiClipboardConfig.cs
@@ -0,0 +1,80 @@
+using System.Runtime.InteropServices;
+using ImGuiNET;
+
+namespace Dalamud.Interface.Internal;
+
+///
+/// Configures the ImGui clipboard behaviour to work nicely with XIV.
+///
+///
+///
+/// XIV uses '\r' for line endings and will truncate all text after a '\n' character.
+/// This means that copy/pasting multi-line text from ImGui to XIV will only copy the first line.
+///
+///
+/// ImGui uses '\n' for line endings and will ignore '\r' entirely.
+/// This means that copy/pasting multi-line text from XIV to ImGui will copy all the text
+/// without line breaks.
+///
+///
+/// To fix this we normalize all clipboard line endings entering/exiting ImGui to '\r\n' which
+/// works for both ImGui and XIV.
+///
+///
+internal static class ImGuiClipboardConfig
+{
+ private delegate void SetClipboardTextDelegate(IntPtr userData, string text);
+ private delegate string GetClipboardTextDelegate();
+
+ private static SetClipboardTextDelegate? _setTextOriginal = null;
+ private static GetClipboardTextDelegate? _getTextOriginal = null;
+
+ // These must exist as variables to prevent them from being GC'd
+ private static SetClipboardTextDelegate? _setText = null;
+ private static GetClipboardTextDelegate? _getText = null;
+
+ public static void Apply()
+ {
+ var io = ImGui.GetIO();
+ if (_setTextOriginal == null)
+ {
+ _setTextOriginal =
+ Marshal.GetDelegateForFunctionPointer(io.SetClipboardTextFn);
+ }
+
+ if (_getTextOriginal == null)
+ {
+ _getTextOriginal =
+ Marshal.GetDelegateForFunctionPointer(io.GetClipboardTextFn);
+ }
+
+ _setText = new SetClipboardTextDelegate(SetClipboardText);
+ _getText = new GetClipboardTextDelegate(GetClipboardText);
+
+ io.SetClipboardTextFn = Marshal.GetFunctionPointerForDelegate(_setText);
+ io.GetClipboardTextFn = Marshal.GetFunctionPointerForDelegate(_getText);
+ }
+
+ public static void Unapply()
+ {
+ var io = ImGui.GetIO();
+ if (_setTextOriginal != null)
+ {
+ io.SetClipboardTextFn = Marshal.GetFunctionPointerForDelegate(_setTextOriginal);
+ }
+ if (_getTextOriginal != null)
+ {
+ io.GetClipboardTextFn = Marshal.GetFunctionPointerForDelegate(_getTextOriginal);
+ }
+ }
+
+ private static void SetClipboardText(IntPtr userData, string text)
+ {
+ _setTextOriginal!(userData, text.ReplaceLineEndings("\r\n"));
+ }
+
+ private static string GetClipboardText()
+ {
+ return _getTextOriginal!().ReplaceLineEndings("\r\n");
+ }
+}
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index 1b12fd853..7d164c01f 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -240,6 +240,7 @@ internal class InterfaceManager : IDisposable, IServiceType
this.processMessageHook?.Dispose();
}).Wait();
+ ImGuiClipboardConfig.Unapply();
this.scene?.Dispose();
}
@@ -628,6 +629,7 @@ internal class InterfaceManager : IDisposable, IServiceType
ImGui.GetIO().FontGlobalScale = configuration.GlobalUiScale;
this.SetupFonts();
+ ImGuiClipboardConfig.Apply();
if (!configuration.IsDocking)
{