diff options
| author | 2018-12-23 14:35:13 -0500 | |
|---|---|---|
| committer | 2018-12-23 14:35:13 -0500 | |
| commit | f95f6c7d86af9857cb737a741fc847bf2c5d8413 (patch) | |
| tree | b5c02a35cdb18b78c648bc3a6f98b87e68d2e651 | |
| parent | Merge pull request #1780 from DarkLordZach/controller-profiles (diff) | |
| parent | applets: Correct event ResetTypes from OneShot to Sticky (diff) | |
| download | yuzu-f95f6c7d86af9857cb737a741fc847bf2c5d8413.tar.gz yuzu-f95f6c7d86af9857cb737a741fc847bf2c5d8413.tar.xz yuzu-f95f6c7d86af9857cb737a741fc847bf2c5d8413.zip | |
Merge pull request #1781 from DarkLordZach/applet-profile-select
am: Implement HLE profile selector applet
| -rw-r--r-- | src/core/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/core/core.cpp | 11 | ||||
| -rw-r--r-- | src/core/core.h | 5 | ||||
| -rw-r--r-- | src/core/frontend/applets/profile_select.cpp | 19 | ||||
| -rw-r--r-- | src/core/frontend/applets/profile_select.h | 27 | ||||
| -rw-r--r-- | src/core/hle/service/am/am.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/service/am/applets/profile_select.cpp | 77 | ||||
| -rw-r--r-- | src/core/hle/service/am/applets/profile_select.h | 50 | ||||
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/yuzu/applets/profile_select.cpp | 168 | ||||
| -rw-r--r-- | src/yuzu/applets/profile_select.h | 73 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 24 | ||||
| -rw-r--r-- | src/yuzu/main.h | 2 |
13 files changed, 466 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 882c9ab59..93f5ba3fe 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -83,6 +83,8 @@ add_library(core STATIC | |||
| 83 | file_sys/vfs_vector.h | 83 | file_sys/vfs_vector.h |
| 84 | file_sys/xts_archive.cpp | 84 | file_sys/xts_archive.cpp |
| 85 | file_sys/xts_archive.h | 85 | file_sys/xts_archive.h |
| 86 | frontend/applets/profile_select.cpp | ||
| 87 | frontend/applets/profile_select.h | ||
| 86 | frontend/applets/software_keyboard.cpp | 88 | frontend/applets/software_keyboard.cpp |
| 87 | frontend/applets/software_keyboard.h | 89 | frontend/applets/software_keyboard.h |
| 88 | frontend/emu_window.cpp | 90 | frontend/emu_window.cpp |
| @@ -162,6 +164,8 @@ add_library(core STATIC | |||
| 162 | hle/service/am/applet_oe.h | 164 | hle/service/am/applet_oe.h |
| 163 | hle/service/am/applets/applets.cpp | 165 | hle/service/am/applets/applets.cpp |
| 164 | hle/service/am/applets/applets.h | 166 | hle/service/am/applets/applets.h |
| 167 | hle/service/am/applets/profile_select.cpp | ||
| 168 | hle/service/am/applets/profile_select.h | ||
| 165 | hle/service/am/applets/software_keyboard.cpp | 169 | hle/service/am/applets/software_keyboard.cpp |
| 166 | hle/service/am/applets/software_keyboard.h | 170 | hle/service/am/applets/software_keyboard.h |
| 167 | hle/service/am/applets/stub_applet.cpp | 171 | hle/service/am/applets/stub_applet.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index ce7851538..fd10199ec 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -99,6 +99,8 @@ struct System::Impl { | |||
| 99 | virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); | 99 | virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); |
| 100 | 100 | ||
| 101 | /// Create default implementations of applets if one is not provided. | 101 | /// Create default implementations of applets if one is not provided. |
| 102 | if (profile_selector == nullptr) | ||
| 103 | profile_selector = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>(); | ||
| 102 | if (software_keyboard == nullptr) | 104 | if (software_keyboard == nullptr) |
| 103 | software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>(); | 105 | software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>(); |
| 104 | 106 | ||
| @@ -229,6 +231,7 @@ struct System::Impl { | |||
| 229 | bool is_powered_on = false; | 231 | bool is_powered_on = false; |
| 230 | 232 | ||
| 231 | /// Frontend applets | 233 | /// Frontend applets |
| 234 | std::unique_ptr<Core::Frontend::ProfileSelectApplet> profile_selector; | ||
| 232 | std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; | 235 | std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; |
| 233 | 236 | ||
| 234 | /// Service manager | 237 | /// Service manager |
| @@ -424,6 +427,14 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const { | |||
| 424 | return impl->virtual_filesystem; | 427 | return impl->virtual_filesystem; |
| 425 | } | 428 | } |
| 426 | 429 | ||
| 430 | void System::SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet) { | ||
| 431 | impl->profile_selector = std::move(applet); | ||
| 432 | } | ||
| 433 | |||
| 434 | const Core::Frontend::ProfileSelectApplet& System::GetProfileSelector() const { | ||
| 435 | return *impl->profile_selector; | ||
| 436 | } | ||
| 437 | |||
| 427 | void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) { | 438 | void System::SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet) { |
| 428 | impl->software_keyboard = std::move(applet); | 439 | impl->software_keyboard = std::move(applet); |
| 429 | } | 440 | } |
diff --git a/src/core/core.h b/src/core/core.h index 71031dfcf..869921493 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "core/file_sys/vfs_types.h" | 12 | #include "core/file_sys/vfs_types.h" |
| 13 | #include "core/hle/kernel/object.h" | 13 | #include "core/hle/kernel/object.h" |
| 14 | #include "frontend/applets/profile_select.h" | ||
| 14 | 15 | ||
| 15 | namespace Core::Frontend { | 16 | namespace Core::Frontend { |
| 16 | class EmuWindow; | 17 | class EmuWindow; |
| @@ -241,6 +242,10 @@ public: | |||
| 241 | 242 | ||
| 242 | std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; | 243 | std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; |
| 243 | 244 | ||
| 245 | void SetProfileSelector(std::unique_ptr<Core::Frontend::ProfileSelectApplet> applet); | ||
| 246 | |||
| 247 | const Core::Frontend::ProfileSelectApplet& GetProfileSelector() const; | ||
| 248 | |||
| 244 | void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet); | 249 | void SetSoftwareKeyboard(std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> applet); |
| 245 | 250 | ||
| 246 | const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const; | 251 | const Core::Frontend::SoftwareKeyboardApplet& GetSoftwareKeyboard() const; |
diff --git a/src/core/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp new file mode 100644 index 000000000..fbf5f2a9e --- /dev/null +++ b/src/core/frontend/applets/profile_select.cpp | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/frontend/applets/profile_select.h" | ||
| 6 | #include "core/settings.h" | ||
| 7 | |||
| 8 | namespace Core::Frontend { | ||
| 9 | |||
| 10 | ProfileSelectApplet::~ProfileSelectApplet() = default; | ||
| 11 | |||
| 12 | void DefaultProfileSelectApplet::SelectProfile( | ||
| 13 | std::function<void(std::optional<Service::Account::UUID>)> callback) const { | ||
| 14 | Service::Account::ProfileManager manager; | ||
| 15 | callback(manager.GetUser(Settings::values.current_user).value_or(Service::Account::UUID{})); | ||
| 16 | LOG_INFO(Service_ACC, "called, selecting current user instead of prompting..."); | ||
| 17 | } | ||
| 18 | |||
| 19 | } // namespace Core::Frontend | ||
diff --git a/src/core/frontend/applets/profile_select.h b/src/core/frontend/applets/profile_select.h new file mode 100644 index 000000000..fc8f7ae94 --- /dev/null +++ b/src/core/frontend/applets/profile_select.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <functional> | ||
| 8 | #include <optional> | ||
| 9 | #include "core/hle/service/acc/profile_manager.h" | ||
| 10 | |||
| 11 | namespace Core::Frontend { | ||
| 12 | |||
| 13 | class ProfileSelectApplet { | ||
| 14 | public: | ||
| 15 | virtual ~ProfileSelectApplet(); | ||
| 16 | |||
| 17 | virtual void SelectProfile( | ||
| 18 | std::function<void(std::optional<Service::Account::UUID>)> callback) const = 0; | ||
| 19 | }; | ||
| 20 | |||
| 21 | class DefaultProfileSelectApplet final : public ProfileSelectApplet { | ||
| 22 | public: | ||
| 23 | void SelectProfile( | ||
| 24 | std::function<void(std::optional<Service::Account::UUID>)> callback) const override; | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // namespace Core::Frontend | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 27c31aad2..5fc02a521 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "core/hle/service/am/applet_ae.h" | 19 | #include "core/hle/service/am/applet_ae.h" |
| 20 | #include "core/hle/service/am/applet_oe.h" | 20 | #include "core/hle/service/am/applet_oe.h" |
| 21 | #include "core/hle/service/am/applets/applets.h" | 21 | #include "core/hle/service/am/applets/applets.h" |
| 22 | #include "core/hle/service/am/applets/profile_select.h" | ||
| 22 | #include "core/hle/service/am/applets/software_keyboard.h" | 23 | #include "core/hle/service/am/applets/software_keyboard.h" |
| 23 | #include "core/hle/service/am/applets/stub_applet.h" | 24 | #include "core/hle/service/am/applets/stub_applet.h" |
| 24 | #include "core/hle/service/am/idle.h" | 25 | #include "core/hle/service/am/idle.h" |
| @@ -39,6 +40,7 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2}; | |||
| 39 | constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; | 40 | constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; |
| 40 | 41 | ||
| 41 | enum class AppletId : u32 { | 42 | enum class AppletId : u32 { |
| 43 | ProfileSelect = 0x10, | ||
| 42 | SoftwareKeyboard = 0x11, | 44 | SoftwareKeyboard = 0x11, |
| 43 | }; | 45 | }; |
| 44 | 46 | ||
| @@ -775,6 +777,8 @@ ILibraryAppletCreator::~ILibraryAppletCreator() = default; | |||
| 775 | 777 | ||
| 776 | static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) { | 778 | static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) { |
| 777 | switch (id) { | 779 | switch (id) { |
| 780 | case AppletId::ProfileSelect: | ||
| 781 | return std::make_shared<Applets::ProfileSelect>(); | ||
| 778 | case AppletId::SoftwareKeyboard: | 782 | case AppletId::SoftwareKeyboard: |
| 779 | return std::make_shared<Applets::SoftwareKeyboard>(); | 783 | return std::make_shared<Applets::SoftwareKeyboard>(); |
| 780 | default: | 784 | default: |
diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp new file mode 100644 index 000000000..4c7b45454 --- /dev/null +++ b/src/core/hle/service/am/applets/profile_select.cpp | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/string_util.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | #include "core/frontend/applets/software_keyboard.h" | ||
| 11 | #include "core/hle/service/am/am.h" | ||
| 12 | #include "core/hle/service/am/applets/profile_select.h" | ||
| 13 | |||
| 14 | namespace Service::AM::Applets { | ||
| 15 | |||
| 16 | constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; | ||
| 17 | |||
| 18 | ProfileSelect::ProfileSelect() = default; | ||
| 19 | ProfileSelect::~ProfileSelect() = default; | ||
| 20 | |||
| 21 | void ProfileSelect::Initialize() { | ||
| 22 | complete = false; | ||
| 23 | status = RESULT_SUCCESS; | ||
| 24 | final_data.clear(); | ||
| 25 | |||
| 26 | Applet::Initialize(); | ||
| 27 | |||
| 28 | const auto user_config_storage = broker.PopNormalDataToApplet(); | ||
| 29 | ASSERT(user_config_storage != nullptr); | ||
| 30 | const auto& user_config = user_config_storage->GetData(); | ||
| 31 | |||
| 32 | ASSERT(user_config.size() >= sizeof(UserSelectionConfig)); | ||
| 33 | std::memcpy(&config, user_config.data(), sizeof(UserSelectionConfig)); | ||
| 34 | } | ||
| 35 | |||
| 36 | bool ProfileSelect::TransactionComplete() const { | ||
| 37 | return complete; | ||
| 38 | } | ||
| 39 | |||
| 40 | ResultCode ProfileSelect::GetStatus() const { | ||
| 41 | return status; | ||
| 42 | } | ||
| 43 | |||
| 44 | void ProfileSelect::ExecuteInteractive() { | ||
| 45 | UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); | ||
| 46 | } | ||
| 47 | |||
| 48 | void ProfileSelect::Execute() { | ||
| 49 | if (complete) { | ||
| 50 | broker.PushNormalDataFromApplet(IStorage{final_data}); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | |||
| 54 | const auto& frontend{Core::System::GetInstance().GetProfileSelector()}; | ||
| 55 | |||
| 56 | frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); }); | ||
| 57 | } | ||
| 58 | |||
| 59 | void ProfileSelect::SelectionComplete(std::optional<Account::UUID> uuid) { | ||
| 60 | UserSelectionOutput output{}; | ||
| 61 | |||
| 62 | if (uuid.has_value() && uuid->uuid != Account::INVALID_UUID) { | ||
| 63 | output.result = 0; | ||
| 64 | output.uuid_selected = uuid->uuid; | ||
| 65 | } else { | ||
| 66 | status = ERR_USER_CANCELLED_SELECTION; | ||
| 67 | output.result = ERR_USER_CANCELLED_SELECTION.raw; | ||
| 68 | output.uuid_selected = Account::INVALID_UUID; | ||
| 69 | } | ||
| 70 | |||
| 71 | final_data = std::vector<u8>(sizeof(UserSelectionOutput)); | ||
| 72 | std::memcpy(final_data.data(), &output, final_data.size()); | ||
| 73 | broker.PushNormalDataFromApplet(IStorage{final_data}); | ||
| 74 | broker.SignalStateChanged(); | ||
| 75 | } | ||
| 76 | |||
| 77 | } // namespace Service::AM::Applets | ||
diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h new file mode 100644 index 000000000..787485f22 --- /dev/null +++ b/src/core/hle/service/am/applets/profile_select.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "common/common_funcs.h" | ||
| 10 | #include "core/hle/service/acc/profile_manager.h" | ||
| 11 | #include "core/hle/service/am/applets/applets.h" | ||
| 12 | |||
| 13 | namespace Service::AM::Applets { | ||
| 14 | |||
| 15 | struct UserSelectionConfig { | ||
| 16 | // TODO(DarkLordZach): RE this structure | ||
| 17 | // It seems to be flags and the like that determine the UI of the applet on the switch... from | ||
| 18 | // my research this is safe to ignore for now. | ||
| 19 | INSERT_PADDING_BYTES(0xA0); | ||
| 20 | }; | ||
| 21 | static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has incorrect size."); | ||
| 22 | |||
| 23 | struct UserSelectionOutput { | ||
| 24 | u64 result; | ||
| 25 | u128 uuid_selected; | ||
| 26 | }; | ||
| 27 | static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size."); | ||
| 28 | |||
| 29 | class ProfileSelect final : public Applet { | ||
| 30 | public: | ||
| 31 | ProfileSelect(); | ||
| 32 | ~ProfileSelect() override; | ||
| 33 | |||
| 34 | void Initialize() override; | ||
| 35 | |||
| 36 | bool TransactionComplete() const override; | ||
| 37 | ResultCode GetStatus() const override; | ||
| 38 | void ExecuteInteractive() override; | ||
| 39 | void Execute() override; | ||
| 40 | |||
| 41 | void SelectionComplete(std::optional<Account::UUID> uuid); | ||
| 42 | |||
| 43 | private: | ||
| 44 | UserSelectionConfig config; | ||
| 45 | bool complete = false; | ||
| 46 | ResultCode status = RESULT_SUCCESS; | ||
| 47 | std::vector<u8> final_data; | ||
| 48 | }; | ||
| 49 | |||
| 50 | } // namespace Service::AM::Applets | ||
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 0691b91a2..17ecaafde 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -7,6 +7,8 @@ add_executable(yuzu | |||
| 7 | Info.plist | 7 | Info.plist |
| 8 | about_dialog.cpp | 8 | about_dialog.cpp |
| 9 | about_dialog.h | 9 | about_dialog.h |
| 10 | applets/profile_select.cpp | ||
| 11 | applets/profile_select.h | ||
| 10 | applets/software_keyboard.cpp | 12 | applets/software_keyboard.cpp |
| 11 | applets/software_keyboard.h | 13 | applets/software_keyboard.h |
| 12 | bootmanager.cpp | 14 | bootmanager.cpp |
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp new file mode 100644 index 000000000..5c1b65a2c --- /dev/null +++ b/src/yuzu/applets/profile_select.cpp | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <mutex> | ||
| 6 | #include <QDialogButtonBox> | ||
| 7 | #include <QLabel> | ||
| 8 | #include <QLineEdit> | ||
| 9 | #include <QScrollArea> | ||
| 10 | #include <QStandardItemModel> | ||
| 11 | #include <QVBoxLayout> | ||
| 12 | #include "common/file_util.h" | ||
| 13 | #include "common/string_util.h" | ||
| 14 | #include "core/hle/lock.h" | ||
| 15 | #include "yuzu/applets/profile_select.h" | ||
| 16 | #include "yuzu/main.h" | ||
| 17 | |||
| 18 | // Same backup JPEG used by acc IProfile::GetImage if no jpeg found | ||
| 19 | constexpr std::array<u8, 107> backup_jpeg{ | ||
| 20 | 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, | ||
| 21 | 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, | ||
| 22 | 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, | ||
| 23 | 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, | ||
| 24 | 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, | ||
| 25 | 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, | ||
| 26 | 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | ||
| 27 | }; | ||
| 28 | |||
| 29 | QString FormatUserEntryText(const QString& username, Service::Account::UUID uuid) { | ||
| 30 | return QtProfileSelectionDialog::tr( | ||
| 31 | "%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " | ||
| 32 | "00112233-4455-6677-8899-AABBCCDDEEFF))") | ||
| 33 | .arg(username, QString::fromStdString(uuid.FormatSwitch())); | ||
| 34 | } | ||
| 35 | |||
| 36 | QString GetImagePath(Service::Account::UUID uuid) { | ||
| 37 | const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 38 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | ||
| 39 | return QString::fromStdString(path); | ||
| 40 | } | ||
| 41 | |||
| 42 | QPixmap GetIcon(Service::Account::UUID uuid) { | ||
| 43 | QPixmap icon{GetImagePath(uuid)}; | ||
| 44 | |||
| 45 | if (!icon) { | ||
| 46 | icon.fill(Qt::black); | ||
| 47 | icon.loadFromData(backup_jpeg.data(), static_cast<u32>(backup_jpeg.size())); | ||
| 48 | } | ||
| 49 | |||
| 50 | return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); | ||
| 51 | } | ||
| 52 | |||
| 53 | QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent) | ||
| 54 | : QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) { | ||
| 55 | outer_layout = new QVBoxLayout; | ||
| 56 | |||
| 57 | instruction_label = new QLabel(tr("Select a user:")); | ||
| 58 | |||
| 59 | scroll_area = new QScrollArea; | ||
| 60 | |||
| 61 | buttons = new QDialogButtonBox; | ||
| 62 | buttons->addButton(tr("Cancel"), QDialogButtonBox::RejectRole); | ||
| 63 | buttons->addButton(tr("OK"), QDialogButtonBox::AcceptRole); | ||
| 64 | |||
| 65 | connect(buttons, &QDialogButtonBox::accepted, this, &QtProfileSelectionDialog::accept); | ||
| 66 | connect(buttons, &QDialogButtonBox::rejected, this, &QtProfileSelectionDialog::reject); | ||
| 67 | |||
| 68 | outer_layout->addWidget(instruction_label); | ||
| 69 | outer_layout->addWidget(scroll_area); | ||
| 70 | outer_layout->addWidget(buttons); | ||
| 71 | |||
| 72 | layout = new QVBoxLayout; | ||
| 73 | tree_view = new QTreeView; | ||
| 74 | item_model = new QStandardItemModel(tree_view); | ||
| 75 | tree_view->setModel(item_model); | ||
| 76 | |||
| 77 | tree_view->setAlternatingRowColors(true); | ||
| 78 | tree_view->setSelectionMode(QHeaderView::SingleSelection); | ||
| 79 | tree_view->setSelectionBehavior(QHeaderView::SelectRows); | ||
| 80 | tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 81 | tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 82 | tree_view->setSortingEnabled(true); | ||
| 83 | tree_view->setEditTriggers(QHeaderView::NoEditTriggers); | ||
| 84 | tree_view->setUniformRowHeights(true); | ||
| 85 | tree_view->setIconSize({64, 64}); | ||
| 86 | tree_view->setContextMenuPolicy(Qt::NoContextMenu); | ||
| 87 | |||
| 88 | item_model->insertColumns(0, 1); | ||
| 89 | item_model->setHeaderData(0, Qt::Horizontal, "Users"); | ||
| 90 | |||
| 91 | // We must register all custom types with the Qt Automoc system so that we are able to use it | ||
| 92 | // with signals/slots. In this case, QList falls under the umbrells of custom types. | ||
| 93 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); | ||
| 94 | |||
| 95 | layout->setContentsMargins(0, 0, 0, 0); | ||
| 96 | layout->setSpacing(0); | ||
| 97 | layout->addWidget(tree_view); | ||
| 98 | |||
| 99 | scroll_area->setLayout(layout); | ||
| 100 | |||
| 101 | connect(tree_view, &QTreeView::clicked, this, &QtProfileSelectionDialog::SelectUser); | ||
| 102 | |||
| 103 | const auto& profiles = profile_manager->GetAllUsers(); | ||
| 104 | for (const auto& user : profiles) { | ||
| 105 | Service::Account::ProfileBase profile; | ||
| 106 | if (!profile_manager->GetProfileBase(user, profile)) | ||
| 107 | continue; | ||
| 108 | |||
| 109 | const auto username = Common::StringFromFixedZeroTerminatedBuffer( | ||
| 110 | reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); | ||
| 111 | |||
| 112 | list_items.push_back(QList<QStandardItem*>{new QStandardItem{ | ||
| 113 | GetIcon(user), FormatUserEntryText(QString::fromStdString(username), user)}}); | ||
| 114 | } | ||
| 115 | |||
| 116 | for (const auto& item : list_items) | ||
| 117 | item_model->appendRow(item); | ||
| 118 | |||
| 119 | setLayout(outer_layout); | ||
| 120 | setWindowTitle(tr("Profile Selector")); | ||
| 121 | resize(550, 400); | ||
| 122 | } | ||
| 123 | |||
| 124 | QtProfileSelectionDialog::~QtProfileSelectionDialog() = default; | ||
| 125 | |||
| 126 | void QtProfileSelectionDialog::accept() { | ||
| 127 | ok = true; | ||
| 128 | QDialog::accept(); | ||
| 129 | } | ||
| 130 | |||
| 131 | void QtProfileSelectionDialog::reject() { | ||
| 132 | ok = false; | ||
| 133 | user_index = 0; | ||
| 134 | QDialog::reject(); | ||
| 135 | } | ||
| 136 | |||
| 137 | bool QtProfileSelectionDialog::GetStatus() const { | ||
| 138 | return ok; | ||
| 139 | } | ||
| 140 | |||
| 141 | u32 QtProfileSelectionDialog::GetIndex() const { | ||
| 142 | return user_index; | ||
| 143 | } | ||
| 144 | |||
| 145 | void QtProfileSelectionDialog::SelectUser(const QModelIndex& index) { | ||
| 146 | user_index = index.row(); | ||
| 147 | } | ||
| 148 | |||
| 149 | QtProfileSelector::QtProfileSelector(GMainWindow& parent) { | ||
| 150 | connect(this, &QtProfileSelector::MainWindowSelectProfile, &parent, | ||
| 151 | &GMainWindow::ProfileSelectorSelectProfile, Qt::QueuedConnection); | ||
| 152 | connect(&parent, &GMainWindow::ProfileSelectorFinishedSelection, this, | ||
| 153 | &QtProfileSelector::MainWindowFinishedSelection, Qt::DirectConnection); | ||
| 154 | } | ||
| 155 | |||
| 156 | QtProfileSelector::~QtProfileSelector() = default; | ||
| 157 | |||
| 158 | void QtProfileSelector::SelectProfile( | ||
| 159 | std::function<void(std::optional<Service::Account::UUID>)> callback) const { | ||
| 160 | this->callback = std::move(callback); | ||
| 161 | emit MainWindowSelectProfile(); | ||
| 162 | } | ||
| 163 | |||
| 164 | void QtProfileSelector::MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid) { | ||
| 165 | // Acquire the HLE mutex | ||
| 166 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 167 | callback(uuid); | ||
| 168 | } | ||
diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/profile_select.h new file mode 100644 index 000000000..868573324 --- /dev/null +++ b/src/yuzu/applets/profile_select.h | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | #include <QDialog> | ||
| 9 | #include <QList> | ||
| 10 | #include "core/frontend/applets/profile_select.h" | ||
| 11 | |||
| 12 | class GMainWindow; | ||
| 13 | class QDialogButtonBox; | ||
| 14 | class QGraphicsScene; | ||
| 15 | class QLabel; | ||
| 16 | class QScrollArea; | ||
| 17 | class QStandardItem; | ||
| 18 | class QStandardItemModel; | ||
| 19 | class QTreeView; | ||
| 20 | class QVBoxLayout; | ||
| 21 | |||
| 22 | class QtProfileSelectionDialog final : public QDialog { | ||
| 23 | Q_OBJECT | ||
| 24 | |||
| 25 | public: | ||
| 26 | explicit QtProfileSelectionDialog(QWidget* parent); | ||
| 27 | ~QtProfileSelectionDialog() override; | ||
| 28 | |||
| 29 | void accept() override; | ||
| 30 | void reject() override; | ||
| 31 | |||
| 32 | bool GetStatus() const; | ||
| 33 | u32 GetIndex() const; | ||
| 34 | |||
| 35 | private: | ||
| 36 | bool ok = false; | ||
| 37 | u32 user_index = 0; | ||
| 38 | |||
| 39 | void SelectUser(const QModelIndex& index); | ||
| 40 | |||
| 41 | QVBoxLayout* layout; | ||
| 42 | QTreeView* tree_view; | ||
| 43 | QStandardItemModel* item_model; | ||
| 44 | QGraphicsScene* scene; | ||
| 45 | |||
| 46 | std::vector<QList<QStandardItem*>> list_items; | ||
| 47 | |||
| 48 | QVBoxLayout* outer_layout; | ||
| 49 | QLabel* instruction_label; | ||
| 50 | QScrollArea* scroll_area; | ||
| 51 | QDialogButtonBox* buttons; | ||
| 52 | |||
| 53 | std::unique_ptr<Service::Account::ProfileManager> profile_manager; | ||
| 54 | }; | ||
| 55 | |||
| 56 | class QtProfileSelector final : public QObject, public Core::Frontend::ProfileSelectApplet { | ||
| 57 | Q_OBJECT | ||
| 58 | |||
| 59 | public: | ||
| 60 | explicit QtProfileSelector(GMainWindow& parent); | ||
| 61 | ~QtProfileSelector() override; | ||
| 62 | |||
| 63 | void SelectProfile( | ||
| 64 | std::function<void(std::optional<Service::Account::UUID>)> callback) const override; | ||
| 65 | |||
| 66 | signals: | ||
| 67 | void MainWindowSelectProfile() const; | ||
| 68 | |||
| 69 | private: | ||
| 70 | void MainWindowFinishedSelection(std::optional<Service::Account::UUID> uuid); | ||
| 71 | |||
| 72 | mutable std::function<void(std::optional<Service::Account::UUID>)> callback; | ||
| 73 | }; | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 90b212ba5..feb5572c6 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <thread> | 8 | #include <thread> |
| 9 | 9 | ||
| 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. | 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. |
| 11 | #include "applets/profile_select.h" | ||
| 11 | #include "applets/software_keyboard.h" | 12 | #include "applets/software_keyboard.h" |
| 12 | #include "configuration/configure_per_general.h" | 13 | #include "configuration/configure_per_general.h" |
| 13 | #include "core/file_sys/vfs.h" | 14 | #include "core/file_sys/vfs.h" |
| @@ -208,6 +209,28 @@ GMainWindow::~GMainWindow() { | |||
| 208 | delete render_window; | 209 | delete render_window; |
| 209 | } | 210 | } |
| 210 | 211 | ||
| 212 | void GMainWindow::ProfileSelectorSelectProfile() { | ||
| 213 | QtProfileSelectionDialog dialog(this); | ||
| 214 | dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | | ||
| 215 | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint); | ||
| 216 | dialog.setWindowModality(Qt::WindowModal); | ||
| 217 | dialog.exec(); | ||
| 218 | |||
| 219 | if (!dialog.GetStatus()) { | ||
| 220 | emit ProfileSelectorFinishedSelection(std::nullopt); | ||
| 221 | return; | ||
| 222 | } | ||
| 223 | |||
| 224 | Service::Account::ProfileManager manager; | ||
| 225 | const auto uuid = manager.GetUser(dialog.GetIndex()); | ||
| 226 | if (!uuid.has_value()) { | ||
| 227 | emit ProfileSelectorFinishedSelection(std::nullopt); | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | |||
| 231 | emit ProfileSelectorFinishedSelection(uuid); | ||
| 232 | } | ||
| 233 | |||
| 211 | void GMainWindow::SoftwareKeyboardGetText( | 234 | void GMainWindow::SoftwareKeyboardGetText( |
| 212 | const Core::Frontend::SoftwareKeyboardParameters& parameters) { | 235 | const Core::Frontend::SoftwareKeyboardParameters& parameters) { |
| 213 | QtSoftwareKeyboardDialog dialog(this, parameters); | 236 | QtSoftwareKeyboardDialog dialog(this, parameters); |
| @@ -574,6 +597,7 @@ bool GMainWindow::LoadROM(const QString& filename) { | |||
| 574 | 597 | ||
| 575 | system.SetGPUDebugContext(debug_context); | 598 | system.SetGPUDebugContext(debug_context); |
| 576 | 599 | ||
| 600 | system.SetProfileSelector(std::make_unique<QtProfileSelector>(*this)); | ||
| 577 | system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this)); | 601 | system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this)); |
| 578 | 602 | ||
| 579 | const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; | 603 | const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index ca9c50367..4a982bb02 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -99,10 +99,12 @@ signals: | |||
| 99 | // Signal that tells widgets to update icons to use the current theme | 99 | // Signal that tells widgets to update icons to use the current theme |
| 100 | void UpdateThemedIcons(); | 100 | void UpdateThemedIcons(); |
| 101 | 101 | ||
| 102 | void ProfileSelectorFinishedSelection(std::optional<Service::Account::UUID> uuid); | ||
| 102 | void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); | 103 | void SoftwareKeyboardFinishedText(std::optional<std::u16string> text); |
| 103 | void SoftwareKeyboardFinishedCheckDialog(); | 104 | void SoftwareKeyboardFinishedCheckDialog(); |
| 104 | 105 | ||
| 105 | public slots: | 106 | public slots: |
| 107 | void ProfileSelectorSelectProfile(); | ||
| 106 | void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); | 108 | void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters); |
| 107 | void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); | 109 | void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message); |
| 108 | 110 | ||