Compare commits

...

7 Commits

Author SHA1 Message Date
Zach Hilman
a40ed17c28 controller: Correct result codes for GetStatus 2019-03-05 10:19:29 -05:00
Zach Hilman
56beb2c85b qt: Add Qt frontend for controller selector applet 2019-03-05 10:19:29 -05:00
Zach Hilman
0f82eb83f9 configure_input_simple: Add support for checking configuration errors 2019-03-05 10:18:40 -05:00
Zach Hilman
41881d8d26 core: Add support for registering controller applet frontend 2019-03-05 10:18:40 -05:00
Zach Hilman
c67bab2512 frontend: Add frontend responder for controller applet 2019-03-05 10:17:52 -05:00
Zach Hilman
9d6059ba9f applets: Implement controller selector applet 2019-03-05 10:17:52 -05:00
Zach Hilman
7d9ddb9150 controllers/npad: Make DecideBestController public
Needed for default implementation of controller applet.
2019-03-05 10:17:52 -05:00
19 changed files with 565 additions and 50 deletions

View File

@@ -84,6 +84,8 @@ add_library(core STATIC
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
file_sys/xts_archive.h
frontend/applets/controller.cpp
frontend/applets/controller.h
frontend/applets/profile_select.cpp
frontend/applets/profile_select.h
frontend/applets/software_keyboard.cpp
@@ -169,6 +171,8 @@ add_library(core STATIC
hle/service/am/applet_oe.h
hle/service/am/applets/applets.cpp
hle/service/am/applets/applets.h
hle/service/am/applets/controller.cpp
hle/service/am/applets/controller.h
hle/service/am/applets/profile_select.cpp
hle/service/am/applets/profile_select.h
hle/service/am/applets/software_keyboard.cpp

View File

@@ -25,13 +25,13 @@
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/am/applets/software_keyboard.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "frontend/applets/controller.h"
#include "frontend/applets/profile_select.h"
#include "frontend/applets/software_keyboard.h"
#include "frontend/applets/web_browser.h"
@@ -107,6 +107,8 @@ struct System::Impl {
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
/// Create default implementations of applets if one is not provided.
if (controller_applet == nullptr)
controller_applet = std::make_unique<Core::Frontend::DefaultControllerApplet>();
if (profile_selector == nullptr)
profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
if (software_keyboard == nullptr)
@@ -211,6 +213,7 @@ struct System::Impl {
app_loader.reset();
// Clear all applets
controller_applet.reset();
profile_selector.reset();
software_keyboard.reset();
web_browser.reset();
@@ -248,6 +251,7 @@ struct System::Impl {
bool is_powered_on = false;
/// Frontend applets
std::unique_ptr<Core::Frontend::ControllerApplet> controller_applet;
std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector;
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
std::unique_ptr<Core::Frontend::WebBrowserApplet> web_browser;
@@ -453,6 +457,14 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
}
void System::SetControllerApplet(std::unique_ptr<Core::Frontend::ControllerApplet> applet) {
impl->controller_applet = std::move(applet);
}
const Core::Frontend::ControllerApplet& System::GetControllerApplet() const {
return *impl->controller_applet;
}
void System::SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet) {
impl->profile_selector = std::move(applet);
}

View File

@@ -14,6 +14,7 @@
namespace Core::Frontend {
class EmuWindow;
class ControllerApplet;
class ProfileSelectApplet;
class SoftwareKeyboardApplet;
class WebBrowserApplet;
@@ -253,6 +254,10 @@ public:
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
void SetControllerApplet(std::unique_ptr<Core::Frontend::ControllerApplet> applet);
const Core::Frontend::ControllerApplet& GetControllerApplet() const;
void SetProfileSelector(std::unique_ptr<Frontend::ProfileSelectApplet> applet);
const Frontend::ProfileSelectApplet& GetProfileSelector() const;

View File

@@ -0,0 +1,38 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/frontend/applets/controller.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/sm/sm.h"
namespace Core::Frontend {
ControllerApplet::~ControllerApplet() = default;
DefaultControllerApplet::~DefaultControllerApplet() = default;
void DefaultControllerApplet::ReconfigureControllers(std::function<void(bool)> completed,
ControllerParameters parameters) const {
LOG_WARNING(Service_HID, "(STUBBED) called, automatically setting controller types to best fit "
"in configuration, may be incorrect!");
const auto& npad =
Core::System::GetInstance()
.ServiceManager()
.GetService<Service::HID::Hid>("hid")
->GetAppletResource()
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
for (auto& player : Settings::values.players) {
const auto prio = Service::HID::Controller_NPad::MapSettingsTypeToNPad(player.type);
player.type =
Service::HID::Controller_NPad::MapNPadTypeToSettings(npad.DecideBestController(prio));
}
completed(true);
}
} // namespace Core::Frontend

View File

@@ -0,0 +1,47 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <optional>
#include <string>
#include "common/bit_field.h"
#include "common/common_types.h"
namespace Core::Frontend {
struct ControllerParameters {
u8 min_players;
u8 max_players;
bool keep_current_connected;
bool merge_dual_joycons;
bool allowed_single_layout;
bool allowed_pro_controller;
bool allowed_handheld;
bool allowed_joycon_dual;
bool allowed_joycon_left;
bool allowed_joycon_right;
bool horizontal_single_joycons;
};
class ControllerApplet {
public:
virtual ~ControllerApplet();
virtual void ReconfigureControllers(std::function<void(bool)> completed,
ControllerParameters parameters) const = 0;
};
class DefaultControllerApplet final : public ControllerApplet {
public:
~DefaultControllerApplet() override;
void ReconfigureControllers(std::function<void(bool)> completed,
ControllerParameters parameters) const override;
};
} // namespace Core::Frontend

View File

@@ -20,6 +20,7 @@
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/am/applets/controller.h"
#include "core/hle/service/am/applets/profile_select.h"
#include "core/hle/service/am/applets/software_keyboard.h"
#include "core/hle/service/am/applets/stub_applet.h"
@@ -43,6 +44,7 @@ constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3};
constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
enum class AppletId : u32 {
Controller = 0x0C,
ProfileSelect = 0x10,
SoftwareKeyboard = 0x11,
LibAppletOff = 0x17,
@@ -790,6 +792,8 @@ ILibraryAppletCreator::~ILibraryAppletCreator() = default;
static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
switch (id) {
case AppletId::Controller:
return std::make_shared<Applets::Controller>();
case AppletId::ProfileSelect:
return std::make_shared<Applets::ProfileSelect>();
case AppletId::SoftwareKeyboard:

View File

@@ -0,0 +1,96 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/core.h"
#include "core/frontend/applets/controller.h"
#include "core/hle/service/am/applets/controller.h"
#include "core/hle/service/hid/controllers/npad.h"
namespace Service::AM::Applets {
static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
ControllerConfigPrivate config_private, ControllerConfig config) {
Core::Frontend::ControllerParameters params{};
params.min_players = config.minimum_player_count;
params.max_players = config.maximum_player_count;
params.keep_current_connected = config.keep_current_connections;
params.merge_dual_joycons = config.permit_dual_joycons;
params.allowed_single_layout = config.enable_single_mode;
params.horizontal_single_joycons = config_private.npad_hold_type;
HID::Controller_NPad::NPadType styleset;
styleset.raw = config_private.npad_style_set;
params.allowed_pro_controller = styleset.pro_controller;
params.allowed_handheld = styleset.handheld;
params.allowed_joycon_dual = styleset.joycon_dual;
params.allowed_joycon_left = styleset.joycon_left;
params.allowed_joycon_right = styleset.joycon_right;
return params;
}
Controller::Controller() = default;
Controller::~Controller() = default;
void Controller::Initialize() {
Applet::Initialize();
const auto private_config_storage = broker.PopNormalDataToApplet();
ASSERT(private_config_storage != nullptr);
const auto& private_controller_config = private_config_storage->GetData();
ASSERT(private_controller_config.size() >= sizeof(ControllerConfigPrivate));
std::memcpy(&private_config, private_controller_config.data(), sizeof(ControllerConfigPrivate));
const auto config_storage = broker.PopNormalDataToApplet();
ASSERT(config_storage != nullptr);
const auto& controller_config = config_storage->GetData();
ASSERT(controller_config.size() >= sizeof(ControllerConfig));
std::memcpy(&config, controller_config.data(), sizeof(ControllerConfig));
}
bool Controller::TransactionComplete() const {
return complete;
}
ResultCode Controller::GetStatus() const {
return RESULT_SUCCESS;
}
void Controller::ExecuteInteractive() {
UNREACHABLE_MSG("Tried to interact with non-interactable applet.");
}
void Controller::Execute() {
if (complete) {
UNREACHABLE();
}
const auto& frontend{Core::System::GetInstance().GetControllerApplet()};
const auto parameters = ConvertToFrontendParameters(private_config, config);
frontend.ReconfigureControllers([this](bool ok) { ConfigurationComplete(ok); }, parameters);
}
void Controller::ConfigurationComplete(bool ok) {
ControllerConfigOutput out{};
out.player_code = 0;
out.selected_npad_id = static_cast<u32>(
HID::Controller_NPad::MapSettingsTypeToNPad(Settings::values.players[0].type));
out.result = !ok;
complete = true;
std::vector<u8> out_data(sizeof(ControllerConfigOutput));
std::memcpy(out_data.data(), &out, sizeof(ControllerConfigOutput));
broker.PushNormalDataFromApplet(IStorage{out_data});
broker.SignalStateChanged();
}
} // namespace Service::AM::Applets

View File

@@ -0,0 +1,61 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applets.h"
namespace Service::AM::Applets {
struct ControllerConfigPrivate {
u32 private_arg_size; // unknown, seems to be an offset of some kind
u32 normal_arg_size;
u32 flags;
u32 npad_style_set;
u32 npad_hold_type;
};
static_assert(sizeof(ControllerConfigPrivate) == 0x14,
"ControllerConfigPrivate has incorrect size");
struct ControllerConfig {
u8 minimum_player_count;
u8 maximum_player_count;
u8 keep_current_connections;
u8 left_justify;
u8 permit_dual_joycons;
u8 enable_single_mode;
INSERT_PADDING_BYTES(0x216);
};
static_assert(sizeof(ControllerConfig) == 0x21C, "ControllerConfig has incorrect size");
struct ControllerConfigOutput {
u8 player_code;
INSERT_PADDING_BYTES(0x3);
u32 selected_npad_id;
u32 result;
};
static_assert(sizeof(ControllerConfigOutput) == 0xC, "ControllerConfigOutput has incorrect size");
class Controller final : public Applet {
public:
Controller();
~Controller() override;
void Initialize() override;
bool TransactionComplete() const override;
ResultCode GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
void ConfigurationComplete(bool ok);
private:
ControllerConfigPrivate private_config;
ControllerConfig config;
bool complete = false;
};
} // namespace Service::AM::Applets

View File

@@ -33,19 +33,37 @@ enum class JoystickId : std::size_t {
Joystick_Right,
};
static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) {
Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
Settings::ControllerType type) {
switch (type) {
case Settings::ControllerType::ProController:
return Controller_NPad::NPadControllerType::ProController;
return NPadControllerType::ProController;
case Settings::ControllerType::DualJoycon:
return Controller_NPad::NPadControllerType::JoyDual;
return NPadControllerType::JoyDual;
case Settings::ControllerType::LeftJoycon:
return Controller_NPad::NPadControllerType::JoyLeft;
return NPadControllerType::JoyLeft;
case Settings::ControllerType::RightJoycon:
return Controller_NPad::NPadControllerType::JoyRight;
return NPadControllerType::JoyRight;
default:
UNREACHABLE();
return Controller_NPad::NPadControllerType::JoyDual;
return NPadControllerType::JoyDual;
}
}
Settings::ControllerType Controller_NPad::MapNPadTypeToSettings(NPadControllerType type) {
switch (type) {
case NPadControllerType::ProController:
case NPadControllerType::Handheld:
return Settings::ControllerType::ProController;
case NPadControllerType::JoyDual:
return Settings::ControllerType::DualJoycon;
case NPadControllerType::JoyLeft:
return Settings::ControllerType::LeftJoycon;
case NPadControllerType::JoyRight:
return Settings::ControllerType::RightJoycon;
default:
UNREACHABLE();
return Settings::ControllerType::DualJoycon;
}
}
@@ -96,7 +114,7 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
Controller_NPad::Controller_NPad() = default;
Controller_NPad::~Controller_NPad() = default;
void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
const auto controller_type = connected_controllers[controller_idx].type;
auto& controller = shared_memory_entries[controller_idx];
if (controller_type == NPadControllerType::None) {
@@ -448,7 +466,7 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
AddNewController(requested_controller);
} else {
controller.type = requested_controller;
InitNewlyAddedControler(i);
InitNewlyAddedController(i);
}
had_controller_update = true;
}
@@ -513,7 +531,7 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
controller = DecideBestController(controller);
if (controller == NPadControllerType::Handheld) {
connected_controllers[8] = {controller, true};
InitNewlyAddedControler(8);
InitNewlyAddedController(8);
return;
}
const auto pos =
@@ -525,19 +543,19 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
}
const auto controller_id = std::distance(connected_controllers.begin(), pos);
connected_controllers[controller_id] = {controller, true};
InitNewlyAddedControler(controller_id);
InitNewlyAddedController(controller_id);
}
void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
controller = DecideBestController(controller);
if (controller == NPadControllerType::Handheld) {
connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true};
InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD));
InitNewlyAddedController(NPadIdToIndex(NPAD_HANDHELD));
return;
}
connected_controllers[NPadIdToIndex(npad_id)] = {controller, true};
InitNewlyAddedControler(NPadIdToIndex(npad_id));
InitNewlyAddedController(NPadIdToIndex(npad_id));
}
void Controller_NPad::ConnectNPad(u32 npad_id) {
@@ -548,36 +566,6 @@ void Controller_NPad::DisconnectNPad(u32 npad_id) {
connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
}
bool Controller_NPad::IsControllerSupported(NPadControllerType controller) {
if (controller == NPadControllerType::Handheld) {
// Handheld is not even a supported type, lets stop here
if (std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
NPAD_HANDHELD) == supported_npad_id_types.end()) {
return false;
}
// Handheld should not be supported in docked mode
if (Settings::values.use_docked_mode) {
return false;
}
}
switch (controller) {
case NPadControllerType::ProController:
return style.pro_controller;
case NPadControllerType::Handheld:
return style.handheld;
case NPadControllerType::JoyDual:
return style.joycon_dual;
case NPadControllerType::JoyLeft:
return style.joycon_left;
case NPadControllerType::JoyRight:
return style.joycon_right;
case NPadControllerType::Pokeball:
return style.pokeball;
default:
return false;
}
}
Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
if (npad_id == npad_id_list.back() || npad_id == npad_id_list[npad_id_list.size() - 2]) {
// These are controllers without led patterns

View File

@@ -128,6 +128,10 @@ public:
// Specifically for cheat engine and other features.
u32 GetAndResetPressState();
NPadControllerType DecideBestController(NPadControllerType priority) const;
static NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type);
static Settings::ControllerType MapNPadTypeToSettings(NPadControllerType type);
static std::size_t NPadIdToIndex(u32 npad_id);
static u32 IndexToNPad(std::size_t index);
@@ -315,11 +319,9 @@ private:
std::array<ControllerHolder, 10> connected_controllers{};
bool can_controllers_vibrate{true};
void InitNewlyAddedControler(std::size_t controller_idx);
void InitNewlyAddedController(std::size_t controller_idx);
bool IsControllerSupported(NPadControllerType controller) const;
NPadControllerType DecideBestController(NPadControllerType priority) const;
void RequestPadStateUpdate(u32 npad_id);
std::array<ControllerPad, 10> npad_pad_states{};
bool IsControllerSupported(NPadControllerType controller);
};
} // namespace Service::HID

