Merge pull request #10006 from german77/profile_select

service: am: Improve profile select applet
This commit is contained in:
liamwhite 2023-04-01 09:49:22 -04:00 committed by GitHub
commit 1ab052952d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 270 additions and 34 deletions

View file

@ -11,7 +11,8 @@ ProfileSelectApplet::~ProfileSelectApplet() = default;
void DefaultProfileSelectApplet::Close() const {} void DefaultProfileSelectApplet::Close() const {}
void DefaultProfileSelectApplet::SelectProfile(SelectProfileCallback callback) const { void DefaultProfileSelectApplet::SelectProfile(SelectProfileCallback callback,
const ProfileSelectParameters& parameters) const {
Service::Account::ProfileManager manager; Service::Account::ProfileManager manager;
callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{})); callback(manager.GetUser(Settings::values.current_user.GetValue()).value_or(Common::UUID{}));
LOG_INFO(Service_ACC, "called, selecting current user instead of prompting..."); LOG_INFO(Service_ACC, "called, selecting current user instead of prompting...");

View file

@ -5,25 +5,35 @@
#include <functional> #include <functional>
#include <optional> #include <optional>
#include "common/uuid.h"
#include "common/uuid.h"
#include "core/frontend/applets/applet.h" #include "core/frontend/applets/applet.h"
#include "core/hle/service/am/applets/applet_profile_select.h"
namespace Core::Frontend { namespace Core::Frontend {
struct ProfileSelectParameters {
Service::AM::Applets::UiMode mode;
std::array<Common::UUID, 8> invalid_uid_list;
Service::AM::Applets::UiSettingsDisplayOptions display_options;
Service::AM::Applets::UserSelectionPurpose purpose;
};
class ProfileSelectApplet : public Applet { class ProfileSelectApplet : public Applet {
public: public:
using SelectProfileCallback = std::function<void(std::optional<Common::UUID>)>; using SelectProfileCallback = std::function<void(std::optional<Common::UUID>)>;
virtual ~ProfileSelectApplet(); virtual ~ProfileSelectApplet();
virtual void SelectProfile(SelectProfileCallback callback) const = 0; virtual void SelectProfile(SelectProfileCallback callback,
const ProfileSelectParameters& parameters) const = 0;
}; };
class DefaultProfileSelectApplet final : public ProfileSelectApplet { class DefaultProfileSelectApplet final : public ProfileSelectApplet {
public: public:
void Close() const override; void Close() const override;
void SelectProfile(SelectProfileCallback callback) const override; void SelectProfile(SelectProfileCallback callback,
const ProfileSelectParameters& parameters) const override;
}; };
} // namespace Core::Frontend } // namespace Core::Frontend

View file

@ -25,13 +25,29 @@ void ProfileSelect::Initialize() {
final_data.clear(); final_data.clear();
Applet::Initialize(); Applet::Initialize();
profile_select_version = ProfileSelectAppletVersion{common_args.library_version};
const auto user_config_storage = broker.PopNormalDataToApplet(); const auto user_config_storage = broker.PopNormalDataToApplet();
ASSERT(user_config_storage != nullptr); ASSERT(user_config_storage != nullptr);
const auto& user_config = user_config_storage->GetData(); const auto& user_config = user_config_storage->GetData();
ASSERT(user_config.size() >= sizeof(UserSelectionConfig)); LOG_INFO(Service_AM, "Initializing Profile Select Applet with version={}",
std::memcpy(&config, user_config.data(), sizeof(UserSelectionConfig)); profile_select_version);
switch (profile_select_version) {
case ProfileSelectAppletVersion::Version1:
ASSERT(user_config.size() == sizeof(UiSettingsV1));
std::memcpy(&config_old, user_config.data(), sizeof(UiSettingsV1));
break;
case ProfileSelectAppletVersion::Version2:
case ProfileSelectAppletVersion::Version3:
ASSERT(user_config.size() == sizeof(UiSettings));
std::memcpy(&config, user_config.data(), sizeof(UiSettings));
break;
default:
UNIMPLEMENTED_MSG("Unknown profile_select_version = {}", profile_select_version);
break;
}
} }
bool ProfileSelect::TransactionComplete() const { bool ProfileSelect::TransactionComplete() const {
@ -52,11 +68,37 @@ void ProfileSelect::Execute() {
return; return;
} }
frontend.SelectProfile([this](std::optional<Common::UUID> uuid) { SelectionComplete(uuid); }); Core::Frontend::ProfileSelectParameters parameters{};
switch (profile_select_version) {
case ProfileSelectAppletVersion::Version1:
parameters = {
.mode = config_old.mode,
.invalid_uid_list = config_old.invalid_uid_list,
.display_options = config_old.display_options,
.purpose = UserSelectionPurpose::General,
};
break;
case ProfileSelectAppletVersion::Version2:
case ProfileSelectAppletVersion::Version3:
parameters = {
.mode = config.mode,
.invalid_uid_list = config.invalid_uid_list,
.display_options = config.display_options,
.purpose = config.purpose,
};
break;
default:
UNIMPLEMENTED_MSG("Unknown profile_select_version = {}", profile_select_version);
break;
}
frontend.SelectProfile([this](std::optional<Common::UUID> uuid) { SelectionComplete(uuid); },
parameters);
} }
void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) { void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
UserSelectionOutput output{}; UiReturnArg output{};
if (uuid.has_value() && uuid->IsValid()) { if (uuid.has_value() && uuid->IsValid()) {
output.result = 0; output.result = 0;
@ -67,7 +109,7 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
output.uuid_selected = Common::InvalidUUID; output.uuid_selected = Common::InvalidUUID;
} }
final_data = std::vector<u8>(sizeof(UserSelectionOutput)); final_data = std::vector<u8>(sizeof(UiReturnArg));
std::memcpy(final_data.data(), &output, final_data.size()); std::memcpy(final_data.data(), &output, final_data.size());
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(final_data))); broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(final_data)));
broker.SignalStateChanged(); broker.SignalStateChanged();

