|
|
|
@ -9,6 +9,8 @@ |
|
|
|
#include "common/param_package.h"
|
|
|
|
#include "common/string_util.h"
|
|
|
|
#include "core/core.h"
|
|
|
|
#include "core/hid/emulated_controller.h"
|
|
|
|
#include "core/hid/hid_types.h"
|
|
|
|
#include "core/hle/lock.h"
|
|
|
|
#include "core/hle/service/hid/controllers/npad.h"
|
|
|
|
#include "core/hle/service/hid/hid.h"
|
|
|
|
@ -26,48 +28,31 @@ namespace { |
|
|
|
|
|
|
|
constexpr std::size_t HANDHELD_INDEX = 8; |
|
|
|
|
|
|
|
constexpr std::array<std::array<bool, 4>, 8> led_patterns{{ |
|
|
|
{true, false, false, false}, |
|
|
|
{true, true, false, false}, |
|
|
|
{true, true, true, false}, |
|
|
|
{true, true, true, true}, |
|
|
|
{true, false, false, true}, |
|
|
|
{true, false, true, false}, |
|
|
|
{true, false, true, true}, |
|
|
|
{false, true, true, false}, |
|
|
|
}}; |
|
|
|
|
|
|
|
void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index, |
|
|
|
bool connected, Core::System& system) { |
|
|
|
if (!system.IsPoweredOn()) { |
|
|
|
return; |
|
|
|
void UpdateController(Core::HID::EmulatedController* controller, Core::HID::NpadType controller_type, bool connected) { |
|
|
|
if (controller->IsConnected()) { |
|
|
|
controller->Disconnect(); |
|
|
|
} |
|
|
|
controller->SetNpadType(controller_type); |
|
|
|
if (connected) { |
|
|
|
controller->Connect(); |
|
|
|
} |
|
|
|
|
|
|
|
auto& npad = |
|
|
|
system.ServiceManager() |
|
|
|
.GetService<Service::HID::Hid>("hid") |
|
|
|
->GetAppletResource() |
|
|
|
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad); |
|
|
|
|
|
|
|
npad.UpdateControllerAt(Core::HID::EmulatedController::MapSettingsTypeToNPad(controller_type), |
|
|
|
npad_index, connected); |
|
|
|
} |
|
|
|
|
|
|
|
// Returns true if the given controller type is compatible with the given parameters.
|
|
|
|
bool IsControllerCompatible(Settings::ControllerType controller_type, |
|
|
|
bool IsControllerCompatible(Core::HID::NpadType controller_type, |
|
|
|
Core::Frontend::ControllerParameters parameters) { |
|
|
|
switch (controller_type) { |
|
|
|
case Settings::ControllerType::ProController: |
|
|
|
case Core::HID::NpadType::ProController: |
|
|
|
return parameters.allow_pro_controller; |
|
|
|
case Settings::ControllerType::DualJoyconDetached: |
|
|
|
case Core::HID::NpadType::JoyconDual: |
|
|
|
return parameters.allow_dual_joycons; |
|
|
|
case Settings::ControllerType::LeftJoycon: |
|
|
|
case Core::HID::NpadType::JoyconLeft: |
|
|
|
return parameters.allow_left_joycon; |
|
|
|
case Settings::ControllerType::RightJoycon: |
|
|
|
case Core::HID::NpadType::JoyconRight: |
|
|
|
return parameters.allow_right_joycon; |
|
|
|
case Settings::ControllerType::Handheld: |
|
|
|
case Core::HID::NpadType::Handheld: |
|
|
|
return parameters.enable_single_mode && parameters.allow_handheld; |
|
|
|
case Settings::ControllerType::GameCube: |
|
|
|
case Core::HID::NpadType::GameCube: |
|
|
|
return parameters.allow_gamecube_controller; |
|
|
|
default: |
|
|
|
return false; |
|
|
|
@ -198,7 +183,7 @@ QtControllerSelectorDialog::QtControllerSelectorDialog( |
|
|
|
connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged), |
|
|
|
[this, i](int index) { |
|
|
|
UpdateDockedState(GetControllerTypeFromIndex(index, i) == |
|
|
|
Settings::ControllerType::Handheld); |
|
|
|
Core::HID::NpadType::Handheld); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -251,17 +236,17 @@ void QtControllerSelectorDialog::ApplyConfiguration() { |
|
|
|
} |
|
|
|
|
|
|
|
void QtControllerSelectorDialog::LoadConfiguration() { |
|
|
|
const auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); |
|
|
|
for (std::size_t index = 0; index < NUM_PLAYERS; ++index) { |
|
|
|
const auto connected = |
|
|
|
Settings::values.players.GetValue()[index].connected || |
|
|
|
(index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected); |
|
|
|
const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index); |
|
|
|
const auto connected = controller->IsConnected() || (index == 0 && handheld->IsConnected()); |
|
|
|
player_groupboxes[index]->setChecked(connected); |
|
|
|
connected_controller_checkboxes[index]->setChecked(connected); |
|
|
|
emulated_controllers[index]->setCurrentIndex(GetIndexFromControllerType( |
|
|
|
Settings::values.players.GetValue()[index].controller_type, index)); |
|
|
|
emulated_controllers[index]->setCurrentIndex( |
|
|
|
GetIndexFromControllerType(controller->GetNpadType(), index)); |
|
|
|
} |
|
|
|
|
|
|
|
UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected); |
|
|
|
UpdateDockedState(handheld->IsConnected()); |
|
|
|
|
|
|
|
ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue()); |
|
|
|
ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue()); |
|
|
|
@ -417,33 +402,32 @@ void QtControllerSelectorDialog::SetEmulatedControllers(std::size_t player_index |
|
|
|
emulated_controllers[player_index]->clear(); |
|
|
|
|
|
|
|
pairs.emplace_back(emulated_controllers[player_index]->count(), |
|
|
|
Settings::ControllerType::ProController); |
|
|
|
Core::HID::NpadType::ProController); |
|
|
|
emulated_controllers[player_index]->addItem(tr("Pro Controller")); |
|
|
|
|
|
|
|
pairs.emplace_back(emulated_controllers[player_index]->count(), |
|
|
|
Settings::ControllerType::DualJoyconDetached); |
|
|
|
Core::HID::NpadType::JoyconDual); |
|
|
|
emulated_controllers[player_index]->addItem(tr("Dual Joycons")); |
|
|
|
|
|
|
|
pairs.emplace_back(emulated_controllers[player_index]->count(), |
|
|
|
Settings::ControllerType::LeftJoycon); |
|
|
|
Core::HID::NpadType::JoyconLeft); |
|
|
|
emulated_controllers[player_index]->addItem(tr("Left Joycon")); |
|
|
|
|
|
|
|
pairs.emplace_back(emulated_controllers[player_index]->count(), |
|
|
|
Settings::ControllerType::RightJoycon); |
|
|
|
Core::HID::NpadType::JoyconRight); |
|
|
|
emulated_controllers[player_index]->addItem(tr("Right Joycon")); |
|
|
|
|
|
|
|
if (player_index == 0) { |
|
|
|
pairs.emplace_back(emulated_controllers[player_index]->count(), |
|
|
|
Settings::ControllerType::Handheld); |
|
|
|
Core::HID::NpadType::Handheld); |
|
|
|
emulated_controllers[player_index]->addItem(tr("Handheld")); |
|
|
|
} |
|
|
|
|
|
|
|
pairs.emplace_back(emulated_controllers[player_index]->count(), |
|
|
|
Settings::ControllerType::GameCube); |
|
|
|
pairs.emplace_back(emulated_controllers[player_index]->count(), Core::HID::NpadType::GameCube); |
|
|
|
emulated_controllers[player_index]->addItem(tr("GameCube Controller")); |
|
|
|
} |
|
|
|
|
|
|
|
Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex( |
|
|
|
Core::HID::NpadType QtControllerSelectorDialog::GetControllerTypeFromIndex( |
|
|
|
int index, std::size_t player_index) const { |
|
|
|
const auto& pairs = index_controller_type_pairs[player_index]; |
|
|
|
|
|
|
|
@ -451,13 +435,13 @@ Settings::ControllerType QtControllerSelectorDialog::GetControllerTypeFromIndex( |
|
|
|
[index](const auto& pair) { return pair.first == index; }); |
|
|
|
|
|
|
|
if (it == pairs.end()) { |
|
|
|
return Settings::ControllerType::ProController; |
|
|
|
return Core::HID::NpadType::ProController; |
|
|
|
} |
|
|
|
|
|
|
|
return it->second; |
|
|
|
} |
|
|
|
|
|
|
|
int QtControllerSelectorDialog::GetIndexFromControllerType(Settings::ControllerType type, |
|
|
|
int QtControllerSelectorDialog::GetIndexFromControllerType(Core::HID::NpadType type, |
|
|
|
std::size_t player_index) const { |
|
|
|
const auto& pairs = index_controller_type_pairs[player_index]; |
|
|
|
|
|
|
|
@ -481,16 +465,16 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) |
|
|
|
const QString stylesheet = [this, player_index] { |
|
|
|
switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), |
|
|
|
player_index)) { |
|
|
|
case Settings::ControllerType::ProController: |
|
|
|
case Settings::ControllerType::GameCube: |
|
|
|
case Core::HID::NpadType::ProController: |
|
|
|
case Core::HID::NpadType::GameCube: |
|
|
|
return QStringLiteral("image: url(:/controller/applet_pro_controller%0); "); |
|
|
|
case Settings::ControllerType::DualJoyconDetached: |
|
|
|
case Core::HID::NpadType::JoyconDual: |
|
|
|
return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); "); |
|
|
|
case Settings::ControllerType::LeftJoycon: |
|
|
|
case Core::HID::NpadType::JoyconLeft: |
|
|
|
return QStringLiteral("image: url(:/controller/applet_joycon_left%0); "); |
|
|
|
case Settings::ControllerType::RightJoycon: |
|
|
|
case Core::HID::NpadType::JoyconRight: |
|
|
|
return QStringLiteral("image: url(:/controller/applet_joycon_right%0); "); |
|
|
|
case Settings::ControllerType::Handheld: |
|
|
|
case Core::HID::NpadType::Handheld: |
|
|
|
return QStringLiteral("image: url(:/controller/applet_handheld%0); "); |
|
|
|
default: |
|
|
|
return QString{}; |
|
|
|
@ -518,54 +502,42 @@ void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) |
|
|
|
} |
|
|
|
|
|
|
|
void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) { |
|
|
|
auto& player = Settings::values.players.GetValue()[player_index]; |
|
|
|
auto* controller = system.HIDCore().GetEmulatedControllerByIndex(player_index); |
|
|
|
|
|
|
|
const auto controller_type = GetControllerTypeFromIndex( |
|
|
|
emulated_controllers[player_index]->currentIndex(), player_index); |
|
|
|
const auto player_connected = player_groupboxes[player_index]->isChecked() && |
|
|
|
controller_type != Settings::ControllerType::Handheld; |
|
|
|
controller_type != Core::HID::NpadType::Handheld; |
|
|
|
|
|
|
|
if (player.controller_type == controller_type && player.connected == player_connected) { |
|
|
|
if (controller->GetNpadType() == controller_type && |
|
|
|
controller->IsConnected() == player_connected) { |
|
|
|
// Set vibration devices in the event that the input device has changed.
|
|
|
|
ConfigureVibration::SetVibrationDevices(player_index); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// Disconnect the controller first.
|
|
|
|
UpdateController(controller_type, player_index, false, system); |
|
|
|
|
|
|
|
player.controller_type = controller_type; |
|
|
|
player.connected = player_connected; |
|
|
|
UpdateController(controller, controller_type, false); |
|
|
|
|
|
|
|
ConfigureVibration::SetVibrationDevices(player_index); |
|
|
|
|
|
|
|
// Handheld
|
|
|
|
if (player_index == 0) { |
|
|
|
auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; |
|
|
|
if (controller_type == Settings::ControllerType::Handheld) { |
|
|
|
handheld = player; |
|
|
|
if (controller_type == Core::HID::NpadType::Handheld) { |
|
|
|
auto* handheld = |
|
|
|
system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld); |
|
|
|
UpdateController(handheld, Core::HID::NpadType::Handheld, |
|
|
|
player_groupboxes[player_index]->isChecked()); |
|
|
|
} |
|
|
|
handheld.connected = player_groupboxes[player_index]->isChecked() && |
|
|
|
controller_type == Settings::ControllerType::Handheld; |
|
|
|
UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected, system); |
|
|
|
} |
|
|
|
|
|
|
|
if (!player.connected) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// This emulates a delay between disconnecting and reconnecting controllers as some games
|
|
|
|
// do not respond to a change in controller type if it was instantaneous.
|
|
|
|
using namespace std::chrono_literals; |
|
|
|
std::this_thread::sleep_for(60ms); |
|
|
|
|
|
|
|
UpdateController(controller_type, player_index, player_connected, system); |
|
|
|
UpdateController(controller, controller_type, player_connected); |
|
|
|
} |
|
|
|
|
|
|
|
void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { |
|
|
|
if (!player_groupboxes[player_index]->isChecked() || |
|
|
|
GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex(), |
|
|
|
player_index) == Settings::ControllerType::Handheld) { |
|
|
|
player_index) == Core::HID::NpadType::Handheld) { |
|
|
|
led_patterns_boxes[player_index][0]->setChecked(false); |
|
|
|
led_patterns_boxes[player_index][1]->setChecked(false); |
|
|
|
led_patterns_boxes[player_index][2]->setChecked(false); |
|
|
|
@ -573,10 +545,12 @@ void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
led_patterns_boxes[player_index][0]->setChecked(led_patterns[player_index][0]); |
|
|
|
led_patterns_boxes[player_index][1]->setChecked(led_patterns[player_index][1]); |
|
|
|
led_patterns_boxes[player_index][2]->setChecked(led_patterns[player_index][2]); |
|
|
|
led_patterns_boxes[player_index][3]->setChecked(led_patterns[player_index][3]); |
|
|
|
const auto* controller = system.HIDCore().GetEmulatedControllerByIndex(player_index); |
|
|
|
const auto led_pattern = controller->GetLedPattern(); |
|
|
|
led_patterns_boxes[player_index][0]->setChecked(led_pattern.position1); |
|
|
|
led_patterns_boxes[player_index][1]->setChecked(led_pattern.position2); |
|
|
|
led_patterns_boxes[player_index][2]->setChecked(led_pattern.position3); |
|
|
|
led_patterns_boxes[player_index][3]->setChecked(led_pattern.position4); |
|
|
|
} |
|
|
|
|
|
|
|
void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) { |
|
|
|
@ -656,10 +630,9 @@ void QtControllerSelectorDialog::DisableUnsupportedPlayers() { |
|
|
|
} |
|
|
|
|
|
|
|
for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) { |
|
|
|
auto* controller = system.HIDCore().GetEmulatedControllerByIndex(index); |
|
|
|
// Disconnect any unsupported players here and disable or hide them if applicable.
|
|
|
|
Settings::values.players.GetValue()[index].connected = false; |
|
|
|
UpdateController(Settings::values.players.GetValue()[index].controller_type, index, false, |
|
|
|
system); |
|
|
|
UpdateController(controller, controller->GetNpadType(), false); |
|
|
|
// Hide the player widgets when max_supported_controllers is less than or equal to 4.
|
|
|
|
if (max_supported_players <= 4) { |
|
|
|
player_widgets[index]->hide(); |
|
|
|
|