1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 // Based on
8 // https://cs.chromium.org/chromium/src/device/gamepad/gamepad_standard_mappings.h
9 
10 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
11 // Use of this source code is governed by a BSD-style license that can be
12 // found in the LICENSE file.
13 
14 #include "mozilla/dom/GamepadRemapping.h"
15 #include "mozilla/dom/GamepadPlatformService.h"
16 
17 #include <vector>
18 #include <unordered_map>
19 
20 namespace mozilla::dom {
21 
22 // Follow the canonical ordering recommendation for the "Standard Gamepad"
23 // from https://www.w3.org/TR/gamepad/#remapping.
24 enum CanonicalButtonIndex {
25   BUTTON_INDEX_PRIMARY,
26   BUTTON_INDEX_SECONDARY,
27   BUTTON_INDEX_TERTIARY,
28   BUTTON_INDEX_QUATERNARY,
29   BUTTON_INDEX_LEFT_SHOULDER,
30   BUTTON_INDEX_RIGHT_SHOULDER,
31   BUTTON_INDEX_LEFT_TRIGGER,
32   BUTTON_INDEX_RIGHT_TRIGGER,
33   BUTTON_INDEX_BACK_SELECT,
34   BUTTON_INDEX_START,
35   BUTTON_INDEX_LEFT_THUMBSTICK,
36   BUTTON_INDEX_RIGHT_THUMBSTICK,
37   BUTTON_INDEX_DPAD_UP,
38   BUTTON_INDEX_DPAD_DOWN,
39   BUTTON_INDEX_DPAD_LEFT,
40   BUTTON_INDEX_DPAD_RIGHT,
41   BUTTON_INDEX_META,
42   BUTTON_INDEX_COUNT
43 };
44 
45 enum CanonicalAxisIndex {
46   AXIS_INDEX_LEFT_STICK_X,
47   AXIS_INDEX_LEFT_STICK_Y,
48   AXIS_INDEX_RIGHT_STICK_X,
49   AXIS_INDEX_RIGHT_STICK_Y,
50   AXIS_INDEX_COUNT
51 };
52 
53 const float BUTTON_THRESHOLD_VALUE = 0.1f;
54 
NormalizeTouch(long aValue,long aMin,long aMax)55 float NormalizeTouch(long aValue, long aMin, long aMax) {
56   return (2.f * (aValue - aMin) / static_cast<float>(aMax - aMin)) - 1.f;
57 }
58 
AxisNegativeAsButton(float input)59 bool AxisNegativeAsButton(float input) {
60   const float value = (input < -0.5f) ? 1.f : 0.f;
61   return value > BUTTON_THRESHOLD_VALUE;
62 }
63 
AxisPositiveAsButton(float input)64 bool AxisPositiveAsButton(float input) {
65   const float value = (input > 0.5f) ? 1.f : 0.f;
66   return value > BUTTON_THRESHOLD_VALUE;
67 }
68 
AxisToButtonValue(double aValue)69 double AxisToButtonValue(double aValue) {
70   // Mapping axis value range from (-1, +1) to (0, +1).
71   return (aValue + 1.0f) * 0.5f;
72 }
73 
FetchDpadFromAxis(GamepadHandle aHandle,double dir)74 void FetchDpadFromAxis(GamepadHandle aHandle, double dir) {
75   bool up = false;
76   bool right = false;
77   bool down = false;
78   bool left = false;
79 
80   // Dpad is mapped as a direction on one axis, where -1 is up and it
81   // increases clockwise to 1, which is up + left. It's set to a large (> 1.f)
82   // number when nothing is depressed, except on start up, sometimes it's 0.0
83   // for no data, rather than the large number.
84   if (dir != 0.0f) {
85     up = (dir >= -1.f && dir < -0.7f) || (dir >= .95f && dir <= 1.f);
86     right = dir >= -.75f && dir < -.1f;
87     down = dir >= -.2f && dir < .45f;
88     left = dir >= .4f && dir <= 1.f;
89   }
90 
91   RefPtr<GamepadPlatformService> service =
92       GamepadPlatformService::GetParentService();
93   if (!service) {
94     return;
95   }
96 
97   service->NewButtonEvent(aHandle, BUTTON_INDEX_DPAD_UP, up);
98   service->NewButtonEvent(aHandle, BUTTON_INDEX_DPAD_RIGHT, right);
99   service->NewButtonEvent(aHandle, BUTTON_INDEX_DPAD_DOWN, down);
100   service->NewButtonEvent(aHandle, BUTTON_INDEX_DPAD_LEFT, left);
101 }
102 
103 class DefaultRemapper final : public GamepadRemapper {
104  public:
GetAxisCount() const105   virtual uint32_t GetAxisCount() const override { return numAxes; }
106 
GetButtonCount() const107   virtual uint32_t GetButtonCount() const override { return numButtons; }
108 
SetAxisCount(uint32_t aAxisCount)109   virtual void SetAxisCount(uint32_t aAxisCount) override {
110     numAxes = aAxisCount;
111   }
112 
SetButtonCount(uint32_t aButtonCount)113   virtual void SetButtonCount(uint32_t aButtonCount) override {
114     numButtons = aButtonCount;
115   }
116 
GetMappingType() const117   virtual GamepadMappingType GetMappingType() const override {
118     return GamepadMappingType::_empty;
119   }
120 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const121   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
122                                   double aValue) const override {
123     if (GetAxisCount() <= aAxis) {
124       NS_WARNING(
125           nsPrintfCString("Axis idx '%d' doesn't support in DefaultRemapper().",
126                           aAxis)
127               .get());
128       return;
129     }
130     RefPtr<GamepadPlatformService> service =
131         GamepadPlatformService::GetParentService();
132     if (!service) {
133       return;
134     }
135     service->NewAxisMoveEvent(aHandle, aAxis, aValue);
136   }
137 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const138   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
139                                 bool aPressed) const override {
140     if (GetButtonCount() <= aButton) {
141       NS_WARNING(
142           nsPrintfCString(
143               "Button idx '%d' doesn't support in DefaultRemapper().", aButton)
144               .get());
145       return;
146     }
147     RefPtr<GamepadPlatformService> service =
148         GamepadPlatformService::GetParentService();
149     if (!service) {
150       return;
151     }
152     service->NewButtonEvent(aHandle, aButton, aPressed);
153   }
154 
155  private:
156   uint32_t numAxes;
157   uint32_t numButtons;
158 };
159 
160 class ADT1Remapper final : public GamepadRemapper {
161  public:
GetAxisCount() const162   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
163 
GetButtonCount() const164   virtual uint32_t GetButtonCount() const override {
165     return BUTTON_INDEX_COUNT;
166   }
167 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const168   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
169                                   double aValue) const override {
170     RefPtr<GamepadPlatformService> service =
171         GamepadPlatformService::GetParentService();
172     if (!service) {
173       return;
174     }
175 
176     switch (aAxis) {
177       case 0:
178         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
179         break;
180       case 1:
181         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
182         break;
183       case 2:
184         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
185         break;
186       case 3: {
187         const double value = AxisToButtonValue(aValue);
188         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
189                                 value > BUTTON_THRESHOLD_VALUE, value);
190         break;
191       }
192       case 4: {
193         const double value = AxisToButtonValue(aValue);
194         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
195                                 value > BUTTON_THRESHOLD_VALUE, value);
196         break;
197       }
198       case 5:
199         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
200         break;
201       case 9:
202         FetchDpadFromAxis(aHandle, aValue);
203         break;
204       default:
205         NS_WARNING(
206             nsPrintfCString("Axis idx '%d' doesn't support in ADT1Remapper().",
207                             aAxis)
208                 .get());
209         break;
210     }
211   }
212 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const213   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
214                                 bool aPressed) const override {
215     RefPtr<GamepadPlatformService> service =
216         GamepadPlatformService::GetParentService();
217     if (!service) {
218       return;
219     }
220 
221     if (GetButtonCount() <= aButton) {
222       NS_WARNING(
223           nsPrintfCString("Button idx '%d' doesn't support in ADT1Remapper().",
224                           aButton)
225               .get());
226       return;
227     }
228 
229     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
230         {3, BUTTON_INDEX_TERTIARY},
231         {4, BUTTON_INDEX_QUATERNARY},
232         {6, BUTTON_INDEX_LEFT_SHOULDER},
233         {7, BUTTON_INDEX_RIGHT_SHOULDER},
234         {12, BUTTON_INDEX_META},
235         {13, BUTTON_INDEX_LEFT_THUMBSTICK},
236         {14, BUTTON_INDEX_RIGHT_THUMBSTICK}};
237 
238     auto find = buttonMapping.find(aButton);
239     if (find != buttonMapping.end()) {
240       service->NewButtonEvent(aHandle, find->second, aPressed);
241     } else {
242       service->NewButtonEvent(aHandle, aButton, aPressed);
243     }
244   }
245 };
246 
247 class TwoAxesEightKeysRemapper final : public GamepadRemapper {
248  public:
GetAxisCount() const249   virtual uint32_t GetAxisCount() const override { return 0; }
250 
GetButtonCount() const251   virtual uint32_t GetButtonCount() const override {
252     return BUTTON_INDEX_COUNT - 1;
253   }
254 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const255   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
256                                   double aValue) const override {
257     RefPtr<GamepadPlatformService> service =
258         GamepadPlatformService::GetParentService();
259     if (!service) {
260       return;
261     }
262 
263     switch (aAxis) {
264       case 0:
265         service->NewButtonEvent(aHandle, BUTTON_INDEX_DPAD_LEFT,
266                                 AxisNegativeAsButton(aValue));
267         service->NewButtonEvent(aHandle, BUTTON_INDEX_DPAD_RIGHT,
268                                 AxisPositiveAsButton(aValue));
269         break;
270       case 1:
271         service->NewButtonEvent(aHandle, BUTTON_INDEX_DPAD_UP,
272                                 AxisNegativeAsButton(aValue));
273         service->NewButtonEvent(aHandle, BUTTON_INDEX_DPAD_DOWN,
274                                 AxisPositiveAsButton(aValue));
275         break;
276       default:
277         NS_WARNING(
278             nsPrintfCString(
279                 "Axis idx '%d' doesn't support in TwoAxesEightKeysRemapper().",
280                 aAxis)
281                 .get());
282         break;
283     }
284   }
285 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const286   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
287                                 bool aPressed) const override {
288     RefPtr<GamepadPlatformService> service =
289         GamepadPlatformService::GetParentService();
290     if (!service) {
291       return;
292     }
293 
294     if (GetButtonCount() <= aButton) {
295       NS_WARNING(
296           nsPrintfCString(
297               "Button idx '%d' doesn't support in TwoAxesEightKeysRemapper().",
298               aButton)
299               .get());
300       return;
301     }
302 
303     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
304         {0, BUTTON_INDEX_QUATERNARY},
305         {2, BUTTON_INDEX_PRIMARY},
306         {3, BUTTON_INDEX_TERTIARY}};
307 
308     auto find = buttonMapping.find(aButton);
309     if (find != buttonMapping.end()) {
310       service->NewButtonEvent(aHandle, find->second, aPressed);
311     } else {
312       service->NewButtonEvent(aHandle, aButton, aPressed);
313     }
314   }
315 };
316 
317 class StadiaControllerRemapper final : public GamepadRemapper {
318  public:
GetAxisCount() const319   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
320 
GetButtonCount() const321   virtual uint32_t GetButtonCount() const override {
322     return STADIA_BUTTON_COUNT;
323   }
324 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const325   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
326                                   double aValue) const override {
327     RefPtr<GamepadPlatformService> service =
328         GamepadPlatformService::GetParentService();
329     if (!service) {
330       return;
331     }
332 
333     switch (aAxis) {
334       case 0:
335         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
336         break;
337       case 1:
338         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
339         break;
340       case 2:
341         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
342         break;
343       case 3:
344         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
345         break;
346       case 4: {
347         const double value = AxisToButtonValue(aValue);
348         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
349                                 value > BUTTON_THRESHOLD_VALUE, value);
350         break;
351       }
352       case 5: {
353         const double value = AxisToButtonValue(aValue);
354         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
355                                 value > BUTTON_THRESHOLD_VALUE, value);
356         break;
357       }
358       default:
359         NS_WARNING(
360             nsPrintfCString(
361                 "Axis idx '%d' doesn't support in StadiaControllerRemapper().",
362                 aAxis)
363                 .get());
364         break;
365     }
366   }
367 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const368   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
369                                 bool aPressed) const override {
370     RefPtr<GamepadPlatformService> service =
371         GamepadPlatformService::GetParentService();
372     if (!service) {
373       return;
374     }
375 
376     if (STADIA_BUTTON_COUNT <= aButton) {
377       NS_WARNING(
378           nsPrintfCString(
379               "Button idx '%d' doesn't support in StadiaControllerRemapper().",
380               aButton)
381               .get());
382       return;
383     }
384 
385     service->NewButtonEvent(aHandle, aButton, aPressed);
386   }
387 
388  private:
389   enum STADIAButtons {
390     STADIA_BUTTON_EXTRA1 = BUTTON_INDEX_COUNT,
391     STADIA_BUTTON_EXTRA2,
392     STADIA_BUTTON_COUNT
393   };
394 };
395 
396 class Playstation3Remapper final : public GamepadRemapper {
397  public:
398   Playstation3Remapper() = default;
399 
GetAxisCount() const400   uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
401 
GetButtonCount() const402   uint32_t GetButtonCount() const override { return BUTTON_INDEX_COUNT; }
403 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const404   void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
405                           double aValue) const override {
406     RefPtr<GamepadPlatformService> service =
407         GamepadPlatformService::GetParentService();
408     if (!service) {
409       return;
410     }
411 
412     switch (aAxis) {
413       case 0:
414         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
415         break;
416       case 1:
417         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
418         break;
419       case 2:
420         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
421         break;
422       case 5:
423         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
424         break;
425       default:
426         NS_WARNING(
427             nsPrintfCString(
428                 "Axis idx '%d' doesn't support in Playstation3Remapper().",
429                 aAxis)
430                 .get());
431         break;
432     }
433   }
434 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const435   void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
436                         bool aPressed) const override {
437     RefPtr<GamepadPlatformService> service =
438         GamepadPlatformService::GetParentService();
439     if (!service) {
440       return;
441     }
442 
443     const std::vector<uint32_t> buttonMapping = {BUTTON_INDEX_BACK_SELECT,
444                                                  BUTTON_INDEX_LEFT_THUMBSTICK,
445                                                  BUTTON_INDEX_RIGHT_THUMBSTICK,
446                                                  BUTTON_INDEX_START,
447                                                  BUTTON_INDEX_DPAD_UP,
448                                                  BUTTON_INDEX_DPAD_RIGHT,
449                                                  BUTTON_INDEX_DPAD_DOWN,
450                                                  BUTTON_INDEX_DPAD_LEFT,
451                                                  BUTTON_INDEX_LEFT_TRIGGER,
452                                                  BUTTON_INDEX_RIGHT_TRIGGER,
453                                                  BUTTON_INDEX_LEFT_SHOULDER,
454                                                  BUTTON_INDEX_RIGHT_SHOULDER,
455                                                  BUTTON_INDEX_QUATERNARY,
456                                                  BUTTON_INDEX_SECONDARY,
457                                                  BUTTON_INDEX_PRIMARY,
458                                                  BUTTON_INDEX_TERTIARY,
459                                                  BUTTON_INDEX_META};
460 
461     if (buttonMapping.size() <= aButton) {
462       NS_WARNING(
463           nsPrintfCString(
464               "Button idx '%d' doesn't support in Playstation3Remapper().",
465               aButton)
466               .get());
467       return;
468     }
469     service->NewButtonEvent(aHandle, buttonMapping[aButton], aPressed);
470   }
471 };
472 
473 class Dualshock4Remapper final : public GamepadRemapper {
474  public:
Dualshock4Remapper()475   Dualshock4Remapper() {
476     mLastTouches.SetLength(TOUCH_EVENT_COUNT);
477     mLastTouchId.SetLength(TOUCH_EVENT_COUNT);
478   }
479 
GetAxisCount() const480   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
481 
GetButtonCount() const482   virtual uint32_t GetButtonCount() const override {
483     return DUALSHOCK_BUTTON_COUNT;
484   }
485 
GetLightIndicatorCount() const486   virtual uint32_t GetLightIndicatorCount() const override {
487     return LIGHT_INDICATOR_COUNT;
488   }
489 
GetLightIndicators(nsTArray<GamepadLightIndicatorType> & aTypes) const490   virtual void GetLightIndicators(
491       nsTArray<GamepadLightIndicatorType>& aTypes) const override {
492     const uint32_t len = GetLightIndicatorCount();
493     aTypes.SetLength(len);
494     for (uint32_t i = 0; i < len; ++i) {
495       aTypes[i] = GamepadLightIndicatorType::Rgb;
496     }
497   }
498 
GetTouchEventCount() const499   virtual uint32_t GetTouchEventCount() const override {
500     return TOUCH_EVENT_COUNT;
501   }
502 
GetLightColorReport(uint8_t aRed,uint8_t aGreen,uint8_t aBlue,std::vector<uint8_t> & aReport) const503   virtual void GetLightColorReport(
504       uint8_t aRed, uint8_t aGreen, uint8_t aBlue,
505       std::vector<uint8_t>& aReport) const override {
506     const size_t report_length = 32;
507     aReport.resize(report_length);
508     aReport.assign(report_length, 0);
509 
510     aReport[0] = 0x05;  // report ID USB only
511     aReport[1] = 0x02;  // LED only
512     aReport[6] = aRed;
513     aReport[7] = aGreen;
514     aReport[8] = aBlue;
515   }
516 
GetMaxInputReportLength() const517   virtual uint32_t GetMaxInputReportLength() const override {
518     return MAX_INPUT_LEN;
519   }
520 
ProcessTouchData(GamepadHandle aHandle,void * aInput)521   virtual void ProcessTouchData(GamepadHandle aHandle, void* aInput) override {
522     nsTArray<GamepadTouchState> touches(TOUCH_EVENT_COUNT);
523     touches.SetLength(TOUCH_EVENT_COUNT);
524     uint8_t* rawData = (uint8_t*)aInput;
525 
526     const uint32_t kTouchDimensionX = 1920;
527     const uint32_t kTouchDimensionY = 942;
528     bool touch0Pressed = (((rawData[35] & 0xff) >> 7) == 0);
529     bool touch1Pressed = (((rawData[39] & 0xff) >> 7) == 0);
530 
531     if ((touch0Pressed && (rawData[35] & 0xff) < mLastTouchId[0]) ||
532         (touch1Pressed && (rawData[39] & 0xff) < mLastTouchId[1])) {
533       mTouchIdBase += 128;
534     }
535 
536     if (touch0Pressed) {
537       touches[0].touchId = mTouchIdBase + (rawData[35] & 0x7f);
538       touches[0].surfaceId = 0;
539       touches[0].position[0] = NormalizeTouch(
540           ((rawData[37] & 0xf) << 8) | rawData[36], 0, (kTouchDimensionX - 1));
541       touches[0].position[1] =
542           NormalizeTouch(rawData[38] << 4 | ((rawData[37] & 0xf0) >> 4), 0,
543                          (kTouchDimensionY - 1));
544       touches[0].surfaceDimensions[0] = kTouchDimensionX;
545       touches[0].surfaceDimensions[1] = kTouchDimensionY;
546       touches[0].isSurfaceDimensionsValid = true;
547       mLastTouchId[0] = rawData[35] & 0x7f;
548     }
549     if (touch1Pressed) {
550       touches[1].touchId = mTouchIdBase + (rawData[39] & 0x7f);
551       touches[1].surfaceId = 0;
552       touches[1].position[0] =
553           NormalizeTouch((((rawData[41] & 0xf) << 8) | rawData[40]) + 1, 0,
554                          (kTouchDimensionX - 1));
555       touches[1].position[1] =
556           NormalizeTouch(rawData[42] << 4 | ((rawData[41] & 0xf0) >> 4), 0,
557                          (kTouchDimensionY - 1));
558       touches[1].surfaceDimensions[0] = kTouchDimensionX;
559       touches[1].surfaceDimensions[1] = kTouchDimensionY;
560       touches[1].isSurfaceDimensionsValid = true;
561       mLastTouchId[1] = rawData[39] & 0x7f;
562     }
563 
564     RefPtr<GamepadPlatformService> service =
565         GamepadPlatformService::GetParentService();
566     if (!service) {
567       return;
568     }
569 
570     // Avoid to send duplicate untouched events to the gamepad service.
571     if ((mLastTouches[0] != touch0Pressed) || touch0Pressed) {
572       service->NewMultiTouchEvent(aHandle, 0, touches[0]);
573     }
574     if ((mLastTouches[1] != touch1Pressed) || touch1Pressed) {
575       service->NewMultiTouchEvent(aHandle, 1, touches[1]);
576     }
577     mLastTouches[0] = touch0Pressed;
578     mLastTouches[1] = touch1Pressed;
579   }
580 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const581   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
582                                   double aValue) const override {
583     RefPtr<GamepadPlatformService> service =
584         GamepadPlatformService::GetParentService();
585     if (!service) {
586       return;
587     }
588 
589     switch (aAxis) {
590       case 0:
591         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
592         break;
593       case 1:
594         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
595         break;
596       case 2:
597         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
598         break;
599       case 3: {
600         const double value = AxisToButtonValue(aValue);
601         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
602                                 value > BUTTON_THRESHOLD_VALUE, value);
603         break;
604       }
605       case 4: {
606         const double value = AxisToButtonValue(aValue);
607         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
608                                 value > BUTTON_THRESHOLD_VALUE, value);
609         break;
610       }
611       case 5:
612         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
613         break;
614       case 9:
615         FetchDpadFromAxis(aHandle, aValue);
616         break;
617       default:
618         NS_WARNING(
619             nsPrintfCString(
620                 "Axis idx '%d' doesn't support in Dualshock4Remapper().", aAxis)
621                 .get());
622         break;
623     }
624   }
625 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const626   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
627                                 bool aPressed) const override {
628     RefPtr<GamepadPlatformService> service =
629         GamepadPlatformService::GetParentService();
630     if (!service) {
631       return;
632     }
633 
634     const std::vector<uint32_t> buttonMapping = {BUTTON_INDEX_TERTIARY,
635                                                  BUTTON_INDEX_PRIMARY,
636                                                  BUTTON_INDEX_SECONDARY,
637                                                  BUTTON_INDEX_QUATERNARY,
638                                                  BUTTON_INDEX_LEFT_SHOULDER,
639                                                  BUTTON_INDEX_RIGHT_SHOULDER,
640                                                  BUTTON_INDEX_LEFT_TRIGGER,
641                                                  BUTTON_INDEX_RIGHT_TRIGGER,
642                                                  BUTTON_INDEX_BACK_SELECT,
643                                                  BUTTON_INDEX_START,
644                                                  BUTTON_INDEX_LEFT_THUMBSTICK,
645                                                  BUTTON_INDEX_RIGHT_THUMBSTICK,
646                                                  BUTTON_INDEX_META,
647                                                  DUALSHOCK_BUTTON_TOUCHPAD};
648 
649     if (buttonMapping.size() <= aButton) {
650       NS_WARNING(nsPrintfCString(
651                      "Button idx '%d' doesn't support in Dualshock4Remapper().",
652                      aButton)
653                      .get());
654       return;
655     }
656 
657     service->NewButtonEvent(aHandle, buttonMapping[aButton], aPressed);
658   }
659 
660  private:
661   enum Dualshock4Buttons {
662     DUALSHOCK_BUTTON_TOUCHPAD = BUTTON_INDEX_COUNT,
663     DUALSHOCK_BUTTON_COUNT
664   };
665 
666   static const uint32_t LIGHT_INDICATOR_COUNT = 1;
667   static const uint32_t TOUCH_EVENT_COUNT = 2;
668   static const uint32_t MAX_INPUT_LEN = 68;
669 
670   nsTArray<unsigned long> mLastTouchId;
671   nsTArray<bool> mLastTouches;
672   unsigned long mTouchIdBase = 0;
673 };
674 
675 class Xbox360Remapper final : public GamepadRemapper {
676  public:
GetAxisCount() const677   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
678 
GetButtonCount() const679   virtual uint32_t GetButtonCount() const override {
680     return BUTTON_INDEX_COUNT;
681   }
682 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const683   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
684                                   double aValue) const override {
685     RefPtr<GamepadPlatformService> service =
686         GamepadPlatformService::GetParentService();
687     if (!service) {
688       return;
689     }
690 
691     switch (aAxis) {
692       case 0:
693         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
694         break;
695       case 1:
696         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
697         break;
698       case 2: {
699         const double value = AxisToButtonValue(aValue);
700         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
701                                 value > BUTTON_THRESHOLD_VALUE, value);
702         break;
703       }
704       case 3:
705         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
706         break;
707       case 4:
708         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
709         break;
710       case 5: {
711         const double value = AxisToButtonValue(aValue);
712         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
713                                 value > BUTTON_THRESHOLD_VALUE, value);
714         break;
715       }
716       default:
717         NS_WARNING(
718             nsPrintfCString(
719                 "Axis idx '%d' doesn't support in Xbox360Remapper().", aAxis)
720                 .get());
721         break;
722     }
723   }
724 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const725   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
726                                 bool aPressed) const override {
727     RefPtr<GamepadPlatformService> service =
728         GamepadPlatformService::GetParentService();
729     if (!service) {
730       return;
731     }
732 
733     if (GetButtonCount() <= aButton) {
734       NS_WARNING(
735           nsPrintfCString(
736               "Button idx '%d' doesn't support in Xbox360Remapper().", aButton)
737               .get());
738       return;
739     }
740 
741     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
742         {6, BUTTON_INDEX_LEFT_THUMBSTICK}, {7, BUTTON_INDEX_RIGHT_THUMBSTICK},
743         {8, BUTTON_INDEX_START},           {9, BUTTON_INDEX_BACK_SELECT},
744         {10, BUTTON_INDEX_META},           {11, BUTTON_INDEX_DPAD_UP},
745         {12, BUTTON_INDEX_DPAD_DOWN},      {13, BUTTON_INDEX_DPAD_LEFT},
746         {14, BUTTON_INDEX_DPAD_RIGHT}};
747 
748     auto find = buttonMapping.find(aButton);
749     if (find != buttonMapping.end()) {
750       service->NewButtonEvent(aHandle, find->second, aPressed);
751     } else {
752       service->NewButtonEvent(aHandle, aButton, aPressed);
753     }
754   }
755 };
756 
757 class XboxOneSRemapper final : public GamepadRemapper {
758  public:
GetAxisCount() const759   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
760 
GetButtonCount() const761   virtual uint32_t GetButtonCount() const override {
762     return BUTTON_INDEX_COUNT;
763   }
764 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const765   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
766                                   double aValue) const override {
767     RefPtr<GamepadPlatformService> service =
768         GamepadPlatformService::GetParentService();
769     if (!service) {
770       return;
771     }
772 
773     switch (aAxis) {
774       case 0:
775         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
776         break;
777       case 1:
778         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
779         break;
780       case 2: {
781         const double value = AxisToButtonValue(aValue);
782         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
783                                 value > BUTTON_THRESHOLD_VALUE, value);
784         break;
785       }
786       case 3:
787         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
788         break;
789       case 4:
790         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
791         break;
792       case 5: {
793         const double value = AxisToButtonValue(aValue);
794         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
795                                 value > BUTTON_THRESHOLD_VALUE, value);
796         break;
797       }
798       case 9:
799         FetchDpadFromAxis(aHandle, aValue);
800         break;
801       default:
802         NS_WARNING(
803             nsPrintfCString(
804                 "Axis idx '%d' doesn't support in XboxOneSRemapper().", aAxis)
805                 .get());
806         break;
807     }
808   }
809 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const810   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
811                                 bool aPressed) const override {
812     RefPtr<GamepadPlatformService> service =
813         GamepadPlatformService::GetParentService();
814     if (!service) {
815       return;
816     }
817 
818     if (GetButtonCount() <= aButton) {
819       NS_WARNING(
820           nsPrintfCString(
821               "Button idx '%d' doesn't support in XboxOneSRemapper().", aButton)
822               .get());
823       return;
824     }
825 
826     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
827         {6, BUTTON_INDEX_BACK_SELECT},
828         {7, BUTTON_INDEX_START},
829         {8, BUTTON_INDEX_LEFT_THUMBSTICK},
830         {9, BUTTON_INDEX_RIGHT_THUMBSTICK},
831         {10, BUTTON_INDEX_META}};
832 
833     auto find = buttonMapping.find(aButton);
834     if (find != buttonMapping.end()) {
835       service->NewButtonEvent(aHandle, find->second, aPressed);
836     } else {
837       service->NewButtonEvent(aHandle, aButton, aPressed);
838     }
839   }
840 };
841 
842 class XboxOneS2016FirmwareRemapper final : public GamepadRemapper {
843  public:
GetAxisCount() const844   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
845 
GetButtonCount() const846   virtual uint32_t GetButtonCount() const override {
847     return BUTTON_INDEX_COUNT;
848   }
849 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const850   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
851                                   double aValue) const override {
852     RefPtr<GamepadPlatformService> service =
853         GamepadPlatformService::GetParentService();
854     if (!service) {
855       return;
856     }
857 
858     switch (aAxis) {
859       case 0:
860         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
861         break;
862       case 1:
863         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
864         break;
865       case 2:
866         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
867         break;
868       case 3: {
869         const double value = AxisToButtonValue(aValue);
870         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
871                                 value > BUTTON_THRESHOLD_VALUE, value);
872         break;
873       }
874       case 4: {
875         const double value = AxisToButtonValue(aValue);
876         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
877                                 value > BUTTON_THRESHOLD_VALUE, value);
878         break;
879       }
880       case 5:
881         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
882         break;
883       case 9:
884         FetchDpadFromAxis(aHandle, aValue);
885         break;
886       default:
887         NS_WARNING(nsPrintfCString("Axis idx '%d' doesn't support in "
888                                    "XboxOneS2016FirmwareRemapper().",
889                                    aAxis)
890                        .get());
891         break;
892     }
893   }
894 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const895   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
896                                 bool aPressed) const override {
897     RefPtr<GamepadPlatformService> service =
898         GamepadPlatformService::GetParentService();
899     if (!service) {
900       return;
901     }
902 
903     if (GetButtonCount() <= aButton) {
904       NS_WARNING(nsPrintfCString("Button idx '%d' doesn't support in "
905                                  "XboxOneS2016FirmwareRemapper().",
906                                  aButton)
907                      .get());
908       return;
909     }
910 
911     // kMicrosoftProductXboxOneSWireless2016 controller received a firmware
912     // update in 2019 that changed which field is populated with the meta button
913     // state. In order to cover the old and new cases, we have to check both
914     // fields of {12, 15} buttons.
915     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
916         {0, BUTTON_INDEX_PRIMARY},
917         {1, BUTTON_INDEX_SECONDARY},
918         {3, BUTTON_INDEX_TERTIARY},
919         {4, BUTTON_INDEX_QUATERNARY},
920         {6, BUTTON_INDEX_LEFT_SHOULDER},
921         {7, BUTTON_INDEX_RIGHT_SHOULDER},
922         {11, BUTTON_INDEX_START},
923         {12, BUTTON_INDEX_META},
924         {13, BUTTON_INDEX_LEFT_THUMBSTICK},
925         {14, BUTTON_INDEX_RIGHT_THUMBSTICK},
926         {15, BUTTON_INDEX_META},
927         {16, BUTTON_INDEX_BACK_SELECT}};
928 
929     auto find = buttonMapping.find(aButton);
930     if (find != buttonMapping.end()) {
931       service->NewButtonEvent(aHandle, find->second, aPressed);
932     } else {
933       service->NewButtonEvent(aHandle, aButton, aPressed);
934     }
935   }
936 };
937 
938 class XboxOneRemapper final : public GamepadRemapper {
939  public:
GetAxisCount() const940   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
941 
GetButtonCount() const942   virtual uint32_t GetButtonCount() const override {
943     return BUTTON_INDEX_COUNT;
944   }
945 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const946   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
947                                   double aValue) const override {
948     RefPtr<GamepadPlatformService> service =
949         GamepadPlatformService::GetParentService();
950     if (!service) {
951       return;
952     }
953 
954     switch (aAxis) {
955       case 0:
956         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
957         break;
958       case 1:
959         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
960         break;
961       case 2:
962         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
963         break;
964       case 3:
965         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
966         break;
967       case 9:
968         FetchDpadFromAxis(aHandle, aValue);
969         break;
970       case 10: {
971         const double value = AxisToButtonValue(aValue);
972         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
973                                 value > BUTTON_THRESHOLD_VALUE, value);
974         break;
975       }
976       case 11: {
977         const double value = AxisToButtonValue(aValue);
978         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
979                                 value > BUTTON_THRESHOLD_VALUE, value);
980         break;
981       }
982       default:
983         NS_WARNING(
984             nsPrintfCString(
985                 "Axis idx '%d' doesn't support in XboxOneRemapper().", aAxis)
986                 .get());
987         break;
988     }
989   }
990 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const991   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
992                                 bool aPressed) const override {
993     RefPtr<GamepadPlatformService> service =
994         GamepadPlatformService::GetParentService();
995     if (!service) {
996       return;
997     }
998 
999     if (GetButtonCount() <= aButton) {
1000       NS_WARNING(
1001           nsPrintfCString(
1002               "Button idx '%d' doesn't support in XboxOneRemapper().", aButton)
1003               .get());
1004       return;
1005     }
1006 
1007     // Accessing {30, 31} buttons looks strange to me
1008     // and without an avilable device to help verify it.
1009     // It is according to `MapperXboxOneBluetooth()` in
1010     // https://cs.chromium.org/chromium/src/device/gamepad/gamepad_standard_mappings_mac.mm
1011     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
1012         {0, BUTTON_INDEX_PRIMARY},
1013         {1, BUTTON_INDEX_SECONDARY},
1014         {3, BUTTON_INDEX_TERTIARY},
1015         {4, BUTTON_INDEX_QUATERNARY},
1016         {6, BUTTON_INDEX_LEFT_SHOULDER},
1017         {7, BUTTON_INDEX_RIGHT_SHOULDER},
1018         {11, BUTTON_INDEX_START},
1019         {13, BUTTON_INDEX_LEFT_THUMBSTICK},
1020         {14, BUTTON_INDEX_RIGHT_THUMBSTICK},
1021         {30, BUTTON_INDEX_META},
1022         {31, BUTTON_INDEX_BACK_SELECT}};
1023 
1024     auto find = buttonMapping.find(aButton);
1025     if (find != buttonMapping.end()) {
1026       service->NewButtonEvent(aHandle, find->second, aPressed);
1027     } else {
1028       service->NewButtonEvent(aHandle, aButton, aPressed);
1029     }
1030   }
1031 };
1032 
1033 class LogitechDInputRemapper final : public GamepadRemapper {
1034  public:
GetAxisCount() const1035   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
1036 
GetButtonCount() const1037   virtual uint32_t GetButtonCount() const override {
1038     // The Logitech button (BUTTON_INDEX_META) is not accessible through the
1039     // device's D-mode.
1040     return BUTTON_INDEX_COUNT - 1;
1041   }
1042 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const1043   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
1044                                   double aValue) const override {
1045     RefPtr<GamepadPlatformService> service =
1046         GamepadPlatformService::GetParentService();
1047     if (!service) {
1048       return;
1049     }
1050 
1051     switch (aAxis) {
1052       case 0:
1053         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
1054         break;
1055       case 1:
1056         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
1057         break;
1058       case 2:
1059         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
1060         break;
1061       case 5:
1062         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
1063         break;
1064       case 9:
1065         FetchDpadFromAxis(aHandle, aValue);
1066         break;
1067       default:
1068         NS_WARNING(
1069             nsPrintfCString(
1070                 "Axis idx '%d' doesn't support in LogitechDInputRemapper().",
1071                 aAxis)
1072                 .get());
1073         break;
1074     }
1075   }
1076 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const1077   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
1078                                 bool aPressed) const override {
1079     RefPtr<GamepadPlatformService> service =
1080         GamepadPlatformService::GetParentService();
1081     if (!service) {
1082       return;
1083     }
1084 
1085     if (GetButtonCount() <= aButton) {
1086       NS_WARNING(
1087           nsPrintfCString(
1088               "Button idx '%d' doesn't support in LogitechDInputRemapper().",
1089               aButton)
1090               .get());
1091       return;
1092     }
1093 
1094     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
1095         {0, BUTTON_INDEX_TERTIARY},
1096         {1, BUTTON_INDEX_PRIMARY},
1097         {2, BUTTON_INDEX_SECONDARY}};
1098 
1099     auto find = buttonMapping.find(aButton);
1100     if (find != buttonMapping.end()) {
1101       service->NewButtonEvent(aHandle, find->second, aPressed);
1102     } else {
1103       service->NewButtonEvent(aHandle, aButton, aPressed);
1104     }
1105   }
1106 };
1107 
1108 class SwitchJoyConRemapper final : public GamepadRemapper {
1109  public:
GetAxisCount() const1110   virtual uint32_t GetAxisCount() const override { return 2; }
1111 
GetButtonCount() const1112   virtual uint32_t GetButtonCount() const override {
1113     return BUTTON_INDEX_COUNT;
1114   }
1115 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const1116   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
1117                                   double aValue) const override {
1118     if (GetAxisCount() <= aAxis) {
1119       NS_WARNING(
1120           nsPrintfCString(
1121               "Axis idx '%d' doesn't support in SwitchJoyConRemapper().", aAxis)
1122               .get());
1123       return;
1124     }
1125     RefPtr<GamepadPlatformService> service =
1126         GamepadPlatformService::GetParentService();
1127     if (!service) {
1128       return;
1129     }
1130 
1131     service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
1132   }
1133 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const1134   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
1135                                 bool aPressed) const override {
1136     RefPtr<GamepadPlatformService> service =
1137         GamepadPlatformService::GetParentService();
1138     if (!service) {
1139       return;
1140     }
1141 
1142     service->NewButtonEvent(aHandle, aButton, aPressed);
1143   }
1144 };
1145 
1146 class SwitchProRemapper final : public GamepadRemapper {
1147  public:
GetAxisCount() const1148   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
1149 
GetButtonCount() const1150   virtual uint32_t GetButtonCount() const override {
1151     // The Switch Pro controller has a Capture button that has no equivalent in
1152     // the Standard Gamepad.
1153     return SWITCHPRO_BUTTON_COUNT;
1154   }
1155 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const1156   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
1157                                   double aValue) const override {
1158     if (GetAxisCount() <= aAxis) {
1159       NS_WARNING(
1160           nsPrintfCString(
1161               "Axis idx '%d' doesn't support in SwitchProRemapper().", aAxis)
1162               .get());
1163       return;
1164     }
1165     RefPtr<GamepadPlatformService> service =
1166         GamepadPlatformService::GetParentService();
1167     if (!service) {
1168       return;
1169     }
1170 
1171     service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
1172   }
1173 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const1174   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
1175                                 bool aPressed) const override {
1176     RefPtr<GamepadPlatformService> service =
1177         GamepadPlatformService::GetParentService();
1178     if (!service) {
1179       return;
1180     }
1181 
1182     service->NewButtonEvent(aHandle, aButton, aPressed);
1183   }
1184 
1185  private:
1186   enum SwitchProButtons {
1187     SWITCHPRO_BUTTON_EXTRA = BUTTON_INDEX_COUNT,
1188     SWITCHPRO_BUTTON_COUNT
1189   };
1190 };
1191 
1192 class NvShieldRemapper final : public GamepadRemapper {
1193  public:
GetAxisCount() const1194   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
1195 
GetButtonCount() const1196   virtual uint32_t GetButtonCount() const override {
1197     return SHIELD_BUTTON_COUNT;
1198   }
1199 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const1200   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
1201                                   double aValue) const override {
1202     RefPtr<GamepadPlatformService> service =
1203         GamepadPlatformService::GetParentService();
1204     if (!service) {
1205       return;
1206     }
1207 
1208     switch (aAxis) {
1209       case 0:
1210         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
1211         break;
1212       case 1:
1213         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
1214         break;
1215       case 2:
1216         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
1217         break;
1218       case 3: {
1219         const double value = AxisToButtonValue(aValue);
1220         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
1221                                 value > BUTTON_THRESHOLD_VALUE, value);
1222         break;
1223       }
1224       case 4: {
1225         const double value = AxisToButtonValue(aValue);
1226         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
1227                                 value > BUTTON_THRESHOLD_VALUE, value);
1228         break;
1229       }
1230       case 5:
1231         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
1232         break;
1233       case 9:
1234         FetchDpadFromAxis(aHandle, aValue);
1235         break;
1236       default:
1237         NS_WARNING(
1238             nsPrintfCString(
1239                 "Axis idx '%d' doesn't support in NvShieldRemapper().", aAxis)
1240                 .get());
1241         break;
1242     }
1243   }
1244 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const1245   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
1246                                 bool aPressed) const override {
1247     RefPtr<GamepadPlatformService> service =
1248         GamepadPlatformService::GetParentService();
1249     if (!service) {
1250       return;
1251     }
1252 
1253     if (GetButtonCount() <= aButton) {
1254       NS_WARNING(
1255           nsPrintfCString(
1256               "Button idx '%d' doesn't support in NvShieldRemapper().", aButton)
1257               .get());
1258       return;
1259     }
1260 
1261     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
1262         {2, BUTTON_INDEX_META},
1263         {3, BUTTON_INDEX_TERTIARY},
1264         {4, BUTTON_INDEX_QUATERNARY},
1265         {5, SHIELD_BUTTON_CIRCLE},
1266         {6, BUTTON_INDEX_LEFT_SHOULDER},
1267         {7, BUTTON_INDEX_RIGHT_SHOULDER},
1268         {9, BUTTON_INDEX_BACK_SELECT},
1269         {11, BUTTON_INDEX_START},
1270         {13, BUTTON_INDEX_LEFT_THUMBSTICK},
1271         {14, BUTTON_INDEX_RIGHT_THUMBSTICK}};
1272 
1273     auto find = buttonMapping.find(aButton);
1274     if (find != buttonMapping.end()) {
1275       service->NewButtonEvent(aHandle, find->second, aPressed);
1276     } else {
1277       service->NewButtonEvent(aHandle, aButton, aPressed);
1278     }
1279   }
1280 
1281  private:
1282   enum ShieldButtons {
1283     SHIELD_BUTTON_CIRCLE = BUTTON_INDEX_COUNT,
1284     SHIELD_BUTTON_COUNT
1285   };
1286 };
1287 
1288 class NvShield2017Remapper final : public GamepadRemapper {
1289  public:
GetAxisCount() const1290   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
1291 
GetButtonCount() const1292   virtual uint32_t GetButtonCount() const override {
1293     return SHIELD2017_BUTTON_COUNT;
1294   }
1295 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const1296   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
1297                                   double aValue) const override {
1298     RefPtr<GamepadPlatformService> service =
1299         GamepadPlatformService::GetParentService();
1300     if (!service) {
1301       return;
1302     }
1303 
1304     switch (aAxis) {
1305       case 0:
1306         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
1307         break;
1308       case 1:
1309         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
1310         break;
1311       case 2:
1312         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
1313         break;
1314       case 3: {
1315         const double value = AxisToButtonValue(aValue);
1316         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
1317                                 value > BUTTON_THRESHOLD_VALUE, value);
1318         break;
1319       }
1320       case 4: {
1321         const double value = AxisToButtonValue(aValue);
1322         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
1323                                 value > BUTTON_THRESHOLD_VALUE, value);
1324         break;
1325       }
1326       case 5:
1327         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
1328         break;
1329       case 9:
1330         FetchDpadFromAxis(aHandle, aValue);
1331         break;
1332       default:
1333         NS_WARNING(
1334             nsPrintfCString(
1335                 "Axis idx '%d' doesn't support in NvShield2017Remapper().",
1336                 aAxis)
1337                 .get());
1338         break;
1339     }
1340   }
1341 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const1342   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
1343                                 bool aPressed) const override {
1344     RefPtr<GamepadPlatformService> service =
1345         GamepadPlatformService::GetParentService();
1346     if (!service) {
1347       return;
1348     }
1349 
1350     if (GetButtonCount() <= aButton) {
1351       NS_WARNING(
1352           nsPrintfCString(
1353               "Button idx '%d' doesn't support in NvShield2017Remapper().",
1354               aButton)
1355               .get());
1356       return;
1357     }
1358 
1359     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
1360         {2, BUTTON_INDEX_META},
1361         {3, BUTTON_INDEX_TERTIARY},
1362         {4, BUTTON_INDEX_QUATERNARY},
1363         {5, BUTTON_INDEX_START},
1364         {6, BUTTON_INDEX_LEFT_SHOULDER},
1365         {7, BUTTON_INDEX_RIGHT_SHOULDER},
1366         {8, BUTTON_INDEX_BACK_SELECT},
1367         {11, SHIELD2017_BUTTON_PLAYPAUSE},
1368         {13, BUTTON_INDEX_LEFT_THUMBSTICK},
1369         {14, BUTTON_INDEX_RIGHT_THUMBSTICK}};
1370 
1371     auto find = buttonMapping.find(aButton);
1372     if (find != buttonMapping.end()) {
1373       service->NewButtonEvent(aHandle, find->second, aPressed);
1374     } else {
1375       service->NewButtonEvent(aHandle, aButton, aPressed);
1376     }
1377   }
1378 
1379  private:
1380   enum Shield2017Buttons {
1381     SHIELD2017_BUTTON_PLAYPAUSE = BUTTON_INDEX_COUNT,
1382     SHIELD2017_BUTTON_COUNT
1383   };
1384 };
1385 
1386 class IBuffaloRemapper final : public GamepadRemapper {
1387  public:
GetAxisCount() const1388   virtual uint32_t GetAxisCount() const override { return 2; }
1389 
GetButtonCount() const1390   virtual uint32_t GetButtonCount() const override {
1391     return BUTTON_INDEX_COUNT - 1; /* no meta */
1392   }
1393 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const1394   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
1395                                   double aValue) const override {
1396     RefPtr<GamepadPlatformService> service =
1397         GamepadPlatformService::GetParentService();
1398     if (!service) {
1399       return;
1400     }
1401 
1402     switch (aAxis) {
1403       case 0:
1404         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
1405         service->NewButtonEvent(aHandle, BUTTON_INDEX_DPAD_LEFT,
1406                                 AxisNegativeAsButton(aValue));
1407         service->NewButtonEvent(aHandle, BUTTON_INDEX_DPAD_RIGHT,
1408                                 AxisPositiveAsButton(aValue));
1409         break;
1410       case 1:
1411         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
1412         service->NewButtonEvent(aHandle, BUTTON_INDEX_DPAD_UP,
1413                                 AxisNegativeAsButton(aValue));
1414         service->NewButtonEvent(aHandle, BUTTON_INDEX_DPAD_DOWN,
1415                                 AxisPositiveAsButton(aValue));
1416         break;
1417       default:
1418         NS_WARNING(
1419             nsPrintfCString(
1420                 "Axis idx '%d' doesn't support in IBuffaloRemapper().", aAxis)
1421                 .get());
1422         break;
1423     }
1424   }
1425 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const1426   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
1427                                 bool aPressed) const override {
1428     RefPtr<GamepadPlatformService> service =
1429         GamepadPlatformService::GetParentService();
1430     if (!service) {
1431       return;
1432     }
1433 
1434     if (GetButtonCount() <= aButton) {
1435       NS_WARNING(
1436           nsPrintfCString(
1437               "Button idx '%d' doesn't support in IBuffaloRemapper().", aButton)
1438               .get());
1439       return;
1440     }
1441 
1442     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
1443         {0, BUTTON_INDEX_SECONDARY},     {1, BUTTON_INDEX_PRIMARY},
1444         {2, BUTTON_INDEX_QUATERNARY},    {3, BUTTON_INDEX_TERTIARY},
1445         {5, BUTTON_INDEX_RIGHT_TRIGGER}, {6, BUTTON_INDEX_BACK_SELECT},
1446         {7, BUTTON_INDEX_START}};
1447 
1448     auto find = buttonMapping.find(aButton);
1449     if (find != buttonMapping.end()) {
1450       service->NewButtonEvent(aHandle, find->second, aPressed);
1451     } else {
1452       service->NewButtonEvent(aHandle, aButton, aPressed);
1453     }
1454   }
1455 };
1456 
1457 class XSkillsRemapper final : public GamepadRemapper {
1458  public:
GetAxisCount() const1459   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
1460 
GetButtonCount() const1461   virtual uint32_t GetButtonCount() const override {
1462     return GAMECUBE_BUTTON_COUNT;
1463   }
1464 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const1465   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
1466                                   double aValue) const override {
1467     RefPtr<GamepadPlatformService> service =
1468         GamepadPlatformService::GetParentService();
1469     if (!service) {
1470       return;
1471     }
1472 
1473     switch (aAxis) {
1474       case 0:
1475         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
1476         break;
1477       case 1:
1478         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
1479         break;
1480       case 2:
1481         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
1482         break;
1483       case 3: {
1484         const double value = AxisToButtonValue(aValue);
1485         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
1486                                 value > BUTTON_THRESHOLD_VALUE, value);
1487         break;
1488       }
1489       case 4: {
1490         const double value = AxisToButtonValue(aValue);
1491         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
1492                                 value > BUTTON_THRESHOLD_VALUE, value);
1493         break;
1494       }
1495       case 5:
1496         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
1497         break;
1498       default:
1499         NS_WARNING(
1500             nsPrintfCString(
1501                 "Axis idx '%d' doesn't support in XSkillsRemapper().", aAxis)
1502                 .get());
1503         break;
1504     }
1505   }
1506 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const1507   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
1508                                 bool aPressed) const override {
1509     RefPtr<GamepadPlatformService> service =
1510         GamepadPlatformService::GetParentService();
1511     if (!service) {
1512       return;
1513     }
1514 
1515     if (GetButtonCount() <= aButton) {
1516       NS_WARNING(
1517           nsPrintfCString(
1518               "Button idx '%d' doesn't support in XSkillsRemapper().", aButton)
1519               .get());
1520       return;
1521     }
1522 
1523     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
1524         {0, BUTTON_INDEX_PRIMARY},     // A
1525         {1, BUTTON_INDEX_TERTIARY},    // B
1526         {2, BUTTON_INDEX_SECONDARY},   // X
1527         {3, BUTTON_INDEX_QUATERNARY},  // Y
1528         {4, GAMECUBE_BUTTON_LEFT_TRIGGER_CLICK},
1529         {5, GAMECUBE_BUTTON_RIGHT_TRIGGER_CLICK},
1530         {6, BUTTON_INDEX_RIGHT_SHOULDER},
1531         {7, BUTTON_INDEX_START},
1532         {8, BUTTON_INDEX_DPAD_LEFT},
1533         {9, BUTTON_INDEX_DPAD_RIGHT},
1534         {10, BUTTON_INDEX_DPAD_DOWN},
1535         {11, BUTTON_INDEX_DPAD_UP}};
1536 
1537     auto find = buttonMapping.find(aButton);
1538     if (find != buttonMapping.end()) {
1539       service->NewButtonEvent(aHandle, find->second, aPressed);
1540     } else {
1541       service->NewButtonEvent(aHandle, aButton, aPressed);
1542     }
1543   }
1544 
1545  private:
1546   enum GamecubeButtons {
1547     GAMECUBE_BUTTON_LEFT_TRIGGER_CLICK = BUTTON_INDEX_COUNT,
1548     GAMECUBE_BUTTON_RIGHT_TRIGGER_CLICK,
1549     GAMECUBE_BUTTON_COUNT
1550   };
1551 };
1552 
1553 class BoomN64PsxRemapper final : public GamepadRemapper {
1554  public:
GetAxisCount() const1555   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
1556 
GetButtonCount() const1557   virtual uint32_t GetButtonCount() const override {
1558     return BUTTON_INDEX_COUNT - 1;  // no meta
1559   }
1560 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const1561   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
1562                                   double aValue) const override {
1563     RefPtr<GamepadPlatformService> service =
1564         GamepadPlatformService::GetParentService();
1565     if (!service) {
1566       return;
1567     }
1568 
1569     switch (aAxis) {
1570       case 0:
1571         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
1572         break;
1573       case 1:
1574         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
1575         break;
1576       case 2:
1577         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
1578         break;
1579       case 5:
1580         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
1581         break;
1582       default:
1583         NS_WARNING(
1584             nsPrintfCString(
1585                 "Axis idx '%d' doesn't support in BoomN64PsxRemapper().", aAxis)
1586                 .get());
1587         break;
1588     }
1589   }
1590 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const1591   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
1592                                 bool aPressed) const override {
1593     RefPtr<GamepadPlatformService> service =
1594         GamepadPlatformService::GetParentService();
1595     if (!service) {
1596       return;
1597     }
1598 
1599     const std::vector<uint32_t> buttonMapping = {
1600         BUTTON_INDEX_QUATERNARY,       BUTTON_INDEX_SECONDARY,
1601         BUTTON_INDEX_PRIMARY,          BUTTON_INDEX_TERTIARY,
1602         BUTTON_INDEX_LEFT_TRIGGER,     BUTTON_INDEX_RIGHT_TRIGGER,
1603         BUTTON_INDEX_LEFT_SHOULDER,    BUTTON_INDEX_RIGHT_SHOULDER,
1604         BUTTON_INDEX_BACK_SELECT,      BUTTON_INDEX_LEFT_THUMBSTICK,
1605         BUTTON_INDEX_RIGHT_THUMBSTICK, BUTTON_INDEX_START,
1606         BUTTON_INDEX_DPAD_UP,          BUTTON_INDEX_DPAD_RIGHT,
1607         BUTTON_INDEX_DPAD_DOWN,        BUTTON_INDEX_DPAD_LEFT};
1608 
1609     if (buttonMapping.size() <= aButton) {
1610       NS_WARNING(nsPrintfCString(
1611                      "Button idx '%d' doesn't support in BoomN64PsxRemapper().",
1612                      aButton)
1613                      .get());
1614       return;
1615     }
1616 
1617     service->NewButtonEvent(aHandle, buttonMapping[aButton], aPressed);
1618   }
1619 
1620  private:
1621   enum GamecubeButtons {
1622     GAMECUBE_BUTTON_LEFT_TRIGGER_CLICK = BUTTON_INDEX_COUNT,
1623     GAMECUBE_BUTTON_RIGHT_TRIGGER_CLICK,
1624     GAMECUBE_BUTTON_COUNT
1625   };
1626 };
1627 
1628 class StadiaControllerOldFirmwareRemapper final : public GamepadRemapper {
1629  public:
GetAxisCount() const1630   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
1631 
GetButtonCount() const1632   virtual uint32_t GetButtonCount() const override {
1633     return ANALOG_GAMEPAD_BUTTON_COUNT;
1634   }
1635 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const1636   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
1637                                   double aValue) const override {
1638     RefPtr<GamepadPlatformService> service =
1639         GamepadPlatformService::GetParentService();
1640     if (!service) {
1641       return;
1642     }
1643 
1644     switch (aAxis) {
1645       case 0:
1646         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
1647         break;
1648       case 1:
1649         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
1650         break;
1651       case 2:
1652         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
1653         break;
1654       case 3: {
1655         const double value = AxisToButtonValue(aValue);
1656         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
1657                                 value > BUTTON_THRESHOLD_VALUE, value);
1658         break;
1659       }
1660       case 4: {
1661         const double value = AxisToButtonValue(aValue);
1662         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
1663                                 value > BUTTON_THRESHOLD_VALUE, value);
1664         break;
1665       }
1666       case 5:
1667         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
1668         break;
1669       case 9:
1670         FetchDpadFromAxis(aHandle, aValue);
1671         break;
1672       default:
1673         NS_WARNING(
1674             nsPrintfCString(
1675                 "Axis idx '%d' doesn't support in AnalogGamepadRemapper().",
1676                 aAxis)
1677                 .get());
1678         break;
1679     }
1680   }
1681 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const1682   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
1683                                 bool aPressed) const override {
1684     RefPtr<GamepadPlatformService> service =
1685         GamepadPlatformService::GetParentService();
1686     if (!service) {
1687       return;
1688     }
1689 
1690     if (GetButtonCount() <= aButton) {
1691       NS_WARNING(
1692           nsPrintfCString(
1693               "Button idx '%d' doesn't support in AnalogGamepadRemapper().",
1694               aButton)
1695               .get());
1696       return;
1697     }
1698 
1699     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
1700         {3, BUTTON_INDEX_TERTIARY},
1701         {4, BUTTON_INDEX_QUATERNARY},
1702         {6, BUTTON_INDEX_LEFT_SHOULDER},
1703         {7, BUTTON_INDEX_RIGHT_SHOULDER},
1704         {10, BUTTON_INDEX_BACK_SELECT},
1705         {11, BUTTON_INDEX_META},
1706         {12, BUTTON_INDEX_START},
1707         {13, BUTTON_INDEX_LEFT_THUMBSTICK},
1708         {14, BUTTON_INDEX_RIGHT_THUMBSTICK},
1709         {16, ANALOG_GAMEPAD_BUTTON_EXTRA},
1710         {17, ANALOG_GAMEPAD_BUTTON_EXTRA2}};
1711 
1712     auto find = buttonMapping.find(aButton);
1713     if (find != buttonMapping.end()) {
1714       service->NewButtonEvent(aHandle, find->second, aPressed);
1715     } else {
1716       service->NewButtonEvent(aHandle, aButton, aPressed);
1717     }
1718   }
1719 
1720  private:
1721   enum AnalogGamepadButtons {
1722     ANALOG_GAMEPAD_BUTTON_EXTRA = BUTTON_INDEX_COUNT,
1723     ANALOG_GAMEPAD_BUTTON_EXTRA2,
1724     ANALOG_GAMEPAD_BUTTON_COUNT
1725   };
1726 };
1727 
1728 class RazerServalRemapper final : public GamepadRemapper {
1729  public:
GetAxisCount() const1730   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
1731 
GetButtonCount() const1732   virtual uint32_t GetButtonCount() const override {
1733     return BUTTON_INDEX_COUNT - 1; /* no meta */
1734   }
1735 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const1736   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
1737                                   double aValue) const override {
1738     RefPtr<GamepadPlatformService> service =
1739         GamepadPlatformService::GetParentService();
1740     if (!service) {
1741       return;
1742     }
1743 
1744     switch (aAxis) {
1745       case 0:
1746         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
1747         break;
1748       case 1:
1749         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
1750         break;
1751       case 2:
1752         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
1753         break;
1754       case 3: {
1755         const double value = AxisToButtonValue(aValue);
1756         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
1757                                 value > BUTTON_THRESHOLD_VALUE, value);
1758         break;
1759       }
1760       case 4: {
1761         const double value = AxisToButtonValue(aValue);
1762         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
1763                                 value > BUTTON_THRESHOLD_VALUE, value);
1764         break;
1765       }
1766       case 5:
1767         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
1768         break;
1769       case 9:
1770         FetchDpadFromAxis(aHandle, aValue);
1771         break;
1772       default:
1773         NS_WARNING(
1774             nsPrintfCString(
1775                 "Axis idx '%d' doesn't support in RazerServalRemapper().",
1776                 aAxis)
1777                 .get());
1778         break;
1779     }
1780   }
1781 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const1782   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
1783                                 bool aPressed) const override {
1784     RefPtr<GamepadPlatformService> service =
1785         GamepadPlatformService::GetParentService();
1786     if (!service) {
1787       return;
1788     }
1789 
1790     if (GetButtonCount() <= aButton) {
1791       NS_WARNING(
1792           nsPrintfCString(
1793               "Button idx '%d' doesn't support in RazerServalRemapper().",
1794               aButton)
1795               .get());
1796       return;
1797     }
1798 
1799     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
1800         {3, BUTTON_INDEX_TERTIARY},         {4, BUTTON_INDEX_QUATERNARY},
1801         {6, BUTTON_INDEX_LEFT_SHOULDER},    {7, BUTTON_INDEX_RIGHT_SHOULDER},
1802         {10, BUTTON_INDEX_BACK_SELECT},     {11, BUTTON_INDEX_START},
1803         {12, BUTTON_INDEX_START},           {13, BUTTON_INDEX_LEFT_THUMBSTICK},
1804         {14, BUTTON_INDEX_RIGHT_THUMBSTICK}};
1805 
1806     auto find = buttonMapping.find(aButton);
1807     if (find != buttonMapping.end()) {
1808       service->NewButtonEvent(aHandle, find->second, aPressed);
1809     } else {
1810       service->NewButtonEvent(aHandle, aButton, aPressed);
1811     }
1812   }
1813 };
1814 
1815 class MogaProRemapper final : public GamepadRemapper {
1816  public:
GetAxisCount() const1817   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
1818 
GetButtonCount() const1819   virtual uint32_t GetButtonCount() const override {
1820     return BUTTON_INDEX_COUNT - 1; /* no meta */
1821   }
1822 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const1823   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
1824                                   double aValue) const override {
1825     RefPtr<GamepadPlatformService> service =
1826         GamepadPlatformService::GetParentService();
1827     if (!service) {
1828       return;
1829     }
1830 
1831     switch (aAxis) {
1832       case 0:
1833         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
1834         break;
1835       case 1:
1836         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
1837         break;
1838       case 2:
1839         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
1840         break;
1841       case 3: {
1842         const double value = AxisToButtonValue(aValue);
1843         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
1844                                 value > BUTTON_THRESHOLD_VALUE, value);
1845         break;
1846       }
1847       case 4: {
1848         const double value = AxisToButtonValue(aValue);
1849         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
1850                                 value > BUTTON_THRESHOLD_VALUE, value);
1851         break;
1852       }
1853       case 5:
1854         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
1855         break;
1856       case 9:
1857         FetchDpadFromAxis(aHandle, aValue);
1858         break;
1859       default:
1860         NS_WARNING(
1861             nsPrintfCString(
1862                 "Axis idx '%d' doesn't support in MogaProRemapper().", aAxis)
1863                 .get());
1864         break;
1865     }
1866   }
1867 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const1868   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
1869                                 bool aPressed) const override {
1870     RefPtr<GamepadPlatformService> service =
1871         GamepadPlatformService::GetParentService();
1872     if (!service) {
1873       return;
1874     }
1875 
1876     if (GetButtonCount() <= aButton) {
1877       NS_WARNING(
1878           nsPrintfCString(
1879               "Button idx '%d' doesn't support in MogaProRemapper().", aButton)
1880               .get());
1881       return;
1882     }
1883 
1884     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
1885         {3, BUTTON_INDEX_TERTIARY},         {4, BUTTON_INDEX_QUATERNARY},
1886         {6, BUTTON_INDEX_LEFT_SHOULDER},    {7, BUTTON_INDEX_RIGHT_SHOULDER},
1887         {11, BUTTON_INDEX_START},           {13, BUTTON_INDEX_LEFT_THUMBSTICK},
1888         {14, BUTTON_INDEX_RIGHT_THUMBSTICK}};
1889 
1890     auto find = buttonMapping.find(aButton);
1891     if (find != buttonMapping.end()) {
1892       service->NewButtonEvent(aHandle, find->second, aPressed);
1893     } else {
1894       service->NewButtonEvent(aHandle, aButton, aPressed);
1895     }
1896   }
1897 };
1898 
1899 class OnLiveWirelessRemapper final : public GamepadRemapper {
1900  public:
GetAxisCount() const1901   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
1902 
GetButtonCount() const1903   virtual uint32_t GetButtonCount() const override {
1904     return BUTTON_INDEX_COUNT;
1905   }
1906 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const1907   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
1908                                   double aValue) const override {
1909     RefPtr<GamepadPlatformService> service =
1910         GamepadPlatformService::GetParentService();
1911     if (!service) {
1912       return;
1913     }
1914 
1915     switch (aAxis) {
1916       case 0:
1917         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
1918         break;
1919       case 1:
1920         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
1921         break;
1922       case 2: {
1923         const double value = AxisToButtonValue(aValue);
1924         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
1925                                 value > BUTTON_THRESHOLD_VALUE, value);
1926         break;
1927       }
1928       case 3:
1929         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
1930         break;
1931       case 4:
1932         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
1933         break;
1934       case 5: {
1935         const double value = AxisToButtonValue(aValue);
1936         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
1937                                 value > BUTTON_THRESHOLD_VALUE, value);
1938         break;
1939       }
1940       case 9:
1941         FetchDpadFromAxis(aHandle, aValue);
1942         break;
1943       default:
1944         NS_WARNING(
1945             nsPrintfCString(
1946                 "Axis idx '%d' doesn't support in OnLiveWirelessRemapper().",
1947                 aAxis)
1948                 .get());
1949         break;
1950     }
1951   }
1952 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const1953   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
1954                                 bool aPressed) const override {
1955     RefPtr<GamepadPlatformService> service =
1956         GamepadPlatformService::GetParentService();
1957     if (!service) {
1958       return;
1959     }
1960 
1961     if (GetButtonCount() <= aButton) {
1962       NS_WARNING(
1963           nsPrintfCString(
1964               "Button idx '%d' doesn't support in OnLiveWirelessRemapper().",
1965               aButton)
1966               .get());
1967       return;
1968     }
1969 
1970     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
1971         {3, BUTTON_INDEX_TERTIARY},
1972         {4, BUTTON_INDEX_QUATERNARY},
1973         {6, BUTTON_INDEX_LEFT_SHOULDER},
1974         {7, BUTTON_INDEX_RIGHT_SHOULDER},
1975         {12, BUTTON_INDEX_META},
1976         {13, BUTTON_INDEX_LEFT_THUMBSTICK},
1977         {14, BUTTON_INDEX_RIGHT_THUMBSTICK}};
1978 
1979     auto find = buttonMapping.find(aButton);
1980     if (find != buttonMapping.end()) {
1981       service->NewButtonEvent(aHandle, find->second, aPressed);
1982     } else {
1983       service->NewButtonEvent(aHandle, aButton, aPressed);
1984     }
1985   }
1986 };
1987 
1988 class OUYARemapper final : public GamepadRemapper {
1989  public:
GetAxisCount() const1990   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
1991 
GetButtonCount() const1992   virtual uint32_t GetButtonCount() const override {
1993     return BUTTON_INDEX_COUNT;
1994   }
1995 
RemapAxisMoveEvent(GamepadHandle aHandle,uint32_t aAxis,double aValue) const1996   virtual void RemapAxisMoveEvent(GamepadHandle aHandle, uint32_t aAxis,
1997                                   double aValue) const override {
1998     RefPtr<GamepadPlatformService> service =
1999         GamepadPlatformService::GetParentService();
2000     if (!service) {
2001       return;
2002     }
2003 
2004     switch (aAxis) {
2005       case 0:
2006         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_X, aValue);
2007         break;
2008       case 1:
2009         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_LEFT_STICK_Y, aValue);
2010         break;
2011       case 2: {
2012         const double value = AxisToButtonValue(aValue);
2013         service->NewButtonEvent(aHandle, BUTTON_INDEX_LEFT_TRIGGER,
2014                                 value > BUTTON_THRESHOLD_VALUE, value);
2015         break;
2016       }
2017       case 3:
2018         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_X, aValue);
2019         break;
2020       case 4:
2021         service->NewAxisMoveEvent(aHandle, AXIS_INDEX_RIGHT_STICK_Y, aValue);
2022         break;
2023       case 5: {
2024         const double value = AxisToButtonValue(aValue);
2025         service->NewButtonEvent(aHandle, BUTTON_INDEX_RIGHT_TRIGGER,
2026                                 value > BUTTON_THRESHOLD_VALUE, value);
2027         break;
2028       }
2029       default:
2030         NS_WARNING(
2031             nsPrintfCString("Axis idx '%d' doesn't support in OUYARemapper().",
2032                             aAxis)
2033                 .get());
2034         break;
2035     }
2036   }
2037 
RemapButtonEvent(GamepadHandle aHandle,uint32_t aButton,bool aPressed) const2038   virtual void RemapButtonEvent(GamepadHandle aHandle, uint32_t aButton,
2039                                 bool aPressed) const override {
2040     RefPtr<GamepadPlatformService> service =
2041         GamepadPlatformService::GetParentService();
2042     if (!service) {
2043       return;
2044     }
2045 
2046     if (GetButtonCount() <= aButton) {
2047       NS_WARNING(
2048           nsPrintfCString("Button idx '%d' doesn't support in OUYARemapper().",
2049                           aButton)
2050               .get());
2051       return;
2052     }
2053 
2054     const std::unordered_map<uint32_t, uint32_t> buttonMapping = {
2055         {1, BUTTON_INDEX_TERTIARY},         {2, BUTTON_INDEX_QUATERNARY},
2056         {3, BUTTON_INDEX_SECONDARY},        {6, BUTTON_INDEX_LEFT_THUMBSTICK},
2057         {7, BUTTON_INDEX_RIGHT_THUMBSTICK}, {8, BUTTON_INDEX_DPAD_UP},
2058         {9, BUTTON_INDEX_DPAD_DOWN},        {10, BUTTON_INDEX_DPAD_LEFT},
2059         {11, BUTTON_INDEX_DPAD_RIGHT},      {15, BUTTON_INDEX_META}};
2060 
2061     auto find = buttonMapping.find(aButton);
2062     if (find != buttonMapping.end()) {
2063       service->NewButtonEvent(aHandle, find->second, aPressed);
2064     } else {
2065       service->NewButtonEvent(aHandle, aButton, aPressed);
2066     }
2067   }
2068 };
2069 
GetGamepadRemapper(const uint16_t aVendorId,const uint16_t aProductId,bool & aUsingDefault)2070 already_AddRefed<GamepadRemapper> GetGamepadRemapper(const uint16_t aVendorId,
2071                                                      const uint16_t aProductId,
2072                                                      bool& aUsingDefault) {
2073   const std::vector<GamepadRemappingData> remappingRules = {
2074       {GamepadId::kAsusTekProduct4500, new ADT1Remapper()},
2075       {GamepadId::kDragonRiseProduct0011, new TwoAxesEightKeysRemapper()},
2076       {GamepadId::kGoogleProduct2c40, new ADT1Remapper()},
2077       {GamepadId::kGoogleProduct9400, new StadiaControllerRemapper()},
2078       {GamepadId::kLogitechProductc216, new LogitechDInputRemapper()},
2079       {GamepadId::kLogitechProductc218, new LogitechDInputRemapper()},
2080       {GamepadId::kLogitechProductc219, new LogitechDInputRemapper()},
2081       {GamepadId::kMicrosoftProductXbox360Wireless, new Xbox360Remapper()},
2082       {GamepadId::kMicrosoftProductXbox360Wireless2, new Xbox360Remapper()},
2083       {GamepadId::kMicrosoftProductXboxOneElite2Wireless,
2084        new XboxOneRemapper()},
2085       {GamepadId::kMicrosoftProductXboxOneSWireless, new XboxOneSRemapper()},
2086       {GamepadId::kMicrosoftProductXboxOneSWireless2016,
2087        new XboxOneS2016FirmwareRemapper()},
2088       {GamepadId::kMicrosoftProductXboxAdaptiveWireless, new XboxOneRemapper()},
2089       {GamepadId::kNintendoProduct2006, new SwitchJoyConRemapper()},
2090       {GamepadId::kNintendoProduct2007, new SwitchJoyConRemapper()},
2091       {GamepadId::kNintendoProduct2009, new SwitchProRemapper()},
2092       {GamepadId::kNintendoProduct200e, new SwitchProRemapper()},
2093       {GamepadId::kNvidiaProduct7210, new NvShieldRemapper()},
2094       {GamepadId::kNvidiaProduct7214, new NvShield2017Remapper()},
2095       {GamepadId::kPadixProduct2060, new IBuffaloRemapper()},
2096       {GamepadId::kPlayComProduct0005, new XSkillsRemapper()},
2097       {GamepadId::kPrototypeVendorProduct0667, new BoomN64PsxRemapper()},
2098       {GamepadId::kPrototypeVendorProduct9401,
2099        new StadiaControllerOldFirmwareRemapper()},
2100       {GamepadId::kRazer1532Product0900, new RazerServalRemapper()},
2101       {GamepadId::kSonyProduct0268, new Playstation3Remapper()},
2102       {GamepadId::kSonyProduct05c4, new Dualshock4Remapper()},
2103       {GamepadId::kSonyProduct09cc, new Dualshock4Remapper()},
2104       {GamepadId::kSonyProduct0ba0, new Dualshock4Remapper()},
2105       {GamepadId::kVendor20d6Product6271, new MogaProRemapper()},
2106       {GamepadId::kVendor2378Product1008, new OnLiveWirelessRemapper()},
2107       {GamepadId::kVendor2378Product100a, new OnLiveWirelessRemapper()},
2108       {GamepadId::kVendor2836Product0001, new OUYARemapper()}};
2109   const GamepadId id = static_cast<GamepadId>((aVendorId << 16) | aProductId);
2110 
2111   for (uint32_t i = 0; i < remappingRules.size(); ++i) {
2112     if (id == remappingRules[i].id) {
2113       aUsingDefault = false;
2114       return do_AddRef(remappingRules[i].remapping.get());
2115     }
2116   }
2117 
2118   RefPtr<GamepadRemapper> defaultRemapper = new DefaultRemapper();
2119   aUsingDefault = true;
2120   return do_AddRef(defaultRemapper.get());
2121 }
2122 
2123 }  // namespace mozilla::dom
2124