Merge branch 'master' into flytext

This commit is contained in:
goaaats 2021-08-11 16:37:33 +02:00 committed by GitHub
commit 4bf1e0f679
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
127 changed files with 5204 additions and 2348 deletions

View file

@ -50,7 +50,7 @@ namespace Dalamud.Game.Gui
}
/// <summary>
/// A delegate type used with the <see cref="OnChatMessage"/> event.
/// A delegate type used with the <see cref="ChatGui.ChatMessage"/> event.
/// </summary>
/// <param name="type">The type of chat.</param>
/// <param name="senderId">The sender ID.</param>
@ -60,7 +60,7 @@ namespace Dalamud.Game.Gui
public delegate void OnMessageDelegate(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled);
/// <summary>
/// A delegate type used with the <see cref="OnCheckMessageHandled"/> event.
/// A delegate type used with the <see cref="ChatGui.CheckMessageHandled"/> event.
/// </summary>
/// <param name="type">The type of chat.</param>
/// <param name="senderId">The sender ID.</param>
@ -70,7 +70,7 @@ namespace Dalamud.Game.Gui
public delegate void OnCheckMessageHandledDelegate(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled);
/// <summary>
/// A delegate type used with the <see cref="OnChatMessageHandled"/> event.
/// A delegate type used with the <see cref="ChatGui.ChatMessageHandled"/> event.
/// </summary>
/// <param name="type">The type of chat.</param>
/// <param name="senderId">The sender ID.</param>
@ -79,7 +79,7 @@ namespace Dalamud.Game.Gui
public delegate void OnMessageHandledDelegate(XivChatType type, uint senderId, SeString sender, SeString message);
/// <summary>
/// A delegate type used with the <see cref="OnChatMessageUnhandled"/> event.
/// A delegate type used with the <see cref="ChatGui.ChatMessageUnhandled"/> event.
/// </summary>
/// <param name="type">The type of chat.</param>
/// <param name="senderId">The sender ID.</param>
@ -99,22 +99,22 @@ namespace Dalamud.Game.Gui
/// <summary>
/// Event that will be fired when a chat message is sent to chat by the game.
/// </summary>
public event OnMessageDelegate OnChatMessage;
public event OnMessageDelegate ChatMessage;
/// <summary>
/// Event that allows you to stop messages from appearing in chat by setting the isHandled parameter to true.
/// </summary>
public event OnCheckMessageHandledDelegate OnCheckMessageHandled;
public event OnCheckMessageHandledDelegate CheckMessageHandled;
/// <summary>
/// Event that will be fired when a chat message is handled by Dalamud or a Plugin.
/// </summary>
public event OnMessageHandledDelegate OnChatMessageHandled;
public event OnMessageHandledDelegate ChatMessageHandled;
/// <summary>
/// Event that will be fired when a chat message is not handled by Dalamud or a Plugin.
/// </summary>
public event OnMessageUnhandledDelegate OnChatMessageUnhandled;
public event OnMessageUnhandledDelegate ChatMessageUnhandled;
/// <summary>
/// Gets the ID of the last linked item.
@ -365,11 +365,11 @@ namespace Dalamud.Game.Gui
// Call events
var isHandled = false;
this.OnCheckMessageHandled?.Invoke(chattype, senderid, ref parsedSender, ref parsedMessage, ref isHandled);
this.CheckMessageHandled?.Invoke(chattype, senderid, ref parsedSender, ref parsedMessage, ref isHandled);
if (!isHandled)
{
this.OnChatMessage?.Invoke(chattype, senderid, ref parsedSender, ref parsedMessage, ref isHandled);
this.ChatMessage?.Invoke(chattype, senderid, ref parsedSender, ref parsedMessage, ref isHandled);
}
var newEdited = parsedMessage.Encode();
@ -395,12 +395,12 @@ namespace Dalamud.Game.Gui
// Print the original chat if it's handled.
if (isHandled)
{
this.OnChatMessageHandled?.Invoke(chattype, senderid, parsedSender, parsedMessage);
this.ChatMessageHandled?.Invoke(chattype, senderid, parsedSender, parsedMessage);
}
else
{
retVal = this.printMessageHook.Original(manager, chattype, pSenderName, messagePtr, senderid, parameter);
this.OnChatMessageUnhandled?.Invoke(chattype, senderid, parsedSender, parsedMessage);
this.ChatMessageUnhandled?.Invoke(chattype, senderid, parsedSender, parsedMessage);
}
if (this.baseAddress == IntPtr.Zero)

