diff --git a/sphaira/include/app.hpp b/sphaira/include/app.hpp index e2b583f..da2d4d2 100644 --- a/sphaira/include/app.hpp +++ b/sphaira/include/app.hpp @@ -192,6 +192,28 @@ public: } } + static auto GetAccountList() -> std::vector { + std::vector out; + + AccountUid uids[8]; + s32 account_count; + if (R_SUCCEEDED(accountListAllUsers(uids, std::size(uids), &account_count))) { + for (s32 i = 0; i < account_count; i++) { + AccountProfile profile; + if (R_SUCCEEDED(accountGetProfile(&profile, uids[i]))) { + ON_SCOPE_EXIT(accountProfileClose(&profile)); + + AccountProfileBase base; + if (R_SUCCEEDED(accountProfileGet(&profile, nullptr, &base))) { + out.emplace_back(base); + } + } + } + } + + return out; + } + // private: static constexpr inline auto CONFIG_PATH = "/config/sphaira/config.ini"; static constexpr inline auto PLAYLOG_PATH = "/config/sphaira/playlog.ini"; diff --git a/sphaira/include/ui/menus/filebrowser.hpp b/sphaira/include/ui/menus/filebrowser.hpp index 3f32cd1..8d2c66f 100644 --- a/sphaira/include/ui/menus/filebrowser.hpp +++ b/sphaira/include/ui/menus/filebrowser.hpp @@ -154,6 +154,8 @@ struct FsDirCollection { using FsDirCollections = std::vector; +void SignalChange(); + struct Menu; struct FsView final : Widget { @@ -244,7 +246,7 @@ private: } void Sort(); - void SortAndFindLastFile(); + void SortAndFindLastFile(bool scan = false); void SetIndexFromLastFile(const LastFile& last_file); void OnDeleteCallback(); diff --git a/sphaira/include/ui/menus/homebrew.hpp b/sphaira/include/ui/menus/homebrew.hpp index 595c859..d04188a 100644 --- a/sphaira/include/ui/menus/homebrew.hpp +++ b/sphaira/include/ui/menus/homebrew.hpp @@ -25,6 +25,7 @@ enum OrderType { using LayoutType = grid::LayoutType; auto GetNroEntries() -> std::span; +void SignalChange(); struct Menu final : grid::Menu { Menu(); @@ -47,7 +48,7 @@ private: void InstallHomebrew(); void ScanHomebrew(); void Sort(); - void SortAndFindLastFile(); + void SortAndFindLastFile(bool scan = false); void FreeEntries(); void OnLayoutChange(); @@ -61,6 +62,7 @@ private: std::vector m_entries{}; s64 m_index{}; // where i am in the array std::unique_ptr m_list{}; + bool m_dirty{}; option::OptionLong m_sort{INI_SECTION, "sort", SortType::SortType_AlphabeticalStar}; option::OptionLong m_order{INI_SECTION, "order", OrderType::OrderType_Descending}; diff --git a/sphaira/include/ui/menus/save_menu.hpp b/sphaira/include/ui/menus/save_menu.hpp index 535ba65..c661b4a 100644 --- a/sphaira/include/ui/menus/save_menu.hpp +++ b/sphaira/include/ui/menus/save_menu.hpp @@ -81,6 +81,8 @@ enum OrderType { using LayoutType = grid::LayoutType; +void SignalChange(); + struct Menu final : grid::Menu { Menu(u32 flags); ~Menu(); diff --git a/sphaira/source/ui/menus/appstore.cpp b/sphaira/source/ui/menus/appstore.cpp index c23dace..44b5714 100644 --- a/sphaira/source/ui/menus/appstore.cpp +++ b/sphaira/source/ui/menus/appstore.cpp @@ -1,4 +1,5 @@ #include "ui/menus/appstore.hpp" +#include "ui/menus/homebrew.hpp" #include "ui/sidebar.hpp" #include "ui/popup_list.hpp" #include "ui/progress_box.hpp" @@ -798,6 +799,7 @@ void EntryMenu::UpdateOptions() { App::Push(std::make_shared(m_entry.image.image, "Downloading "_i18n, m_entry.title, [this](auto pbox){ return InstallApp(pbox, m_entry); }, [this](Result rc){ + homebrew::SignalChange(); App::PushErrorBox(rc, "Failed to, TODO: add message here"_i18n); if (R_SUCCEEDED(rc)) { @@ -813,6 +815,7 @@ void EntryMenu::UpdateOptions() { App::Push(std::make_shared(m_entry.image.image, "Uninstalling "_i18n, m_entry.title, [this](auto pbox){ return UninstallApp(pbox, m_entry); }, [this](Result rc){ + homebrew::SignalChange(); App::PushErrorBox(rc, "Failed to, TODO: add message here"_i18n); if (R_SUCCEEDED(rc)) { diff --git a/sphaira/source/ui/menus/filebrowser.cpp b/sphaira/source/ui/menus/filebrowser.cpp index 9fe5e36..03e53d5 100644 --- a/sphaira/source/ui/menus/filebrowser.cpp +++ b/sphaira/source/ui/menus/filebrowser.cpp @@ -44,6 +44,8 @@ namespace sphaira::ui::menu::filebrowser { namespace { +constinit UEvent g_change_uevent; + constexpr FsEntry FS_ENTRY_DEFAULT{ "microSD card", "/", FsType::Sd, FsEntryFlag_Assoc, }; @@ -289,6 +291,10 @@ auto GetRomIcon(fs::Fs* fs, ProgressBox* pbox, std::string filename, const RomDa } // namespace +void SignalChange() { + ueventSignal(&g_change_uevent); +} + FsView::FsView(Menu* menu, const fs::FsPath& path, const FsEntry& entry, ViewSide side) : m_menu{menu}, m_side{side} { this->SetActions( std::make_pair(Button::L2, Action{[this](){ @@ -1083,13 +1089,17 @@ void FsView::Sort() { std::sort(m_entries_current.begin(), m_entries_current.end(), sorter); } -void FsView::SortAndFindLastFile() { +void FsView::SortAndFindLastFile(bool scan) { std::optional last_file; if (!m_path.empty() && !m_entries_current.empty()) { last_file = LastFile(GetEntry().name, m_index, m_list->GetYoff(), m_entries_current.size()); } - Sort(); + if (scan) { + Scan(m_path); + } else { + Sort(); + } if (last_file.has_value()) { SetIndexFromLastFile(*last_file); @@ -1851,12 +1861,22 @@ Menu::Menu(u32 flags) : MenuBase{"FileBrowser"_i18n, flags} { } view = view_left = std::make_shared(this, ViewSide::Left); + ueventCreate(&g_change_uevent, true); } Menu::~Menu() { } void Menu::Update(Controller* controller, TouchInfo* touch) { + if (R_SUCCEEDED(waitSingle(waiterForUEvent(&g_change_uevent), 0))) { + if (IsSplitScreen()) { + view_left->SortAndFindLastFile(true); + view_right->SortAndFindLastFile(true); + } else { + view->SortAndFindLastFile(true); + } + } + // workaround the buttons not being display properly. // basically, inherit all actions from the view, draw them, // then restore state after. diff --git a/sphaira/source/ui/menus/game_menu.cpp b/sphaira/source/ui/menus/game_menu.cpp index ee559dc..af7e4d0 100644 --- a/sphaira/source/ui/menus/game_menu.cpp +++ b/sphaira/source/ui/menus/game_menu.cpp @@ -5,6 +5,7 @@ #include "defines.hpp" #include "i18n.hpp" #include "image.hpp" +#include "swkbd.hpp" #include "ui/menus/game_menu.hpp" #include "ui/sidebar.hpp" @@ -945,6 +946,33 @@ Menu::Menu(u32 flags) : grid::Menu{"Games"_i18n, flags} { options->Add(std::make_shared("Title cache"_i18n, m_title_cache.Get(), [this](bool& v_out){ m_title_cache.Set(v_out); })); + + // todo: impl this. + #if 0 + options->Add(std::make_shared("Create save"_i18n, [this](){ + ui::PopupList::Items items{}; + const auto accounts = App::GetAccountList(); + for (auto& p : accounts) { + items.emplace_back(p.nickname); + } + + fsCreateSaveDataFileSystem; + + App::Push(std::make_shared( + "Select user to create save for"_i18n, items, [accounts](auto op_index){ + if (op_index) { + s64 out; + if (R_SUCCEEDED(swkbd::ShowNumPad(out, "Enter the save size"_i18n.c_str()))) { + } + } + } + )); + + // 1. Select user to create save for. + // 2. Enter the save size. + // 3. Enter the journal size (0 for default). + })); + #endif }}) ); diff --git a/sphaira/source/ui/menus/homebrew.cpp b/sphaira/source/ui/menus/homebrew.cpp index 931f747..afaac7f 100644 --- a/sphaira/source/ui/menus/homebrew.cpp +++ b/sphaira/source/ui/menus/homebrew.cpp @@ -20,6 +20,7 @@ namespace sphaira::ui::menu::homebrew { namespace { Menu* g_menu{}; +constinit UEvent g_change_uevent; auto GenerateStarPath(const fs::FsPath& nro_path) -> fs::FsPath { fs::FsPath out{}; @@ -35,6 +36,10 @@ void FreeEntry(NVGcontext* vg, NroEntry& e) { } // namespace +void SignalChange() { + ueventSignal(&g_change_uevent); +} + auto GetNroEntries() -> std::span { if (!g_menu) { return {}; @@ -139,6 +144,7 @@ Menu::Menu() : grid::Menu{"Homebrew"_i18n, MenuFlag_Tab} { ); OnLayoutChange(); + ueventCreate(&g_change_uevent, true); } Menu::~Menu() { @@ -147,6 +153,14 @@ Menu::~Menu() { } void Menu::Update(Controller* controller, TouchInfo* touch) { + if (R_SUCCEEDED(waitSingle(waiterForUEvent(&g_change_uevent), 0))) { + m_dirty = true; + } + + if (m_dirty) { + SortAndFindLastFile(true); + } + MenuBase::Update(controller, touch); m_list->OnUpdate(controller, touch, m_index, m_entries.size(), [this](bool touch, auto i) { if (touch && m_index == i) { @@ -297,6 +311,7 @@ void Menu::ScanHomebrew() { this->Sort(); SetIndex(0); + m_dirty = false; } void Menu::Sort() { @@ -391,9 +406,13 @@ void Menu::Sort() { std::sort(m_entries.begin(), m_entries.end(), sorter); } -void Menu::SortAndFindLastFile() { +void Menu::SortAndFindLastFile(bool scan) { const auto path = m_entries[m_index].path; - Sort(); + if (scan) { + ScanHomebrew(); + } else { + Sort(); + } SetIndex(0); s64 index = -1; diff --git a/sphaira/source/ui/menus/save_menu.cpp b/sphaira/source/ui/menus/save_menu.cpp index 3cf6ff4..b374523 100644 --- a/sphaira/source/ui/menus/save_menu.cpp +++ b/sphaira/source/ui/menus/save_menu.cpp @@ -41,6 +41,8 @@ constexpr u32 NX_SAVE_META_MAGIC = 0x4A4B5356; // JKSV constexpr u32 NX_SAVE_META_VERSION = 1; constexpr const char* NX_SAVE_META_NAME = ".nx_save_meta.bin"; +constinit UEvent g_change_uevent; + // https://github.com/J-D-K/JKSV/issues/264#issuecomment-2618962807 struct NXSaveMeta { u32 magic{}; // NX_SAVE_META_MAGIC @@ -714,6 +716,10 @@ void ThreadFunc(void* user) { } // namespace +void SignalChange() { + ueventSignal(&g_change_uevent); +} + ThreadData::ThreadData() { ueventCreate(&m_uevent, true); mutexInit(&m_mutex_id); @@ -928,21 +934,7 @@ Menu::Menu(u32 flags) : grid::Menu{"Saves"_i18n, flags} { OnLayoutChange(); nsInitialize(); - AccountUid uids[8]; - s32 account_count; - if (R_SUCCEEDED(accountListAllUsers(uids, std::size(uids), &account_count))) { - for (s32 i = 0; i < account_count; i++) { - AccountProfile profile; - if (R_SUCCEEDED(accountGetProfile(&profile, uids[i]))) { - ON_SCOPE_EXIT(accountProfileClose(&profile)); - - AccountProfileBase base; - if (R_SUCCEEDED(accountProfileGet(&profile, nullptr, &base))) { - m_accounts.emplace_back(base); - } - } - } - } + m_accounts = App::GetAccountList(); // try and find the last / default account and set that. AccountUid uid{}; @@ -965,6 +957,7 @@ Menu::Menu(u32 flags) : grid::Menu{"Saves"_i18n, flags} { threadCreate(&m_thread, ThreadFunc, &m_thread_data, nullptr, 1024*32, THREAD_PRIO, THREAD_CORE); svcSetThreadCoreMask(m_thread.handle, THREAD_CORE, THREAD_AFFINITY_DEFAULT(THREAD_CORE)); threadStart(&m_thread); + ueventCreate(&g_change_uevent, true); } Menu::~Menu() { @@ -982,6 +975,10 @@ Menu::~Menu() { } void Menu::Update(Controller* controller, TouchInfo* touch) { + if (R_SUCCEEDED(waitSingle(waiterForUEvent(&g_change_uevent), 0))) { + m_dirty = true; + } + if (m_dirty) { App::Notify("Updating application record list"); SortAndFindLastFile(true);