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); } } }