diff options
| -rw-r--r-- | src/core/frontend/applets/controller.h | 1 | ||||
| -rw-r--r-- | src/core/hle/service/hid/controllers/npad.cpp | 63 | ||||
| -rw-r--r-- | src/core/hle/service/hid/controllers/npad.h | 26 | ||||
| -rw-r--r-- | src/input_common/settings.h | 1 | ||||
| -rw-r--r-- | src/yuzu/applets/controller.cpp | 9 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input_player.cpp | 106 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_input_player.h | 6 | 
7 files changed, 206 insertions, 6 deletions
| diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h index dff71d8d9..b0626a0f9 100644 --- a/src/core/frontend/applets/controller.h +++ b/src/core/frontend/applets/controller.h @@ -31,6 +31,7 @@ struct ControllerParameters {      bool allow_dual_joycons{};      bool allow_left_joycon{};      bool allow_right_joycon{}; +    bool allow_gamecube_controller{};  };  class ControllerApplet { diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index dbf198345..70b9f3824 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -21,6 +21,7 @@  namespace Service::HID {  constexpr s32 HID_JOYSTICK_MAX = 0x7fff; +constexpr s32 HID_TRIGGER_MAX = 0x7fff;  [[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;  constexpr std::size_t NPAD_OFFSET = 0x9A00;  constexpr u32 BATTERY_FULL = 2; @@ -48,6 +49,8 @@ Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(          return NPadControllerType::JoyRight;      case Settings::ControllerType::Handheld:          return NPadControllerType::Handheld; +    case Settings::ControllerType::GameCube: +        return NPadControllerType::GameCube;      default:          UNREACHABLE();          return NPadControllerType::ProController; @@ -67,6 +70,8 @@ Settings::ControllerType Controller_NPad::MapNPadToSettingsType(          return Settings::ControllerType::RightJoycon;      case NPadControllerType::Handheld:          return Settings::ControllerType::Handheld; +    case NPadControllerType::GameCube: +        return Settings::ControllerType::GameCube;      default:          UNREACHABLE();          return Settings::ControllerType::ProController; @@ -209,6 +214,13 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {          controller.assignment_mode = NpadAssignments::Single;          controller.footer_type = AppletFooterUiType::JoyRightHorizontal;          break; +    case NPadControllerType::GameCube: +        controller.style_set.gamecube.Assign(1); +        // The GC Controller behaves like a wired Pro Controller +        controller.device_type.fullkey.Assign(1); +        controller.system_properties.is_vertical.Assign(1); +        controller.system_properties.use_plus.Assign(1); +        break;      case NPadControllerType::Pokeball:          controller.style_set.palma.Assign(1);          controller.device_type.palma.Assign(1); @@ -259,6 +271,7 @@ void Controller_NPad::OnInit() {          style.joycon_right.Assign(1);          style.joycon_dual.Assign(1);          style.fullkey.Assign(1); +        style.gamecube.Assign(1);          style.palma.Assign(1);      } @@ -339,6 +352,7 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {      auto& pad_state = npad_pad_states[controller_idx].pad_states;      auto& lstick_entry = npad_pad_states[controller_idx].l_stick;      auto& rstick_entry = npad_pad_states[controller_idx].r_stick; +    auto& trigger_entry = npad_trigger_states[controller_idx];      const auto& button_state = buttons[controller_idx];      const auto& analog_state = sticks[controller_idx];      const auto [stick_l_x_f, stick_l_y_f] = @@ -404,6 +418,17 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {          pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());          pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());      } + +    if (controller_type == NPadControllerType::GameCube) { +        trigger_entry.l_analog = static_cast<s32>( +            button_state[ZL - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0); +        trigger_entry.r_analog = static_cast<s32>( +            button_state[ZR - BUTTON_HID_BEGIN]->GetStatus() ? HID_TRIGGER_MAX : 0); +        pad_state.zl.Assign(false); +        pad_state.zr.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus()); +        pad_state.l.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus()); +        pad_state.r.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus()); +    }  }  void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, @@ -418,6 +443,11 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*              &npad.joy_left_states,  &npad.joy_right_states, &npad.palma_states,              &npad.system_ext_states}; +        // There is the posibility to have more controllers with analog triggers +        const std::array<TriggerGeneric*, 1> controller_triggers{ +            &npad.gc_trigger_states, +        }; +          for (auto* main_controller : controller_npads) {              main_controller->common.entry_count = 16;              main_controller->common.total_entry_count = 17; @@ -435,6 +465,21 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*              cur_entry.timestamp2 = cur_entry.timestamp;          } +        for (auto* analog_trigger : controller_triggers) { +            analog_trigger->entry_count = 16; +            analog_trigger->total_entry_count = 17; + +            const auto& last_entry = analog_trigger->trigger[analog_trigger->last_entry_index]; + +            analog_trigger->timestamp = core_timing.GetCPUTicks(); +            analog_trigger->last_entry_index = (analog_trigger->last_entry_index + 1) % 17; + +            auto& cur_entry = analog_trigger->trigger[analog_trigger->last_entry_index]; + +            cur_entry.timestamp = last_entry.timestamp + 1; +            cur_entry.timestamp2 = cur_entry.timestamp; +        } +          const auto& controller_type = connected_controllers[i].type;          if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { @@ -444,6 +489,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*          RequestPadStateUpdate(npad_index);          auto& pad_state = npad_pad_states[npad_index]; +        auto& trigger_state = npad_trigger_states[npad_index];          auto& main_controller =              npad.fullkey_states.npad[npad.fullkey_states.common.last_entry_index]; @@ -456,6 +502,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*          auto& pokeball_entry = npad.palma_states.npad[npad.palma_states.common.last_entry_index];          auto& libnx_entry =              npad.system_ext_states.npad[npad.system_ext_states.common.last_entry_index]; +        auto& trigger_entry = +            npad.gc_trigger_states.trigger[npad.gc_trigger_states.last_entry_index];          libnx_entry.connection_status.raw = 0;          libnx_entry.connection_status.is_connected.Assign(1); @@ -524,6 +572,18 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*              libnx_entry.connection_status.is_right_connected.Assign(1);              break; +        case NPadControllerType::GameCube: +            main_controller.connection_status.raw = 0; +            main_controller.connection_status.is_connected.Assign(1); +            main_controller.connection_status.is_wired.Assign(1); +            main_controller.pad.pad_states.raw = pad_state.pad_states.raw; +            main_controller.pad.l_stick = pad_state.l_stick; +            main_controller.pad.r_stick = pad_state.r_stick; +            trigger_entry.l_analog = trigger_state.l_analog; +            trigger_entry.r_analog = trigger_state.r_analog; + +            libnx_entry.connection_status.is_wired.Assign(1); +            break;          case NPadControllerType::Pokeball:              pokeball_entry.connection_status.raw = 0;              pokeball_entry.connection_status.is_connected.Assign(1); @@ -674,6 +734,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing                  right_sixaxis_entry.orientation = motion_devices[1].orientation;              }              break; +        case NPadControllerType::GameCube:          case NPadControllerType::Pokeball:              break;          } @@ -1135,6 +1196,8 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const              return style.joycon_left;          case NPadControllerType::JoyRight:              return style.joycon_right; +        case NPadControllerType::GameCube: +            return style.gamecube;          case NPadControllerType::Pokeball:              return style.palma;          default: diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 48bab988c..bc2e6779d 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -51,6 +51,7 @@ public:          JoyDual,          JoyLeft,          JoyRight, +        GameCube,          Pokeball,      }; @@ -60,6 +61,7 @@ public:          JoyconDual = 5,          JoyconLeft = 6,          JoyconRight = 7, +        GameCube = 8,          Pokeball = 9,          MaxNpadType = 10,      }; @@ -389,6 +391,25 @@ private:      };      static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size"); +    struct TriggerState { +        s64_le timestamp{}; +        s64_le timestamp2{}; +        s32_le l_analog{}; +        s32_le r_analog{}; +    }; +    static_assert(sizeof(TriggerState) == 0x18, "TriggerState is an invalid size"); + +    struct TriggerGeneric { +        INSERT_PADDING_BYTES(0x4); +        s64_le timestamp; +        INSERT_PADDING_BYTES(0x4); +        s64_le total_entry_count; +        s64_le last_entry_index; +        s64_le entry_count; +        std::array<TriggerState, 17> trigger{}; +    }; +    static_assert(sizeof(TriggerGeneric) == 0x1C8, "TriggerGeneric is an invalid size"); +      struct NPadSystemProperties {          union {              s64_le raw{}; @@ -509,7 +530,9 @@ private:          AppletFooterUiType footer_type;          // nfc_states needs to be checked switchbrew does not match with HW          NfcXcdHandle nfc_states; -        INSERT_PADDING_BYTES(0xdef); +        INSERT_PADDING_BYTES(0x8); // Mutex +        TriggerGeneric gc_trigger_states; +        INSERT_PADDING_BYTES(0xc1f);      };      static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size"); @@ -560,6 +583,7 @@ private:      f32 sixaxis_fusion_parameter2{};      bool sixaxis_at_rest{true};      std::array<ControllerPad, 10> npad_pad_states{}; +    std::array<TriggerState, 10> npad_trigger_states{};      bool is_in_lr_assignment_mode{false};      Core::System& system;  }; diff --git a/src/input_common/settings.h b/src/input_common/settings.h index 75486554b..a59f5d461 100644 --- a/src/input_common/settings.h +++ b/src/input_common/settings.h @@ -340,6 +340,7 @@ enum class ControllerType {      LeftJoycon,      RightJoycon,      Handheld, +    GameCube,  };  struct PlayerInput { diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp index c680fd2c2..b92cd6886 100644 --- a/src/yuzu/applets/controller.cpp +++ b/src/yuzu/applets/controller.cpp @@ -67,6 +67,8 @@ bool IsControllerCompatible(Settings::ControllerType controller_type,          return parameters.allow_right_joycon;      case Settings::ControllerType::Handheld:          return parameters.enable_single_mode && parameters.allow_handheld; +    case Settings::ControllerType::GameCube: +        return parameters.allow_gamecube_controller;      default:          return false;      } @@ -370,7 +372,7 @@ void QtControllerSelectorDialog::SetSupportedControllers() {              QStringLiteral("image: url(:/controller/applet_joycon_right%0_disabled); ").arg(theme));      } -    if (parameters.allow_pro_controller) { +    if (parameters.allow_pro_controller || parameters.allow_gamecube_controller) {          ui->controllerSupported5->setStyleSheet(              QStringLiteral("image: url(:/controller/applet_pro_controller%0); ").arg(theme));      } else { @@ -420,6 +422,10 @@ void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index                             Settings::ControllerType::Handheld);          emulated_controllers[player_index]->addItem(tr("Handheld"));      } + +    pairs.emplace_back(emulated_controllers[player_index]->count(), +                       Settings::ControllerType::GameCube); +    emulated_controllers[player_index]->addItem(tr("GameCube Controller"));  }  Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex( @@ -461,6 +467,7 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index)          switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(),                                             player_index)) {          case Settings::ControllerType::ProController: +        case Settings::ControllerType::GameCube:              return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");          case Settings::ControllerType::DualJoyconDetached:              return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); "); diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index c9d19c948..21d0d3449 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -467,10 +467,14 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i      UpdateControllerIcon();      UpdateControllerAvailableButtons(); +    UpdateControllerEnabledButtons(); +    UpdateControllerButtonNames();      UpdateMotionButtons();      connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {          UpdateControllerIcon();          UpdateControllerAvailableButtons(); +        UpdateControllerEnabledButtons(); +        UpdateControllerButtonNames();          UpdateMotionButtons();      }); @@ -558,9 +562,6 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i              &ConfigureInputPlayer::SaveProfile);      LoadConfiguration(); - -    // TODO(wwylele): enable this when we actually emulate it -    ui->buttonHome->setEnabled(false);      ui->controllerFrame->SetPlayerInput(player_index, buttons_param, analogs_param);      ui->controllerFrame->SetConnectedStatus(ui->groupConnectedController->isChecked());  } @@ -924,6 +925,12 @@ void ConfigureInputPlayer::SetConnectableControllers() {                                                       Settings::ControllerType::Handheld);              ui->comboControllerType->addItem(tr("Handheld"));          } + +        if (enable_all || npad_style_set.gamecube == 1) { +            index_controller_type_pairs.emplace_back(ui->comboControllerType->count(), +                                                     Settings::ControllerType::GameCube); +            ui->comboControllerType->addItem(tr("GameCube Controller")); +        }      };      Core::System& system{Core::System::GetInstance()}; @@ -1014,7 +1021,7 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {      // List of all the widgets that will be hidden by any of the following layouts that need      // "unhidden" after the controller type changes -    const std::array<QWidget*, 9> layout_show = { +    const std::array<QWidget*, 11> layout_show = {          ui->buttonShoulderButtonsSLSR,          ui->horizontalSpacerShoulderButtonsWidget,          ui->horizontalSpacerShoulderButtonsWidget2, @@ -1024,6 +1031,8 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {          ui->buttonShoulderButtonsRight,          ui->buttonMiscButtonsPlusHome,          ui->bottomRight, +        ui->buttonMiscButtonsMinusGroup, +        ui->buttonMiscButtonsScreenshotGroup,      };      for (auto* widget : layout_show) { @@ -1056,6 +1065,14 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {              ui->bottomLeft,          };          break; +    case Settings::ControllerType::GameCube: +        layout_hidden = { +            ui->buttonShoulderButtonsSLSR, +            ui->horizontalSpacerShoulderButtonsWidget2, +            ui->buttonMiscButtonsMinusGroup, +            ui->buttonMiscButtonsScreenshotGroup, +        }; +        break;      }      for (auto* widget : layout_hidden) { @@ -1063,6 +1080,52 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {      }  } +void ConfigureInputPlayer::UpdateControllerEnabledButtons() { +    auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); +    if (debug) { +        layout = Settings::ControllerType::ProController; +    } + +    // List of all the widgets that will be disabled by any of the following layouts that need +    // "enabled" after the controller type changes +    const std::array<QWidget*, 4> layout_enable = { +        ui->buttonHome, +        ui->buttonLStickPressedGroup, +        ui->groupRStickPressed, +        ui->buttonShoulderButtonsButtonLGroup, +    }; + +    for (auto* widget : layout_enable) { +        widget->setEnabled(true); +    } + +    std::vector<QWidget*> layout_disable; +    switch (layout) { +    case Settings::ControllerType::ProController: +    case Settings::ControllerType::DualJoyconDetached: +    case Settings::ControllerType::Handheld: +    case Settings::ControllerType::LeftJoycon: +    case Settings::ControllerType::RightJoycon: +        // TODO(wwylele): enable this when we actually emulate it +        layout_disable = { +            ui->buttonHome, +        }; +        break; +    case Settings::ControllerType::GameCube: +        layout_disable = { +            ui->buttonHome, +            ui->buttonLStickPressedGroup, +            ui->groupRStickPressed, +            ui->buttonShoulderButtonsButtonLGroup, +        }; +        break; +    } + +    for (auto* widget : layout_disable) { +        widget->setEnabled(false); +    } +} +  void ConfigureInputPlayer::UpdateMotionButtons() {      if (debug) {          // Motion isn't used with the debug controller, hide both groupboxes. @@ -1085,6 +1148,11 @@ void ConfigureInputPlayer::UpdateMotionButtons() {          ui->buttonMotionLeftGroup->hide();          ui->buttonMotionRightGroup->show();          break; +    case Settings::ControllerType::GameCube: +        // Hide both "Motion 1/2". +        ui->buttonMotionLeftGroup->hide(); +        ui->buttonMotionRightGroup->hide(); +        break;      case Settings::ControllerType::DualJoyconDetached:      default:          // Show both "Motion 1/2". @@ -1094,6 +1162,36 @@ void ConfigureInputPlayer::UpdateMotionButtons() {      }  } +void ConfigureInputPlayer::UpdateControllerButtonNames() { +    auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); +    if (debug) { +        layout = Settings::ControllerType::ProController; +    } + +    switch (layout) { +    case Settings::ControllerType::ProController: +    case Settings::ControllerType::DualJoyconDetached: +    case Settings::ControllerType::Handheld: +    case Settings::ControllerType::LeftJoycon: +    case Settings::ControllerType::RightJoycon: +        ui->buttonMiscButtonsPlusGroup->setTitle(tr("Plus")); +        ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("ZL")); +        ui->buttonShoulderButtonsZRGroup->setTitle(tr("ZR")); +        ui->buttonShoulderButtonsRGroup->setTitle(tr("R")); +        ui->LStick->setTitle(tr("Left Stick")); +        ui->RStick->setTitle(tr("Right Stick")); +        break; +    case Settings::ControllerType::GameCube: +        ui->buttonMiscButtonsPlusGroup->setTitle(tr("Start / Pause")); +        ui->buttonShoulderButtonsButtonZLGroup->setTitle(tr("L")); +        ui->buttonShoulderButtonsZRGroup->setTitle(tr("R")); +        ui->buttonShoulderButtonsRGroup->setTitle(tr("Z")); +        ui->LStick->setTitle(tr("Control Stick")); +        ui->RStick->setTitle(tr("C-Stick")); +        break; +    } +} +  void ConfigureInputPlayer::UpdateMappingWithDefaults() {      if (ui->comboDevices->currentIndex() == 0) {          return; diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index da2b89136..efe953fbc 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -143,9 +143,15 @@ private:      /// Hides and disables controller settings based on the current controller type.      void UpdateControllerAvailableButtons(); +    /// Disables controller settings based on the current controller type. +    void UpdateControllerEnabledButtons(); +      /// Shows or hides motion groupboxes based on the current controller type.      void UpdateMotionButtons(); +    /// Alters the button names based on the current controller type. +    void UpdateControllerButtonNames(); +      /// Gets the default controller mapping for this device and auto configures the input to match.      void UpdateMappingWithDefaults(); | 
