tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

GamepadRemapping.cpp (71952B)


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