View file

@ -8,6 +8,7 @@ using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Hooking;
using Dalamud.Interface;
using Dalamud.Utility;
using ImGuiNET;
using Serilog;
namespace Dalamud.Game.Gui
@ -32,6 +33,7 @@ namespace Dalamud.Game.Gui
private readonly Hook<HandleItemOutDelegate> handleItemOutHook;
private readonly Hook<HandleActionHoverDelegate> handleActionHoverHook;
private readonly Hook<HandleActionOutDelegate> handleActionOutHook;
private readonly Hook<HandleImmDelegate> handleImmHook;
private readonly Hook<ToggleUiHideDelegate> toggleUiHideHook;
private GetUIMapObjectDelegate getUIMapObject;
@ -57,6 +59,7 @@ namespace Dalamud.Game.Gui
Log.Verbose($"SetGlobalBgm address 0x{this.address.SetGlobalBgm.ToInt64():X}");
Log.Verbose($"HandleItemHover address 0x{this.address.HandleItemHover.ToInt64():X}");
Log.Verbose($"HandleItemOut address 0x{this.address.HandleItemOut.ToInt64():X}");
Log.Verbose($"HandleImm address 0x{this.address.HandleImm.ToInt64():X}");
Log.Verbose($"GetUIObject address 0x{this.address.GetUIObject.ToInt64():X}");
Log.Verbose($"GetAgentModule address 0x{this.address.GetAgentModule.ToInt64():X}");
@ -66,13 +69,15 @@ namespace Dalamud.Game.Gui
this.FlyText = new FlyTextGui(scanner, dalamud);
this.setGlobalBgmHook = new Hook<SetGlobalBgmDelegate>(this.address.SetGlobalBgm, this.HandleSetGlobalBgmDetour);
this.handleItemHoverHook = new Hook<HandleItemHoverDelegate>(this.address.HandleItemHover, this.HandleItemHoverDetour);
this.handleItemHoverHook = new Hook<HandleItemHoverDelegate>(this.address.HandleItemHover, this.HandleItemHoverDetour);
this.handleItemOutHook = new Hook<HandleItemOutDelegate>(this.address.HandleItemOut, this.HandleItemOutDetour);
this.handleActionHoverHook = new Hook<HandleActionHoverDelegate>(this.address.HandleActionHover, this.HandleActionHoverDetour);
this.handleActionOutHook = new Hook<HandleActionOutDelegate>(this.address.HandleActionOut, this.HandleActionOutDetour);
this.handleImmHook = new Hook<HandleImmDelegate>(this.address.HandleImm, this.HandleImmDetour);
this.getUIObject = Marshal.GetDelegateForFunctionPointer<GetUIObjectDelegate>(this.address.GetUIObject);
this.getMatrixSingleton = Marshal.GetDelegateForFunctionPointer<GetMatrixSingletonDelegate>(this.address.GetMatrixSingleton);
@ -136,6 +141,9 @@ namespace Dalamud.Game.Gui
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate IntPtr HandleActionOutDelegate(IntPtr agentActionDetail, IntPtr a2, IntPtr a3, int a4);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate char HandleImmDelegate(IntPtr framework, char a2, byte a3);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate IntPtr ToggleUiHideDelegate(IntPtr thisPtr, byte unknownByte);
@ -285,23 +293,6 @@ namespace Dalamud.Game.Gui
return result;
}
/// <summary>
/// Converts in-world coordinates to screen coordinates (upper left corner origin).
/// </summary>
/// <param name="worldPos">Coordinates in the world.</param>
/// <param name="screenPos">Converted coordinates.</param>
/// <returns>True if worldPos corresponds to a position in front of the camera.</returns>
/// <remarks>
/// This overload requires a conversion to SharpDX vectors, however the penalty should be negligible.
/// </remarks>
public bool WorldToScreen(Position3 worldPos, out Vector2 screenPos)
{
// This overload is necessary due to Positon3 implicit operators.
var result = this.WorldToScreen((SharpDX.Vector3)worldPos, out var sharpScreenPos);
screenPos = sharpScreenPos.ToSystem();
return result;
}
/// <summary>
/// Converts screen coordinates to in-world coordinates via raycasting.
/// </summary>
@ -518,6 +509,7 @@ namespace Dalamud.Game.Gui
this.setGlobalBgmHook.Enable();
this.handleItemHoverHook.Enable();
this.handleItemOutHook.Enable();
this.handleImmHook.Enable();
this.toggleUiHideHook.Enable();
this.handleActionHoverHook.Enable();
this.handleActionOutHook.Enable();
@ -535,6 +527,7 @@ namespace Dalamud.Game.Gui
this.setGlobalBgmHook.Dispose();
this.handleItemHoverHook.Dispose();
this.handleItemOutHook.Dispose();
this.handleImmHook.Dispose();
this.toggleUiHideHook.Dispose();
this.handleActionHoverHook.Dispose();
this.handleActionOutHook.Dispose();
@ -666,5 +659,13 @@ namespace Dalamud.Game.Gui
return this.toggleUiHideHook.Original(thisPtr, unknownByte);
}
private char HandleImmDetour(IntPtr framework, char a2, byte a3)
{
var result = this.handleImmHook.Original(framework, a2, a3);
return ImGui.GetIO().WantTextInput
? (char)0
: result;
}
}
}

View file

@ -52,6 +52,11 @@ namespace Dalamud.Game.Gui
/// </summary>
public IntPtr HandleActionOut { get; private set; }
/// <summary>
/// Gets the address of the native HandleImm method.
/// </summary>
public IntPtr HandleImm { get; private set; }
/// <summary>
/// Gets the address of the native GetUIObject method.
/// </summary>
@ -100,6 +105,7 @@ namespace Dalamud.Game.Gui
this.HandleItemOut = sig.ScanText("48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 4D");
this.HandleActionHover = sig.ScanText("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 83 F8 0F");
this.HandleActionOut = sig.ScanText("48 89 5C 24 ?? 57 48 83 EC 20 48 8B DA 48 8B F9 4D 85 C0 74 1F");
this.HandleImm = sig.ScanText("E8 ?? ?? ?? ?? 84 C0 75 10 48 83 FF 09");
this.GetUIObject = sig.ScanText("E8 ?? ?? ?? ?? 48 8B C8 48 8B 10 FF 52 40 80 88 ?? ?? ?? ?? 01 E9");
this.GetMatrixSingleton = sig.ScanText("E8 ?? ?? ?? ?? 48 8D 4C 24 ?? 48 89 4c 24 ?? 4C 8D 4D ?? 4C 8D 44 24 ??");
this.ScreenToWorld = sig.ScanText("48 83 EC 48 48 8B 05 ?? ?? ?? ?? 4D 8B D1");

View file

