using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DotaWheel
{
class Wheel
{
///
/// Enumeration of all buttons on a Logitech G29
/// Integers represent the values usable with the LogitechGSDK to get
/// information about the button.
///
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,
}
///
/// Different directions of DPAD input.
///
public enum DpadButton
{
UP,
DOWN,
LEFT,
RIGHT,
}
///
/// Different scalar values on the Logitech G29
///
public enum ScalarInput
{
WHEEL,
ACCELERATOR,
BRAKE,
CLUTCH,
}
///
/// The current state of the buttons. true=down, false=up.
///
public Dictionary Buttons { get; private set; }
///
/// 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.
///
public Dictionary ButtonsChanged { get; private set; }
///
/// The current state of the DPAD represented as DpadButton s. true=down, false=up.
///
public Dictionary DpadButtons { get; private set; }
///
/// 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.
///
public Dictionary DpadButtonsChanged { get; private set; }
///
/// The position of the dpad on the previous update.
///
private uint lastDpadPosition = uint.MaxValue;
///
/// The position of the wheel on the previous update.
///
private uint lastWheelPosition = 0;
///
/// The position of the accelerator on the previous update.
///
private uint lastAcceleratorPosition = 0;
///
/// The position of the brake on the previous update.
///
private uint lastBrakePosition = 0;
///
/// THe position of the clutch on the previous update.
///
private uint lastClutchPosition = 0;
///
/// The current position of the DPAD in Degrees * 100.
///
/// - 0=up
/// - 4500=up-right
/// - 9000=right
/// - 13500=down-right
/// - 18000=down
/// - 22500=down-left
/// - 27000=left
/// - 32500=up-left
///
///
public uint DpadPosition { get; private set; }
///
/// The current position of the steering wheel.
///
/// - 0=left
/// - 32768=center
/// - 65535=right
///
///
public uint WheelPosition { get; private set; }
///
/// The current position of the accelerator.
///
/// - 0=depressed
/// - 65535=compressed
///
///
public uint AcceleratorPosition { get; private set; }
///
/// The current position of the brake.
///
/// - 0=depressed
/// - 65535=compressed
///
///
public uint BrakePosition { get; private set; }
///
/// The current position of the clutch.
///
/// - 0=depressed
/// - 65535=compressed
///
///
public uint ClutchPosition { get; private set; }
public Wheel()
{
Buttons = new Dictionary();
ButtonsChanged = new Dictionary();
DpadButtons = new Dictionary();
DpadButtonsChanged = new Dictionary();
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;
}
}
///
/// Calculates whether a scalar input was pressed.
///
/// The previous position of the scalar input
/// The current position of the scalar input
/// The threshold for the scalar input being down or up
///
/// Whether a higher number corrosponds to a pressed input. true=(higher number=pressed), false=(lower number=pressed)
///
/// Whether the scalar input was pressed.
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;
}
}
///
/// Calculates whether a scalar input was released.
///
/// The previous position of the scalar input
/// The current position of the scalar input
/// The threshold for the scalar input being down or up
///
/// Whether a higher number corrosponds to a pressed input. true=(higher number=pressed), false=(lower number=pressed)
///
/// Whether the scalar input was released.
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;
}
}
///
/// Gets the DpadButton representation of the uint dpadPosition.
///
/// The position of the DPAD
/// A set of DpadButtons that represents the buttons pressed by the dpadPosition
private static SortedSet GetButtonsDown(uint dpadPosition)
{
SortedSet ret = new SortedSet();
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;
}
///
/// Gets the set of DpadButtons not in s
///
/// A set of DpadButtons
/// The set of DpadButtons not in the current set of DpadButtons
private static SortedSet GetButtonsNotInSet(SortedSet s)
{
SortedSet ret = new SortedSet();
foreach (DpadButton dpadButton in Enum.GetValues(typeof(DpadButton)))
{
ret.Add(dpadButton);
}
foreach (DpadButton dpadButton in s)
{
ret.Remove(dpadButton);
}
return ret;
}
///
/// Returns whether a button was pressed in the last update.
///
/// The button to check
/// Whether the button was pressed in the last update
public bool ButtonPressed(Button button)
{
if (ButtonsChanged.TryGetValue(button, out bool pressed))
{
return pressed;
}
return false;
}
///
/// Returns whether a Button was released in the last update.
///
/// The Button to check
/// Whether the button was pressed in the last update
public bool ButtonReleased(Button button)
{
if (ButtonsChanged.TryGetValue(button, out bool pressed))
{
return !pressed;
}
return false;
}
///
/// Returns whether a DpadButton was pressed in the last update.
///
/// The DpadButton to check
/// Whether the DpadButton was pressed in the last update
public bool DpadButtonPressed(DpadButton dpadButton)
{
if (DpadButtonsChanged.TryGetValue(dpadButton, out bool pressed))
{
return pressed;
}
return false;
}
///
/// Returns whether a DpadButton was released in the last update.
///
/// The DpadButton to check
/// Whether the DpadButton was released in the last update
public bool DpadButtonReleased(DpadButton dpadButton)
{
if (DpadButtonsChanged.TryGetValue(dpadButton, out bool pressed))
{
return !pressed;
}
return false;
}
///
/// Returns whether the specified ScalarInput was pressed to a certain threshold in the last update.
///
/// The ScalarInput to check
/// The threshold for the ScalarInput to be considered pressed
///
/// Whether a higher number corrosponds to a pressed input. true=(higher number=pressed), false=(lower number=pressed)
///
/// Whether the ScalarInput was pressed to the threshold in the last update
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.");
}
}
///
/// Returns whether the specified ScalarInput was released to a certain threshold in the last update.
///
/// The ScalarInput to check
/// The threshold for the ScalarInput to be considered released
///
/// Whether a higher number corrosponds to a pressed input. true=(higher number=pressed), false=(lower number=pressed)
///
/// Whether the ScalarInput was released from the threshold in the last update
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.");
}
}
///
/// Helper function to update the a button on the specified wheel.
///
/// The index of the wheel for the LogitechGSDK
/// The button to update
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;
}
}
///
/// Updates the DpadButtons and DpadButtonsChanged Dictionaries for the specified button
///
/// The DpadButton to update
/// The set of currently down DpadButtons
/// The set of currently up DpadButtons
/// The set of previously down DpadButtons
/// The set of previously up DpadButtons
private void UpdateDpadButton(
DpadButton dpadButton,
SortedSet cd, SortedSet cu,
SortedSet ld, SortedSet 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;
}
}
///
/// Updates the DpadButtons and DpadButtonsChanged Dictionaries
///
/// The wheel to take the DPAD data from
private void UpdateDpad(LogitechGSDK.DIJOYSTATE2ENGINES wheel)
{
DpadPosition = wheel.rgdwPOV[0];
SortedSet currentButtonsDown = GetButtonsDown(DpadPosition);
SortedSet currentButtonsUp = GetButtonsNotInSet(currentButtonsDown);
SortedSet lastButtonsDown = GetButtonsDown(lastDpadPosition);
SortedSet lastButtonsUp = GetButtonsNotInSet(lastButtonsDown);
foreach (DpadButton dpadButton in Enum.GetValues(typeof(DpadButton)))
{
UpdateDpadButton(dpadButton, currentButtonsDown, currentButtonsUp, lastButtonsDown, lastButtonsUp);
}
}
///
/// Updates the class based on the current state of the wheelIndex.
///
///
/// The index of the wheel to use to update
public void Update(int wheelIndex)
{
// Reset Changed Dictionaries
ButtonsChanged = new Dictionary();
DpadButtonsChanged = new Dictionary();
// 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);
}
}
}