diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 9be2d194c..69b5661c9 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -98,6 +98,8 @@ namespace Dalamud.Game.ClientState /// public KeyState KeyState; + public GamepadState GamepadState; + /// /// Provides access to client conditions/player state. Allows you to check if a player is in a duty, mounted, etc. /// @@ -131,6 +133,8 @@ namespace Dalamud.Game.ClientState this.KeyState = new KeyState(Address, scanner.Module.BaseAddress); + this.GamepadState = new GamepadState(scanner); + this.Condition = new Condition( Address ); this.Targets = new Targets(dalamud, Address); @@ -150,6 +154,7 @@ namespace Dalamud.Game.ClientState } public void Enable() { + this.GamepadState.Enable(); this.PartyList.Enable(); this.setupTerritoryTypeHook.Enable(); } @@ -158,6 +163,7 @@ namespace Dalamud.Game.ClientState this.PartyList.Dispose(); this.setupTerritoryTypeHook.Dispose(); this.Actors.Dispose(); + this.GamepadState.Dispose(); this.dalamud.Framework.OnUpdateEvent -= FrameworkOnOnUpdateEvent; this.dalamud.NetworkHandlers.CfPop += NetworkHandlersOnCfPop; diff --git a/Dalamud/Game/ClientState/GamepadButtons.cs b/Dalamud/Game/ClientState/GamepadButtons.cs new file mode 100644 index 000000000..a3b91711c --- /dev/null +++ b/Dalamud/Game/ClientState/GamepadButtons.cs @@ -0,0 +1,24 @@ +namespace Dalamud.Game.ClientState +{ + public enum GamepadButtons : ushort + { + XINPUT_GAMEPAD_DPAD_UP = 0x0001, + XINPUT_GAMEPAD_DPAD_DOWN = 0x0002, + XINPUT_GAMEPAD_DPAD_LEFT = 0x0004, + XINPUT_GAMEPAD_DPAD_RIGHT = 0x0008, + XINPUT_GAMEPAD_Y = 0x0010, + XINPUT_GAMEPAD_A = 0x0020, + XINPUT_GAMEPAD_X = 0x0040, + XINPUT_GAMEPAD_B = 0x0080, + XINPUT_GAMEPAD_LEFT_1 = 0x0100, + XINPUT_GAMEPAD_LEFT_2 = 0x0200, // The back one + XINPUT_GAMEPAD_LEFT_3 = 0x0400, + XINPUT_GAMEPAD_RIGHT_1 = 0x0800, + XINPUT_GAMEPAD_RIGHT_2 = 0x1000, // The back one + XINPUT_GAMEPAD_RIGHT_3 = 0x2000, + XINPUT_GAMEPAD_START = 0x8000, + XINPUT_GAMEPAD_SELECT = 0x4000, + + + } +} diff --git a/Dalamud/Game/ClientState/GamepadState.cs b/Dalamud/Game/ClientState/GamepadState.cs new file mode 100644 index 000000000..50957ef52 --- /dev/null +++ b/Dalamud/Game/ClientState/GamepadState.cs @@ -0,0 +1,150 @@ +using System; +using System.Windows.Forms; +using Dalamud.Game.ClientState.Structs; +using Dalamud.Hooking; +using OpenGL; + +namespace Dalamud.Game.ClientState +{ + public unsafe class GamepadState + { + + public float ButtonSouth => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_A) > 0 ? 1 : 0; + public float ButtonEast => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_B) > 0 ? 1 : 0; + public float ButtonWest => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_X) > 0 ? 1 : 0; + public float ButtonNorth => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_Y) > 0 ? 1 : 0; + public float DPadDown => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_DPAD_DOWN) > 0 ? 1 : 0; + public float DPadRight => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_DPAD_RIGHT) > 0 ? 1 : 0; + public float DPadUp => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_DPAD_UP) > 0 ? 1 : 0; + public float DPadLeft => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_DPAD_LEFT) > 0 ? 1 : 0; + public float L1 => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_LEFT_1) > 0 ? 1 : 0; + public float L2 => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_LEFT_2) > 0 ? 1 : 0; + public float L3 => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_LEFT_3) > 0 ? 1 : 0; + public float R1 => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_RIGHT_1) > 0 ? 1 : 0; + public float R2 => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_RIGHT_2) > 0 ? 1 : 0; + public float R3 => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_RIGHT_3) > 0 ? 1 : 0; + public float Start => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_START) > 0 ? 1 : 0; + public float Select => (this.buttonsTapped & (ushort)GamepadButtons.XINPUT_GAMEPAD_SELECT) > 0 ? 1 : 0; + public float LeftStickLeft => this.leftStickX < 0 ? -this.leftStickX / 100f : 0; + public float LeftStickRight => this.leftStickX > 0 ? this.leftStickX / 100f : 0; + public float LeftStickUp => this.leftStickY > 0 ? this.leftStickY / 100f : 0; + public float LeftStickDown => this.leftStickY < 0 ? -this.leftStickY / 100f : 0; + public float RightStickLeft => this.rightStickX < 0 ? -this.rightStickX / 100f : 0; + public float RightStickRight => this.rightStickX > 0 ? this.rightStickX / 100f : 0; + public float RightStickUp => this.rightStickY > 0 ? this.rightStickY / 100f : 0; + public float RightStickDown => this.rightStickY < 0 ? -this.rightStickY / 100f : 0; + + public float Tapped(GamepadButtons button) + => (this.buttonsTapped & (ushort)button) > 0 ? 1 : 0; + + public float Holding(GamepadButtons button) + => (this.buttonsHolding & (ushort)button) > 0 ? 1 : 0; + + public float Released(GamepadButtons button) + => (this.buttonsReleased & (ushort)button) > 0 ? 1 : 0; + + private delegate int ControllerPoll(IntPtr controllerInput); + + private Hook controllerPoll; + //private GamepadInput* _gamePadInput; + private bool isDisposed; + + private int leftStickX; + private int leftStickY; + private int rightStickX; + private int rightStickY; + private ushort buttons; + private ushort buttonsTapped; + private ushort buttonsReleased; + private ushort buttonsHolding; + private bool imGuiMode; + + + public GamepadState(SigScanner scanner) + { + const string controllerPollSignature = + "40 ?? 57 41 ?? 48 81 EC ?? ?? ?? ?? 44 0F ?? ?? ?? ?? ?? ?? ?? 48 8B"; + this.controllerPoll = new Hook( + scanner.ScanText(controllerPollSignature), + (ControllerPoll) ControllerPollDetour); + } + + + private unsafe int ControllerPollDetour(IntPtr gamepadInput) + { + //this._gamePadInput =(GamepadInput*) gamepadInput; + var original = this.controllerPoll.Original(gamepadInput); + var input = (GamepadInput*)gamepadInput; + if ( + (input->ButtonFlag_Holding & (ushort)GamepadButtons.XINPUT_GAMEPAD_RIGHT_1) > 0 + && (input->ButtonFlag & (ushort)GamepadButtons.XINPUT_GAMEPAD_LEFT_1) > 0) + { + this.imGuiMode = !this.imGuiMode; + if (!this.imGuiMode) + { + this.leftStickX = 0; + this.leftStickY = 0; + this.rightStickY = 0; + this.rightStickX = 0; + this.buttons = 0; + this.buttonsTapped = 0; + this.buttonsReleased = 0; + this.buttonsHolding = 0; + } + } + + if (this.imGuiMode) + { + this.leftStickX = input->LeftStickX; + this.leftStickY = input->LeftStickY; + this.rightStickX = input->RightStickX; + this.rightStickY = input->RightStickY; + this.buttons = input->ButtonFlag; + this.buttonsTapped = input->ButtonFlag_Tap; + this.buttonsReleased = input->ButtonFlag_Release; + this.buttonsHolding = input->ButtonFlag_Holding; + + input->LeftStickX = 0; + input->LeftStickY = 0; + input->RightStickX = 0; + input->RightStickY = 0; + input->ButtonFlag = 0; + input->ButtonFlag_Tap = 0; + input->ButtonFlag_Release = 0; + input->ButtonFlag_Holding = 0; + } + + // Not so sure about the return value, does not seem to matter if we return the + // original, zero or do the work adjusting the bits. + return original; + } + + public void Enable() + { + this.controllerPoll.Enable(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (this.isDisposed) return; + if (disposing) + { + this.controllerPoll?.Disable(); + this.controllerPoll?.Dispose(); + } + + this.isDisposed = true; + } + + ~GamepadState() + { + Dispose(false); + } + } +} diff --git a/Dalamud/Game/ClientState/Structs/GamepadInput.cs b/Dalamud/Game/ClientState/Structs/GamepadInput.cs new file mode 100644 index 000000000..f9bdd1c00 --- /dev/null +++ b/Dalamud/Game/ClientState/Structs/GamepadInput.cs @@ -0,0 +1,38 @@ +using System.Runtime.InteropServices; + +namespace Dalamud.Game.ClientState.Structs +{ + // It is bigger, but I dunno how big in real + [StructLayout(LayoutKind.Explicit)] + public struct GamepadInput + { + // Each stick is -99 till 99 + [FieldOffset(0x88)] + public int LeftStickX; + + [FieldOffset(0x8C)] + public int LeftStickY; + + [FieldOffset(0x90)] + public int RightStickX; + + [FieldOffset(0x94)] + public int RightStickY; + + // Seems to be source of true, instant population, keeps value while hold. + [FieldOffset(0x98)] + public ushort ButtonFlag; // bitfield + + // Gets populated only if released after a short tick + [FieldOffset(0x9C)] + public ushort ButtonFlag_Tap; // bitfield + + // Gets populated on button release + [FieldOffset(0xA0)] + public ushort ButtonFlag_Release; // bitfield + + // Gets populated after a tick and keeps being set while button is held + [FieldOffset(0xA4)] + public ushort ButtonFlag_Holding; // bitfield + } +} diff --git a/Dalamud/Interface/InterfaceManager.cs b/Dalamud/Interface/InterfaceManager.cs index 854fc9dbe..78681cf86 100644 --- a/Dalamud/Interface/InterfaceManager.cs +++ b/Dalamud/Interface/InterfaceManager.cs @@ -291,6 +291,9 @@ namespace Dalamud.Interface ImGui.GetIO().ConfigFlags |= ImGuiConfigFlags.DockingEnable; } + //TODO (Chiv) Addition + ImGui.GetIO().BackendFlags |= ImGuiBackendFlags.HasGamepad; + ImGuiHelpers.MainViewport = ImGui.GetMainViewport(); Log.Information("[IM] Scene & ImGui setup OK!"); @@ -460,6 +463,23 @@ namespace Dalamud.Interface this.dalamud.ClientState.KeyState.ClearAll(); } + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.Activate] = this.dalamud.ClientState.GamepadState.ButtonSouth; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.Cancel] = this.dalamud.ClientState.GamepadState.ButtonEast; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.Input] = this.dalamud.ClientState.GamepadState.ButtonNorth; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.Menu] = this.dalamud.ClientState.GamepadState.ButtonWest; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.DpadLeft] = this.dalamud.ClientState.GamepadState.DPadLeft; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.DpadRight] = this.dalamud.ClientState.GamepadState.DPadRight; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.DpadUp] = this.dalamud.ClientState.GamepadState.DPadUp; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.DpadDown] = this.dalamud.ClientState.GamepadState.DPadDown; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.LStickLeft] = this.dalamud.ClientState.GamepadState.LeftStickLeft; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.LStickRight] = this.dalamud.ClientState.GamepadState.LeftStickRight; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.LStickUp] = this.dalamud.ClientState.GamepadState.LeftStickUp; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.LStickDown] = this.dalamud.ClientState.GamepadState.LeftStickDown; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.FocusPrev] = this.dalamud.ClientState.GamepadState.L1; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.FocusNext] = this.dalamud.ClientState.GamepadState.R1; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.TweakSlow] = this.dalamud.ClientState.GamepadState.L2; + ImGui.GetIO().NavInputs[(int)ImGuiNavInput.TweakFast] = this.dalamud.ClientState.GamepadState.R2; + // TODO: mouse state? }