View file

@ -16,19 +16,100 @@ class System;
namespace Service::AM::Applets { namespace Service::AM::Applets {
struct UserSelectionConfig { enum class ProfileSelectAppletVersion : u32 {
// TODO(DarkLordZach): RE this structure Version1 = 0x1, // 1.0.0+
// It seems to be flags and the like that determine the UI of the applet on the switch... from Version2 = 0x10000, // 2.0.0+
// my research this is safe to ignore for now. Version3 = 0x20000, // 6.0.0+
INSERT_PADDING_BYTES(0xA0);
}; };
static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has incorrect size.");
struct UserSelectionOutput { // This is nn::account::UiMode
enum class UiMode {
UserSelector,
UserCreator,
EnsureNetworkServiceAccountAvailable,
UserIconEditor,
UserNicknameEditor,
UserCreatorForStarter,
NintendoAccountAuthorizationRequestContext,
IntroduceExternalNetworkServiceAccount,
IntroduceExternalNetworkServiceAccountForRegistration,
NintendoAccountNnidLinker,
LicenseRequirementsForNetworkService,
LicenseRequirementsForNetworkServiceWithUserContextImpl,
UserCreatorForImmediateNaLoginTest,
UserQualificationPromoter,
};
// This is nn::account::UserSelectionPurpose
enum class UserSelectionPurpose {
General,
GameCardRegistration,
EShopLaunch,
EShopItemShow,
PicturePost,
NintendoAccountLinkage,
SettingsUpdate,
SaveDataDeletion,
UserMigration,
SaveDataTransfer,
};
// This is nn::account::NintendoAccountStartupDialogType
enum class NintendoAccountStartupDialogType {
LoginAndCreate,
Login,
Create,
};
// This is nn::account::UserSelectionSettingsForSystemService
struct UserSelectionSettingsForSystemService {
UserSelectionPurpose purpose;
bool enable_user_creation;
INSERT_PADDING_BYTES(0x3);
};
static_assert(sizeof(UserSelectionSettingsForSystemService) == 0x8,
"UserSelectionSettingsForSystemService has incorrect size.");
struct UiSettingsDisplayOptions {
bool is_network_service_account_required;
bool is_skip_enabled;
bool is_system_or_launcher;
bool is_registration_permitted;
bool show_skip_button;
bool aditional_select;
bool show_user_selector;
bool is_unqualified_user_selectable;
};
static_assert(sizeof(UiSettingsDisplayOptions) == 0x8,
"UiSettingsDisplayOptions has incorrect size.");
struct UiSettingsV1 {
UiMode mode;
INSERT_PADDING_BYTES(0x4);
std::array<Common::UUID, 8> invalid_uid_list;
u64 application_id;
UiSettingsDisplayOptions display_options;
};
static_assert(sizeof(UiSettingsV1) == 0x98, "UiSettings has incorrect size.");
// This is nn::account::UiSettings
struct UiSettings {
UiMode mode;
INSERT_PADDING_BYTES(0x4);
std::array<Common::UUID, 8> invalid_uid_list;
u64 application_id;
UiSettingsDisplayOptions display_options;
UserSelectionPurpose purpose;
INSERT_PADDING_BYTES(0x4);
};
static_assert(sizeof(UiSettings) == 0xA0, "UiSettings has incorrect size.");
// This is nn::account::UiReturnArg
struct UiReturnArg {
u64 result; u64 result;
Common::UUID uuid_selected; Common::UUID uuid_selected;
}; };
static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size."); static_assert(sizeof(UiReturnArg) == 0x18, "UiReturnArg has incorrect size.");
class ProfileSelect final : public Applet { class ProfileSelect final : public Applet {
public: public:
@ -49,7 +130,10 @@ public:
private: private:
const Core::Frontend::ProfileSelectApplet& frontend; const Core::Frontend::ProfileSelectApplet& frontend;
UserSelectionConfig config; UiSettings config;
UiSettingsV1 config_old;
ProfileSelectAppletVersion profile_select_version;
bool complete = false; bool complete = false;
Result status = ResultSuccess; Result status = ResultSuccess;
std::vector<u8> final_data; std::vector<u8> final_data;

View file

@ -46,11 +46,13 @@ QPixmap GetIcon(Common::UUID uuid) {
} }
} // Anonymous namespace } // Anonymous namespace
QtProfileSelectionDialog::QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, QWidget* parent) QtProfileSelectionDialog::QtProfileSelectionDialog(
Core::HID::HIDCore& hid_core, QWidget* parent,
const Core::Frontend::ProfileSelectParameters& parameters)
: QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) { : QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
outer_layout = new QVBoxLayout; outer_layout = new QVBoxLayout;
instruction_label = new QLabel(tr("Select a user:")); instruction_label = new QLabel();
scroll_area = new QScrollArea; scroll_area = new QScrollArea;
@ -120,7 +122,8 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(Core::HID::HIDCore& hid_core,
item_model->appendRow(item); item_model->appendRow(item);
setLayout(outer_layout); setLayout(outer_layout);
setWindowTitle(tr("Profile Selector")); SetWindowTitle(parameters);
SetDialogPurpose(parameters);
resize(550, 400); resize(550, 400);
} }
@ -154,6 +157,76 @@ void QtProfileSelectionDialog::SelectUser(const QModelIndex& index) {
user_index = index.row(); user_index = index.row();
} }
void QtProfileSelectionDialog::SetWindowTitle(
const Core::Frontend::ProfileSelectParameters& parameters) {
using Service::AM::Applets::UiMode;
switch (parameters.mode) {
case UiMode::UserCreator:
case UiMode::UserCreatorForStarter:
setWindowTitle(tr("Profile Creator"));
return;
case UiMode::EnsureNetworkServiceAccountAvailable:
setWindowTitle(tr("Profile Selector"));
return;
case UiMode::UserIconEditor:
setWindowTitle(tr("Profile Icon Editor"));
return;
case UiMode::UserNicknameEditor:
setWindowTitle(tr("Profile Nickname Editor"));
return;
case UiMode::NintendoAccountAuthorizationRequestContext:
case UiMode::IntroduceExternalNetworkServiceAccount:
case UiMode::IntroduceExternalNetworkServiceAccountForRegistration:
case UiMode::NintendoAccountNnidLinker:
case UiMode::LicenseRequirementsForNetworkService:
case UiMode::LicenseRequirementsForNetworkServiceWithUserContextImpl:
case UiMode::UserCreatorForImmediateNaLoginTest:
case UiMode::UserQualificationPromoter:
case UiMode::UserSelector:
default:
setWindowTitle(tr("Profile Selector"));
}
}
void QtProfileSelectionDialog::SetDialogPurpose(
const Core::Frontend::ProfileSelectParameters& parameters) {
using Service::AM::Applets::UserSelectionPurpose;
switch (parameters.purpose) {
case UserSelectionPurpose::GameCardRegistration:
instruction_label->setText(tr("Who will receive the points?"));
return;
case UserSelectionPurpose::EShopLaunch:
instruction_label->setText(tr("Who is using Nintendo eShop?"));
return;
case UserSelectionPurpose::EShopItemShow:
instruction_label->setText(tr("Who is making this purchase?"));
return;
case UserSelectionPurpose::PicturePost:
instruction_label->setText(tr("Who is posting?"));
return;
case UserSelectionPurpose::NintendoAccountLinkage:
instruction_label->setText(tr("Select a user to link to a Nintendo Account."));
return;
case UserSelectionPurpose::SettingsUpdate:
instruction_label->setText(tr("Change settings for which user?"));
return;
case UserSelectionPurpose::SaveDataDeletion:
instruction_label->setText(tr("Format data for which user?"));
return;
case UserSelectionPurpose::UserMigration:
instruction_label->setText(tr("Which user will be transferred to another console?"));
return;
case UserSelectionPurpose::SaveDataTransfer:
instruction_label->setText(tr("Send save data for which user?"));
return;
case UserSelectionPurpose::General:
default:
instruction_label->setText(tr("Select a user:"));
return;
}
}
QtProfileSelector::QtProfileSelector(GMainWindow& parent) { QtProfileSelector::QtProfileSelector(GMainWindow& parent) {
connect(this, &QtProfileSelector::MainWindowSelectProfile, &parent, connect(this, &QtProfileSelector::MainWindowSelectProfile, &parent,
&GMainWindow::ProfileSelectorSelectProfile, Qt::QueuedConnection); &GMainWindow::ProfileSelectorSelectProfile, Qt::QueuedConnection);
@ -170,9 +243,11 @@ void QtProfileSelector::Close() const {
emit MainWindowRequestExit(); emit MainWindowRequestExit();
} }
void QtProfileSelector::SelectProfile(SelectProfileCallback callback_) const { void QtProfileSelector::SelectProfile(
SelectProfileCallback callback_,
const Core::Frontend::ProfileSelectParameters& parameters) const {
callback = std::move(callback_); callback = std::move(callback_);
emit MainWindowSelectProfile(); emit MainWindowSelectProfile(parameters);
} }
void QtProfileSelector::MainWindowFinishedSelection(std::optional<Common::UUID> uuid) { void QtProfileSelector::MainWindowFinishedSelection(std::optional<Common::UUID> uuid) {

View file

@ -28,7 +28,8 @@ class QtProfileSelectionDialog final : public QDialog {
Q_OBJECT Q_OBJECT
public: public:
explicit QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, QWidget* parent); explicit QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, QWidget* parent,
const Core::Frontend::ProfileSelectParameters& parameters);
~QtProfileSelectionDialog() override; ~QtProfileSelectionDialog() override;
int exec() override; int exec() override;
@ -40,6 +41,9 @@ public:
private: private:
void SelectUser(const QModelIndex& index); void SelectUser(const QModelIndex& index);
void SetWindowTitle(const Core::Frontend::ProfileSelectParameters& parameters);
void SetDialogPurpose(const Core::Frontend::ProfileSelectParameters& parameters);
int user_index = 0; int user_index = 0;
QVBoxLayout* layout; QVBoxLayout* layout;
@ -66,10 +70,11 @@ public:
~QtProfileSelector() override; ~QtProfileSelector() override;
void Close() const override; void Close() const override;
void SelectProfile(SelectProfileCallback callback_) const override; void SelectProfile(SelectProfileCallback callback_,
const Core::Frontend::ProfileSelectParameters& parameters) const override;
signals: signals:
void MainWindowSelectProfile() const; void MainWindowSelectProfile(const Core::Frontend::ProfileSelectParameters& parameters) const;
void MainWindowRequestExit() const; void MainWindowRequestExit() const;
private: private:

View file

@ -576,6 +576,10 @@ void GMainWindow::RegisterMetaTypes() {
// Controller Applet // Controller Applet
qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters"); qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
// Profile Select Applet
qRegisterMetaType<Core::Frontend::ProfileSelectParameters>(
"Core::Frontend::ProfileSelectParameters");
// Software Keyboard Applet // Software Keyboard Applet
qRegisterMetaType<Core::Frontend::KeyboardInitializeParameters>( qRegisterMetaType<Core::Frontend::KeyboardInitializeParameters>(
"Core::Frontend::KeyboardInitializeParameters"); "Core::Frontend::KeyboardInitializeParameters");
@ -652,8 +656,9 @@ void GMainWindow::ControllerSelectorRequestExit() {
} }
} }
void GMainWindow::ProfileSelectorSelectProfile() { void GMainWindow::ProfileSelectorSelectProfile(
profile_select_applet = new QtProfileSelectionDialog(system->HIDCore(), this); const Core::Frontend::ProfileSelectParameters& parameters) {
profile_select_applet = new QtProfileSelectionDialog(system->HIDCore(), this, parameters);
SCOPE_EXIT({ SCOPE_EXIT({
profile_select_applet->deleteLater(); profile_select_applet->deleteLater();
profile_select_applet = nullptr; profile_select_applet = nullptr;
@ -1720,8 +1725,9 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
return true; return true;
} }
bool GMainWindow::SelectAndSetCurrentUser() { bool GMainWindow::SelectAndSetCurrentUser(
QtProfileSelectionDialog dialog(system->HIDCore(), this); const Core::Frontend::ProfileSelectParameters& parameters) {
QtProfileSelectionDialog dialog(system->HIDCore(), this, parameters);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal); dialog.setWindowModality(Qt::WindowModal);
@ -1767,7 +1773,13 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
Settings::LogSettings(); Settings::LogSettings();
if (UISettings::values.select_user_on_boot) { if (UISettings::values.select_user_on_boot) {
if (SelectAndSetCurrentUser() == false) { const Core::Frontend::ProfileSelectParameters parameters{
.mode = Service::AM::Applets::UiMode::UserSelector,
.invalid_uid_list = {},
.display_options = {},
.purpose = Service::AM::Applets::UserSelectionPurpose::General,
};
if (SelectAndSetCurrentUser(parameters) == false) {
return; return;
} }
} }
@ -2059,7 +2071,13 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
if (has_user_save) { if (has_user_save) {
// User save data // User save data
const auto select_profile = [this] { const auto select_profile = [this] {
QtProfileSelectionDialog dialog(system->HIDCore(), this); const Core::Frontend::ProfileSelectParameters parameters{
.mode = Service::AM::Applets::UiMode::UserSelector,
.invalid_uid_list = {},
.display_options = {},
.purpose = Service::AM::Applets::UserSelectionPurpose::General,
};
QtProfileSelectionDialog dialog(system->HIDCore(), this, parameters);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal); dialog.setWindowModality(Qt::WindowModal);

View file

@ -69,6 +69,7 @@ struct ControllerParameters;
struct InlineAppearParameters; struct InlineAppearParameters;
struct InlineTextParameters; struct InlineTextParameters;
struct KeyboardInitializeParameters; struct KeyboardInitializeParameters;
struct ProfileSelectParameters;
} // namespace Core::Frontend } // namespace Core::Frontend
namespace DiscordRPC { namespace DiscordRPC {
@ -203,7 +204,7 @@ public slots:
void SoftwareKeyboardExit(); void SoftwareKeyboardExit();
void ErrorDisplayDisplayError(QString error_code, QString error_text); void ErrorDisplayDisplayError(QString error_code, QString error_text);
void ErrorDisplayRequestExit(); void ErrorDisplayRequestExit();
void ProfileSelectorSelectProfile(); void ProfileSelectorSelectProfile(const Core::Frontend::ProfileSelectParameters& parameters);
void ProfileSelectorRequestExit(); void ProfileSelectorRequestExit();
void WebBrowserOpenWebPage(const std::string& main_url, const std::string& additional_args, void WebBrowserOpenWebPage(const std::string& main_url, const std::string& additional_args,
bool is_local); bool is_local);
@ -242,7 +243,7 @@ private:
void SetDiscordEnabled(bool state); void SetDiscordEnabled(bool state);
void LoadAmiibo(const QString& filename); void LoadAmiibo(const QString& filename);
bool SelectAndSetCurrentUser(); bool SelectAndSetCurrentUser(const Core::Frontend::ProfileSelectParameters& parameters);
/** /**
* Stores the filename in the recently loaded files list. * Stores the filename in the recently loaded files list.