View File

@@ -7,6 +7,9 @@
#include "controllers/controller_base.h"
#include "core/hle/service/service.h"
#include "controllers/controller_base.h"
#include "core/hle/service/service.h"
namespace Core::Timing {
struct EventType;
}

View File

@@ -7,6 +7,8 @@ add_executable(yuzu
Info.plist
about_dialog.cpp
about_dialog.h
applets/controller.cpp
applets/controller.h
applets/profile_select.cpp
applets/profile_select.h
applets/software_keyboard.cpp

View File

@@ -0,0 +1,65 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QDialogButtonBox>
#include <QLabel>
#include "core/hle/lock.h"
#include "core/hle/service/hid/hid.h"
#include "yuzu/applets/controller.h"
#include "yuzu/configuration/configure_input_simple.h"
#include "yuzu/main.h"
QtControllerAppletDialog::QtControllerAppletDialog(
QWidget* parent, Core::Frontend::ControllerParameters parameters) {
layout = new QVBoxLayout;
buttons = new QDialogButtonBox;
buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
buttons->addButton(tr("OK"), QDialogButtonBox::AcceptRole);
connect(buttons, &QDialogButtonBox::accepted, this, &QtControllerAppletDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QtControllerAppletDialog::reject);
input = new ConfigureInputSimple(this, parameters);
layout->addWidget(input);
layout->addWidget(buttons);
setLayout(layout);
}
QtControllerAppletDialog::~QtControllerAppletDialog() = default;
void QtControllerAppletDialog::accept() {
input->applyConfiguration();
ok = true;
QDialog::accept();
}
void QtControllerAppletDialog::reject() {
ok = false;
QDialog::reject();
}
bool QtControllerAppletDialog::GetStatus() const {
return ok;
}
QtControllerApplet::QtControllerApplet(GMainWindow& parent) {
connect(this, &QtControllerApplet::MainWindowReconfigureControllers, &parent,
&GMainWindow::ControllerAppletReconfigureControllers, Qt::QueuedConnection);
connect(&parent, &GMainWindow::ControllerAppletReconfigureFinished, this,
&QtControllerApplet::MainWindowReconfigureFinished, Qt::QueuedConnection);
}
QtControllerApplet::~QtControllerApplet() = default;
void QtControllerApplet::ReconfigureControllers(
std::function<void(bool)> completed, Core::Frontend::ControllerParameters parameters) const {
this->completed = std::move(completed);
emit MainWindowReconfigureControllers(parameters);
}
void QtControllerApplet::MainWindowReconfigureFinished(bool ok) {
// Acquire the HLE mutex
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
completed(ok);
}

View File

@@ -0,0 +1,55 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <QDialog>
#include "core/frontend/applets/controller.h"
class ConfigureInputSimple;
class GMainWindow;
class QDialogButtonBox;
class QVBoxLayout;
class QtControllerAppletDialog final : public QDialog {
Q_OBJECT
public:
QtControllerAppletDialog(QWidget* parent, Core::Frontend::ControllerParameters parameters);
~QtControllerAppletDialog() override;
void accept() override;
void reject() override;
bool GetStatus() const;
private:
bool ok = false;
QVBoxLayout* layout;
QDialogButtonBox* buttons;
ConfigureInputSimple* input;
Core::Frontend::ControllerParameters parameters;
};
class QtControllerApplet final : public QObject, public Core::Frontend::ControllerApplet {
Q_OBJECT
public:
explicit QtControllerApplet(GMainWindow& parent);
~QtControllerApplet() override;
void ReconfigureControllers(std::function<void(bool)> completed,
Core::Frontend::ControllerParameters parameters) const override;
signals:
void MainWindowReconfigureControllers(Core::Frontend::ControllerParameters parameters) const;
private:
void MainWindowReconfigureFinished(bool ok);
mutable std::function<void(bool)> completed;
};

View File

@@ -90,8 +90,9 @@ void ApplyInputProfileConfiguration(int profile_index) {
INPUT_PROFILES.at(std::min(profile_index, static_cast<int>(INPUT_PROFILES.size() - 1))))();
}
ConfigureInputSimple::ConfigureInputSimple(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInputSimple>()) {
ConfigureInputSimple::ConfigureInputSimple(
QWidget* parent, std::optional<Core::Frontend::ControllerParameters> constraints)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureInputSimple>()), constraints(constraints) {
ui->setupUi(this);
for (const auto& profile : INPUT_PROFILES) {
@@ -104,6 +105,7 @@ ConfigureInputSimple::ConfigureInputSimple(QWidget* parent)
connect(ui->profile_configure, &QPushButton::pressed, this, &ConfigureInputSimple::OnConfigure);
this->loadConfiguration();
UpdateErrors();
}
ConfigureInputSimple::~ConfigureInputSimple() = default;
@@ -130,8 +132,96 @@ void ConfigureInputSimple::OnSelectProfile(int index) {
const auto old_docked = Settings::values.use_docked_mode;
ApplyInputProfileConfiguration(index);
OnDockedModeChanged(old_docked, Settings::values.use_docked_mode);
UpdateErrors();
}
void ConfigureInputSimple::OnConfigure() {
std::get<2>(INPUT_PROFILES.at(ui->profile_combobox->currentIndex()))(this);
UpdateErrors();
}
static bool AnyPlayersMatchingType(Settings::ControllerType type) {
return std::any_of(
Settings::values.players.begin(), Settings::values.players.begin() + 8,
[type](const Settings::PlayerInput& in) { return in.type == type && in.connected; });
}
void ConfigureInputSimple::UpdateErrors() {
if (constraints == std::nullopt) {
ui->error_view->setHidden(true);
return;
}
ui->error_view->setHidden(false);
QString text;
const auto number_player =
std::count_if(Settings::values.players.begin(), Settings::values.players.begin() + 9,
[](const Settings::PlayerInput& in) { return in.connected; });
if (number_player < constraints->min_players) {
text += tr("<li>The game requires a <i>minimum</i> of %1 players, you currently have %2 "
"players.</li>")
.arg(constraints->min_players)
.arg(number_player);
}
if (number_player > constraints->max_players) {
text += tr("<li>The game allows a <i>maximum</i> of %1 players, you currently have %2 "
"players.</li>")
.arg(constraints->max_players)
.arg(number_player);
}
if (AnyPlayersMatchingType(Settings::ControllerType::ProController) &&
!constraints->allowed_pro_controller) {
text += tr("<li>The game does not allow the use of the <i>Pro Controller</i>.</li>");
}
if (AnyPlayersMatchingType(Settings::ControllerType::DualJoycon) &&
!constraints->allowed_joycon_dual) {
text += tr("<li>The game does not allow the use of <i>Dual Joycons</i>.</li>");
}
if (AnyPlayersMatchingType(Settings::ControllerType::LeftJoycon) &&
!constraints->allowed_joycon_left) {
text += tr("<li>The game does not allow the use of the <i>Single Left Joycon</i> "
"controller.</li>");
}
if (AnyPlayersMatchingType(Settings::ControllerType::RightJoycon) &&
!constraints->allowed_joycon_right) {
text += tr("<li>The game does not allow the use of the <i>Single Right Joycon</i> "
"controller.</li>");
}
if (Settings::values.players[HANDHELD_INDEX].connected && !constraints->allowed_handheld) {
text += tr("<li>The game does not allow the use of the <i>Handheld</i> Controller.</li>");
}
const auto has_single = AnyPlayersMatchingType(Settings::ControllerType::LeftJoycon) ||
AnyPlayersMatchingType(Settings::ControllerType::RightJoycon);
if (has_single && !constraints->allowed_single_layout) {
text += tr("<li>The game does not allow <i>single joycon</i> controllers.</li>");
}
if (has_single && constraints->merge_dual_joycons) {
text += tr(
"<li>The game requires that pairs of <i>single joycons</i> be merged into one <i>Dual "
"Joycon</i> controller.</li>");
}
if (!text.isEmpty()) {
is_valid = false;
ui->error_view->setText(
"<h4 style=\"color:#CC0000;\">The following errors were identified in "
"your input configuration:</h4><ul>" +
text + "</ul>");
} else {
is_valid = true;
ui->error_view->setText(
tr("<h4 style=\"color:#00CC00;\">Your input configuration is valid.</h4>"));
}
}

View File

@@ -5,9 +5,12 @@
#pragma once
#include <memory>
#include <optional>
#include <QWidget>
#include "core/frontend/applets/controller.h"
class QPushButton;
class QString;
class QTimer;
@@ -23,7 +26,9 @@ class ConfigureInputSimple : public QWidget {
Q_OBJECT
public:
explicit ConfigureInputSimple(QWidget* parent = nullptr);
explicit ConfigureInputSimple(
QWidget* parent = nullptr,
std::optional<Core::Frontend::ControllerParameters> constraints = std::nullopt);
~ConfigureInputSimple() override;
/// Save all button configurations to settings file
@@ -36,5 +41,9 @@ private:
void OnSelectProfile(int index);
void OnConfigure();
void UpdateErrors();
std::unique_ptr<Ui::ConfigureInputSimple> ui;
std::optional<Core::Frontend::ControllerParameters> constraints;
bool is_valid = true;
};

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>473</width>
<height>685</height>
<height>247</height>
</rect>
</property>
<property name="windowTitle">
@@ -90,6 +90,16 @@
</property>
</spacer>
</item>
<item>
<widget class="QTextBrowser" name="error_view">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>120</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>

View File

@@ -8,12 +8,15 @@
#include <thread>
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
#include "applets/controller.h"
#include "applets/profile_select.h"
#include "applets/software_keyboard.h"
#include "applets/web_browser.h"
#include "configuration/configure_input_simple.h"
#include "configuration/configure_per_general.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h"
#include "core/frontend/applets/controller.h"
#include "core/frontend/scope_acquire_window_context.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/applets/applets.h"
@@ -222,6 +225,17 @@ GMainWindow::~GMainWindow() {
delete render_window;
}
void GMainWindow::ControllerAppletReconfigureControllers(
const Core::Frontend::ControllerParameters& parameters) {
QtControllerAppletDialog dialog(this, parameters);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
dialog.exec();
emit ControllerAppletReconfigureFinished(dialog.GetStatus());
}
void GMainWindow::ProfileSelectorSelectProfile() {
QtProfileSelectionDialog dialog(this);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
@@ -777,6 +791,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
system.SetGPUDebugContext(debug_context);
system.SetControllerApplet(std::make_unique<QtControllerApplet>(*this));
system.SetProfileSelector(std::make_unique<QtProfileSelector>(*this));
system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this));
system.SetWebBrowser(std::make_unique<QtWebBrowser>(*this));
@@ -1491,6 +1506,7 @@ void GMainWindow::OnMenuRecentFile() {
void GMainWindow::OnStartGame() {
emu_thread->SetRunning(true);
qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>(
"Core::Frontend::SoftwareKeyboardParameters");
qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");

View File

@@ -33,6 +33,7 @@ class WaitTreeWidget;
enum class GameListOpenTarget;
namespace Core::Frontend {
struct ControllerParameters;
struct SoftwareKeyboardParameters;
} // namespace Core::Frontend
@@ -102,7 +103,10 @@ signals:
// Signal that tells widgets to update icons to use the current theme
void UpdateThemedIcons();
void ControllerAppletReconfigureFinished(bool ok);
void ProfileSelectorFinishedSelection(std::optional<Service::Account::UUID> uuid);
void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
void SoftwareKeyboardFinishedCheckDialog();
@@ -111,7 +115,11 @@ signals:
public slots:
void OnLoadComplete();
void ControllerAppletReconfigureControllers(
const Core::Frontend::ControllerParameters& parameters);
void ProfileSelectorSelectProfile();
void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
void WebBrowserOpenPage(std::string_view filename, std::string_view arguments);