2525 */
2626
2727#include " XInput.h"
28+ #include < limits>
2829
2930// --------------------------------------------------------
3031// XInput Button Maps |
@@ -72,7 +73,9 @@ constexpr const XInputMap_Button * getButtonFromEnum(XInputControl ctrl) {
7273 case (BUTTON_Y): return &Map_ButtonY;
7374 case (BUTTON_LB): return &Map_ButtonLB;
7475 case (BUTTON_RB): return &Map_ButtonRB;
76+ case (JOY_LEFT):
7577 case (BUTTON_L3): return &Map_ButtonL3;
78+ case (JOY_RIGHT):
7679 case (BUTTON_R3): return &Map_ButtonR3;
7780 case (BUTTON_START): return &Map_ButtonStart;
7881 case (BUTTON_BACK): return &Map_ButtonBack;
@@ -89,9 +92,15 @@ constexpr const XInputMap_Button * getButtonFromEnum(XInputControl ctrl) {
8992struct XInputMap_Trigger {
9093 constexpr XInputMap_Trigger (uint8_t i)
9194 : index(i) {}
95+ static const XInputGamepad::Range range;
9296 const uint8_t index;
9397};
9498
99+ const XInputGamepad::Range XInputMap_Trigger::range = {
100+ std::numeric_limits<uint8_t >::min (), // 0
101+ std::numeric_limits<uint8_t >::max () // 255
102+ };
103+
95104static const XInputMap_Trigger Map_TriggerLeft (4 );
96105static const XInputMap_Trigger Map_TriggerRight (5 );
97106
@@ -111,12 +120,18 @@ constexpr const XInputMap_Trigger * getTriggerFromEnum(XInputControl ctrl) {
111120struct XInputMap_Joystick {
112121 constexpr XInputMap_Joystick (uint8_t xl, uint8_t xh, uint8_t yl, uint8_t yh)
113122 : x_low(xl), x_high(xh), y_low(yl), y_high(yh) {}
123+ static const XInputGamepad::Range range;
114124 const uint8_t x_low;
115125 const uint8_t x_high;
116126 const uint8_t y_low;
117127 const uint8_t y_high;
118128};
119129
130+ const XInputGamepad::Range XInputMap_Joystick::range = {
131+ std::numeric_limits<int16_t >::min (), // -32768
132+ std::numeric_limits<int16_t >::max () // 32767
133+ };
134+
120135static const XInputMap_Joystick Map_JoystickLeft (6 , 7 , 8 , 9 );
121136static const XInputMap_Joystick Map_JoystickRight (10 , 11 , 12 , 13 );
122137
@@ -151,8 +166,7 @@ static const XInputMap_Rumble RumbleRight(4, 1); // Small motor
151166XInputGamepad::XInputGamepad () :
152167 tx(), rumble() // Zero initialize arrays
153168{
154- tx[0 ] = 0x00 ; // Message type
155- tx[1 ] = 0x14 ; // Packet size
169+ reset ();
156170}
157171
158172void XInputGamepad::press (XInputControl button) {
@@ -170,40 +184,53 @@ void XInputGamepad::setButton(XInputControl button, boolean state) {
170184 else { tx[buttonData->index ] &= ~(buttonData->mask ); } // Release
171185 }
172186 else {
173- setTrigger (button, state ? 255 : 0 ); // Treat trigger like a button
187+ Range * triggerRange = getRangeFromEnum (button);
188+ if (triggerRange == nullptr ) return ; // Not a trigger (or joystick, but the trigger function will ignore that)
189+ setTrigger (button, state ? triggerRange->max : triggerRange->min ); // Treat trigger like a button
174190 }
175191}
176192
177- // To-do: add SOCD cleaner
178193void XInputGamepad::setDpad (XInputControl pad, boolean state) {
179194 setButton (pad, state);
180195}
181196
182-
183197void XInputGamepad::setDpad (boolean up, boolean down, boolean left, boolean right) {
198+ // Simultaneous Opposite Cardinal Directions (SOCD) Cleaner
199+ if (up && down) { down = false ; } // Up + Down = Up
200+ if (left && right) { left = false ; right = false ; } // Left + Right = Neutral
201+
184202 setDpad (DPAD_UP, up);
185203 setDpad (DPAD_DOWN, down);
186204 setDpad (DPAD_LEFT, left);
187205 setDpad (DPAD_RIGHT, right);
188206}
189207
190- void XInputGamepad::setTrigger (XInputControl trigger, uint8_t val) {
208+ void XInputGamepad::setTrigger (XInputControl trigger, int32_t val) {
191209 const XInputMap_Trigger * triggerData = getTriggerFromEnum (trigger);
192210 if (triggerData == nullptr ) return ; // Not a trigger
211+ val = rescaleInput (val, *getRangeFromEnum (trigger), triggerData->range );
193212 tx[triggerData->index ] = val;
194213}
195214
196- void XInputGamepad::setJoystick (XInputControl joy, int16_t x, int16_t y) {
215+ void XInputGamepad::setJoystick (XInputControl joy, int32_t x, int32_t y) {
197216 const XInputMap_Joystick * joyData = getJoyFromEnum (joy);
198217 if (joyData == nullptr ) return ; // Not a joystick
199218
219+ x = rescaleInput (x, *getRangeFromEnum (joy), joyData->range );
220+ y = rescaleInput (y, *getRangeFromEnum (joy), joyData->range );
221+
200222 tx[joyData->x_low ] = lowByte (x);
201223 tx[joyData->x_high ] = highByte (x);
202224
203225 tx[joyData->y_low ] = lowByte (y);
204226 tx[joyData->y_high ] = highByte (y);
205227}
206228
229+ void XInputGamepad::releaseAll () {
230+ const uint8_t offset = 2 ; // Skip message type and packet size
231+ memset (tx + offset, 0x00 , sizeof (tx) - offset); // Clear TX array
232+ }
233+
207234uint8_t XInputGamepad::getPlayer () const {
208235 return player;
209236}
@@ -277,4 +304,58 @@ void XInputGamepad::parseLED(uint8_t leds) {
277304 }
278305}
279306
307+ XInputGamepad::Range * XInputGamepad::getRangeFromEnum (XInputControl ctrl) {
308+ switch (ctrl) {
309+ case (TRIGGER_LEFT): return &rangeTrigLeft;
310+ case (TRIGGER_RIGHT): return &rangeTrigRight;
311+ case (JOY_LEFT): return &rangeJoyLeft;
312+ case (JOY_RIGHT): return &rangeJoyRight;
313+ default : return nullptr ;
314+ }
315+ }
316+
317+ int32_t XInputGamepad::rescaleInput (int32_t val, Range in, Range out) {
318+ if (in.min == out.min && in.max == out.max ) return val; // Ranges identical
319+ if (val <= in.min ) return out.min ; // Out of range -
320+ if (val >= in.max ) return out.max ; // Out of range +
321+ return map (val, in.min , in.max , out.min , out.max );
322+ }
323+
324+ void XInputGamepad::setTriggerRange (int32_t rangeMin, int32_t rangeMax) {
325+ setRange (TRIGGER_LEFT, rangeMin, rangeMax);
326+ setRange (TRIGGER_RIGHT, rangeMin, rangeMax);
327+ }
328+
329+ void XInputGamepad::setJoystickRange (int32_t rangeMin, int32_t rangeMax) {
330+ setRange (JOY_LEFT, rangeMin, rangeMax);
331+ setRange (JOY_RIGHT, rangeMin, rangeMax);
332+ }
333+
334+ void XInputGamepad::setRange (XInputControl ctrl, int32_t rangeMin, int32_t rangeMax) {
335+ if (rangeMin >= rangeMax) return ; // Error: Max < Min
336+
337+ Range * range = getRangeFromEnum (ctrl);
338+ if (range == nullptr ) return ; // Not an addressable range
339+
340+ range->min = rangeMin;
341+ range->max = rangeMax;
342+ }
343+
344+ // Resets class back to initial values
345+ void XInputGamepad::reset () {
346+ // Reset control data (tx)
347+ releaseAll (); // Clear TX buffer
348+ tx[0 ] = 0x00 ; // Set tx message type
349+ tx[1 ] = 0x14 ; // Set tx packet size (20)
350+
351+ // Reset received data (rx)
352+ player = 0 ; // Not connected, no player
353+ memset (rumble, 0x00 , sizeof (rumble)); // Clear rumble values
354+ ledPattern = XInputLEDPattern::Off; // No LEDs on
355+
356+ // Reset rescale ranges
357+ setTriggerRange (XInputMap_Trigger::range.min , XInputMap_Trigger::range.max );
358+ setJoystickRange (XInputMap_Joystick::range.min , XInputMap_Joystick::range.max );
359+ }
360+
280361XInputGamepad XInput;
0 commit comments