@ -0,0 +1,234 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using Dalamud.Logging.Internal;
using ImGuiNET;
using static Dalamud.NativeFunctions;
namespace Dalamud.Game.Gui.Internal
{
/// <summary>
/// This class handles IME for non-English users.
/// </summary>
internal class DalamudIME : IDisposable
{
private static readonly ModuleLog Log = new("IME");
private readonly Dalamud dalamud;
private IntPtr interfaceHandle;
private IntPtr wndProcPtr;
private IntPtr oldWndProcPtr;
private WndProcDelegate wndProcDelegate;
/// <summary>
/// Initializes a new instance of the <see cref="DalamudIME"/> class.
/// </summary>
/// <param name="dalamud">The Dalamud instance.</param>
internal DalamudIME(Dalamud dalamud)
{
this.dalamud = dalamud;
}
private delegate long WndProcDelegate(IntPtr hWnd, uint msg, ulong wParam, long lParam);
/// <summary>
/// Gets a value indicating whether the module is enabled.
/// </summary>
internal bool IsEnabled { get; private set; }
/// <summary>
/// Gets the imm candidates.
/// </summary>
internal List<string> ImmCand { get; private set; } = new();
/// <summary>
/// Gets the imm component.
/// </summary>
internal string ImmComp { get; private set; } = string.Empty;
/// <inheritdoc/>
public void Dispose()
{
if (this.oldWndProcPtr != IntPtr.Zero)
{
SetWindowLongPtrW(this.interfaceHandle, WindowLongType.WndProc, this.oldWndProcPtr);
this.oldWndProcPtr = IntPtr.Zero;
}
}
/// <summary>
/// Enables the IME module.
/// </summary>
internal void Enable()
{
try
{
this.wndProcDelegate = this.WndProcDetour;
this.interfaceHandle = this.dalamud.InterfaceManager.WindowHandlePtr;
this.wndProcPtr = Marshal.GetFunctionPointerForDelegate(this.wndProcDelegate);
this.oldWndProcPtr = SetWindowLongPtrW(this.interfaceHandle, WindowLongType.WndProc, this.wndProcPtr);
this.IsEnabled = true;
Log.Information("Enabled!");
}
catch (Exception ex)
{
Log.Information(ex, "Enable failed");
}
}
private void ToggleWindow(bool visible)
{
if (visible)
this.dalamud.DalamudUi.OpenIMEWindow();
else
this.dalamud.DalamudUi.CloseIMEWindow();
}
private long WndProcDetour(IntPtr hWnd, uint msg, ulong wParam, long lParam)
{
try
{
if (hWnd == this.interfaceHandle && ImGui.GetCurrentContext() != IntPtr.Zero && ImGui.GetIO().WantTextInput)
{
var io = ImGui.GetIO();
var wmsg = (WindowsMessage)msg;
switch (wmsg)
{
case WindowsMessage.WM_IME_NOTIFY:
switch ((IMECommand)wParam)
{
case IMECommand.ChangeCandidate:
this.ToggleWindow(true);
if (hWnd == IntPtr.Zero)
return 0;
var hIMC = ImmGetContext(hWnd);
if (hIMC == IntPtr.Zero)
return 0;
var size = ImmGetCandidateListW(hIMC, 0, IntPtr.Zero, 0);
if (size > 0)
{
var candlistPtr = Marshal.AllocHGlobal((int)size);
size = ImmGetCandidateListW(hIMC, 0, candlistPtr, (uint)size);
var candlist = Marshal.PtrToStructure<CandidateList>(candlistPtr);
var pageSize = candlist.PageSize;
var candCount = candlist.Count;
if (pageSize > 0 && candCount > 1)
{
var dwOffsets = new int[candCount];
for (var i = 0; i < candCount; i++)
dwOffsets[i] = Marshal.ReadInt32(candlistPtr + ((i + 6) * sizeof(int)));
var pageStart = candlist.PageStart;
// var pageEnd = pageStart + pageSize;
var cand = new string[pageSize];
this.ImmCand.Clear();
for (var i = 0; i < pageSize; i++)
{
var offStart = dwOffsets[i + pageStart];
var offEnd = i + pageStart + 1 < candCount ? dwOffsets[i + pageStart + 1] : size;
var pStrStart = candlistPtr + (int)offStart;
var pStrEnd = candlistPtr + (int)offEnd;
var len = (int)(pStrEnd.ToInt64() - pStrStart.ToInt64());
if (len > 0)
{
var candBytes = new byte[len];
Marshal.Copy(pStrStart, candBytes, 0, len);
var candStr = Encoding.Unicode.GetString(candBytes);
cand[i] = candStr;
this.ImmCand.Add(candStr);
}
}
}
Marshal.FreeHGlobal(candlistPtr);
}
break;
case IMECommand.OpenCandidate:
this.ToggleWindow(true);
this.ImmCand.Clear();
break;
case IMECommand.CloseCandidate:
this.ToggleWindow(false);
this.ImmCand.Clear();
break;
default:
break;
}
break;
case WindowsMessage.WM_IME_COMPOSITION:
if ((lParam & (long)IMEComposition.ResultStr) > 0)
{
var hIMC = ImmGetContext(hWnd);
if (hIMC == IntPtr.Zero)
return 0;
var dwSize = ImmGetCompositionStringW(hIMC, IMEComposition.ResultStr, IntPtr.Zero, 0);
var unmanagedPointer = Marshal.AllocHGlobal((int)dwSize);
ImmGetCompositionStringW(hIMC, IMEComposition.ResultStr, unmanagedPointer, (uint)dwSize);
var bytes = new byte[dwSize];
Marshal.Copy(unmanagedPointer, bytes, 0, (int)dwSize);
Marshal.FreeHGlobal(unmanagedPointer);
var lpstr = Encoding.Unicode.GetString(bytes);
io.AddInputCharactersUTF8(lpstr);
this.ImmComp = string.Empty;
this.ImmCand.Clear();
this.ToggleWindow(false);
}
if (((long)(IMEComposition.CompStr | IMEComposition.CompAttr | IMEComposition.CompClause |
IMEComposition.CompReadAttr | IMEComposition.CompReadClause | IMEComposition.CompReadStr) & lParam) > 0)
{
var hIMC = ImmGetContext(hWnd);
if (hIMC == IntPtr.Zero)
return 0;
var dwSize = ImmGetCompositionStringW(hIMC, IMEComposition.CompStr, IntPtr.Zero, 0);
var unmanagedPointer = Marshal.AllocHGlobal((int)dwSize);
ImmGetCompositionStringW(hIMC, IMEComposition.CompStr, unmanagedPointer, (uint)dwSize);
var bytes = new byte[dwSize];
Marshal.Copy(unmanagedPointer, bytes, 0, (int)dwSize);
Marshal.FreeHGlobal(unmanagedPointer);
var lpstr = Encoding.Unicode.GetString(bytes);
this.ImmComp = lpstr;
if (lpstr == string.Empty)
this.ToggleWindow(false);
}
break;
default:
break;
}
}
}
catch (Exception ex)
{
Log.Error(ex, "Prevented a crash in an IME hook");
}
return CallWindowProcW(this.oldWndProcPtr, hWnd, msg, wParam, lParam);
}
}
}

