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?
}