summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/core/frontend/applets/controller.h1
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp63
-rw-r--r--src/core/hle/service/hid/controllers/npad.h26
-rw-r--r--src/input_common/settings.h1
-rw-r--r--src/yuzu/applets/controller.cpp9
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp106
-rw-r--r--src/yuzu/configuration/configure_input_player.h6
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();