• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

luttje / Key2Joy / 6598306412

21 Oct 2023 03:54PM UTC coverage: 45.09% (-7.4%) from 52.519%
6598306412

Pull #50

github

web-flow
Merge bff596568 into 14b7ce9a7
Pull Request #50: Add XInput in preparation for gamepad triggers + add xmldoc

751 of 2289 branches covered (0.0%)

Branch coverage included in aggregate %.

2384 of 2384 new or added lines in 80 files covered. (100.0%)

3845 of 7904 relevant lines covered (48.65%)

15964.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

22.45
/Core/Key2Joy.Core/LowLevelInput/XInput/XInputGamepad.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Runtime.InteropServices;
4
using Key2Joy.Mapping;
5
using Key2Joy.Mapping.Triggers.GamePad;
6

7
namespace Key2Joy.LowLevelInput.XInput;
8

9
/// <summary>
10
/// Represents the state of a controller.
11
/// </summary>
12
[StructLayout(LayoutKind.Explicit)]
13
public struct XInputGamePad : IEquatable<XInputGamePad>
14
{
15
    public const int ThumbstickValueMin = short.MinValue;
16
    public const int ThumbstickValueMax = short.MaxValue;
17
    public const int TriggerValueMin = 0;
18
    public const int TriggerValueMax = 255;
19

20
    /// <summary>
21
    /// Can be used as a positive and negative value to filter left thumbstick input.
22
    /// </summary>
23
    public const int XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE = 7849;
24

25
    /// <summary>
26
    /// Can be used as a positive and negative value to filter right thumbstick input.
27
    /// </summary>
28
    public const int XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE = 8689;
29

30
    /// <summary>
31
    /// May be used as the value which bLeftTrigger and bRightTrigger must be greater than to register as pressed. This is optional, but often desirable. Xbox 360 Controller buttons do not manifest crosstalk.
32
    /// </summary>
33
    public const int XINPUT_GAMEPAD_TRIGGER_THRESHOLD = 30;
34

35
    /// <summary>
36
    /// Bitmask of the device digital buttons. A set bit indicates that the corresponding button is pressed.
37
    /// </summary>
38
    /// <remarks>
39
    /// Refer to XINPUT_GAMEPAD_* bitmasks for specific button mappings.
40
    /// Bits that are set but not defined are reserved, and their state is undefined.
41
    /// </summary>
42
    [MarshalAs(UnmanagedType.I2)]
43
    [FieldOffset(0)]
44
    public short ButtonsBitmask;
45

46
    /// <summary>
47
    /// The current value of the left trigger analog control. The value is between 0 and 255.
48
    /// </summary>
49
    [MarshalAs(UnmanagedType.I1)]
50
    [FieldOffset(2)]
51
    public byte LeftTrigger;
52

53
    /// <summary>
54
    /// The current value of the right trigger analog control. The value is between 0 and 255.
55
    /// </summary>
56
    [MarshalAs(UnmanagedType.I1)]
57
    [FieldOffset(3)]
58
    public byte RightTrigger;
59

60
    /// <summary>
61
    /// Left thumbstick x-axis value. Negative values signify down or to the left,
62
    /// positive values signify up or to the right. The value is between -32768 and 32767.
63
    /// A value of 0 is centered. Negative values signify down or to the left. Positive values
64
    /// signify up or to the right.
65
    /// </summary>
66
    [MarshalAs(UnmanagedType.I2)]
67
    [FieldOffset(4)]
68
    public short LeftThumbX;
69

70
    /// <summary>
71
    /// Left thumbstick y-axis value. The value is between -32768 and 32767.
72
    /// </summary>
73
    [MarshalAs(UnmanagedType.I2)]
74
    [FieldOffset(6)]
75
    public short LeftThumbY;
76

77
    /// <summary>
78
    /// Right thumbstick x-axis value. The value is between -32768 and 32767.
79
    /// </summary>
80
    [MarshalAs(UnmanagedType.I2)]
81
    [FieldOffset(8)]
82
    public short RightThumbX;
83

84
    /// <summary>
85
    /// Right thumbstick y-axis value. The value is between -32768 and 32767.
86
    /// </summary>
87
    [MarshalAs(UnmanagedType.I2)]
88
    [FieldOffset(10)]
89
    public short RightThumbY;
90

91
    /// <summary>
92
    /// Checks if a specific button or buttons represented by the bitmask are pressed.
93
    /// </summary>
94
    /// <param name="buttonFlags">The bitmask representing the button(s).</param>
95
    /// <returns>True if the button(s) are pressed, otherwise false.</returns>
96
    public readonly bool IsButtonPressed(GamePadButton buttonFlags)
97
        => (this.ButtonsBitmask & (int)buttonFlags) == (int)buttonFlags;
×
98

99
    /// <summary>
100
    /// Checks if a specific button or buttons represented by the bitmask are present.
101
    /// </summary>
102
    /// <param name="buttonFlags">The bitmask representing the button(s).</param>
103
    /// <returns>True if the button(s) are present, otherwise false.</returns>
104
    public readonly bool IsButtonPresent(GamePadButton buttonFlags)
105
        => (this.ButtonsBitmask & (int)buttonFlags) == (int)buttonFlags;
×
106

107
    /// <summary>
108
    /// Returns all pressed buttons in a List.
109
    /// </summary>
110
    /// <returns></returns>
111
    public readonly IList<GamePadButton> GetPressedButtonsList()
112
    {
113
        var pressedButtons = new List<GamePadButton>();
×
114

115
        foreach (GamePadButton button in Enum.GetValues(typeof(GamePadButton)))
×
116
        {
117
            if (this.IsButtonPressed(button))
×
118
            {
119
                pressedButtons.Add(button);
×
120
            }
121
        }
122

123
        return pressedButtons;
×
124
    }
125

126
    /// <summary>
127
    /// Checks if the thumb stick on the given side has moved past a certain threshold (or else the default deadzone)
128
    /// </summary>
129
    /// <param name="side"></param>
130
    /// <param name="deltaMargin"></param>
131
    /// <returns></returns>
132
    public readonly bool IsThumbstickMoved(GamePadSide side, ExactAxisDirection? deltaMargin = null)
133
    {
134
        var defaultDeadzone = side == GamePadSide.Left ? XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE : XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE;
8✔
135
        var thumbstickX = side == GamePadSide.Left ? this.LeftThumbX : this.RightThumbX;
8✔
136
        var thumbstickY = side == GamePadSide.Left ? this.LeftThumbY : this.RightThumbY;
8✔
137
        var deadzoneX = deltaMargin?.X * ThumbstickValueMax ?? defaultDeadzone;
8✔
138
        var deadzoneY = deltaMargin?.Y * ThumbstickValueMax ?? defaultDeadzone;
8✔
139

140
        // We must convert to an int, otherwise the absolute of short -32768 (32768) would fail since it's too big
141
        if (Math.Abs((int)thumbstickX) > deadzoneX || Math.Abs((int)thumbstickY) > deadzoneY)
8✔
142
        {
143
            return true;
4✔
144
        }
145

146
        return false;
4✔
147
    }
148

149
    /// <summary>
150
    /// Checks if the trigger on a certain side has pulled back past a certain threshold (or else the default deadzone)
151
    /// </summary>
152
    /// <param name="side"></param>
153
    /// <param name="deltaMargin"></param>
154
    /// <returns></returns>
155
    public readonly bool IsTriggerPulled(GamePadSide side, float? deltaMargin)
156
    {
157
        var deadzone = deltaMargin ?? XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
×
158
        var trigger = side == GamePadSide.Left ? this.LeftTrigger : this.RightTrigger;
×
159

160
        if (trigger > deadzone)
×
161
        {
162
            return true;
×
163
        }
164

165
        return false;
×
166
    }
167

168
    /// <summary>
169
    /// Returns the stick delta for a given side as an exact axis fraction.
170
    /// </summary>
171
    /// <param name="side"></param>
172
    /// <returns></returns>
173
    public readonly ExactAxisDirection GetStickDelta(GamePadSide side)
174
    {
175
        if (side == GamePadSide.Left)
×
176
        {
177
            return new ExactAxisDirection(
×
178
                (float)this.LeftThumbX / ThumbstickValueMax,
×
179
                (float)this.LeftThumbY / ThumbstickValueMax);
×
180
        }
181

182
        return new ExactAxisDirection(
×
183
            (float)this.RightThumbX / ThumbstickValueMax,
×
184
            (float)this.RightThumbY / ThumbstickValueMax);
×
185
    }
186

187
    /// <summary>
188
    /// Returns the trigger delta for a given side as an exact axis fraction.
189
    /// </summary>
190
    /// <param name="side"></param>
191
    /// <returns></returns>
192
    public readonly float GetTriggerDelta(GamePadSide side)
193
    {
194
        if (side == GamePadSide.Left)
×
195
        {
196
            return (float)this.LeftTrigger / TriggerValueMax;
×
197
        }
198

199
        return (float)this.RightTrigger / TriggerValueMax;
×
200
    }
201

202
    /// <summary>
203
    /// Copies the values from the source gamepad to the current instance.
204
    /// </summary>
205
    /// <param name="source">The source gamepad from which values should be copied.</param>
206
    public void Copy(XInputGamePad source)
207
    {
208
        this.LeftThumbX = source.LeftThumbX;
×
209
        this.LeftThumbY = source.LeftThumbY;
×
210
        this.RightThumbX = source.RightThumbX;
×
211
        this.RightThumbY = source.RightThumbY;
×
212
        this.LeftTrigger = source.LeftTrigger;
×
213
        this.RightTrigger = source.RightTrigger;
×
214
        this.ButtonsBitmask = source.ButtonsBitmask;
×
215
    }
×
216

217
    /// <inheritdoc/>
218
    public readonly bool Equals(XInputGamePad other)
219
        => this.ButtonsBitmask == other.ButtonsBitmask
×
220
        && this.LeftTrigger == other.LeftTrigger
×
221
        && this.RightTrigger == other.RightTrigger
×
222
        && this.LeftThumbX == other.LeftThumbX
×
223
        && this.LeftThumbY == other.LeftThumbY
×
224
        && this.RightThumbX == other.RightThumbX
×
225
        && this.RightThumbY == other.RightThumbY;
×
226

227
    /// <inheritdoc/>
228
    public override readonly bool Equals(object obj)
229
        => obj is XInputGamePad gamepad && this.Equals(gamepad);
×
230

231
    /// <inheritdoc/>
232
    public override int GetHashCode()
233
    {
234
        var hashCode = 235782390;
×
235
        hashCode = (hashCode * -1521134295) + this.ButtonsBitmask.GetHashCode();
×
236
        hashCode = (hashCode * -1521134295) + this.LeftTrigger.GetHashCode();
×
237
        hashCode = (hashCode * -1521134295) + this.RightTrigger.GetHashCode();
×
238
        hashCode = (hashCode * -1521134295) + this.LeftThumbX.GetHashCode();
×
239
        hashCode = (hashCode * -1521134295) + this.LeftThumbY.GetHashCode();
×
240
        hashCode = (hashCode * -1521134295) + this.RightThumbX.GetHashCode();
×
241
        hashCode = (hashCode * -1521134295) + this.RightThumbY.GetHashCode();
×
242
        return hashCode;
×
243
    }
244

245
    public override string ToString()
246
        => $"{nameof(XInputGamePad)} Buttons: {this.ButtonsBitmask}, LeftTrigger: {this.LeftTrigger}, RightTrigger: {this.RightTrigger}, LeftThumb: ({this.LeftThumbX}, {this.LeftThumbY}), RightThumb: ({this.RightThumbX}, {this.RightThumbY})";
×
247
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc