diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index be4278ec5..b4adb991c 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -9,6 +9,7 @@ using Dalamud.Data; using Dalamud.Game; using Dalamud.Game.ClientState; using Dalamud.Game.Command; +using Dalamud.Game.Gui.Internal; using Dalamud.Game.Internal; using Dalamud.Game.Network.Internal; using Dalamud.Game.Text.SeStringHandling; @@ -100,6 +101,11 @@ namespace Dalamud /// internal InterfaceManager InterfaceManager { get; private set; } + /// + /// Gets the Input Method subsystem. + /// + internal DalamudIME IME { get; private set; } + /// /// Gets ClientState subsystem. /// @@ -293,6 +299,16 @@ namespace Dalamud } } + try + { + this.IME = new DalamudIME(this); + Log.Information("[T2] IME OK!"); + } + catch (Exception e) + { + Log.Information(e, "Could not init IME."); + } + this.Data = new DataManager(this.StartInfo.Language, this.InterfaceManager); try { @@ -415,6 +431,11 @@ namespace Dalamud { this.hasDisposedPlugins = true; + // this must be done before unloading interface manager, in order to do rebuild + // the correct cascaded WndProc (IME -> RawDX11Scene -> Game). Otherwise the game + // will not receive any windows messages + this.IME?.Dispose(); + // this must be done before unloading plugins, or it can cause a race condition // due to rendering happening on another thread, where a plugin might receive // a render call after it has been disposed, which can crash if it attempts to diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index 85d8d9c86..040fccca1 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -51,8 +51,9 @@ - IDE0003;IDE0044;IDE1006;CS1591;CS1701;CS1702 + IDE0003;IDE0044;IDE1006;CA1822;CS1591;CS1701;CS1702 + diff --git a/Dalamud/Game/Gui/GameGui.cs b/Dalamud/Game/Gui/GameGui.cs index 0d33a7df4..9a4bd6943 100644 --- a/Dalamud/Game/Gui/GameGui.cs +++ b/Dalamud/Game/Gui/GameGui.cs @@ -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 handleItemOutHook; private readonly Hook handleActionHoverHook; private readonly Hook handleActionOutHook; + private readonly Hook handleImmHook; private readonly Hook 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}"); @@ -65,13 +68,15 @@ namespace Dalamud.Game.Gui this.Toast = new ToastGui(scanner, dalamud); this.setGlobalBgmHook = new Hook(this.address.SetGlobalBgm, this.HandleSetGlobalBgmDetour); - this.handleItemHoverHook = new Hook(this.address.HandleItemHover, this.HandleItemHoverDetour); + this.handleItemHoverHook = new Hook(this.address.HandleItemHover, this.HandleItemHoverDetour); this.handleItemOutHook = new Hook(this.address.HandleItemOut, this.HandleItemOutDetour); this.handleActionHoverHook = new Hook(this.address.HandleActionHover, this.HandleActionHoverDetour); this.handleActionOutHook = new Hook(this.address.HandleActionOut, this.HandleActionOutDetour); + this.handleImmHook = new Hook(this.address.HandleImm, this.HandleImmDetour); + this.getUIObject = Marshal.GetDelegateForFunctionPointer(this.address.GetUIObject); this.getMatrixSingleton = Marshal.GetDelegateForFunctionPointer(this.address.GetMatrixSingleton); @@ -135,6 +140,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); @@ -494,6 +502,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(); @@ -510,6 +519,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(); @@ -641,5 +651,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; + } } } diff --git a/Dalamud/Game/Gui/GameGuiAddressResolver.cs b/Dalamud/Game/Gui/GameGuiAddressResolver.cs index b3e04d68d..b68ea9496 100644 --- a/Dalamud/Game/Gui/GameGuiAddressResolver.cs +++ b/Dalamud/Game/Gui/GameGuiAddressResolver.cs @@ -52,6 +52,11 @@ namespace Dalamud.Game.Gui /// public IntPtr HandleActionOut { get; private set; } + /// + /// Gets the address of the native HandleImm method. + /// + public IntPtr HandleImm { get; private set; } + /// /// Gets the address of the native GetUIObject method. /// @@ -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"); diff --git a/Dalamud/Game/Gui/Internal/DalamudIME.cs b/Dalamud/Game/Gui/Internal/DalamudIME.cs new file mode 100644 index 000000000..de1c4a6a5 --- /dev/null +++ b/Dalamud/Game/Gui/Internal/DalamudIME.cs @@ -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 +{ + /// + /// This class handles IME for non-English users. + /// + 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; + + /// + /// Initializes a new instance of the class. + /// + /// The Dalamud instance. + internal DalamudIME(Dalamud dalamud) + { + this.dalamud = dalamud; + } + + private delegate long WndProcDelegate(IntPtr hWnd, uint msg, ulong wParam, long lParam); + + /// + /// Gets a value indicating whether the module is enabled. + /// + internal bool IsEnabled { get; private set; } + + /// + /// Gets the imm candidates. + /// + internal List ImmCand { get; private set; } = new(); + + /// + /// Gets the imm component. + /// + internal string ImmComp { get; private set; } = string.Empty; + + /// + public void Dispose() + { + if (this.oldWndProcPtr != IntPtr.Zero) + { + SetWindowLongPtrW(this.interfaceHandle, WindowLongType.WndProc, this.oldWndProcPtr); + this.oldWndProcPtr = IntPtr.Zero; + } + } + + /// + /// Enables the IME module. + /// + 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(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); + } + } +} diff --git a/Dalamud/Interface/Internal/DalamudCommands.cs b/Dalamud/Interface/Internal/DalamudCommands.cs index 1a4f64815..f3a895ff3 100644 --- a/Dalamud/Interface/Internal/DalamudCommands.cs +++ b/Dalamud/Interface/Internal/DalamudCommands.cs @@ -84,6 +84,12 @@ namespace Dalamud.Interface.Internal ShowInHelp = false, }); + this.dalamud.CommandManager.AddHandler("/xlime", new CommandInfo(this.OnDebugDrawIMEPanel) + { + HelpMessage = Loc.Localize("DalamudIMEPanelHelp", "Draw IME panel"), + ShowInHelp = false, + }); + this.dalamud.CommandManager.AddHandler("/xllog", new CommandInfo(this.OnOpenLog) { HelpMessage = Loc.Localize("DalamudDevLogHelp", "Open dev log DEBUG"), @@ -245,6 +251,11 @@ namespace Dalamud.Interface.Internal this.dalamud.DalamudUi.OpenDataWindow(arguments); } + private void OnDebugDrawIMEPanel(string command, string arguments) + { + this.dalamud.DalamudUi.OpenIMEWindow(); + } + private void OnOpenLog(string command, string arguments) { this.dalamud.DalamudUi.ToggleLogWindow(); diff --git a/Dalamud/Interface/Internal/DalamudInterface.cs b/Dalamud/Interface/Internal/DalamudInterface.cs index ab70b15b2..68c733ee2 100644 --- a/Dalamud/Interface/Internal/DalamudInterface.cs +++ b/Dalamud/Interface/Internal/DalamudInterface.cs @@ -32,6 +32,7 @@ namespace Dalamud.Interface.Internal private readonly CreditsWindow creditsWindow; private readonly DataWindow dataWindow; private readonly GamepadModeNotifierWindow gamepadModeNotifierWindow; + private readonly IMEWindow imeWindow; private readonly ConsoleWindow consoleWindow; private readonly PluginStatWindow pluginStatWindow; private readonly PluginInstallerWindow pluginWindow; @@ -64,6 +65,7 @@ namespace Dalamud.Interface.Internal this.creditsWindow = new CreditsWindow(dalamud) { IsOpen = false }; this.dataWindow = new DataWindow(dalamud) { IsOpen = false }; this.gamepadModeNotifierWindow = new GamepadModeNotifierWindow(); + this.imeWindow = new IMEWindow(dalamud); this.consoleWindow = new ConsoleWindow(dalamud) { IsOpen = this.dalamud.Configuration.LogOpenAtStartup }; this.pluginStatWindow = new PluginStatWindow(dalamud) { IsOpen = false }; this.pluginWindow = new PluginInstallerWindow(dalamud) { IsOpen = false }; @@ -77,6 +79,7 @@ namespace Dalamud.Interface.Internal this.windowSystem.AddWindow(this.creditsWindow); this.windowSystem.AddWindow(this.dataWindow); this.windowSystem.AddWindow(this.gamepadModeNotifierWindow); + this.windowSystem.AddWindow(this.imeWindow); this.windowSystem.AddWindow(this.consoleWindow); this.windowSystem.AddWindow(this.pluginStatWindow); this.windowSystem.AddWindow(this.pluginWindow); @@ -160,6 +163,11 @@ namespace Dalamud.Interface.Internal /// public void OpenGamepadModeNotifierWindow() => this.gamepadModeNotifierWindow.IsOpen = true; + /// + /// Opens the . + /// + public void OpenIMEWindow() => this.imeWindow.IsOpen = true; + /// /// Opens the . /// @@ -192,6 +200,15 @@ namespace Dalamud.Interface.Internal #endregion + #region Close + + /// + /// Closes the . + /// + public void CloseIMEWindow() => this.imeWindow.IsOpen = false; + + #endregion + #region Toggle /// @@ -237,6 +254,11 @@ namespace Dalamud.Interface.Internal /// public void ToggleGamepadModeNotifierWindow() => this.gamepadModeNotifierWindow.Toggle(); + /// + /// Toggles the . + /// + public void ToggleIMEWindow() => this.imeWindow.Toggle(); + /// /// Toggles the . /// diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index 82ae2812c..e368aab3b 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -397,6 +397,8 @@ namespace Dalamud.Interface.Internal ImGuiHelpers.MainViewport = ImGui.GetMainViewport(); Log.Information("[IM] Scene & ImGui setup OK!"); + + this.dalamud.IME.Enable(); } // Process information needed by ImGuiHelpers each frame. diff --git a/Dalamud/Interface/Internal/Windows/IMEWindow.cs b/Dalamud/Interface/Internal/Windows/IMEWindow.cs new file mode 100644 index 000000000..addaeaec6 --- /dev/null +++ b/Dalamud/Interface/Internal/Windows/IMEWindow.cs @@ -0,0 +1,46 @@ +using System.Numerics; + +using Dalamud.Game.Gui.Internal; +using Dalamud.Interface.Windowing; +using ImGuiNET; + +namespace Dalamud.Interface.Internal.Windows +{ + /// + /// A window for displaying IME details. + /// + internal class IMEWindow : Window + { + private readonly DalamudIME dalamudIME; + + /// + /// Initializes a new instance of the class. + /// + /// The Dalamud instance. + public IMEWindow(Dalamud dalamud) + : base("Dalamud IME", ImGuiWindowFlags.NoTitleBar | ImGuiWindowFlags.NoFocusOnAppearing) + { + this.dalamudIME = dalamud.IME; + this.Size = new Vector2(100, 200); + this.SizeCondition = ImGuiCond.FirstUseEver; + } + + /// + public override void Draw() + { + if (this.dalamudIME == null || !this.dalamudIME.IsEnabled) + { + ImGui.Text("IME unavailable."); + return; + } + + ImGui.Text(this.dalamudIME.ImmComp); + + ImGui.Separator(); + for (var i = 0; i < this.dalamudIME.ImmCand.Count; i++) + { + ImGui.Text($"{i + 1}. {this.dalamudIME.ImmCand[i]}"); + } + } + } +} diff --git a/Dalamud/NativeFunctions.cs b/Dalamud/NativeFunctions.cs index 0a4fbc5bd..38d9173f4 100644 --- a/Dalamud/NativeFunctions.cs +++ b/Dalamud/NativeFunctions.cs @@ -48,6 +48,92 @@ namespace Dalamud TimerNoFG = 12, } + /// + /// IDC_* from winuser. + /// + public enum CursorType + { + /// + /// Standard arrow and small hourglass. + /// + AppStarting = 32650, + + /// + /// Standard arrow. + /// + Arrow = 32512, + + /// + /// Crosshair. + /// + Cross = 32515, + + /// + /// Hand. + /// + Hand = 32649, + + /// + /// Arrow and question mark. + /// + Help = 32651, + + /// + /// I-beam. + /// + IBeam = 32513, + + /// + /// Obsolete for applications marked version 4.0 or later. + /// + Icon = 32641, + + /// + /// Slashed circle. + /// + No = 32648, + + /// + /// Obsolete for applications marked version 4.0 or later.Use IDC_SIZEALL. + /// + Size = 32640, + + /// + /// Four-pointed arrow pointing north, south, east, and west. + /// + SizeAll = 32646, + + /// + /// Double-pointed arrow pointing northeast and southwest. + /// + SizeNeSw = 32643, + + /// + /// Double-pointed arrow pointing north and south. + /// + SizeNS = 32645, + + /// + /// Double-pointed arrow pointing northwest and southeast. + /// + SizeNwSe = 32642, + + /// + /// Double-pointed arrow pointing west and east. + /// + SizeWE = 32644, + + /// + /// Vertical arrow. + /// + UpArrow = 32516, + + /// + /// Hourglass. + /// + Wait = 32514, + } + /// /// MB_* from winuser. /// @@ -235,6 +321,329 @@ namespace Dalamud ServiceNotification = 0x200000, } + /// + /// GWL_* from winuser. + /// + public enum WindowLongType + { + /// + /// Sets a new extended window style. + /// + ExStyle = -20, + + /// + /// Sets a new application instance handle. + /// + HInstance = -6, + + /// + /// Sets a new identifier of the child window.The window cannot be a top-level window. + /// + Id = -12, + + /// + /// Sets a new window style. + /// + Style = -16, + + /// + /// Sets the user data associated with the window. This data is intended for use by the application that created the window. Its value is initially zero. + /// + UserData = -21, + + /// + /// Sets a new address for the window procedure. + /// + WndProc = -4, + + // The following values are also available when the hWnd parameter identifies a dialog box. + + // /// + // /// Sets the new pointer to the dialog box procedure. + // /// + // DWLP_DLGPROC = DWLP_MSGRESULT + sizeof(LRESULT), + + /// + /// Sets the return value of a message processed in the dialog box procedure. + /// + MsgResult = 0, + + // /// + // /// Sets new extra information that is private to the application, such as handles or pointers. + // /// + // DWLP_USER = DWLP_DLGPROC + sizeof(DLGPROC), + } + + /// + /// WM_* from winuser. + /// These are spread throughout multiple files, find the documentation manually if you need it. + /// https://gist.github.com/amgine/2395987. + /// + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "No documentation available.")] + public enum WindowsMessage + { + WM_NULL = 0x0000, + WM_CREATE = 0x0001, + WM_DESTROY = 0x0002, + WM_MOVE = 0x0003, + WM_SIZE = 0x0005, + WM_ACTIVATE = 0x0006, + WM_SETFOCUS = 0x0007, + WM_KILLFOCUS = 0x0008, + WM_ENABLE = 0x000A, + WM_SETREDRAW = 0x000B, + WM_SETTEXT = 0x000C, + WM_GETTEXT = 0x000D, + WM_GETTEXTLENGTH = 0x000E, + WM_PAINT = 0x000F, + WM_CLOSE = 0x0010, + WM_QUERYENDSESSION = 0x0011, + WM_QUERYOPEN = 0x0013, + WM_ENDSESSION = 0x0016, + WM_QUIT = 0x0012, + WM_ERASEBKGND = 0x0014, + WM_SYSCOLORCHANGE = 0x0015, + WM_SHOWWINDOW = 0x0018, + WM_WININICHANGE = 0x001A, + WM_SETTINGCHANGE = WM_WININICHANGE, + WM_DEVMODECHANGE = 0x001B, + WM_ACTIVATEAPP = 0x001C, + WM_FONTCHANGE = 0x001D, + WM_TIMECHANGE = 0x001E, + WM_CANCELMODE = 0x001F, + WM_SETCURSOR = 0x0020, + WM_MOUSEACTIVATE = 0x0021, + WM_CHILDACTIVATE = 0x0022, + WM_QUEUESYNC = 0x0023, + WM_GETMINMAXINFO = 0x0024, + WM_PAINTICON = 0x0026, + WM_ICONERASEBKGND = 0x0027, + WM_NEXTDLGCTL = 0x0028, + WM_SPOOLERSTATUS = 0x002A, + WM_DRAWITEM = 0x002B, + WM_MEASUREITEM = 0x002C, + WM_DELETEITEM = 0x002D, + WM_VKEYTOITEM = 0x002E, + WM_CHARTOITEM = 0x002F, + WM_SETFONT = 0x0030, + WM_GETFONT = 0x0031, + WM_SETHOTKEY = 0x0032, + WM_GETHOTKEY = 0x0033, + WM_QUERYDRAGICON = 0x0037, + WM_COMPAREITEM = 0x0039, + WM_GETOBJECT = 0x003D, + WM_COMPACTING = 0x0041, + WM_COMMNOTIFY = 0x0044, + WM_WINDOWPOSCHANGING = 0x0046, + WM_WINDOWPOSCHANGED = 0x0047, + WM_POWER = 0x0048, + WM_COPYDATA = 0x004A, + WM_CANCELJOURNAL = 0x004B, + WM_NOTIFY = 0x004E, + WM_INPUTLANGCHANGEREQUEST = 0x0050, + WM_INPUTLANGCHANGE = 0x0051, + WM_TCARD = 0x0052, + WM_HELP = 0x0053, + WM_USERCHANGED = 0x0054, + WM_NOTIFYFORMAT = 0x0055, + WM_CONTEXTMENU = 0x007B, + WM_STYLECHANGING = 0x007C, + WM_STYLECHANGED = 0x007D, + WM_DISPLAYCHANGE = 0x007E, + WM_GETICON = 0x007F, + WM_SETICON = 0x0080, + WM_NCCREATE = 0x0081, + WM_NCDESTROY = 0x0082, + WM_NCCALCSIZE = 0x0083, + WM_NCHITTEST = 0x0084, + WM_NCPAINT = 0x0085, + WM_NCACTIVATE = 0x0086, + WM_GETDLGCODE = 0x0087, + WM_SYNCPAINT = 0x0088, + + WM_NCMOUSEMOVE = 0x00A0, + WM_NCLBUTTONDOWN = 0x00A1, + WM_NCLBUTTONUP = 0x00A2, + WM_NCLBUTTONDBLCLK = 0x00A3, + WM_NCRBUTTONDOWN = 0x00A4, + WM_NCRBUTTONUP = 0x00A5, + WM_NCRBUTTONDBLCLK = 0x00A6, + WM_NCMBUTTONDOWN = 0x00A7, + WM_NCMBUTTONUP = 0x00A8, + WM_NCMBUTTONDBLCLK = 0x00A9, + WM_NCXBUTTONDOWN = 0x00AB, + WM_NCXBUTTONUP = 0x00AC, + WM_NCXBUTTONDBLCLK = 0x00AD, + + WM_INPUT_DEVICE_CHANGE = 0x00FE, + WM_INPUT = 0x00FF, + + WM_KEYFIRST = WM_KEYDOWN, + WM_KEYDOWN = 0x0100, + WM_KEYUP = 0x0101, + WM_CHAR = 0x0102, + WM_DEADCHAR = 0x0103, + WM_SYSKEYDOWN = 0x0104, + WM_SYSKEYUP = 0x0105, + WM_SYSCHAR = 0x0106, + WM_SYSDEADCHAR = 0x0107, + WM_UNICHAR = 0x0109, + WM_KEYLAST = WM_UNICHAR, + + WM_IME_STARTCOMPOSITION = 0x010D, + WM_IME_ENDCOMPOSITION = 0x010E, + WM_IME_COMPOSITION = 0x010F, + WM_IME_KEYLAST = WM_IME_COMPOSITION, + + WM_INITDIALOG = 0x0110, + WM_COMMAND = 0x0111, + WM_SYSCOMMAND = 0x0112, + WM_TIMER = 0x0113, + WM_HSCROLL = 0x0114, + WM_VSCROLL = 0x0115, + WM_INITMENU = 0x0116, + WM_INITMENUPOPUP = 0x0117, + WM_MENUSELECT = 0x011F, + WM_MENUCHAR = 0x0120, + WM_ENTERIDLE = 0x0121, + WM_MENURBUTTONUP = 0x0122, + WM_MENUDRAG = 0x0123, + WM_MENUGETOBJECT = 0x0124, + WM_UNINITMENUPOPUP = 0x0125, + WM_MENUCOMMAND = 0x0126, + + WM_CHANGEUISTATE = 0x0127, + WM_UPDATEUISTATE = 0x0128, + WM_QUERYUISTATE = 0x0129, + + WM_CTLCOLORMSGBOX = 0x0132, + WM_CTLCOLOREDIT = 0x0133, + WM_CTLCOLORLISTBOX = 0x0134, + WM_CTLCOLORBTN = 0x0135, + WM_CTLCOLORDLG = 0x0136, + WM_CTLCOLORSCROLLBAR = 0x0137, + WM_CTLCOLORSTATIC = 0x0138, + MN_GETHMENU = 0x01E1, + + WM_MOUSEFIRST = WM_MOUSEMOVE, + WM_MOUSEMOVE = 0x0200, + WM_LBUTTONDOWN = 0x0201, + WM_LBUTTONUP = 0x0202, + WM_LBUTTONDBLCLK = 0x0203, + WM_RBUTTONDOWN = 0x0204, + WM_RBUTTONUP = 0x0205, + WM_RBUTTONDBLCLK = 0x0206, + WM_MBUTTONDOWN = 0x0207, + WM_MBUTTONUP = 0x0208, + WM_MBUTTONDBLCLK = 0x0209, + WM_MOUSEWHEEL = 0x020A, + WM_XBUTTONDOWN = 0x020B, + WM_XBUTTONUP = 0x020C, + WM_XBUTTONDBLCLK = 0x020D, + WM_MOUSEHWHEEL = 0x020E, + + WM_PARENTNOTIFY = 0x0210, + WM_ENTERMENULOOP = 0x0211, + WM_EXITMENULOOP = 0x0212, + + WM_NEXTMENU = 0x0213, + WM_SIZING = 0x0214, + WM_CAPTURECHANGED = 0x0215, + WM_MOVING = 0x0216, + + WM_POWERBROADCAST = 0x0218, + + WM_DEVICECHANGE = 0x0219, + + WM_MDICREATE = 0x0220, + WM_MDIDESTROY = 0x0221, + WM_MDIACTIVATE = 0x0222, + WM_MDIRESTORE = 0x0223, + WM_MDINEXT = 0x0224, + WM_MDIMAXIMIZE = 0x0225, + WM_MDITILE = 0x0226, + WM_MDICASCADE = 0x0227, + WM_MDIICONARRANGE = 0x0228, + WM_MDIGETACTIVE = 0x0229, + + WM_MDISETMENU = 0x0230, + WM_ENTERSIZEMOVE = 0x0231, + WM_EXITSIZEMOVE = 0x0232, + WM_DROPFILES = 0x0233, + WM_MDIREFRESHMENU = 0x0234, + + WM_IME_SETCONTEXT = 0x0281, + WM_IME_NOTIFY = 0x0282, + WM_IME_CONTROL = 0x0283, + WM_IME_COMPOSITIONFULL = 0x0284, + WM_IME_SELECT = 0x0285, + WM_IME_CHAR = 0x0286, + WM_IME_REQUEST = 0x0288, + WM_IME_KEYDOWN = 0x0290, + WM_IME_KEYUP = 0x0291, + + WM_MOUSEHOVER = 0x02A1, + WM_MOUSELEAVE = 0x02A3, + WM_NCMOUSEHOVER = 0x02A0, + WM_NCMOUSELEAVE = 0x02A2, + + WM_WTSSESSION_CHANGE = 0x02B1, + + WM_TABLET_FIRST = 0x02c0, + WM_TABLET_LAST = 0x02df, + + WM_CUT = 0x0300, + WM_COPY = 0x0301, + WM_PASTE = 0x0302, + WM_CLEAR = 0x0303, + WM_UNDO = 0x0304, + WM_RENDERFORMAT = 0x0305, + WM_RENDERALLFORMATS = 0x0306, + WM_DESTROYCLIPBOARD = 0x0307, + WM_DRAWCLIPBOARD = 0x0308, + WM_PAINTCLIPBOARD = 0x0309, + WM_VSCROLLCLIPBOARD = 0x030A, + WM_SIZECLIPBOARD = 0x030B, + WM_ASKCBFORMATNAME = 0x030C, + WM_CHANGECBCHAIN = 0x030D, + WM_HSCROLLCLIPBOARD = 0x030E, + WM_QUERYNEWPALETTE = 0x030F, + WM_PALETTEISCHANGING = 0x0310, + WM_PALETTECHANGED = 0x0311, + WM_HOTKEY = 0x0312, + + WM_PRINT = 0x0317, + WM_PRINTCLIENT = 0x0318, + + WM_APPCOMMAND = 0x0319, + + WM_THEMECHANGED = 0x031A, + + WM_CLIPBOARDUPDATE = 0x031D, + + WM_DWMCOMPOSITIONCHANGED = 0x031E, + WM_DWMNCRENDERINGCHANGED = 0x031F, + WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320, + WM_DWMWINDOWMAXIMIZEDCHANGE = 0x0321, + + WM_GETTITLEBARINFOEX = 0x033F, + + WM_HANDHELDFIRST = 0x0358, + WM_HANDHELDLAST = 0x035F, + + WM_AFXFIRST = 0x0360, + WM_AFXLAST = 0x037F, + + WM_PENWINFIRST = 0x0380, + WM_PENWINLAST = 0x038F, + + WM_APP = 0x8000, + + WM_USER = 0x0400, + + WM_REFLECT = WM_USER + 0x1C00, + } + /// /// Returns true if the current application has focus, false otherwise. /// @@ -254,6 +663,38 @@ namespace Dalamud return activeProcId == Environment.ProcessId; } + /// + /// Passes message information to the specified window procedure. + /// + /// + /// The previous window procedure. If this value is obtained by calling the GetWindowLong function with the nIndex parameter set to + /// GWL_WNDPROC or DWL_DLGPROC, it is actually either the address of a window or dialog box procedure, or a special internal value + /// meaningful only to CallWindowProc. + /// + /// + /// A handle to the window procedure to receive the message. + /// + /// + /// The message. + /// + /// + /// Additional message-specific information. The contents of this parameter depend on the value of the Msg parameter. + /// + /// + /// More additional message-specific information. The contents of this parameter depend on the value of the Msg parameter. + /// + /// + /// Use the CallWindowProc function for window subclassing. Usually, all windows with the same class share one window procedure. A + /// subclass is a window or set of windows with the same class whose messages are intercepted and processed by another window procedure + /// (or procedures) before being passed to the window procedure of the class. + /// The SetWindowLong function creates the subclass by changing the window procedure associated with a particular window, causing the + /// system to call the new window procedure instead of the previous one.An application must pass any messages not processed by the new + /// window procedure to the previous window procedure by calling CallWindowProc.This allows the application to create a chain of window + /// procedures. + /// + [DllImport("user32.dll")] + public static extern long CallWindowProcW(IntPtr lpPrevWndFunc, IntPtr hWnd, uint msg, ulong wParam, long lParam); + /// /// See https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-flashwindowex. /// Flashes the specified window. It does not change the active state of the window. @@ -326,6 +767,31 @@ namespace Dalamud [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] public static extern int MessageBoxW(IntPtr hWnd, string text, string caption, MessageBoxType type); + /// + /// Changes an attribute of the specified window. The function also sets a value at the specified offset in the extra window memory. + /// + /// + /// A handle to the window and, indirectly, the class to which the window belongs. The SetWindowLongPtr function fails if the + /// process that owns the window specified by the hWnd parameter is at a higher process privilege in the UIPI hierarchy than the + /// process the calling thread resides in. + /// + /// + /// The zero-based offset to the value to be set. Valid values are in the range zero through the number of bytes of extra window + /// memory, minus the size of a LONG_PTR. To set any other value, specify one of the values. + /// + /// + /// The replacement value. + /// + /// + /// If the function succeeds, the return value is the previous value of the specified offset. If the function fails, the return + /// value is zero.To get extended error information, call GetLastError. If the previous value is zero and the function succeeds, + /// the return value is zero, but the function does not clear the last error information. To determine success or failure, clear + /// the last error information by calling SetLastError with 0, then call SetWindowLongPtr.Function failure will be indicated by + /// a return value of zero and a GetLastError result that is nonzero. + /// + [DllImport("user32.dll", SetLastError = true)] + public static extern IntPtr SetWindowLongPtrW(IntPtr hWnd, WindowLongType nIndex, IntPtr dwNewLong); + /// /// See https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-flashwinfo. /// Contains the flash status for a window and the number of times the system should flash the window. @@ -361,6 +827,308 @@ namespace Dalamud } } + /// + /// Native imm32 functions. + /// + internal static partial class NativeFunctions + { + /// + /// GCS_* from imm32. + /// These values are used with ImmGetCompositionString and WM_IME_COMPOSITION. + /// + [Flags] + public enum IMEComposition + { + /// + /// Retrieve or update the attribute of the composition string. + /// + CompAttr = 0x0010, + + /// + /// Retrieve or update clause information of the composition string. + /// + CompClause = 0x0020, + + /// + /// Retrieve or update the attributes of the reading string of the current composition. + /// + CompReadAttr = 0x0002, + + /// + /// Retrieve or update the clause information of the reading string of the composition string. + /// + CompReadClause = 0x0004, + + /// + /// Retrieve or update the reading string of the current composition. + /// + CompReadStr = 0x0001, + + /// + /// Retrieve or update the current composition string. + /// + CompStr = 0x0008, + + /// + /// Retrieve or update the cursor position in composition string. + /// + CursorPos = 0x0080, + + /// + /// Retrieve or update the starting position of any changes in composition string. + /// + DeltaStart = 0x0100, + + /// + /// Retrieve or update clause information of the result string. + /// + ResultClause = 0x1000, + + /// + /// Retrieve or update clause information of the reading string. + /// + ResultReadClause = 0x0400, + + /// + /// Retrieve or update the reading string. + /// + ResultReadStr = 0x0200, + + /// + /// Retrieve or update the string of the composition result. + /// + ResultStr = 0x0800, + } + + /// + /// IMN_* from imm32. + /// Input Method Manager Commands, this enum is not exhaustive. + /// + public enum IMECommand + { + /// + /// Notifies the application when an IME is about to change the content of the candidate window. + /// + ChangeCandidate = 0x0003, + + /// + /// Notifies an application when an IME is about to close the candidates window. + /// + CloseCandidate = 0x0004, + + /// + /// Notifies an application when an IME is about to open the candidate window. + /// + OpenCandidate = 0x0005, + + /// + /// Notifies an application when the conversion mode of the input context is updated. + /// + SetConversionMode = 0x0006, + } + + /// + /// Returns the input context associated with the specified window. + /// + /// Unnamed parameter 1. + /// + /// Returns the handle to the input context. + /// + [DllImport("imm32.dll")] + public static extern IntPtr ImmGetContext(IntPtr hWnd); + + /// + /// Retrieves information about the composition string. + /// + /// + /// Unnamed parameter 1. + /// + /// + /// Unnamed parameter 2. + /// + /// + /// Pointer to a buffer in which the function retrieves the composition string information. + /// + /// + /// Size, in bytes, of the output buffer, even if the output is a Unicode string. The application sets this parameter to 0 + /// if the function is to return the size of the required output buffer. + /// + /// + /// Returns the number of bytes copied to the output buffer. If dwBufLen is set to 0, the function returns the buffer size, + /// in bytes, required to receive all requested information, excluding the terminating null character. The return value is + /// always the size, in bytes, even if the requested data is a Unicode string. + /// This function returns one of the following negative error codes if it does not succeed: + /// - IMM_ERROR_NODATA.Composition data is not ready in the input context. + /// - IMM_ERROR_GENERAL.General error detected by IME. + /// + [DllImport("imm32.dll")] + public static extern long ImmGetCompositionStringW(IntPtr hImc, IMEComposition arg2, IntPtr lpBuf, uint dwBufLen); + + /// + /// Retrieves a candidate list. + /// + /// + /// Unnamed parameter 1. + /// + /// + /// Zero-based index of the candidate list. + /// + /// + /// Pointer to a CANDIDATELIST structure in which the function retrieves the candidate list. + /// + /// + /// Size, in bytes, of the buffer to receive the candidate list. The application can specify 0 for this parameter if the + /// function is to return the required size of the output buffer only. + /// + /// + /// Returns the number of bytes copied to the candidate list buffer if successful. If the application has supplied 0 for + /// the dwBufLen parameter, the function returns the size required for the candidate list buffer. The function returns 0 + /// if it does not succeed. + /// + [DllImport("imm32.dll")] + public static extern long ImmGetCandidateListW(IntPtr hImc, uint deIndex, IntPtr lpCandList, uint dwBufLen); + + /// + /// Sets the position of the composition window. + /// + /// + /// Unnamed parameter 1. + /// + /// + /// Pointer to a COMPOSITIONFORM structure that contains the new position and other related information about + /// the composition window. + /// + /// + /// Returns a nonzero value if successful, or 0 otherwise. + /// + [DllImport("imm32.dll", CharSet = CharSet.Auto)] + public static extern bool ImmSetCompositionWindow(IntPtr hImc, ref CompositionForm frm); + + /// + /// Releases the input context and unlocks the memory associated in the input context. An application must call this + /// function for each call to the ImmGetContext function. + /// + /// + /// Unnamed parameter 1. + /// + /// + /// Unnamed parameter 2. + /// + /// + /// Returns a nonzero value if successful, or 0 otherwise. + /// + [DllImport("imm32.dll", CharSet = CharSet.Auto)] + public static extern bool ImmReleaseContext(IntPtr hwnd, IntPtr hImc); + + /// + /// Contains information about a candidate list. + /// + public struct CandidateList + { + /// + /// Size, in bytes, of the structure, the offset array, and all candidate strings. + /// + public int Size; + + /// + /// Candidate style values. This member can have one or more of the IME_CAND_* values. + /// + public int Style; + + /// + /// Number of candidate strings. + /// + public int Count; + + /// + /// Index of the selected candidate string. + /// + public int Selection; + + /// + /// Index of the first candidate string in the candidate window. This varies as the user presses the PAGE UP and PAGE DOWN keys. + /// + public int PageStart; + + /// + /// Number of candidate strings to be shown in one page in the candidate window. The user can move to the next page by pressing IME-defined keys, such as the PAGE UP or PAGE DOWN key. If this number is 0, an application can define a proper value by itself. + /// + public int PageSize; + + // /// + // /// Offset to the start of the first candidate string, relative to the start of this structure. The offsets + // /// for subsequent strings immediately follow this member, forming an array of 32-bit offsets. + // /// + // public IntPtr Offset; // manually handle + } + + /// + /// Contains style and position information for a composition window. + /// + [StructLayout(LayoutKind.Sequential)] + public struct CompositionForm + { + /// + /// Position style. This member can be one of the CFS_* values. + /// + public int Style; + + /// + /// A POINT structure containing the coordinates of the upper left corner of the composition window. + /// + public Point CurrentPos; + + /// + /// A RECT structure containing the coordinates of the upper left and lower right corners of the composition window. + /// + public Rect Area; + } + + /// + /// Contains coordinates for a point. + /// + [StructLayout(LayoutKind.Sequential)] + public struct Point + { + /// + /// The X position. + /// + public int X; + + /// + /// The Y position. + /// + public int Y; + } + + /// + /// Contains dimensions for a rectangle. + /// + [StructLayout(LayoutKind.Sequential)] + public struct Rect + { + /// + /// The left position. + /// + public int Left; + + /// + /// The top position. + /// + public int Top; + + /// + /// The right position. + /// + public int Right; + + /// + /// The bottom position. + /// + public int Bottom; + } + } + /// /// Native kernel32 functions. ///