|
|
|
@ -1,148 +1,367 @@
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
|
|
|
|
using OpenTK.Input;
|
|
|
|
|
|
|
|
|
|
using OpenTKGamePad = OpenTK.Input.GamePad;
|
|
|
|
|
|
|
|
|
|
namespace BizHawk.Client.EmuHawk
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Modified OpenTK Gamepad Handler<br/>
|
|
|
|
|
/// The jump from OpenTK 1.x to 3.x broke the original <see cref="Joystick">OpenTK.Input.Joystick</see> implementation, but we gain <see cref="OpenTKGamePad">OpenTK.Input.GamePad</see> support on Unix. However, the gamepad auto-mapping is a little suspect, so we use both methods.<br/>
|
|
|
|
|
/// As a side-effect, it should make it easier to implement virtual→host haptics in the future.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class OTK_GamePad
|
|
|
|
|
{
|
|
|
|
|
//Note: OpenTK has both Gamepad and Joystick classes. An OpenTK Gamepad is a simplified version of Joystick
|
|
|
|
|
//with pre-defined features that match an XInput controller. They did this to mimic XNA's controller API.
|
|
|
|
|
//We're going to use Joystick directly, because it gives us full access to all possible buttons.
|
|
|
|
|
//And it looks like GamePad itself isn't supported on OpenTK OS X.
|
|
|
|
|
#region Static Members
|
|
|
|
|
|
|
|
|
|
public static List<OTK_GamePad> Devices;
|
|
|
|
|
private const int MAX_JOYSTICKS = 4; //They don't have a way to query this for some reason. 4 is the minimum promised.
|
|
|
|
|
/// <remarks>They don't have a way to query this for some reason. 4 is the minimum promised.</remarks>
|
|
|
|
|
private const int MAX_GAMEPADS = 4;
|
|
|
|
|
|
|
|
|
|
private static readonly object _syncObj = new object();
|
|
|
|
|
|
|
|
|
|
private static readonly List<OTK_GamePad> Devices = new List<OTK_GamePad>();
|
|
|
|
|
|
|
|
|
|
/// <remarks>Initialization is only called once when MainForm loads</remarks>
|
|
|
|
|
public static void Initialize()
|
|
|
|
|
{
|
|
|
|
|
Devices = new List<OTK_GamePad>();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < MAX_JOYSTICKS; i++)
|
|
|
|
|
CloseAll();
|
|
|
|
|
var playerCount = 0;
|
|
|
|
|
for (var i = 0; i < MAX_GAMEPADS; i++)
|
|
|
|
|
{
|
|
|
|
|
JoystickState jss = Joystick.GetState(i);
|
|
|
|
|
if (jss.IsConnected)
|
|
|
|
|
if (OpenTKGamePad.GetState(i).IsConnected || Joystick.GetState(i).IsConnected)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"joydevice index: {i}"); //OpenTK doesn't expose the GUID, even though it stores it internally...
|
|
|
|
|
|
|
|
|
|
OTK_GamePad ogp = new OTK_GamePad(i);
|
|
|
|
|
Devices.Add(ogp);
|
|
|
|
|
Console.WriteLine($"OTK GamePad/Joystick index: {i}");
|
|
|
|
|
Devices.Add(new OTK_GamePad(i, ++playerCount));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static IEnumerable<OTK_GamePad> EnumerateDevices()
|
|
|
|
|
{
|
|
|
|
|
lock (_syncObj)
|
|
|
|
|
{
|
|
|
|
|
foreach (var device in Devices) yield return device;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void UpdateAll()
|
|
|
|
|
{
|
|
|
|
|
foreach (var device in Devices)
|
|
|
|
|
device.Update();
|
|
|
|
|
lock (_syncObj)
|
|
|
|
|
{
|
|
|
|
|
foreach (var device in Devices) device.Update();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void CloseAll()
|
|
|
|
|
{
|
|
|
|
|
if (Devices != null)
|
|
|
|
|
lock (_syncObj)
|
|
|
|
|
{
|
|
|
|
|
Devices.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ********************************** Instance Members **********************************
|
|
|
|
|
|
|
|
|
|
readonly Guid _guid;
|
|
|
|
|
readonly int _stickIdx;
|
|
|
|
|
JoystickState state = new JoystickState();
|
|
|
|
|
|
|
|
|
|
OTK_GamePad(int index)
|
|
|
|
|
/// <summary>The things that use <see cref="GetFloats"/> (analog controls) appear to require values -10000.0..10000.0 rather than the -1.0..1.0 that OpenTK returns (although even then the results may be slightly outside of these bounds)</summary>
|
|
|
|
|
private static float ConstrainFloatInput(float num)
|
|
|
|
|
{
|
|
|
|
|
_guid = Guid.NewGuid();
|
|
|
|
|
_stickIdx = index;
|
|
|
|
|
if (num > 1) return 10000.0f;
|
|
|
|
|
if (num < -1) return -10000.0f;
|
|
|
|
|
return num * 10000.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Instance Members
|
|
|
|
|
|
|
|
|
|
/// <summary>The GUID as detected by OpenTK.Input.Joystick (or if that failed, a random one generated on construction)</summary>
|
|
|
|
|
public readonly Guid Guid;
|
|
|
|
|
|
|
|
|
|
/// <summary>Signals whether OpenTK returned a GUID for this device</summary>
|
|
|
|
|
private readonly bool _guidObtained;
|
|
|
|
|
|
|
|
|
|
/// <summary>The OpenTK device index</summary>
|
|
|
|
|
private readonly int _deviceIndex;
|
|
|
|
|
|
|
|
|
|
/// <summary>The index to lookup into Devices</summary>
|
|
|
|
|
private readonly int _playerIndex;
|
|
|
|
|
|
|
|
|
|
/// <summary>The name (if any) that OpenTK GamePad has resolved via its internal mapping database</summary>
|
|
|
|
|
private readonly string _name;
|
|
|
|
|
|
|
|
|
|
/// <summary>The object returned by <see cref="OpenTKGamePad.GetCapabilities"/></summary>
|
|
|
|
|
private readonly GamePadCapabilities? _gamePadCapabilities;
|
|
|
|
|
|
|
|
|
|
/// <summary>The object returned by <see cref="Joystick.GetCapabilities"/></summary>
|
|
|
|
|
private readonly JoystickCapabilities? _joystickCapabilities;
|
|
|
|
|
|
|
|
|
|
/// <summary>For use in keybind boxes</summary>
|
|
|
|
|
public readonly string InputNamePrefix;
|
|
|
|
|
|
|
|
|
|
/// <summary>Public check on whether mapped gamepad config is being used</summary>
|
|
|
|
|
public bool MappedGamePad => _gamePadCapabilities?.IsMapped == true;
|
|
|
|
|
|
|
|
|
|
/// <summary>Gamepad Device state information - updated constantly</summary>
|
|
|
|
|
private GamePadState state;
|
|
|
|
|
|
|
|
|
|
/// <summary>Joystick Device state information - updated constantly</summary>
|
|
|
|
|
private JoystickState jState;
|
|
|
|
|
|
|
|
|
|
private OTK_GamePad(int index, int playerIndex)
|
|
|
|
|
{
|
|
|
|
|
_deviceIndex = index;
|
|
|
|
|
_playerIndex = playerIndex;
|
|
|
|
|
|
|
|
|
|
if (Joystick.GetState(_deviceIndex).IsConnected)
|
|
|
|
|
{
|
|
|
|
|
Guid = Joystick.GetGuid(_deviceIndex);
|
|
|
|
|
_guidObtained = true;
|
|
|
|
|
_joystickCapabilities = Joystick.GetCapabilities(_deviceIndex);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Guid = Guid.NewGuid();
|
|
|
|
|
_guidObtained = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (OpenTKGamePad.GetState(_deviceIndex).IsConnected)
|
|
|
|
|
{
|
|
|
|
|
_name = OpenTKGamePad.GetName(_deviceIndex);
|
|
|
|
|
_gamePadCapabilities = OpenTKGamePad.GetCapabilities(_deviceIndex);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_name = "OTK GamePad Undetermined Name";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InputNamePrefix = $"{(MappedGamePad ? "X" : "J")}{_playerIndex} ";
|
|
|
|
|
|
|
|
|
|
Update();
|
|
|
|
|
InitializeCallbacks();
|
|
|
|
|
|
|
|
|
|
Console.WriteLine($"Initialising OpenTK GamePad: {Guid}");
|
|
|
|
|
Console.WriteLine($"OpenTK Mapping: {_name}");
|
|
|
|
|
|
|
|
|
|
InitializeMappings();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Update()
|
|
|
|
|
{
|
|
|
|
|
state = Joystick.GetState(_stickIdx);
|
|
|
|
|
// update both here just in case
|
|
|
|
|
var tmpState = OpenTKGamePad.GetState(_deviceIndex);
|
|
|
|
|
DebugState(tmpState);
|
|
|
|
|
state = tmpState;
|
|
|
|
|
var tmpJstate = Joystick.GetState(_deviceIndex);
|
|
|
|
|
DebugState(tmpJstate);
|
|
|
|
|
jState = tmpJstate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Conditional("DEBUG")]
|
|
|
|
|
private void DebugState(GamePadState tmpState)
|
|
|
|
|
{
|
|
|
|
|
if (!tmpState.Equals(state)) Debug.WriteLine($"GamePad State:\t{tmpState}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Conditional("DEBUG")]
|
|
|
|
|
private void DebugState(JoystickState tmpJstate)
|
|
|
|
|
{
|
|
|
|
|
if (!tmpJstate.Equals(jState)) Debug.WriteLine($"Joystick State:\t{tmpJstate}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IEnumerable<Tuple<string, float>> GetFloats()
|
|
|
|
|
{
|
|
|
|
|
for (int pi = 0; pi < 64; pi++)
|
|
|
|
|
yield return new Tuple<string, float>(pi.ToString(), 10.0f * state.GetAxis(pi));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>FOR DEBUGGING ONLY</summary>
|
|
|
|
|
public JoystickState GetInternalState()
|
|
|
|
|
{
|
|
|
|
|
return state;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string Name { get { return $"Joystick {_stickIdx}"; } }
|
|
|
|
|
public Guid Guid { get { return _guid; } }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public string ButtonName(int index)
|
|
|
|
|
{
|
|
|
|
|
return names[index];
|
|
|
|
|
}
|
|
|
|
|
public bool Pressed(int index)
|
|
|
|
|
{
|
|
|
|
|
return actions[index]();
|
|
|
|
|
}
|
|
|
|
|
public int NumButtons { get; private set; }
|
|
|
|
|
|
|
|
|
|
private readonly List<string> names = new List<string>();
|
|
|
|
|
private readonly List<Func<bool>> actions = new List<Func<bool>>();
|
|
|
|
|
|
|
|
|
|
void AddItem(string _name, Func<bool> callback)
|
|
|
|
|
{
|
|
|
|
|
names.Add(_name);
|
|
|
|
|
actions.Add(callback);
|
|
|
|
|
NumButtons++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void InitializeCallbacks()
|
|
|
|
|
{
|
|
|
|
|
const int dzp = 400;
|
|
|
|
|
const int dzn = -400;
|
|
|
|
|
|
|
|
|
|
names.Clear();
|
|
|
|
|
actions.Clear();
|
|
|
|
|
NumButtons = 0;
|
|
|
|
|
|
|
|
|
|
AddItem("X+", () => state.GetAxis(0) >= dzp);
|
|
|
|
|
AddItem("X-", () => state.GetAxis(0) <= dzn);
|
|
|
|
|
AddItem("Y+", () => state.GetAxis(1) >= dzp);
|
|
|
|
|
AddItem("Y-", () => state.GetAxis(1) <= dzn);
|
|
|
|
|
AddItem("Z+", () => state.GetAxis(2) >= dzp);
|
|
|
|
|
AddItem("Z-", () => state.GetAxis(2) <= dzn);
|
|
|
|
|
|
|
|
|
|
// Enjoy our delicious sliders. They're smaller than regular burgers but cost more.
|
|
|
|
|
|
|
|
|
|
int jb = 1;
|
|
|
|
|
for (int i = 0; i < 64; i++)
|
|
|
|
|
if (MappedGamePad)
|
|
|
|
|
{
|
|
|
|
|
AddItem($"B{jb}", () => state.GetButton(i)==ButtonState.Pressed);
|
|
|
|
|
jb++;
|
|
|
|
|
// automapping identified - use OpenTKGamePad
|
|
|
|
|
yield return new Tuple<string, float>("LeftThumbX", ConstrainFloatInput(state.ThumbSticks.Left.X));
|
|
|
|
|
yield return new Tuple<string, float>("LeftThumbY", ConstrainFloatInput(state.ThumbSticks.Left.Y));
|
|
|
|
|
yield return new Tuple<string, float>("RightThumbX", ConstrainFloatInput(state.ThumbSticks.Right.X));
|
|
|
|
|
yield return new Tuple<string, float>("RightThumbY", ConstrainFloatInput(state.ThumbSticks.Right.Y));
|
|
|
|
|
yield return new Tuple<string, float>("LeftTrigger", ConstrainFloatInput(state.Triggers.Left));
|
|
|
|
|
yield return new Tuple<string, float>("RightTrigger", ConstrainFloatInput(state.Triggers.Right));
|
|
|
|
|
yield break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jb = 1;
|
|
|
|
|
foreach (JoystickHat enval in Enum.GetValues(typeof(JoystickHat)))
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
AddItem($"POV{jb}U", () => state.GetHat(enval).IsUp);
|
|
|
|
|
AddItem($"POV{jb}D", () => state.GetHat(enval).IsDown);
|
|
|
|
|
AddItem($"POV{jb}L", () => state.GetHat(enval).IsLeft);
|
|
|
|
|
AddItem($"POV{jb}R", () => state.GetHat(enval).IsRight);
|
|
|
|
|
jb++;
|
|
|
|
|
// use Joystick
|
|
|
|
|
yield return new Tuple<string, float>("X", ConstrainFloatInput(jState.GetAxis(0)));
|
|
|
|
|
yield return new Tuple<string, float>("Y", ConstrainFloatInput(jState.GetAxis(1)));
|
|
|
|
|
yield return new Tuple<string, float>("Z", ConstrainFloatInput(jState.GetAxis(2)));
|
|
|
|
|
yield return new Tuple<string, float>("W", ConstrainFloatInput(jState.GetAxis(3)));
|
|
|
|
|
yield return new Tuple<string, float>("V", ConstrainFloatInput(jState.GetAxis(4)));
|
|
|
|
|
yield return new Tuple<string, float>("S", ConstrainFloatInput(jState.GetAxis(5)));
|
|
|
|
|
yield return new Tuple<string, float>("Q", ConstrainFloatInput(jState.GetAxis(6)));
|
|
|
|
|
yield return new Tuple<string, float>("P", ConstrainFloatInput(jState.GetAxis(7)));
|
|
|
|
|
yield return new Tuple<string, float>("N", ConstrainFloatInput(jState.GetAxis(8)));
|
|
|
|
|
|
|
|
|
|
for (var i = 9; i < 64; i++)
|
|
|
|
|
{
|
|
|
|
|
var j = i;
|
|
|
|
|
yield return new Tuple<string, float>($"Axis{j.ToString()}", ConstrainFloatInput(jState.GetAxis(j)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
yield break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Note that this does not appear to work at this time. I probably need to have more infos.
|
|
|
|
|
public void SetVibration(int left, int right)
|
|
|
|
|
public string Name => $"Joystick {_playerIndex} ({_name})";
|
|
|
|
|
|
|
|
|
|
/// <summary>Contains name and delegate function for all buttons, hats and axis</summary>
|
|
|
|
|
public readonly List<ButtonObject> buttonObjects = new List<ButtonObject>();
|
|
|
|
|
|
|
|
|
|
private void AddItem(string name, Func<bool> pressed) =>
|
|
|
|
|
buttonObjects.Add(new ButtonObject
|
|
|
|
|
{
|
|
|
|
|
ButtonName = name,
|
|
|
|
|
ButtonAction = pressed
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
public struct ButtonObject
|
|
|
|
|
{
|
|
|
|
|
//Not supported in OTK Joystick. It is supported for OTK Gamepad, but I have to use Joystick for reasons mentioned above.
|
|
|
|
|
public string ButtonName;
|
|
|
|
|
public Func<bool> ButtonAction;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Setup mappings prior to button initialization.<br/>
|
|
|
|
|
/// This is also here in case in the future we want users to be able to supply their own mappings for a device, perhaps via an input form. Possibly wishful thinking/overly complex.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void InitializeMappings()
|
|
|
|
|
{
|
|
|
|
|
if (_guidObtained)
|
|
|
|
|
{
|
|
|
|
|
// placeholder for if/when we figure out how to supply OpenTK with custom GamePadConfigurationDatabase entries
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// currently OpenTK has an internal database of mappings for the GamePad class: https://github.com/opentk/opentk/blob/master/src/OpenTK/Input/GamePadConfigurationDatabase.cs
|
|
|
|
|
if (MappedGamePad)
|
|
|
|
|
{
|
|
|
|
|
// internal map detected - use OpenTKGamePad
|
|
|
|
|
InitializeGamePadControls();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// no internal map detected - use Joystick
|
|
|
|
|
InitializeJoystickControls();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void InitializeJoystickControls()
|
|
|
|
|
{
|
|
|
|
|
// OpenTK's GetAxis returns float values (as opposed to the shorts of SlimDX)
|
|
|
|
|
const float ConversionFactor = 1.0f / short.MaxValue;
|
|
|
|
|
const float dzp = (short)4000 * ConversionFactor;
|
|
|
|
|
const float dzn = (short)-4000 * ConversionFactor;
|
|
|
|
|
//const float dzt = 0.6f;
|
|
|
|
|
|
|
|
|
|
// axis
|
|
|
|
|
AddItem("X+", () => jState.GetAxis(0) >= dzp);
|
|
|
|
|
AddItem("X-", () => jState.GetAxis(0) <= dzn);
|
|
|
|
|
AddItem("Y+", () => jState.GetAxis(1) >= dzp);
|
|
|
|
|
AddItem("Y-", () => jState.GetAxis(1) <= dzn);
|
|
|
|
|
AddItem("Z+", () => jState.GetAxis(2) >= dzp);
|
|
|
|
|
AddItem("Z-", () => jState.GetAxis(2) <= dzn);
|
|
|
|
|
AddItem("W+", () => jState.GetAxis(3) >= dzp);
|
|
|
|
|
AddItem("W-", () => jState.GetAxis(3) <= dzn);
|
|
|
|
|
AddItem("V+", () => jState.GetAxis(4) >= dzp);
|
|
|
|
|
AddItem("V-", () => jState.GetAxis(4) <= dzn);
|
|
|
|
|
AddItem("S+", () => jState.GetAxis(5) >= dzp);
|
|
|
|
|
AddItem("S-", () => jState.GetAxis(5) <= dzn);
|
|
|
|
|
AddItem("Q+", () => jState.GetAxis(6) >= dzp);
|
|
|
|
|
AddItem("Q-", () => jState.GetAxis(6) <= dzn);
|
|
|
|
|
AddItem("P+", () => jState.GetAxis(7) >= dzp);
|
|
|
|
|
AddItem("P-", () => jState.GetAxis(7) <= dzn);
|
|
|
|
|
AddItem("N+", () => jState.GetAxis(8) >= dzp);
|
|
|
|
|
AddItem("N-", () => jState.GetAxis(8) <= dzn);
|
|
|
|
|
// should be enough axis, but just in case:
|
|
|
|
|
for (var i = 9; i < 64; i++)
|
|
|
|
|
{
|
|
|
|
|
var j = i;
|
|
|
|
|
AddItem($"Axis{j.ToString()}+", () => jState.GetAxis(j) >= dzp);
|
|
|
|
|
AddItem($"Axis{j.ToString()}-", () => jState.GetAxis(j) <= dzn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// buttons
|
|
|
|
|
for (var i = 0; i < (_joystickCapabilities?.ButtonCount ?? 0); i++)
|
|
|
|
|
{
|
|
|
|
|
var j = i;
|
|
|
|
|
AddItem($"B{i + 1}", () => jState.GetButton(j) == ButtonState.Pressed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// hats
|
|
|
|
|
AddItem("POV1U", () => jState.GetHat(JoystickHat.Hat0).IsUp);
|
|
|
|
|
AddItem("POV1D", () => jState.GetHat(JoystickHat.Hat0).IsDown);
|
|
|
|
|
AddItem("POV1L", () => jState.GetHat(JoystickHat.Hat0).IsLeft);
|
|
|
|
|
AddItem("POV1R", () => jState.GetHat(JoystickHat.Hat0).IsRight);
|
|
|
|
|
AddItem("POV2U", () => jState.GetHat(JoystickHat.Hat1).IsUp);
|
|
|
|
|
AddItem("POV2D", () => jState.GetHat(JoystickHat.Hat1).IsDown);
|
|
|
|
|
AddItem("POV2L", () => jState.GetHat(JoystickHat.Hat1).IsLeft);
|
|
|
|
|
AddItem("POV2R", () => jState.GetHat(JoystickHat.Hat1).IsRight);
|
|
|
|
|
AddItem("POV3U", () => jState.GetHat(JoystickHat.Hat2).IsUp);
|
|
|
|
|
AddItem("POV3D", () => jState.GetHat(JoystickHat.Hat2).IsDown);
|
|
|
|
|
AddItem("POV3L", () => jState.GetHat(JoystickHat.Hat2).IsLeft);
|
|
|
|
|
AddItem("POV3R", () => jState.GetHat(JoystickHat.Hat2).IsRight);
|
|
|
|
|
AddItem("POV4U", () => jState.GetHat(JoystickHat.Hat3).IsUp);
|
|
|
|
|
AddItem("POV4D", () => jState.GetHat(JoystickHat.Hat3).IsDown);
|
|
|
|
|
AddItem("POV4L", () => jState.GetHat(JoystickHat.Hat3).IsLeft);
|
|
|
|
|
AddItem("POV4R", () => jState.GetHat(JoystickHat.Hat3).IsRight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void InitializeGamePadControls()
|
|
|
|
|
{
|
|
|
|
|
// OpenTK's ThumbSticks contain float values (as opposed to the shorts of SlimDX)
|
|
|
|
|
const float ConversionFactor = 1.0f / short.MaxValue;
|
|
|
|
|
const float dzp = (short)4000 * ConversionFactor;
|
|
|
|
|
const float dzn = (short)-4000 * ConversionFactor;
|
|
|
|
|
const float dzt = 0.6f;
|
|
|
|
|
|
|
|
|
|
// buttons
|
|
|
|
|
AddItem("A", () => state.Buttons.A == ButtonState.Pressed);
|
|
|
|
|
AddItem("B", () => state.Buttons.B == ButtonState.Pressed);
|
|
|
|
|
AddItem("X", () => state.Buttons.X == ButtonState.Pressed);
|
|
|
|
|
AddItem("Y", () => state.Buttons.Y == ButtonState.Pressed);
|
|
|
|
|
AddItem("Guide", () => state.Buttons.BigButton == ButtonState.Pressed);
|
|
|
|
|
AddItem("Start", () => state.Buttons.Start == ButtonState.Pressed);
|
|
|
|
|
AddItem("Back", () => state.Buttons.Back == ButtonState.Pressed);
|
|
|
|
|
AddItem("LeftThumb", () => state.Buttons.LeftStick == ButtonState.Pressed);
|
|
|
|
|
AddItem("RightThumb", () => state.Buttons.RightStick == ButtonState.Pressed);
|
|
|
|
|
AddItem("LeftShoulder", () => state.Buttons.LeftShoulder == ButtonState.Pressed);
|
|
|
|
|
AddItem("RightShoulder", () => state.Buttons.RightShoulder == ButtonState.Pressed);
|
|
|
|
|
|
|
|
|
|
// dpad
|
|
|
|
|
AddItem("DpadUp", () => state.DPad.Up == ButtonState.Pressed);
|
|
|
|
|
AddItem("DpadDown", () => state.DPad.Down == ButtonState.Pressed);
|
|
|
|
|
AddItem("DpadLeft", () => state.DPad.Left == ButtonState.Pressed);
|
|
|
|
|
AddItem("DpadRight", () => state.DPad.Right == ButtonState.Pressed);
|
|
|
|
|
|
|
|
|
|
// sticks
|
|
|
|
|
AddItem("LStickUp", () => state.ThumbSticks.Left.Y >= dzp);
|
|
|
|
|
AddItem("LStickDown", () => state.ThumbSticks.Left.Y <= dzn);
|
|
|
|
|
AddItem("LStickLeft", () => state.ThumbSticks.Left.X <= dzn);
|
|
|
|
|
AddItem("LStickRight", () => state.ThumbSticks.Left.X >= dzp);
|
|
|
|
|
AddItem("RStickUp", () => state.ThumbSticks.Right.Y >= dzp);
|
|
|
|
|
AddItem("RStickDown", () => state.ThumbSticks.Right.Y <= dzn);
|
|
|
|
|
AddItem("RStickLeft", () => state.ThumbSticks.Right.X <= dzn);
|
|
|
|
|
AddItem("RStickRight", () => state.ThumbSticks.Right.X >= dzp);
|
|
|
|
|
|
|
|
|
|
// triggers
|
|
|
|
|
AddItem("LeftTrigger", () => state.Triggers.Left > dzt);
|
|
|
|
|
AddItem("RightTrigger", () => state.Triggers.Right > dzt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sets the gamepad's left and right vibration
|
|
|
|
|
/// We don't currently use this in Bizhawk - do we have any cores that support this?
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="left"></param>
|
|
|
|
|
/// <param name="right"></param>
|
|
|
|
|
public void SetVibration(float left, float right) => OpenTKGamePad.SetVibration(
|
|
|
|
|
_deviceIndex,
|
|
|
|
|
_gamePadCapabilities?.HasLeftVibrationMotor == true ? left : 0,
|
|
|
|
|
_gamePadCapabilities?.HasRightVibrationMotor == true ? right : 0
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|