View file

@ -85,17 +85,17 @@ namespace Dalamud.Game.Gui
/// <summary>
/// Event that will be fired when a toast is sent by the game or a plugin.
/// </summary>
public event OnNormalToastDelegate OnToast;
public event OnNormalToastDelegate Toast;
/// <summary>
/// Event that will be fired when a quest toast is sent by the game or a plugin.
/// </summary>
public event OnQuestToastDelegate OnQuestToast;
public event OnQuestToastDelegate QuestToast;
/// <summary>
/// Event that will be fired when an error toast is sent by the game or a plugin.
/// </summary>
public event OnErrorToastDelegate OnErrorToast;
public event OnErrorToastDelegate ErrorToast;
#endregion
@ -231,7 +231,7 @@ namespace Dalamud.Game.Gui
Speed = (ToastSpeed)isFast,
};
this.OnToast?.Invoke(ref str, ref options, ref isHandled);
this.Toast?.Invoke(ref str, ref options, ref isHandled);
// do nothing if handled
if (isHandled)
@ -323,7 +323,7 @@ namespace Dalamud.Game.Gui
PlaySound = playSound == 1,
};
this.OnQuestToast?.Invoke(ref str, ref options, ref isHandled);
this.QuestToast?.Invoke(ref str, ref options, ref isHandled);
// do nothing if handled
if (isHandled)
@ -409,7 +409,7 @@ namespace Dalamud.Game.Gui
var isHandled = false;
var str = this.ParseString(text);
this.OnErrorToast?.Invoke(ref str, ref isHandled);
this.ErrorToast?.Invoke(ref str, ref isHandled);
// do nothing if handled
if (isHandled)