When profile select applet is asking to select a profile (#99)
Co-authored-by: N3xoX1 <n3xox1@nxemu.com>
This commit is contained in:
parent
9270b8f9c3
commit
cff490d184
|
|
@ -0,0 +1,86 @@
|
|||
.profile-select-body
|
||||
{
|
||||
padding: 0.75rem 0.5rem 0.25rem;
|
||||
min-width: 30rem;
|
||||
min-height: 18rem;
|
||||
color: var(--color-ui-text-primary);
|
||||
font-family: var(--font-ui);
|
||||
font-size: 0.9rem;
|
||||
flow: vertical;
|
||||
height: *;
|
||||
}
|
||||
|
||||
.profile-select-instruction
|
||||
{
|
||||
margin: 0 0 0.65rem 0;
|
||||
color: var(--color-ui-text-primary);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.profile-grid
|
||||
{
|
||||
flow: horizontal-wrap;
|
||||
border: 1px solid #55575a;
|
||||
border-radius: 5px;
|
||||
background: #1e1e1e;
|
||||
padding: 0.5rem;
|
||||
width: 100%;
|
||||
height: *;
|
||||
overflow-y: auto;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.profile-tile
|
||||
{
|
||||
position: relative;
|
||||
width: 96px;
|
||||
margin: 0.35rem;
|
||||
padding: 0.25rem;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 5px;
|
||||
flow: vertical;
|
||||
horizontal-align: center;
|
||||
cursor: pointer;
|
||||
behavior: clickable;
|
||||
}
|
||||
|
||||
.profile-tile:hover
|
||||
{
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.profile-tile.current
|
||||
{
|
||||
background: rgba(61, 174, 233, 0.22);
|
||||
border: 2px solid #3daee9;
|
||||
}
|
||||
|
||||
.profile-tile.current > .profile-tile-name
|
||||
{
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.profile-tile-avatar
|
||||
{
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
border-radius: 4px;
|
||||
background: #111111;
|
||||
overflow: hidden;
|
||||
foreground-repeat: no-repeat;
|
||||
foreground-position: 50% 50%;
|
||||
foreground-size: 100% 100%;
|
||||
}
|
||||
|
||||
.profile-tile-name
|
||||
{
|
||||
width: 90px;
|
||||
margin-top: 0.35rem;
|
||||
text-align: center;
|
||||
color: var(--color-ui-text-primary);
|
||||
font-size: 0.85rem;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<html window-frame="solid-with-shadow" window-icon="window-icon.png">
|
||||
<head>
|
||||
<link href="base.css" rel="stylesheet" type="text/css" />
|
||||
<link href="dialog.css" rel="stylesheet" type="text/css" />
|
||||
<link href="profile_select_dialog.css" rel="stylesheet" type="text/css" />
|
||||
<title>Profile Selector</title>
|
||||
</head>
|
||||
<header>
|
||||
<window-caption role="window-caption"><img src="window-icon-32.png" /><span id="ProfileSelectTitle">Profile Selector</span></window-caption>
|
||||
<window-buttons>
|
||||
<window-button role="window-close"></window-button>
|
||||
</window-buttons>
|
||||
</header>
|
||||
<body>
|
||||
<div class="profile-select-body">
|
||||
<div id="ProfileSelectInstruction" class="profile-select-instruction">Select a user:</div>
|
||||
<div id="profileSelectGrid" class="profile-grid"></div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<button id="profileSelectCancel" role="window-close">Cancel</button>
|
||||
<button id="profileSelectOk" role="window-ok">OK</button>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -24,7 +24,7 @@ typedef struct CabinetParameters
|
|||
|
||||
typedef void (CALL *CabinetFinishedFn)(void * user_data, bool success, const char * amiibo_name_utf8);
|
||||
|
||||
nxinterface ICabinetFrontendApplet
|
||||
nxinterface ICabinetApplet
|
||||
{
|
||||
virtual void Close() = 0;
|
||||
virtual void ShowCabinetApplet(void * user_data, CabinetFinishedFn on_finished, const CabinetParameters * parameters) = 0;
|
||||
|
|
@ -52,7 +52,7 @@ typedef struct ControllerHostParameters
|
|||
|
||||
typedef void (CALL *ControllerReconfigureFn)(void * user_data, bool ok);
|
||||
|
||||
nxinterface IControllerFrontendApplet
|
||||
nxinterface IControllerApplet
|
||||
{
|
||||
virtual void Close() = 0;
|
||||
virtual void ReconfigureControllers(void * user_data, ControllerReconfigureFn on_complete, const ControllerHostParameters * parameters) = 0;
|
||||
|
|
@ -60,7 +60,7 @@ nxinterface IControllerFrontendApplet
|
|||
|
||||
typedef void (CALL *SimpleFinishedFn)(void * user_data);
|
||||
|
||||
nxinterface IErrorFrontendApplet
|
||||
nxinterface IErrorApplet
|
||||
{
|
||||
virtual void Close() = 0;
|
||||
virtual void ShowError(uint32_t result_raw, void * user_data, SimpleFinishedFn finished) const = 0;
|
||||
|
|
@ -68,7 +68,7 @@ nxinterface IErrorFrontendApplet
|
|||
virtual void ShowCustomErrorText(uint32_t result_raw, const char * dialog_text_utf8, const char * fullscreen_text_utf8, void * user_data, SimpleFinishedFn finished) const = 0;
|
||||
};
|
||||
|
||||
nxinterface IMiiEditFrontendApplet
|
||||
nxinterface IMiiEditApplet
|
||||
{
|
||||
virtual void Close() = 0;
|
||||
virtual void ShowMiiEdit(void * user_data, SimpleFinishedFn finished) const = 0;
|
||||
|
|
@ -76,7 +76,7 @@ nxinterface IMiiEditFrontendApplet
|
|||
|
||||
typedef void (CALL *BoolFinishedFn)(void * user_data, bool ok);
|
||||
|
||||
nxinterface IParentalControlsFrontendApplet
|
||||
nxinterface IParentalControlsApplet
|
||||
{
|
||||
virtual void Close() = 0;
|
||||
virtual void VerifyPIN(void * user_data, BoolFinishedFn finished, bool suspend_future_verification_temporarily) = 0;
|
||||
|
|
@ -85,7 +85,7 @@ nxinterface IParentalControlsFrontendApplet
|
|||
virtual void ChangePIN(void * user_data, SimpleFinishedFn finished) = 0;
|
||||
};
|
||||
|
||||
nxinterface IPhotoViewerFrontendApplet
|
||||
nxinterface IPhotoViewerApplet
|
||||
{
|
||||
virtual void Close() = 0;
|
||||
virtual void ShowPhotosForApplication(uint64_t title_id, void * user_data, SimpleFinishedFn finished) const = 0;
|
||||
|
|
@ -146,7 +146,7 @@ typedef struct ProfileSelectHostParameters
|
|||
|
||||
typedef void (CALL *ProfileSelectFinishedFn)(void * user_data, bool has_uuid, const uint8_t uuid_bytes[16]);
|
||||
|
||||
nxinterface IProfileSelectFrontendApplet
|
||||
nxinterface IProfileSelectApplet
|
||||
{
|
||||
virtual void Close() = 0;
|
||||
virtual void SelectProfile(void * user_data, ProfileSelectFinishedFn finished, const ProfileSelectHostParameters * parameters) const = 0;
|
||||
|
|
@ -265,7 +265,7 @@ typedef struct InlineTextHostParameters
|
|||
typedef void (CALL *SwkbdSubmitNormalFn)(void * user_data, uint32_t result_raw, const uint16_t * text_utf16, uint32_t text_utf16_unit_count, bool confirmed);
|
||||
typedef void (CALL *SwkbdSubmitInlineFn)(void * user_data, uint32_t reply_raw, const uint16_t * text_utf16, uint32_t text_utf16_unit_count, int32_t cursor);
|
||||
|
||||
nxinterface ISoftwareKeyboardFrontendApplet
|
||||
nxinterface ISoftwareKeyboardApplet
|
||||
{
|
||||
virtual void Close() = 0;
|
||||
virtual void InitializeKeyboard(bool is_inline, const KeyboardInitializeParameters * initialize_parameters, void * user_data_normal, SwkbdSubmitNormalFn submit_normal, void * user_data_inline, SwkbdSubmitInlineFn submit_inline) = 0;
|
||||
|
|
@ -290,7 +290,7 @@ enum class WebExitReasonHost : uint32_t
|
|||
typedef void (CALL *ExtractRomFsFn)(void * user_data);
|
||||
typedef void (CALL *OpenWebPageFn)(void * user_data, uint32_t exit_reason_raw, const char * last_url_utf8);
|
||||
|
||||
nxinterface IWebBrowserFrontendApplet
|
||||
nxinterface IWebBrowserApplet
|
||||
{
|
||||
virtual void Close() = 0;
|
||||
virtual void OpenLocalWebPage(const char * local_url_utf8, void * extract_user_data, ExtractRomFsFn extract_romfs, void * open_user_data, OpenWebPageFn open_callback) const = 0;
|
||||
|
|
@ -695,7 +695,7 @@ nxinterface IOperatingSystem
|
|||
virtual PerfStatsResults GetAndResetPerfStats() = 0;
|
||||
virtual void SetEmulationPaused(bool paused) = 0;
|
||||
virtual bool IsEmulationPaused() const = 0;
|
||||
virtual void SetFrontendApplets(ICabinetFrontendApplet * cabinet, IControllerFrontendApplet * controller, IErrorFrontendApplet * error, IMiiEditFrontendApplet * mii_edit, IParentalControlsFrontendApplet * parental_controls, IPhotoViewerFrontendApplet * photo_viewer, IProfileSelectFrontendApplet * profile_select, ISoftwareKeyboardFrontendApplet * software_keyboard, IWebBrowserFrontendApplet * web_browser) = 0;
|
||||
virtual void SetFrontendApplets(ICabinetApplet * cabinet, IControllerApplet * controller, IErrorApplet * error, IMiiEditApplet * mii_edit, IParentalControlsApplet * parental_controls, IPhotoViewerApplet * photo_viewer, IProfileSelectApplet * profile_select, ISoftwareKeyboardApplet * software_keyboard, IWebBrowserApplet * web_browser) = 0;
|
||||
virtual void SetPlayerButtonState(uint32_t player_index, uint32_t button_ordinal, bool pressed) = 0;
|
||||
virtual void SetPlayerAnalogState(uint32_t player_index, uint32_t stick_index, float x, float y) = 0;
|
||||
virtual uint32_t GetProfileCount() const = 0;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#include <nxemu-module-spec/operating_system.h>
|
||||
|
||||
class DefaultCabinetApplet final :
|
||||
public ICabinetFrontendApplet
|
||||
public ICabinetApplet
|
||||
{
|
||||
public:
|
||||
void Close() override;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class HIDCore;
|
|||
}
|
||||
|
||||
class DefaultControllerApplet final :
|
||||
public IControllerFrontendApplet
|
||||
public IControllerApplet
|
||||
{
|
||||
public:
|
||||
explicit DefaultControllerApplet(Core::HID::HIDCore & hid_core_);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public:
|
|||
};
|
||||
|
||||
class DefaultErrorApplet final :
|
||||
public IErrorFrontendApplet
|
||||
public IErrorApplet
|
||||
{
|
||||
public:
|
||||
void Close() override;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#include <nxemu-module-spec/operating_system.h>
|
||||
|
||||
class DefaultParentalControlsApplet final :
|
||||
public IParentalControlsFrontendApplet
|
||||
public IParentalControlsApplet
|
||||
{
|
||||
public:
|
||||
void Close() override;
|
||||
|
|
@ -18,7 +18,7 @@ public:
|
|||
};
|
||||
|
||||
class DefaultPhotoViewerApplet final :
|
||||
public IPhotoViewerFrontendApplet
|
||||
public IPhotoViewerApplet
|
||||
{
|
||||
public:
|
||||
void Close() override;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#include <nxemu-module-spec/operating_system.h>
|
||||
|
||||
class DefaultMiiEditApplet final :
|
||||
public IMiiEditFrontendApplet
|
||||
public IMiiEditApplet
|
||||
{
|
||||
public:
|
||||
void Close() override;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ struct ProfileSelectParameters
|
|||
};
|
||||
|
||||
class DefaultProfileSelectApplet final :
|
||||
public IProfileSelectFrontendApplet
|
||||
public IProfileSelectApplet
|
||||
{
|
||||
public:
|
||||
void Close() override;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ struct InlineTextParameters
|
|||
};
|
||||
|
||||
class DefaultSoftwareKeyboardApplet final :
|
||||
public ISoftwareKeyboardFrontendApplet
|
||||
public ISoftwareKeyboardApplet
|
||||
{
|
||||
public:
|
||||
void Close() override;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
#include <nxemu-module-spec/operating_system.h>
|
||||
|
||||
class DefaultWebBrowserApplet final :
|
||||
public IWebBrowserFrontendApplet
|
||||
public IWebBrowserApplet
|
||||
{
|
||||
public:
|
||||
void Close() override;
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ static ControllerHostParameters ConvertToFrontendParameters(
|
|||
return params;
|
||||
}
|
||||
|
||||
Controller::Controller(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, IControllerFrontendApplet & frontend_) :
|
||||
Controller::Controller(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, IControllerApplet & frontend_) :
|
||||
FrontendApplet{system_, applet_, applet_mode_},
|
||||
frontend{frontend_}
|
||||
{
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ static_assert(sizeof(ControllerSupportResultInfo) == 0xC,
|
|||
class Controller final : public FrontendApplet {
|
||||
public:
|
||||
explicit Controller(Core::System& system_, std::shared_ptr<Applet> applet_,
|
||||
LibraryAppletMode applet_mode_, IControllerFrontendApplet & frontend_);
|
||||
LibraryAppletMode applet_mode_, IControllerApplet & frontend_);
|
||||
~Controller() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
|
@ -136,7 +136,7 @@ public:
|
|||
private:
|
||||
static void OnConfigurationComplete(void * user_data, bool is_success);
|
||||
|
||||
[[maybe_unused]] IControllerFrontendApplet & frontend;
|
||||
[[maybe_unused]] IControllerApplet & frontend;
|
||||
ControllerAppletVersion controller_applet_version;
|
||||
ControllerSupportArgPrivate controller_private_arg;
|
||||
ControllerSupportArgOld controller_user_arg_old;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
namespace Service::AM::Frontend
|
||||
{
|
||||
|
||||
MiiEdit::MiiEdit(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, IMiiEditFrontendApplet & frontend_) :
|
||||
MiiEdit::MiiEdit(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, IMiiEditApplet & frontend_) :
|
||||
FrontendApplet{system_, applet_, applet_mode_},
|
||||
frontend{frontend_}
|
||||
{
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ namespace Service::AM::Frontend
|
|||
class MiiEdit final : public FrontendApplet
|
||||
{
|
||||
public:
|
||||
explicit MiiEdit(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, IMiiEditFrontendApplet & frontend_);
|
||||
explicit MiiEdit(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, IMiiEditApplet & frontend_);
|
||||
~MiiEdit() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
|
@ -39,7 +39,7 @@ public:
|
|||
void MiiEditOutputForCharInfoEditing(MiiEditResult result, const MiiEditCharInfo & char_info);
|
||||
|
||||
private:
|
||||
IMiiEditFrontendApplet & frontend;
|
||||
IMiiEditApplet & frontend;
|
||||
|
||||
MiiEditAppletInputCommon applet_input_common{};
|
||||
MiiEditAppletInputV3 applet_input_v3{};
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <cstring>
|
||||
|
||||
#include "core/core.h"
|
||||
#include "applets/profile_select.h"
|
||||
#include "core/hle/service/acc/errors.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/frontend/applet_profile_select.h"
|
||||
|
|
@ -11,11 +12,39 @@
|
|||
#include "yuzu_common/string_util.h"
|
||||
#include "yuzu_common/yuzu_assert.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
ProfileSelectHostParameters CreateHostParameters(Service::AM::Frontend::UiMode mode, const std::array<Common::UUID, 8> & invalid_uid_list, const Service::AM::Frontend::UiSettingsDisplayOptions & display_options, Service::AM::Frontend::UserSelectionPurpose purpose)
|
||||
{
|
||||
ProfileSelectHostParameters parameters{};
|
||||
parameters.mode = (ProfileUiMode)mode;
|
||||
for (size_t i = 0; i < invalid_uid_list.size(); ++i)
|
||||
{
|
||||
std::memcpy(parameters.invalid_uid_list[i], invalid_uid_list[i].uuid.data(), sizeof(parameters.invalid_uid_list[i]));
|
||||
}
|
||||
parameters.display_options = {
|
||||
.is_network_service_account_required = display_options.is_network_service_account_required,
|
||||
.is_skip_enabled = display_options.is_skip_enabled,
|
||||
.is_system_or_launcher = display_options.is_system_or_launcher,
|
||||
.is_registration_permitted = display_options.is_registration_permitted,
|
||||
.show_skip_button = display_options.show_skip_button,
|
||||
.additional_select = display_options.additional_select,
|
||||
.show_user_selector = display_options.show_user_selector,
|
||||
.is_unqualified_user_selectable = display_options.is_unqualified_user_selectable,
|
||||
};
|
||||
parameters.purpose = (UserSelectionPurposeHost)purpose;
|
||||
return parameters;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Service::AM::Frontend
|
||||
{
|
||||
|
||||
ProfileSelect::ProfileSelect(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_) :
|
||||
FrontendApplet{system_, applet_, applet_mode_}
|
||||
ProfileSelect::ProfileSelect(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, IProfileSelectApplet & frontend_) :
|
||||
FrontendApplet{system_, applet_, applet_mode_},
|
||||
frontend{frontend_}
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +94,30 @@ void ProfileSelect::ExecuteInteractive()
|
|||
|
||||
void ProfileSelect::Execute()
|
||||
{
|
||||
UNIMPLEMENTED();
|
||||
if (complete)
|
||||
{
|
||||
PushOutData(std::make_shared<IStorage>(system, std::move(final_data)));
|
||||
Exit();
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileSelectHostParameters parameters{};
|
||||
|
||||
switch (profile_select_version)
|
||||
{
|
||||
case ProfileSelectAppletVersion::Version1:
|
||||
parameters = CreateHostParameters(config_old.mode, config_old.invalid_uid_list, config_old.display_options, UserSelectionPurpose::General);
|
||||
break;
|
||||
case ProfileSelectAppletVersion::Version2:
|
||||
case ProfileSelectAppletVersion::Version3:
|
||||
parameters = CreateHostParameters(config.mode, config.invalid_uid_list, config.display_options, config.purpose);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unknown profile_select_version = {}", profile_select_version);
|
||||
break;
|
||||
}
|
||||
|
||||
frontend.SelectProfile(this, OnProfileSelected, ¶meters);
|
||||
}
|
||||
|
||||
void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid)
|
||||
|
|
@ -93,8 +145,23 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid)
|
|||
|
||||
Result ProfileSelect::RequestExit()
|
||||
{
|
||||
UNIMPLEMENTED();
|
||||
frontend.Close();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void CALL ProfileSelect::OnProfileSelected(void * user_data, bool has_uuid, const uint8_t uuid_bytes[16])
|
||||
{
|
||||
ProfileSelect * const self = (ProfileSelect *)user_data;
|
||||
if (has_uuid)
|
||||
{
|
||||
std::array<u8, 16> uuid{};
|
||||
std::memcpy(uuid.data(), uuid_bytes, uuid.size());
|
||||
self->SelectionComplete(Common::UUID{uuid});
|
||||
}
|
||||
else
|
||||
{
|
||||
self->SelectionComplete(std::nullopt);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::AM::Frontend
|
||||
|
|
|
|||
|
|
@ -5,25 +5,29 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include "yuzu_common/common_funcs.h"
|
||||
#include "yuzu_common/uuid.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/am/frontend/applets.h"
|
||||
#include "yuzu_common/common_funcs.h"
|
||||
#include "yuzu_common/uuid.h"
|
||||
|
||||
namespace Core {
|
||||
namespace Core
|
||||
{
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::AM::Frontend {
|
||||
namespace Service::AM::Frontend
|
||||
{
|
||||
|
||||
enum class ProfileSelectAppletVersion : u32 {
|
||||
enum class ProfileSelectAppletVersion : u32
|
||||
{
|
||||
Version1 = 0x1, // 1.0.0+
|
||||
Version2 = 0x10000, // 2.0.0+
|
||||
Version3 = 0x20000, // 6.0.0+
|
||||
};
|
||||
|
||||
// This is nn::account::UiMode
|
||||
enum class UiMode {
|
||||
enum class UiMode
|
||||
{
|
||||
UserSelector,
|
||||
UserCreator,
|
||||
EnsureNetworkServiceAccountAvailable,
|
||||
|
|
@ -41,7 +45,8 @@ enum class UiMode {
|
|||
};
|
||||
|
||||
// This is nn::account::UserSelectionPurpose
|
||||
enum class UserSelectionPurpose {
|
||||
enum class UserSelectionPurpose
|
||||
{
|
||||
General,
|
||||
GameCardRegistration,
|
||||
EShopLaunch,
|
||||
|
|
@ -55,14 +60,16 @@ enum class UserSelectionPurpose {
|
|||
};
|
||||
|
||||
// This is nn::account::NintendoAccountStartupDialogType
|
||||
enum class NintendoAccountStartupDialogType {
|
||||
enum class NintendoAccountStartupDialogType
|
||||
{
|
||||
LoginAndCreate,
|
||||
Login,
|
||||
Create,
|
||||
};
|
||||
|
||||
// This is nn::account::UserSelectionSettingsForSystemService
|
||||
struct UserSelectionSettingsForSystemService {
|
||||
struct UserSelectionSettingsForSystemService
|
||||
{
|
||||
UserSelectionPurpose purpose;
|
||||
bool enable_user_creation;
|
||||
INSERT_PADDING_BYTES(0x3);
|
||||
|
|
@ -70,7 +77,8 @@ struct UserSelectionSettingsForSystemService {
|
|||
static_assert(sizeof(UserSelectionSettingsForSystemService) == 0x8,
|
||||
"UserSelectionSettingsForSystemService has incorrect size.");
|
||||
|
||||
struct UiSettingsDisplayOptions {
|
||||
struct UiSettingsDisplayOptions
|
||||
{
|
||||
bool is_network_service_account_required;
|
||||
bool is_skip_enabled;
|
||||
bool is_system_or_launcher;
|
||||
|
|
@ -80,10 +88,10 @@ struct UiSettingsDisplayOptions {
|
|||
bool show_user_selector;
|
||||
bool is_unqualified_user_selectable;
|
||||
};
|
||||
static_assert(sizeof(UiSettingsDisplayOptions) == 0x8,
|
||||
"UiSettingsDisplayOptions has incorrect size.");
|
||||
static_assert(sizeof(UiSettingsDisplayOptions) == 0x8, "UiSettingsDisplayOptions has incorrect size.");
|
||||
|
||||
struct UiSettingsV1 {
|
||||
struct UiSettingsV1
|
||||
{
|
||||
UiMode mode;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
std::array<Common::UUID, 8> invalid_uid_list;
|
||||
|
|
@ -93,7 +101,8 @@ struct UiSettingsV1 {
|
|||
static_assert(sizeof(UiSettingsV1) == 0x98, "UiSettings has incorrect size.");
|
||||
|
||||
// This is nn::account::UiSettings
|
||||
struct UiSettings {
|
||||
struct UiSettings
|
||||
{
|
||||
UiMode mode;
|
||||
INSERT_PADDING_BYTES(0x4);
|
||||
std::array<Common::UUID, 8> invalid_uid_list;
|
||||
|
|
@ -105,16 +114,17 @@ struct UiSettings {
|
|||
static_assert(sizeof(UiSettings) == 0xA0, "UiSettings has incorrect size.");
|
||||
|
||||
// This is nn::account::UiReturnArg
|
||||
struct UiReturnArg {
|
||||
struct UiReturnArg
|
||||
{
|
||||
u64 result;
|
||||
Common::UUID uuid_selected;
|
||||
};
|
||||
static_assert(sizeof(UiReturnArg) == 0x18, "UiReturnArg has incorrect size.");
|
||||
|
||||
class ProfileSelect final : public FrontendApplet {
|
||||
class ProfileSelect final : public FrontendApplet
|
||||
{
|
||||
public:
|
||||
explicit ProfileSelect(Core::System& system_, std::shared_ptr<Applet> applet_,
|
||||
LibraryAppletMode applet_mode_);
|
||||
explicit ProfileSelect(Core::System& system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, IProfileSelectApplet& frontend_);
|
||||
~ProfileSelect() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
|
@ -127,6 +137,9 @@ public:
|
|||
void SelectionComplete(std::optional<Common::UUID> uuid);
|
||||
|
||||
private:
|
||||
static void CALL OnProfileSelected(void* user_data, bool has_uuid, const uint8_t uuid_bytes[16]);
|
||||
|
||||
IProfileSelectApplet& frontend;
|
||||
|
||||
UiSettings config;
|
||||
UiSettingsV1 config_old;
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ void SetReplyBase(std::vector<u8> & reply, SwkbdState state, SwkbdReplyType repl
|
|||
|
||||
} // Anonymous namespace
|
||||
|
||||
SoftwareKeyboard::SoftwareKeyboard(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, ISoftwareKeyboardFrontendApplet & frontend_) :
|
||||
SoftwareKeyboard::SoftwareKeyboard(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, ISoftwareKeyboardApplet & frontend_) :
|
||||
FrontendApplet{system_, applet_, applet_mode_},
|
||||
frontend{frontend_}
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class SoftwareKeyboard final :
|
|||
public FrontendApplet
|
||||
{
|
||||
public:
|
||||
explicit SoftwareKeyboard(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, ISoftwareKeyboardFrontendApplet & frontend_);
|
||||
explicit SoftwareKeyboard(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, ISoftwareKeyboardApplet & frontend_);
|
||||
~SoftwareKeyboard() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
|
@ -150,7 +150,7 @@ private:
|
|||
void ReplyChangedStringUtf8V2();
|
||||
void ReplyMovedCursorUtf8V2();
|
||||
|
||||
ISoftwareKeyboardFrontendApplet & frontend;
|
||||
ISoftwareKeyboardApplet & frontend;
|
||||
|
||||
SwkbdAppletVersion swkbd_applet_version;
|
||||
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ void ExtractSharedFonts(Core::System & system)
|
|||
|
||||
} // namespace
|
||||
|
||||
WebBrowser::WebBrowser(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, IWebBrowserFrontendApplet & frontend_) :
|
||||
WebBrowser::WebBrowser(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, IWebBrowserApplet & frontend_) :
|
||||
FrontendApplet{system_, applet_, applet_mode_},
|
||||
frontend(frontend_)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace Service::AM::Frontend
|
|||
class WebBrowser final : public FrontendApplet
|
||||
{
|
||||
public:
|
||||
WebBrowser(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, IWebBrowserFrontendApplet & frontend_);
|
||||
WebBrowser(Core::System & system_, std::shared_ptr<Applet> applet_, LibraryAppletMode applet_mode_, IWebBrowserApplet & frontend_);
|
||||
~WebBrowser() override;
|
||||
|
||||
void Initialize() override;
|
||||
|
|
@ -68,7 +68,7 @@ private:
|
|||
void ExecuteWifi();
|
||||
void ExecuteLobby();
|
||||
|
||||
IWebBrowserFrontendApplet & frontend;
|
||||
IWebBrowserApplet & frontend;
|
||||
|
||||
bool complete{false};
|
||||
Result status{ResultSuccess};
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ FrontendAppletSet::FrontendAppletSet() :
|
|||
{
|
||||
}
|
||||
|
||||
FrontendAppletSet::FrontendAppletSet(ICabinetFrontendApplet * cabinet_, IControllerFrontendApplet * controller_, IErrorFrontendApplet * error_, IMiiEditFrontendApplet * mii_edit_, IParentalControlsFrontendApplet * parental_controls, IPhotoViewerFrontendApplet * photo_viewer, IProfileSelectFrontendApplet * profile_select_, ISoftwareKeyboardFrontendApplet * software_keyboard_, IWebBrowserFrontendApplet * web_browser_) :
|
||||
FrontendAppletSet::FrontendAppletSet(ICabinetApplet * cabinet_, IControllerApplet * controller_, IErrorApplet * error_, IMiiEditApplet * mii_edit_, IParentalControlsApplet * parental_controls, IPhotoViewerApplet * photo_viewer, IProfileSelectApplet * profile_select_, ISoftwareKeyboardApplet * software_keyboard_, IWebBrowserApplet * web_browser_) :
|
||||
cabinet(cabinet_),
|
||||
controller(controller_),
|
||||
error(error_),
|
||||
|
|
@ -232,7 +232,7 @@ std::shared_ptr<FrontendApplet> FrontendAppletHolder::GetApplet(std::shared_ptr<
|
|||
case AppletId::Error:
|
||||
return std::make_shared<Error>(system, applet, mode);
|
||||
case AppletId::ProfileSelect:
|
||||
return std::make_shared<ProfileSelect>(system, applet, mode);
|
||||
return std::make_shared<ProfileSelect>(system, applet, mode, *frontend.profile_select);
|
||||
case AppletId::SoftwareKeyboard:
|
||||
return std::make_shared<SoftwareKeyboard>(system, applet, mode, *frontend.software_keyboard);
|
||||
case AppletId::MiiEdit:
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ protected:
|
|||
struct FrontendAppletSet
|
||||
{
|
||||
FrontendAppletSet();
|
||||
FrontendAppletSet(ICabinetFrontendApplet * cabinet_, IControllerFrontendApplet * controller_, IErrorFrontendApplet * error_, IMiiEditFrontendApplet * mii_edit_, IParentalControlsFrontendApplet * parental_controls, IPhotoViewerFrontendApplet * photo_viewer, IProfileSelectFrontendApplet * profile_select_, ISoftwareKeyboardFrontendApplet * software_keyboard_, IWebBrowserFrontendApplet * web_browser_);
|
||||
FrontendAppletSet(ICabinetApplet * cabinet_, IControllerApplet * controller_, IErrorApplet * error_, IMiiEditApplet * mii_edit_, IParentalControlsApplet * parental_controls, IPhotoViewerApplet * photo_viewer, IProfileSelectApplet * profile_select_, ISoftwareKeyboardApplet * software_keyboard_, IWebBrowserApplet * web_browser_);
|
||||
~FrontendAppletSet();
|
||||
|
||||
FrontendAppletSet(const FrontendAppletSet &) = delete;
|
||||
|
|
@ -87,15 +87,15 @@ struct FrontendAppletSet
|
|||
FrontendAppletSet(FrontendAppletSet &&) noexcept;
|
||||
FrontendAppletSet & operator=(FrontendAppletSet &&) noexcept;
|
||||
|
||||
ICabinetFrontendApplet * cabinet;
|
||||
IControllerFrontendApplet * controller;
|
||||
IErrorFrontendApplet * error;
|
||||
IMiiEditFrontendApplet * mii_edit;
|
||||
IParentalControlsFrontendApplet * parental_controls;
|
||||
IPhotoViewerFrontendApplet * photo_viewer;
|
||||
IProfileSelectFrontendApplet * profile_select;
|
||||
ISoftwareKeyboardFrontendApplet * software_keyboard;
|
||||
IWebBrowserFrontendApplet * web_browser;
|
||||
ICabinetApplet * cabinet;
|
||||
IControllerApplet * controller;
|
||||
IErrorApplet * error;
|
||||
IMiiEditApplet * mii_edit;
|
||||
IParentalControlsApplet * parental_controls;
|
||||
IPhotoViewerApplet * photo_viewer;
|
||||
IProfileSelectApplet * profile_select;
|
||||
ISoftwareKeyboardApplet * software_keyboard;
|
||||
IWebBrowserApplet * web_browser;
|
||||
};
|
||||
|
||||
class FrontendAppletHolder
|
||||
|
|
|
|||
|
|
@ -421,7 +421,7 @@ bool OSManager::IsEmulationPaused() const
|
|||
return !m_emuThread->IsRunning();
|
||||
}
|
||||
|
||||
void OSManager::SetFrontendApplets(ICabinetFrontendApplet * cabinet, IControllerFrontendApplet * controller, IErrorFrontendApplet * error, IMiiEditFrontendApplet * mii_edit, IParentalControlsFrontendApplet * parental_controls, IPhotoViewerFrontendApplet * photo_viewer, IProfileSelectFrontendApplet * profile_select, ISoftwareKeyboardFrontendApplet * software_keyboard, IWebBrowserFrontendApplet * web_browser)
|
||||
void OSManager::SetFrontendApplets(ICabinetApplet * cabinet, IControllerApplet * controller, IErrorApplet * error, IMiiEditApplet * mii_edit, IParentalControlsApplet * parental_controls, IPhotoViewerApplet * photo_viewer, IProfileSelectApplet * profile_select, ISoftwareKeyboardApplet * software_keyboard, IWebBrowserApplet * web_browser)
|
||||
{
|
||||
Service::AM::Frontend::FrontendAppletSet applets{};
|
||||
applets.cabinet = cabinet;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public:
|
|||
PerfStatsResults GetAndResetPerfStats() override;
|
||||
void SetEmulationPaused(bool paused) override;
|
||||
bool IsEmulationPaused() const override;
|
||||
void SetFrontendApplets(ICabinetFrontendApplet * cabinet, IControllerFrontendApplet * controller, IErrorFrontendApplet * error, IMiiEditFrontendApplet * mii_edit, IParentalControlsFrontendApplet * parental_controls, IPhotoViewerFrontendApplet * photo_viewer, IProfileSelectFrontendApplet * profile_select, ISoftwareKeyboardFrontendApplet * software_keyboard, IWebBrowserFrontendApplet * web_browser) override;
|
||||
void SetFrontendApplets(ICabinetApplet * cabinet, IControllerApplet * controller, IErrorApplet * error, IMiiEditApplet * mii_edit, IParentalControlsApplet * parental_controls, IPhotoViewerApplet * photo_viewer, IProfileSelectApplet * profile_select, ISoftwareKeyboardApplet * software_keyboard, IWebBrowserApplet * web_browser) override;
|
||||
void SetPlayerButtonState(uint32_t player_index, uint32_t button_ordinal, bool pressed) override;
|
||||
void SetPlayerAnalogState(uint32_t player_index, uint32_t stick_index, float x, float y) override;
|
||||
uint32_t GetProfileCount() const override;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,481 @@
|
|||
#include "profile_select.h"
|
||||
|
||||
#include "user_interface/html_utils.h"
|
||||
#include <common/std_string.h>
|
||||
#include <nxemu-core/modules/system_modules.h>
|
||||
|
||||
ProfileSelectApplet::ProfileSelectApplet() :
|
||||
m_sciterUI(nullptr),
|
||||
m_modules(nullptr),
|
||||
m_parentHwnd(nullptr),
|
||||
m_window(nullptr),
|
||||
m_selectedIndex(0),
|
||||
m_dialogOpen(false),
|
||||
m_pending(false),
|
||||
m_userData(nullptr),
|
||||
m_finished(nullptr),
|
||||
m_parameters{}
|
||||
{
|
||||
}
|
||||
|
||||
void ProfileSelectApplet::Attach(ISciterUI & sciterUI, SystemModules & modules, SciterElement rootElement, void * parentHwnd)
|
||||
{
|
||||
m_sciterUI = &sciterUI;
|
||||
m_modules = &modules;
|
||||
m_rootElement = rootElement;
|
||||
m_parentHwnd = parentHwnd;
|
||||
|
||||
if (m_rootElement.IsValid())
|
||||
{
|
||||
m_sciterUI->AttachHandler(m_rootElement, IID_EVENTSINK, (IEventSink *)this);
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileSelectApplet::Detach()
|
||||
{
|
||||
CloseDialog();
|
||||
|
||||
if (m_sciterUI != nullptr && m_rootElement.IsValid())
|
||||
{
|
||||
m_sciterUI->DetachHandler(m_rootElement, IID_EVENTSINK, (IEventSink *)this);
|
||||
}
|
||||
|
||||
m_sciterUI = nullptr;
|
||||
m_modules = nullptr;
|
||||
m_rootElement = {};
|
||||
m_parentHwnd = nullptr;
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_pending = false;
|
||||
m_userData = nullptr;
|
||||
m_finished = nullptr;
|
||||
m_parameters = {};
|
||||
}
|
||||
|
||||
void ProfileSelectApplet::Close()
|
||||
{
|
||||
ProfileSelectFinishedFn finished = nullptr;
|
||||
void * userData = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
finished = m_finished;
|
||||
userData = m_userData;
|
||||
m_pending = false;
|
||||
m_finished = nullptr;
|
||||
m_userData = nullptr;
|
||||
}
|
||||
|
||||
CloseDialog();
|
||||
|
||||
if (finished != nullptr)
|
||||
{
|
||||
uint8_t uuidBytes[HOST_PROFILE_UUID_SIZE]{};
|
||||
finished(userData, false, uuidBytes);
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileSelectApplet::SelectProfile(void * user_data, ProfileSelectFinishedFn finished, const ProfileSelectHostParameters * parameters) const
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_userData = user_data;
|
||||
m_finished = finished;
|
||||
m_parameters = parameters != nullptr ? *parameters : ProfileSelectHostParameters{};
|
||||
m_pending = true;
|
||||
}
|
||||
|
||||
if (m_rootElement.IsValid())
|
||||
{
|
||||
m_rootElement.PostEvent(EVENT_PROFILE_SELECT);
|
||||
}
|
||||
else if (finished != nullptr)
|
||||
{
|
||||
uint8_t uuidBytes[HOST_PROFILE_UUID_SIZE]{};
|
||||
finished(user_data, false, uuidBytes);
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileSelectApplet::ProcessPendingSelect()
|
||||
{
|
||||
bool pending = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
pending = m_pending;
|
||||
m_pending = false;
|
||||
}
|
||||
|
||||
if (!pending)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ShowDialog();
|
||||
}
|
||||
|
||||
bool ProfileSelectApplet::OnEvent(SCITER_ELEMENT /*element*/, SCITER_ELEMENT /*source*/, uint32_t event_code, uint64_t /*reason*/)
|
||||
{
|
||||
if (event_code == EVENT_PROFILE_SELECT)
|
||||
{
|
||||
ProcessPendingSelect();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProfileSelectApplet::OnClick(SCITER_ELEMENT element, SCITER_ELEMENT source, uint32_t /*reason*/)
|
||||
{
|
||||
SciterElement clickElem(element);
|
||||
const std::string elementId = clickElem.GetAttributeByName("id");
|
||||
|
||||
if (elementId == "profileSelectOk")
|
||||
{
|
||||
AcceptSelection();
|
||||
return true;
|
||||
}
|
||||
else if (elementId == "profileSelectCancel")
|
||||
{
|
||||
CancelSelection();
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t index = -1;
|
||||
if (ResolveTileIndex(source, index))
|
||||
{
|
||||
SelectIndex(index);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProfileSelectApplet::ResolveTileIndex(SCITER_ELEMENT source, int32_t & index) const
|
||||
{
|
||||
SciterElement elem(source);
|
||||
for (SciterElement walk = elem; walk.IsValid(); walk = walk.GetParent())
|
||||
{
|
||||
if (walk.GetAttribute("role") == "profile-tile")
|
||||
{
|
||||
const std::string indexAttr = walk.GetAttribute("data-index");
|
||||
if (indexAttr.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
index = std::stoi(indexAttr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ProfileSelectApplet::ShowDialog()
|
||||
{
|
||||
if (m_sciterUI == nullptr || m_modules == nullptr || !m_modules->IsValid() || m_dialogOpen)
|
||||
{
|
||||
CancelSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
IOperatingSystem & os = m_modules->Modules().OperatingSystem();
|
||||
const uint32_t count = os.GetProfileCount();
|
||||
if (count == 0)
|
||||
{
|
||||
CancelSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
if (count == 1)
|
||||
{
|
||||
HostProfileInfo profile{};
|
||||
if (os.GetProfile(0, &profile))
|
||||
{
|
||||
Finish(true, profile.uuid);
|
||||
}
|
||||
else
|
||||
{
|
||||
CancelSelection();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
WINDOW_WIDTH = 560,
|
||||
WINDOW_HEIGHT = 420,
|
||||
};
|
||||
|
||||
m_selectedIndex = 0;
|
||||
m_window = nullptr;
|
||||
if (!m_sciterUI->WindowCreate(m_parentHwnd, "profile_select_dialog.html", 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, SUIW_CHILD, m_window))
|
||||
{
|
||||
CancelSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
m_dialogOpen = true;
|
||||
|
||||
SciterElement root(m_window->GetRootElement());
|
||||
if (root.IsValid())
|
||||
{
|
||||
ProfileSelectHostParameters parameters{};
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
parameters = m_parameters;
|
||||
}
|
||||
|
||||
SciterElement titleEl(root.GetElementByID("ProfileSelectTitle"));
|
||||
if (titleEl.IsValid())
|
||||
{
|
||||
titleEl.SetText(WindowTitleForMode(parameters.mode));
|
||||
}
|
||||
|
||||
SciterElement instructionEl(root.GetElementByID("ProfileSelectInstruction"));
|
||||
if (instructionEl.IsValid())
|
||||
{
|
||||
instructionEl.SetText(InstructionForPurpose(parameters.purpose));
|
||||
}
|
||||
|
||||
PopulateProfiles();
|
||||
AttachClickHandler(*m_sciterUI, root.GetElementByID("profileSelectOk"), this);
|
||||
AttachClickHandler(*m_sciterUI, root.GetElementByID("profileSelectCancel"), this);
|
||||
}
|
||||
|
||||
m_window->CenterWindow();
|
||||
m_window->RunModal();
|
||||
m_dialogOpen = false;
|
||||
m_window = nullptr;
|
||||
|
||||
bool needsCancel = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
needsCancel = m_finished != nullptr;
|
||||
}
|
||||
if (needsCancel)
|
||||
{
|
||||
CancelSelection();
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileSelectApplet::PopulateProfiles()
|
||||
{
|
||||
if (m_window == nullptr || m_sciterUI == nullptr || m_modules == nullptr || !m_modules->IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SciterElement root(m_window->GetRootElement());
|
||||
SciterElement grid = root.IsValid() ? root.GetElementByID("profileSelectGrid") : nullptr;
|
||||
if (!grid.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
grid.Clear();
|
||||
|
||||
IOperatingSystem & os = m_modules->Modules().OperatingSystem();
|
||||
const uint32_t count = os.GetProfileCount();
|
||||
if (m_selectedIndex < 0 || (uint32_t)m_selectedIndex >= count)
|
||||
{
|
||||
m_selectedIndex = 0;
|
||||
}
|
||||
|
||||
std::string html;
|
||||
std::vector<std::string> imageUris;
|
||||
imageUris.reserve(count);
|
||||
|
||||
for (uint32_t i = 0; i < count; ++i)
|
||||
{
|
||||
HostProfileInfo profile{};
|
||||
if (!os.GetProfile(i, &profile))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool selected = (int32_t)i == m_selectedIndex;
|
||||
const std::string imageUri = GetImageFileUrl(profile);
|
||||
imageUris.push_back(imageUri);
|
||||
|
||||
html += stdstr_f(
|
||||
"<div class=\"profile-tile%s\" role=\"profile-tile\" data-index=\"%u\">"
|
||||
"<div class=\"profile-tile-avatar\"></div>"
|
||||
"<div class=\"profile-tile-name\">%s</div>"
|
||||
"</div>",
|
||||
selected ? " current" : "",
|
||||
i,
|
||||
HtmlEscape(profile.username).c_str());
|
||||
}
|
||||
|
||||
grid.SetHTML((const uint8_t *)html.c_str(), html.size());
|
||||
m_sciterUI->AttachHandler(grid, IID_ICLICKSINK, (IClickSink *)this);
|
||||
|
||||
for (uint32_t i = 0; i < grid.GetChildCount() && i < imageUris.size(); ++i)
|
||||
{
|
||||
if (imageUris[i].empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SciterElement tile = grid.GetChild(i);
|
||||
if (!tile.IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SciterElement avatar = tile.FindFirst(".profile-tile-avatar");
|
||||
if (avatar.IsValid())
|
||||
{
|
||||
avatar.SetStyleAttribute("foreground-image", ("url(" + imageUris[i] + ")").c_str());
|
||||
avatar.SetStyleAttribute("foreground-size", "100% 100%");
|
||||
avatar.SetStyleAttribute("foreground-repeat", "no-repeat");
|
||||
avatar.SetStyleAttribute("foreground-position", "50% 50%");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileSelectApplet::SelectIndex(int32_t index)
|
||||
{
|
||||
if (index == m_selectedIndex || m_window == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SciterElement root(m_window->GetRootElement());
|
||||
SciterElement grid = root.IsValid() ? root.GetElementByID("profileSelectGrid") : nullptr;
|
||||
if (!grid.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SciterElement newTile = grid.FindFirst(stdstr_f("[data-index='%d']", index).c_str());
|
||||
if (!newTile.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SciterElement oldTile = grid.FindFirst(stdstr_f("[data-index='%d']", m_selectedIndex).c_str());
|
||||
if (oldTile.IsValid())
|
||||
{
|
||||
oldTile.SetAttribute("class", "profile-tile");
|
||||
}
|
||||
|
||||
newTile.SetAttribute("class", "profile-tile current");
|
||||
m_selectedIndex = index;
|
||||
}
|
||||
|
||||
void ProfileSelectApplet::AcceptSelection()
|
||||
{
|
||||
if (m_modules == nullptr || !m_modules->IsValid())
|
||||
{
|
||||
CancelSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
HostProfileInfo profile{};
|
||||
if (!m_modules->Modules().OperatingSystem().GetProfile((uint32_t)m_selectedIndex, &profile))
|
||||
{
|
||||
CancelSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
const HostProfileInfo selected = profile;
|
||||
CloseDialog();
|
||||
Finish(true, selected.uuid);
|
||||
}
|
||||
|
||||
void ProfileSelectApplet::CancelSelection()
|
||||
{
|
||||
CloseDialog();
|
||||
uint8_t uuidBytes[HOST_PROFILE_UUID_SIZE]{};
|
||||
Finish(false, uuidBytes);
|
||||
}
|
||||
|
||||
void ProfileSelectApplet::CloseDialog()
|
||||
{
|
||||
if (m_window == nullptr || m_window->IsClosed())
|
||||
{
|
||||
m_window = nullptr;
|
||||
m_dialogOpen = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_dialogOpen = false;
|
||||
m_window->Destroy();
|
||||
m_window = nullptr;
|
||||
}
|
||||
|
||||
void ProfileSelectApplet::Finish(bool hasUuid, const uint8_t uuidBytes[HOST_PROFILE_UUID_SIZE])
|
||||
{
|
||||
ProfileSelectFinishedFn finished = nullptr;
|
||||
void * userData = nullptr;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
finished = m_finished;
|
||||
userData = m_userData;
|
||||
m_finished = nullptr;
|
||||
m_userData = nullptr;
|
||||
m_pending = false;
|
||||
}
|
||||
|
||||
if (finished != nullptr)
|
||||
{
|
||||
finished(userData, hasUuid, uuidBytes);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ProfileSelectApplet::GetImageFileUrl(const HostProfileInfo & profile) const
|
||||
{
|
||||
if (m_modules == nullptr || !m_modules->IsValid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
char path[1024] = {};
|
||||
if (!m_modules->Modules().OperatingSystem().GetProfileImagePath(profile.uuid, path, sizeof(path)) || path[0] == '\0')
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return ImageDataUriFromFile(path);
|
||||
}
|
||||
|
||||
const char * ProfileSelectApplet::WindowTitleForMode(ProfileUiMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ProfileUiMode::UserCreator:
|
||||
case ProfileUiMode::UserCreatorForStarter:
|
||||
return "Profile Creator";
|
||||
case ProfileUiMode::UserIconEditor:
|
||||
return "Profile Icon Editor";
|
||||
case ProfileUiMode::UserNicknameEditor:
|
||||
return "Profile Nickname Editor";
|
||||
default:
|
||||
return "Profile Selector";
|
||||
}
|
||||
}
|
||||
|
||||
const char * ProfileSelectApplet::InstructionForPurpose(UserSelectionPurposeHost purpose)
|
||||
{
|
||||
switch (purpose)
|
||||
{
|
||||
case UserSelectionPurposeHost::GameCardRegistration:
|
||||
return "Who will receive the points?";
|
||||
case UserSelectionPurposeHost::EShopLaunch:
|
||||
return "Who is using Nintendo eShop?";
|
||||
case UserSelectionPurposeHost::EShopItemShow:
|
||||
return "Who is making this purchase?";
|
||||
case UserSelectionPurposeHost::PicturePost:
|
||||
return "Who is posting?";
|
||||
case UserSelectionPurposeHost::NintendoAccountLinkage:
|
||||
return "Select a user to link to a Nintendo Account.";
|
||||
case UserSelectionPurposeHost::SettingsUpdate:
|
||||
return "Change settings for which user?";
|
||||
case UserSelectionPurposeHost::SaveDataDeletion:
|
||||
return "Format data for which user?";
|
||||
case UserSelectionPurposeHost::UserMigration:
|
||||
return "Which user will be transferred to another console?";
|
||||
case UserSelectionPurposeHost::SaveDataTransfer:
|
||||
return "Send save data for which user?";
|
||||
case UserSelectionPurposeHost::General:
|
||||
default:
|
||||
return "Select a user:";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include <nxemu-module-spec/operating_system.h>
|
||||
#include <sciter_element.h>
|
||||
#include <sciter_handler.h>
|
||||
#include <mutex>
|
||||
|
||||
class SystemModules;
|
||||
|
||||
class ProfileSelectApplet :
|
||||
public IProfileSelectApplet,
|
||||
public IEventSink,
|
||||
public IClickSink
|
||||
{
|
||||
public:
|
||||
ProfileSelectApplet();
|
||||
|
||||
void Attach(ISciterUI & sciterUI, SystemModules & modules, SciterElement rootElement, void * parentHwnd);
|
||||
void Detach();
|
||||
|
||||
// IProfileSelectApplet
|
||||
void Close() override;
|
||||
void SelectProfile(void * user_data, ProfileSelectFinishedFn finished, const ProfileSelectHostParameters * parameters) const override;
|
||||
|
||||
// IEventSink
|
||||
bool OnEvent(SCITER_ELEMENT element, SCITER_ELEMENT source, uint32_t event_code, uint64_t reason) override;
|
||||
|
||||
// IClickSink
|
||||
bool OnClick(SCITER_ELEMENT element, SCITER_ELEMENT source, uint32_t reason) override;
|
||||
|
||||
private:
|
||||
ProfileSelectApplet(const ProfileSelectApplet &) = delete;
|
||||
ProfileSelectApplet & operator=(const ProfileSelectApplet &) = delete;
|
||||
|
||||
static constexpr uint32_t EVENT_PROFILE_SELECT = 0x2008;
|
||||
|
||||
void ProcessPendingSelect();
|
||||
void ShowDialog();
|
||||
void PopulateProfiles();
|
||||
void SelectIndex(int32_t index);
|
||||
bool ResolveTileIndex(SCITER_ELEMENT source, int32_t & index) const;
|
||||
void AcceptSelection();
|
||||
void CancelSelection();
|
||||
void CloseDialog();
|
||||
void Finish(bool hasUuid, const uint8_t uuidBytes[HOST_PROFILE_UUID_SIZE]);
|
||||
std::string GetImageFileUrl(const HostProfileInfo & profile) const;
|
||||
|
||||
static const char * WindowTitleForMode(ProfileUiMode mode);
|
||||
static const char * InstructionForPurpose(UserSelectionPurposeHost purpose);
|
||||
|
||||
ISciterUI * m_sciterUI;
|
||||
SystemModules * m_modules;
|
||||
SciterElement m_rootElement;
|
||||
void * m_parentHwnd;
|
||||
ISciterWindow * m_window;
|
||||
int32_t m_selectedIndex;
|
||||
bool m_dialogOpen;
|
||||
|
||||
mutable std::mutex m_mutex;
|
||||
mutable bool m_pending;
|
||||
mutable void * m_userData;
|
||||
mutable ProfileSelectFinishedFn m_finished;
|
||||
mutable ProfileSelectHostParameters m_parameters;
|
||||
};
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
#include <nxemu-module-spec/operating_system.h>
|
||||
|
||||
class WebBrowserApplet final :
|
||||
public IWebBrowserFrontendApplet
|
||||
public IWebBrowserApplet
|
||||
{
|
||||
public:
|
||||
WebBrowserApplet();
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="applets\profile_select.cpp" />
|
||||
<ClCompile Include="applets\web_browser.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="settings\ui_settings.cpp" />
|
||||
|
|
@ -69,6 +70,7 @@
|
|||
<ClCompile Include="user_interface\widgets\rom_browser.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="applets\profile_select.h" />
|
||||
<ClInclude Include="applets\web_browser.h" />
|
||||
<ClInclude Include="settings\ui_identifiers.h" />
|
||||
<ClInclude Include="settings\ui_settings.h" />
|
||||
|
|
|
|||
|
|
@ -135,6 +135,9 @@
|
|||
<ClCompile Include="user_interface\settings\game_config_addons.cpp">
|
||||
<Filter>Source Files\user_interface\settings</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="applets\profile_select.cpp">
|
||||
<Filter>Source Files\applets</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="user_interface\resource.h">
|
||||
|
|
@ -236,6 +239,9 @@
|
|||
<ClInclude Include="user_interface\settings\game_config_addons.h">
|
||||
<Filter>Header Files\user_interface\settings</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="applets\profile_select.h">
|
||||
<Filter>Header Files\applets</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="user_interface\nxemu.rc">
|
||||
|
|
|
|||
|
|
@ -289,6 +289,7 @@ const std::string * SciterMainWindow::MenuIconSvg(GuiAction action)
|
|||
SciterMainWindow::~SciterMainWindow()
|
||||
{
|
||||
Notification::GetInstance().ClearSciterContext();
|
||||
m_ProfileSelect.Detach();
|
||||
m_WebBrowser.DetachWindow();
|
||||
|
||||
SettingsStore & settings = SettingsStore::GetInstance();
|
||||
|
|
@ -333,8 +334,9 @@ void SciterMainWindow::RegisterApplets()
|
|||
return;
|
||||
}
|
||||
IOperatingSystem & os = m_modules.Modules().OperatingSystem();
|
||||
m_ProfileSelect.Attach(m_sciterUI, m_modules, m_rootElement, (void *)m_window->GetHandle());
|
||||
m_WebBrowser.AttachToWindow(m_window->GetHandle());
|
||||
os.SetFrontendApplets(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &m_WebBrowser);
|
||||
os.SetFrontendApplets(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &m_ProfileSelect, nullptr, &m_WebBrowser);
|
||||
}
|
||||
|
||||
void SciterMainWindow::ResetMenu()
|
||||
|
|
@ -1491,6 +1493,7 @@ void SciterMainWindow::OnRecetGame(uint32_t fileIndex)
|
|||
|
||||
void SciterMainWindow::OnWindowDestroy(HWINDOW /*hWnd*/)
|
||||
{
|
||||
m_ProfileSelect.Detach();
|
||||
m_WebBrowser.DetachWindow();
|
||||
m_sciterUI.Stop();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
#include "applets/profile_select.h"
|
||||
#include "applets/web_browser.h"
|
||||
#include "startup_checks.h"
|
||||
#include "user_interface/discord_presence.h"
|
||||
|
|
@ -214,6 +215,7 @@ private:
|
|||
std::string m_pendingGameConfigPath;
|
||||
std::unique_ptr<InputConfig> m_inputConfig;
|
||||
std::unique_ptr<AboutDialog> m_aboutDialog;
|
||||
ProfileSelectApplet m_ProfileSelect;
|
||||
WebBrowserApplet m_WebBrowser;
|
||||
float m_resolutionUpFactor;
|
||||
bool m_useMultiCore;
|
||||
|
|
|
|||
Loading…
Reference in New Issue