482 lines
19 KiB
C#
482 lines
19 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace DotaWheel
|
|
{
|
|
class Wheel
|
|
{
|
|
/// <summary>
|
|
/// Enumeration of all buttons on a Logitech G29
|
|
/// Integers represent the values usable with the LogitechGSDK to get
|
|
/// information about the button.
|
|
/// </summary>
|
|
public enum Button : int
|
|
{
|
|
X = 0,
|
|
SQUARE = 1,
|
|
CIRCLE = 2,
|
|
TRIANGLE = 3,
|
|
RIGHT_PADDLE = 4,
|
|
LEFT_PADDLE = 5,
|
|
R2 = 6,
|
|
L2 = 7,
|
|
SHARE = 8,
|
|
OPTION = 9,
|
|
R3 = 10,
|
|
L3 = 11,
|
|
PLUS = 19,
|
|
MINUS = 20,
|
|
ROT_RIGHT = 21,
|
|
ROT_LEFT = 22,
|
|
ROT_CENTER = 23,
|
|
PS = 24,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Different directions of DPAD input.
|
|
/// </summary>
|
|
public enum DpadButton
|
|
{
|
|
UP,
|
|
DOWN,
|
|
LEFT,
|
|
RIGHT,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Different scalar values on the Logitech G29
|
|
/// </summary>
|
|
public enum ScalarInput
|
|
{
|
|
WHEEL,
|
|
ACCELERATOR,
|
|
BRAKE,
|
|
CLUTCH,
|
|
}
|
|
|
|
/// <summary>
|
|
/// The current state of the buttons. true=down, false=up.
|
|
/// </summary>
|
|
public Dictionary<Button, bool> Buttons { get; private set; }
|
|
/// <summary>
|
|
/// Buttons that were pressed or released since the last update. true=down, false=up.
|
|
/// If a button is in this dictionary, it means that its state changed since the last update.
|
|
/// </summary>
|
|
public Dictionary<Button, bool> ButtonsChanged { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The current state of the DPAD represented as DpadButton s. true=down, false=up.
|
|
/// </summary>
|
|
public Dictionary<DpadButton, bool> DpadButtons { get; private set; }
|
|
/// <summary>
|
|
/// DPAD buttons that were pressed or released since the last update. true=down, false=up.
|
|
/// If a DpadButton is in this dictionary, it means that its state changed since the last update.
|
|
/// </summary>
|
|
public Dictionary<DpadButton, bool> DpadButtonsChanged { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The position of the dpad on the previous update.
|
|
/// </summary>
|
|
private uint lastDpadPosition = uint.MaxValue;
|
|
/// <summary>
|
|
/// The position of the wheel on the previous update.
|
|
/// </summary>
|
|
private uint lastWheelPosition = 0;
|
|
/// <summary>
|
|
/// The position of the accelerator on the previous update.
|
|
/// </summary>
|
|
private uint lastAcceleratorPosition = 0;
|
|
/// <summary>
|
|
/// The position of the brake on the previous update.
|
|
/// </summary>
|
|
private uint lastBrakePosition = 0;
|
|
/// <summary>
|
|
/// THe position of the clutch on the previous update.
|
|
/// </summary>
|
|
private uint lastClutchPosition = 0;
|
|
|
|
|
|
/// <summary>
|
|
/// The current position of the DPAD in Degrees * 100.
|
|
/// <list type="bullet">
|
|
/// <item>0=up</item>
|
|
/// <item>4500=up-right</item>
|
|
/// <item>9000=right</item>
|
|
/// <item>13500=down-right</item>
|
|
/// <item>18000=down</item>
|
|
/// <item>22500=down-left</item>
|
|
/// <item>27000=left</item>
|
|
/// <item>32500=up-left</item>
|
|
/// </list>
|
|
/// </summary>
|
|
public uint DpadPosition { get; private set; }
|
|
/// <summary>
|
|
/// The current position of the steering wheel.
|
|
/// <list type="bullet">
|
|
/// <item>0=left</item>
|
|
/// <item>32768=center</item>
|
|
/// <item>65535=right</item>
|
|
/// </list>
|
|
/// </summary>
|
|
public uint WheelPosition { get; private set; }
|
|
/// <summary>
|
|
/// The current position of the accelerator.
|
|
/// <list type="bullet">
|
|
/// <item>0=depressed</item>
|
|
/// <item>65535=compressed</item>
|
|
/// </list>
|
|
/// </summary>
|
|
public uint AcceleratorPosition { get; private set; }
|
|
/// <summary>
|
|
/// The current position of the brake.
|
|
/// <list type="bullet">
|
|
/// <item>0=depressed</item>
|
|
/// <item>65535=compressed</item>
|
|
/// </list>
|
|
/// </summary>
|
|
public uint BrakePosition { get; private set; }
|
|
/// <summary>
|
|
/// The current position of the clutch.
|
|
/// <list type="bullet">
|
|
/// <item>0=depressed</item>
|
|
/// <item>65535=compressed</item>
|
|
/// </list>
|
|
/// </summary>
|
|
public uint ClutchPosition { get; private set; }
|
|
|
|
public Wheel()
|
|
{
|
|
Buttons = new Dictionary<Button, bool>();
|
|
ButtonsChanged = new Dictionary<Button, bool>();
|
|
DpadButtons = new Dictionary<DpadButton, bool>();
|
|
DpadButtonsChanged = new Dictionary<DpadButton, bool>();
|
|
|
|
DpadPosition = 0;
|
|
WheelPosition = 0;
|
|
AcceleratorPosition = 0;
|
|
BrakePosition = 0;
|
|
ClutchPosition = 0;
|
|
|
|
// Initialize the buttons
|
|
foreach (Button button in Enum.GetValues(typeof(Button)))
|
|
{
|
|
Buttons[button] = false;
|
|
}
|
|
foreach (DpadButton dpadButton in Enum.GetValues(typeof(DpadButton)))
|
|
{
|
|
DpadButtons[dpadButton] = false;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Calculates whether a scalar input was pressed.
|
|
/// </summary>
|
|
/// <param name="lp">The previous position of the scalar input</param>
|
|
/// <param name="cp">The current position of the scalar input</param>
|
|
/// <param name="threshold">The threshold for the scalar input being down or up</param>
|
|
/// <param name="positiveDown">
|
|
/// Whether a higher number corrosponds to a pressed input. true=(higher number=pressed), false=(lower number=pressed)
|
|
/// </param>
|
|
/// <returns>Whether the scalar input was pressed.</returns>
|
|
private static bool PseudoScalarPressed(uint lp, uint cp, uint threshold, bool positiveDown = false)
|
|
{
|
|
if (positiveDown)
|
|
{
|
|
return cp >= threshold && lp < threshold;
|
|
}
|
|
else
|
|
{
|
|
return cp <= threshold && lp > threshold;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculates whether a scalar input was released.
|
|
/// </summary>
|
|
/// <param name="lp">The previous position of the scalar input</param>
|
|
/// <param name="cp">The current position of the scalar input</param>
|
|
/// <param name="threshold">The threshold for the scalar input being down or up</param>
|
|
/// <param name="positiveDown">
|
|
/// Whether a higher number corrosponds to a pressed input. true=(higher number=pressed), false=(lower number=pressed)
|
|
/// </param>
|
|
/// <returns>Whether the scalar input was released.</returns>
|
|
private static bool PseudoScalarReleased(uint lp, uint cp, uint threshold, bool positiveDown = false)
|
|
{
|
|
if (positiveDown)
|
|
{
|
|
return cp < threshold && lp >= threshold;
|
|
}
|
|
else
|
|
{
|
|
return cp > threshold && lp <= threshold;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the DpadButton representation of the uint dpadPosition.
|
|
/// </summary>
|
|
/// <param name="dpadPosition">The position of the DPAD</param>
|
|
/// <returns>A set of DpadButtons that represents the buttons pressed by the dpadPosition</returns>
|
|
private static SortedSet<DpadButton> GetButtonsDown(uint dpadPosition)
|
|
{
|
|
SortedSet<DpadButton> ret = new SortedSet<DpadButton>();
|
|
if (dpadPosition < 9000 || (dpadPosition > 27000 && dpadPosition < 36000))
|
|
{
|
|
ret.Add(DpadButton.UP);
|
|
}
|
|
if (dpadPosition > 9000 && dpadPosition < 27000)
|
|
{
|
|
ret.Add(DpadButton.DOWN);
|
|
}
|
|
if (dpadPosition < 18000 && dpadPosition > 0)
|
|
{
|
|
ret.Add(DpadButton.RIGHT);
|
|
}
|
|
if (dpadPosition > 18000 && dpadPosition < 36000)
|
|
{
|
|
ret.Add(DpadButton.LEFT);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the set of DpadButtons not in s
|
|
/// </summary>
|
|
/// <param name="s">A set of DpadButtons</param>
|
|
/// <returns>The set of DpadButtons not in the current set of DpadButtons</returns>
|
|
private static SortedSet<DpadButton> GetButtonsNotInSet(SortedSet<DpadButton> s)
|
|
{
|
|
SortedSet<DpadButton> ret = new SortedSet<DpadButton>();
|
|
foreach (DpadButton dpadButton in Enum.GetValues(typeof(DpadButton)))
|
|
{
|
|
ret.Add(dpadButton);
|
|
}
|
|
foreach (DpadButton dpadButton in s)
|
|
{
|
|
ret.Remove(dpadButton);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns whether a button was pressed in the last update.
|
|
/// </summary>
|
|
/// <param name="button">The button to check</param>
|
|
/// <returns>Whether the button was pressed in the last update</returns>
|
|
public bool ButtonPressed(Button button)
|
|
{
|
|
if (ButtonsChanged.TryGetValue(button, out bool pressed))
|
|
{
|
|
return pressed;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns whether a Button was released in the last update.
|
|
/// </summary>
|
|
/// <param name="button">The Button to check</param>
|
|
/// <returns>Whether the button was pressed in the last update</returns>
|
|
public bool ButtonReleased(Button button)
|
|
{
|
|
if (ButtonsChanged.TryGetValue(button, out bool pressed))
|
|
{
|
|
return !pressed;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns whether a DpadButton was pressed in the last update.
|
|
/// </summary>
|
|
/// <param name="dpadButton">The DpadButton to check</param>
|
|
/// <returns>Whether the DpadButton was pressed in the last update</returns>
|
|
public bool DpadButtonPressed(DpadButton dpadButton)
|
|
{
|
|
if (DpadButtonsChanged.TryGetValue(dpadButton, out bool pressed))
|
|
{
|
|
return pressed;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns whether a DpadButton was released in the last update.
|
|
/// </summary>
|
|
/// <param name="dpadButton">The DpadButton to check</param>
|
|
/// <returns>Whether the DpadButton was released in the last update</returns>
|
|
public bool DpadButtonReleased(DpadButton dpadButton)
|
|
{
|
|
if (DpadButtonsChanged.TryGetValue(dpadButton, out bool pressed))
|
|
{
|
|
return !pressed;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns whether the specified ScalarInput was pressed to a certain threshold in the last update.
|
|
/// </summary>
|
|
/// <param name="scalarInput">The ScalarInput to check</param>
|
|
/// <param name="threshold">The threshold for the ScalarInput to be considered pressed</param>
|
|
/// <param name="positiveDown">
|
|
/// Whether a higher number corrosponds to a pressed input. true=(higher number=pressed), false=(lower number=pressed)
|
|
/// </param>
|
|
/// <returns>Whether the ScalarInput was pressed to the threshold in the last update</returns>
|
|
public bool ScalarInputPressed(ScalarInput scalarInput, uint threshold, bool positiveDown = false)
|
|
{
|
|
switch (scalarInput)
|
|
{
|
|
case ScalarInput.WHEEL:
|
|
return PseudoScalarPressed(lastWheelPosition, WheelPosition, threshold, positiveDown);
|
|
case ScalarInput.ACCELERATOR:
|
|
return PseudoScalarPressed(lastAcceleratorPosition, AcceleratorPosition, threshold, positiveDown);
|
|
case ScalarInput.BRAKE:
|
|
return PseudoScalarPressed(lastBrakePosition, BrakePosition, threshold, positiveDown);
|
|
case ScalarInput.CLUTCH:
|
|
return PseudoScalarPressed(lastClutchPosition, ClutchPosition, threshold, positiveDown);
|
|
default:
|
|
throw new NotImplementedException("ScalarInput " + scalarInput.ToString() + " not implemented.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns whether the specified ScalarInput was released to a certain threshold in the last update.
|
|
/// </summary>
|
|
/// <param name="scalarInput">The ScalarInput to check</param>
|
|
/// <param name="threshold">The threshold for the ScalarInput to be considered released</param>
|
|
/// <param name="positiveDown">
|
|
/// Whether a higher number corrosponds to a pressed input. true=(higher number=pressed), false=(lower number=pressed)
|
|
/// </param>
|
|
/// <returns>Whether the ScalarInput was released from the threshold in the last update</returns>
|
|
public bool ScalarInputReleased(ScalarInput scalarInput, uint threshold, bool positiveDown = false)
|
|
{
|
|
switch (scalarInput)
|
|
{
|
|
case ScalarInput.WHEEL:
|
|
return PseudoScalarReleased(lastWheelPosition, WheelPosition, threshold, positiveDown);
|
|
case ScalarInput.ACCELERATOR:
|
|
return PseudoScalarReleased(lastAcceleratorPosition, AcceleratorPosition, threshold, positiveDown);
|
|
case ScalarInput.BRAKE:
|
|
return PseudoScalarReleased(lastBrakePosition, BrakePosition, threshold, positiveDown);
|
|
case ScalarInput.CLUTCH:
|
|
return PseudoScalarReleased(lastClutchPosition, ClutchPosition, threshold, positiveDown);
|
|
default:
|
|
throw new NotImplementedException("ScalarInput " + scalarInput.ToString() + " not implemented.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper function to update the a button on the specified wheel.
|
|
/// </summary>
|
|
/// <param name="wheelIndex">The index of the wheel for the LogitechGSDK</param>
|
|
/// <param name="button">The button to update</param>
|
|
private void UpdateButton(int wheelIndex, Button button)
|
|
{
|
|
if (LogitechGSDK.LogiButtonTriggered(wheelIndex, (int)button))
|
|
{
|
|
ButtonsChanged[button] = true;
|
|
Buttons[button] = true;
|
|
}
|
|
if (LogitechGSDK.LogiButtonReleased(wheelIndex, (int)button))
|
|
{
|
|
ButtonsChanged[button] = false;
|
|
Buttons[button] = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the DpadButtons and DpadButtonsChanged Dictionaries for the specified button
|
|
/// </summary>
|
|
/// <param name="dpadButton">The DpadButton to update</param>
|
|
/// <param name="cd">The set of currently down DpadButtons</param>
|
|
/// <param name="cu">The set of currently up DpadButtons</param>
|
|
/// <param name="ld">The set of previously down DpadButtons</param>
|
|
/// <param name="lu">The set of previously up DpadButtons</param>
|
|
private void UpdateDpadButton(
|
|
DpadButton dpadButton,
|
|
SortedSet<DpadButton> cd, SortedSet<DpadButton> cu,
|
|
SortedSet<DpadButton> ld, SortedSet<DpadButton> lu
|
|
)
|
|
{
|
|
if (cd.Contains(dpadButton) && !ld.Contains(dpadButton))
|
|
{
|
|
DpadButtonsChanged[dpadButton] = true;
|
|
DpadButtons[dpadButton] = true;
|
|
}
|
|
if (cu.Contains(dpadButton) && !lu.Contains(dpadButton))
|
|
{
|
|
DpadButtonsChanged[dpadButton] = false;
|
|
DpadButtons[dpadButton] = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the DpadButtons and DpadButtonsChanged Dictionaries
|
|
/// </summary>
|
|
/// <param name="wheel">The wheel to take the DPAD data from</param>
|
|
private void UpdateDpad(LogitechGSDK.DIJOYSTATE2ENGINES wheel)
|
|
{
|
|
DpadPosition = wheel.rgdwPOV[0];
|
|
|
|
SortedSet<DpadButton> currentButtonsDown = GetButtonsDown(DpadPosition);
|
|
SortedSet<DpadButton> currentButtonsUp = GetButtonsNotInSet(currentButtonsDown);
|
|
|
|
SortedSet<DpadButton> lastButtonsDown = GetButtonsDown(lastDpadPosition);
|
|
SortedSet<DpadButton> lastButtonsUp = GetButtonsNotInSet(lastButtonsDown);
|
|
|
|
foreach (DpadButton dpadButton in Enum.GetValues(typeof(DpadButton)))
|
|
{
|
|
UpdateDpadButton(dpadButton, currentButtonsDown, currentButtonsUp, lastButtonsDown, lastButtonsUp);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the class based on the current state of the wheelIndex.
|
|
/// </summary>
|
|
/// <see cref="LogitechGSDK"/>
|
|
/// <param name="wheelIndex">The index of the wheel to use to update</param>
|
|
public void Update(int wheelIndex)
|
|
{
|
|
// Reset Changed Dictionaries
|
|
ButtonsChanged = new Dictionary<Button, bool>();
|
|
DpadButtonsChanged = new Dictionary<DpadButton, bool>();
|
|
|
|
// Update Last Values
|
|
lastDpadPosition = DpadPosition;
|
|
lastWheelPosition = WheelPosition;
|
|
lastAcceleratorPosition = AcceleratorPosition;
|
|
lastBrakePosition = BrakePosition;
|
|
lastClutchPosition = ClutchPosition;
|
|
|
|
// Load the wheel from the LogitechGSDK
|
|
LogitechGSDK.DIJOYSTATE2ENGINES wheel = LogitechGSDK.LogiGetStateCSharp(wheelIndex);
|
|
|
|
// Update Buttons
|
|
foreach (Button button in Enum.GetValues(typeof(Button)))
|
|
{
|
|
UpdateButton(wheelIndex, button);
|
|
}
|
|
|
|
// Update the D-pad
|
|
UpdateDpad(wheel);
|
|
|
|
// Update Wheel
|
|
WheelPosition = (uint)(wheel.lX + 32768);
|
|
|
|
// Update Accelerator
|
|
AcceleratorPosition = (uint)(wheel.lY + 32768);
|
|
|
|
// Update Brake
|
|
BrakePosition = (uint)(wheel.lRz + 32768);
|
|
|
|
// Update Clutch
|
|
ClutchPosition = (uint)(wheel.rglSlider[0] + 32768);
|
|
}
|
|
}
|
|
}
|