Implement most of cellUserInfo

This commit is contained in:
Megamouse 2021-04-21 23:20:33 +02:00 committed by Ivan
parent 61450335a5
commit 087dccd194
10 changed files with 562 additions and 28 deletions

View File

@ -422,6 +422,7 @@ target_sources(rpcs3_emu PRIVATE
RSX/Overlays/overlay_perf_metrics.cpp RSX/Overlays/overlay_perf_metrics.cpp
RSX/Overlays/overlay_progress_bar.cpp RSX/Overlays/overlay_progress_bar.cpp
RSX/Overlays/overlay_save_dialog.cpp RSX/Overlays/overlay_save_dialog.cpp
RSX/Overlays/overlay_user_list_dialog.cpp
RSX/Overlays/overlay_utils.cpp RSX/Overlays/overlay_utils.cpp
RSX/Overlays/overlays.cpp RSX/Overlays/overlays.cpp
RSX/Overlays/overlay_shader_compile_notification.cpp RSX/Overlays/overlay_shader_compile_notification.cpp

View File

@ -1,6 +1,7 @@
#include "stdafx.h" #include "stdafx.h"
#include "Emu/System.h" #include "Emu/System.h"
#include "Emu/VFS.h" #include "Emu/VFS.h"
#include "Emu/IdManager.h"
#include "Emu/localized_string.h" #include "Emu/localized_string.h"
#include "Emu/Cell/lv2/sys_fs.h" #include "Emu/Cell/lv2/sys_fs.h"
#include "Emu/Cell/lv2/sys_sync.h" #include "Emu/Cell/lv2/sys_sync.h"
@ -97,7 +98,7 @@ vm::gvar<savedata_context> g_savedata_context;
struct savedata_manager struct savedata_manager
{ {
semaphore<> mutex; semaphore<> mutex;
atomic_t<bool> enable_overlay; atomic_t<bool> enable_overlay{false};
}; };
static std::vector<SaveDataEntry> get_save_entries(const std::string& base_dir, const std::string& prefix) static std::vector<SaveDataEntry> get_save_entries(const std::string& base_dir, const std::string& prefix)
@ -931,7 +932,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
return {CELL_SAVEDATA_ERROR_PARAM, "34"}; return {CELL_SAVEDATA_ERROR_PARAM, "34"};
} }
//TODO: If adding the new data to the save_entries vector // TODO: If adding the new data to the save_entries vector
// to be displayed in the save mangaer UI, it should be focused here // to be displayed in the save mangaer UI, it should be focused here
break; break;
} }

View File

@ -1,7 +1,9 @@
#include "stdafx.h" #include "stdafx.h"
#include "Emu/System.h" #include "Emu/System.h"
#include "Emu/VFS.h" #include "Emu/VFS.h"
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUModule.h" #include "Emu/Cell/PPUModule.h"
#include "Emu/RSX/Overlays/overlay_user_list_dialog.h"
#include "cellUserInfo.h" #include "cellUserInfo.h"
@ -10,6 +12,25 @@
LOG_CHANNEL(cellUserInfo); LOG_CHANNEL(cellUserInfo);
struct user_info_manager
{
atomic_t<bool> enable_overlay{false};
atomic_t<bool> dialog_opened{false};
};
std::string get_username(const u32 user_id)
{
std::string username;
if (const fs::file file{Emulator::GetHddDir() + fmt::format("home/%08d/localusername", user_id)})
{
username = file.to_string();
username.resize(CELL_USERINFO_USERNAME_SIZE); // TODO: investigate
}
return username;
}
template<> template<>
void fmt_class_string<CellUserInfoError>::format(std::string& out, u64 arg) void fmt_class_string<CellUserInfoError>::format(std::string& out, u64 arg)
{ {
@ -27,6 +48,21 @@ void fmt_class_string<CellUserInfoError>::format(std::string& out, u64 arg)
}); });
} }
template<>
void fmt_class_string<cell_user_callback_result>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto error)
{
switch (error)
{
STR_CASE(CELL_USERINFO_RET_OK);
STR_CASE(CELL_USERINFO_RET_CANCEL);
}
return unknown;
});
}
error_code cellUserInfoGetStat(u32 id, vm::ptr<CellUserInfoUserStat> stat) error_code cellUserInfoGetStat(u32 id, vm::ptr<CellUserInfoUserStat> stat)
{ {
cellUserInfo.warning("cellUserInfoGetStat(id=%d, stat=*0x%x)", id, stat); cellUserInfo.warning("cellUserInfoGetStat(id=%d, stat=*0x%x)", id, stat);
@ -43,7 +79,7 @@ error_code cellUserInfoGetStat(u32 id, vm::ptr<CellUserInfoUserStat> stat)
id = Emu.GetUsrId(); id = Emu.GetUsrId();
} }
const std::string& path = vfs::get(fmt::format("/dev_hdd0/home/%08d/", id)); const std::string path = vfs::get(fmt::format("/dev_hdd0/home/%08d/", id));
if (!fs::is_dir(path)) if (!fs::is_dir(path))
{ {
@ -70,12 +106,103 @@ error_code cellUserInfoGetStat(u32 id, vm::ptr<CellUserInfoUserStat> stat)
error_code cellUserInfoSelectUser_ListType(vm::ptr<CellUserInfoTypeSet> listType, vm::ptr<CellUserInfoFinishCallback> funcSelect, u32 container, vm::ptr<void> userdata) error_code cellUserInfoSelectUser_ListType(vm::ptr<CellUserInfoTypeSet> listType, vm::ptr<CellUserInfoFinishCallback> funcSelect, u32 container, vm::ptr<void> userdata)
{ {
cellUserInfo.todo("cellUserInfoSelectUser_ListType(listType=*0x%x, funcSelect=*0x%x, container=0x%x, userdata=*0x%x)", listType, funcSelect, container, userdata); cellUserInfo.warning("cellUserInfoSelectUser_ListType(listType=*0x%x, funcSelect=*0x%x, container=0x%x, userdata=*0x%x)", listType, funcSelect, container, userdata);
if (!listType || !funcSelect) // TODO: confirm
{
return CELL_USERINFO_ERROR_PARAM;
}
if (g_fxo->get<user_info_manager>().dialog_opened)
{
return CELL_USERINFO_ERROR_BUSY;
}
std::vector<u32> user_ids;
const std::string home_dir = Emulator::GetHddDir() + "home";
for (const auto& user_folder : fs::dir(home_dir))
{
if (!user_folder.is_directory)
{
continue;
}
// Is the folder name exactly 8 all-numerical characters long?
const u32 user_id = Emulator::CheckUsr(user_folder.name);
if (user_id == 0)
{
continue;
}
// Does the localusername file exist?
if (!fs::is_file(home_dir + "/" + user_folder.name + "/localusername"))
{
continue;
}
// TODO: maybe also restrict this to CELL_USERINFO_USER_MAX
if (listType->type != CELL_USERINFO_LISTTYPE_NOCURRENT || user_id != Emu.GetUsrId())
{
user_ids.push_back(user_id);
}
}
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
if (g_fxo->get<user_info_manager>().dialog_opened.exchange(true))
{
return CELL_USERINFO_ERROR_BUSY;
}
const std::string title = listType->title.get_ptr();
const u32 focused = listType->focus;
cellUserInfo.warning("cellUserInfoSelectUser_ListType: opening user_list_dialog with: title='%s', focused=%d", title, focused);
const bool enable_overlay = g_fxo->get<user_info_manager>().enable_overlay;
const error_code result = manager->create<rsx::overlays::user_list_dialog>()->show(title, focused, user_ids, enable_overlay, [funcSelect, userdata](s32 status)
{
s32 callback_result = CELL_USERINFO_RET_CANCEL;
u32 selected_user_id = 0;
std::string selected_username;
if (status >= 0)
{
callback_result = CELL_USERINFO_RET_OK;
selected_user_id = static_cast<u32>(status);
selected_username = get_username(selected_user_id);
}
cellUserInfo.warning("cellUserInfoSelectUser_ListType: callback_result=%s, selected_user_id=%d, selected_username='%s'", callback_result, selected_user_id, selected_username);
sysutil_register_cb([=](ppu_thread& ppu) -> s32 sysutil_register_cb([=](ppu_thread& ppu) -> s32
{ {
vm::var<CellUserInfoUserStat> selectUser; vm::var<CellUserInfoUserStat> selectUser;
funcSelect(ppu, CELL_OK, selectUser, userdata); if (status >= 0)
{
selectUser->id = selected_user_id;
strcpy_trunc(selectUser->name, selected_username);
}
funcSelect(ppu, callback_result, selectUser, userdata);
return CELL_OK;
});
g_fxo->get<user_info_manager>().dialog_opened = false;
});
return result;
}
cellUserInfo.error("User selection is only possible when the native user interface is enabled in the settings. The currently active user will be selected as a fallback.");
sysutil_register_cb([=](ppu_thread& ppu) -> s32
{
vm::var<CellUserInfoUserStat> selectUser;
selectUser->id = Emu.GetUsrId();
strcpy_trunc(selectUser->name, get_username(Emu.GetUsrId()));
funcSelect(ppu, CELL_USERINFO_RET_OK, selectUser, userdata);
return CELL_OK; return CELL_OK;
}); });
@ -84,12 +211,99 @@ error_code cellUserInfoSelectUser_ListType(vm::ptr<CellUserInfoTypeSet> listType
error_code cellUserInfoSelectUser_SetList(vm::ptr<CellUserInfoListSet> setList, vm::ptr<CellUserInfoFinishCallback> funcSelect, u32 container, vm::ptr<void> userdata) error_code cellUserInfoSelectUser_SetList(vm::ptr<CellUserInfoListSet> setList, vm::ptr<CellUserInfoFinishCallback> funcSelect, u32 container, vm::ptr<void> userdata)
{ {
cellUserInfo.todo("cellUserInfoSelectUser_SetList(setList=*0x%x, funcSelect=*0x%x, container=0x%x, userdata=*0x%x)", setList, funcSelect, container, userdata); cellUserInfo.warning("cellUserInfoSelectUser_SetList(setList=*0x%x, funcSelect=*0x%x, container=0x%x, userdata=*0x%x)", setList, funcSelect, container, userdata);
if (!setList || !funcSelect) // TODO: confirm
{
return CELL_USERINFO_ERROR_PARAM;
}
if (g_fxo->get<user_info_manager>().dialog_opened)
{
return CELL_USERINFO_ERROR_BUSY;
}
std::vector<u32> user_ids;
for (usz i = 0; i < CELL_USERINFO_USER_MAX && i < setList->fixedListNum; i++)
{
if (const u32 id = setList->fixedList->userId[i])
{
user_ids.push_back(id);
}
}
if (user_ids.empty())
{
// TODO: Confirm. Also check if this is possible in cellUserInfoSelectUser_ListType.
cellUserInfo.error("cellUserInfoSelectUser_SetList: callback_result=%s", CELL_USERINFO_ERROR_NOUSER);
sysutil_register_cb([=](ppu_thread& ppu) -> s32 sysutil_register_cb([=](ppu_thread& ppu) -> s32
{ {
vm::var<CellUserInfoUserStat> selectUser; vm::var<CellUserInfoUserStat> selectUser;
funcSelect(ppu, CELL_OK, selectUser, userdata); funcSelect(ppu, CELL_USERINFO_ERROR_NOUSER, selectUser, userdata);
return CELL_OK;
});
return CELL_OK;
}
// TODO: does this function return an error if any (user_id > 0 && not_found) ?
if (auto manager = g_fxo->try_get<rsx::overlays::display_manager>())
{
if (g_fxo->get<user_info_manager>().dialog_opened.exchange(true))
{
return CELL_USERINFO_ERROR_BUSY;
}
const std::string title = setList->title.get_ptr();
const u32 focused = setList->focus;
cellUserInfo.warning("cellUserInfoSelectUser_SetList: opening user_list_dialog with: title='%s', focused=%d", title, focused);
const bool enable_overlay = g_fxo->get<user_info_manager>().enable_overlay;
const error_code result = manager->create<rsx::overlays::user_list_dialog>()->show(title, focused, user_ids, enable_overlay, [funcSelect, userdata](s32 status)
{
s32 callback_result = CELL_USERINFO_RET_CANCEL;
u32 selected_user_id = 0;
std::string selected_username;
if (status >= 0)
{
callback_result = CELL_USERINFO_RET_OK;
selected_user_id = static_cast<u32>(status);
selected_username = get_username(selected_user_id);
}
cellUserInfo.warning("cellUserInfoSelectUser_SetList: callback_result=%s, selected_user_id=%d, selected_username='%s'", callback_result, selected_user_id, selected_username);
sysutil_register_cb([=](ppu_thread& ppu) -> s32
{
vm::var<CellUserInfoUserStat> selectUser;
if (status >= 0)
{
selectUser->id = selected_user_id;
strcpy_trunc(selectUser->name, selected_username);
}
funcSelect(ppu, callback_result, selectUser, userdata);
return CELL_OK;
});
g_fxo->get<user_info_manager>().dialog_opened = false;
});
return result;
}
cellUserInfo.error("User selection is only possible when the native user interface is enabled in the settings. The currently active user will be selected as a fallback.");
sysutil_register_cb([=](ppu_thread& ppu) -> s32
{
vm::var<CellUserInfoUserStat> selectUser;
selectUser->id = Emu.GetUsrId();
strcpy_trunc(selectUser->name, get_username(Emu.GetUsrId()));
funcSelect(ppu, CELL_USERINFO_RET_OK, selectUser, userdata);
return CELL_OK; return CELL_OK;
}); });
@ -98,12 +312,14 @@ error_code cellUserInfoSelectUser_SetList(vm::ptr<CellUserInfoListSet> setList,
void cellUserInfoEnableOverlay(s32 enable) void cellUserInfoEnableOverlay(s32 enable)
{ {
cellUserInfo.todo("cellUserInfoEnableOverlay(enable=%d)", enable); cellUserInfo.notice("cellUserInfoEnableOverlay(enable=%d)", enable);
auto& manager = g_fxo->get<user_info_manager>();
manager.enable_overlay = enable != 0;
} }
error_code cellUserInfoGetList(vm::ptr<u32> listNum, vm::ptr<CellUserInfoUserList> listBuf, vm::ptr<u32> currentUserId) error_code cellUserInfoGetList(vm::ptr<u32> listNum, vm::ptr<CellUserInfoUserList> listBuf, vm::ptr<u32> currentUserId)
{ {
cellUserInfo.todo("cellUserInfoGetList(listNum=*0x%x, listBuf=*0x%x, currentUserId=*0x%x)", listNum, listBuf, currentUserId); cellUserInfo.warning("cellUserInfoGetList(listNum=*0x%x, listBuf=*0x%x, currentUserId=*0x%x)", listNum, listBuf, currentUserId);
// If only listNum is NULL, an error will be returned // If only listNum is NULL, an error will be returned
if (!listNum) if (!listNum)
@ -114,17 +330,58 @@ error_code cellUserInfoGetList(vm::ptr<u32> listNum, vm::ptr<CellUserInfoUserLis
} }
} }
const std::string home_dir = Emulator::GetHddDir() + "home";
std::vector<u32> user_ids;
for (const auto& user_folder : fs::dir(home_dir))
{
if (!user_folder.is_directory)
{
continue;
}
// Is the folder name exactly 8 all-numerical characters long?
const u32 user_id = Emulator::CheckUsr(user_folder.name);
if (user_id == 0)
{
continue;
}
// Does the localusername file exist?
if (!fs::is_file(home_dir + "/" + user_folder.name + "/localusername"))
{
continue;
}
if (user_ids.size() < CELL_USERINFO_USER_MAX)
{
user_ids.push_back(user_id);
}
else
{
cellUserInfo.warning("cellUserInfoGetList: Cannot add user %s. Too many users.", user_folder.name);
}
}
if (listNum) if (listNum)
{ {
*listNum = 1; *listNum = static_cast<u32>(user_ids.size());
} }
if (listBuf) if (listBuf)
{ {
std::memset(listBuf.get_ptr(), 0, listBuf.size()); for (usz i = 0; i < CELL_USERINFO_USER_MAX; i++)
{
// We report only one user, so it must be the current user if (i < user_ids.size())
listBuf->userId[0] = Emu.GetUsrId(); {
listBuf->userId[i] = user_ids[i];
}
else
{
listBuf->userId[i] = 0;
}
}
} }
if (currentUserId) if (currentUserId)

View File

@ -25,12 +25,23 @@ enum CellUserInfoListType
CELL_USERINFO_LISTTYPE_NOCURRENT = 1, CELL_USERINFO_LISTTYPE_NOCURRENT = 1,
}; };
enum enum cell_user_callback_result : u32
{
CELL_USERINFO_RET_OK = 0,
CELL_USERINFO_RET_CANCEL = 1,
};
enum : u32
{ {
CELL_SYSUTIL_USERID_CURRENT = 0, CELL_SYSUTIL_USERID_CURRENT = 0,
CELL_SYSUTIL_USERID_MAX = 99999999, CELL_SYSUTIL_USERID_MAX = 99999999,
}; };
enum : u32
{
CELL_USERINFO_FOCUS_LISTHEAD = 0xffffffff
};
// Structs // Structs
struct CellUserInfoUserStat struct CellUserInfoUserStat
{ {

View File

@ -133,7 +133,8 @@ namespace rsx
// Ignore cancel operation for Ok-only // Ignore cancel operation for Ok-only
return; return;
} }
else if (cancel_only)
if (cancel_only)
{ {
return_code = CELL_MSGDIALOG_BUTTON_ESCAPE; return_code = CELL_MSGDIALOG_BUTTON_ESCAPE;
} }
@ -220,7 +221,7 @@ namespace rsx
{ {
if (interactive) if (interactive)
{ {
if (auto error = run_input_loop()) if (const auto error = run_input_loop())
{ {
rsx_log.error("Dialog input loop exited with error code=%d", error); rsx_log.error("Dialog input loop exited with error code=%d", error);
return error; return error;
@ -249,7 +250,7 @@ namespace rsx
{ {
auto ref = g_fxo->get<display_manager>().get(uid); auto ref = g_fxo->get<display_manager>().get(uid);
if (auto error = run_input_loop()) if (const auto error = run_input_loop())
{ {
rsx_log.error("Dialog input loop exited with error code=%d", error); rsx_log.error("Dialog input loop exited with error code=%d", error);
} }

View File

@ -96,8 +96,8 @@ namespace rsx
m_time_thingy->set_pos(1000, 30); m_time_thingy->set_pos(1000, 30);
m_time_thingy->set_text(date_time::current_time()); m_time_thingy->set_text(date_time::current_time());
static_cast<label*>(m_description.get())->auto_resize(); m_description->auto_resize();
static_cast<label*>(m_time_thingy.get())->auto_resize(); m_time_thingy->auto_resize();
m_dim_background->back_color.a = 0.5f; m_dim_background->back_color.a = 0.5f;
m_description->back_color.a = 0.f; m_description->back_color.a = 0.f;
@ -109,7 +109,7 @@ namespace rsx
void save_dialog::update() void save_dialog::update()
{ {
m_time_thingy->set_text(date_time::current_time()); m_time_thingy->set_text(date_time::current_time());
static_cast<label*>(m_time_thingy.get())->auto_resize(); m_time_thingy->auto_resize();
} }
void save_dialog::on_button_pressed(pad_button button_press) void save_dialog::on_button_pressed(pad_button button_press)
@ -138,6 +138,7 @@ namespace rsx
break; break;
default: default:
rsx_log.trace("[ui] Button %d pressed", static_cast<u8>(button_press)); rsx_log.trace("[ui] Button %d pressed", static_cast<u8>(button_press));
break;
} }
} }
@ -166,7 +167,7 @@ namespace rsx
if (enable_overlay) if (enable_overlay)
{ {
m_dim_background->back_color.a = 1.0f; m_dim_background->back_color.a = 0.9f;
} }
else else
{ {
@ -261,10 +262,10 @@ namespace rsx
m_list->select_entry(focused); m_list->select_entry(focused);
} }
static_cast<label*>(m_description.get())->auto_resize(); m_description->auto_resize();
visible = true; visible = true;
if (auto err = run_input_loop()) if (const auto err = run_input_loop())
return err; return err;
if (return_code >= 0) if (return_code >= 0)
@ -273,7 +274,7 @@ namespace rsx
{ {
return return_code - 1; return return_code - 1;
} }
else if (static_cast<usz>(return_code) == entries.size()) if (static_cast<usz>(return_code) == entries.size())
{ {
return selection_code::new_save; return selection_code::new_save;
} }

View File

@ -0,0 +1,217 @@
#include "stdafx.h"
#include "overlay_user_list_dialog.h"
#include "Emu/System.h"
#include "Emu/system_config.h"
#include "Utilities/StrUtil.h"
#include "Utilities/Thread.h"
namespace rsx
{
namespace overlays
{
user_list_dialog::user_list_entry::user_list_entry(const std::string& username, const std::string& user_id, const std::string& avatar_path)
{
std::unique_ptr<overlay_element> image = std::make_unique<image_view>();
image->set_size(160, 110);
image->set_padding(36, 36, 11, 11); // Square image, 88x88
if (fs::exists(avatar_path))
{
icon_data = std::make_unique<image_info>(avatar_path.c_str());
static_cast<image_view*>(image.get())->set_raw_image(icon_data.get());
}
else
{
// Fallback
static_cast<image_view*>(image.get())->set_image_resource(resource_config::standard_image_resource::square);
}
std::unique_ptr<overlay_element> text_stack = std::make_unique<vertical_layout>();
std::unique_ptr<overlay_element> padding = std::make_unique<spacer>();
std::unique_ptr<overlay_element> header_text = std::make_unique<label>(username);
std::unique_ptr<overlay_element> subtext = std::make_unique<label>(user_id);
padding->set_size(1, 1);
header_text->set_size(800, 40);
header_text->set_font("Arial", 16);
header_text->set_wrap_text(true);
subtext->set_size(800, 0);
subtext->set_font("Arial", 14);
subtext->set_wrap_text(true);
static_cast<label*>(subtext.get())->auto_resize(true);
// Make back color transparent for text
header_text->back_color.a = 0.f;
subtext->back_color.a = 0.f;
static_cast<vertical_layout*>(text_stack.get())->pack_padding = 5;
static_cast<vertical_layout*>(text_stack.get())->add_element(padding);
static_cast<vertical_layout*>(text_stack.get())->add_element(header_text);
static_cast<vertical_layout*>(text_stack.get())->add_element(subtext);
if (text_stack->h > image->h)
{
std::unique_ptr<overlay_element> padding2 = std::make_unique<spacer>();
padding2->set_size(1, 5);
static_cast<vertical_layout*>(text_stack.get())->add_element(padding2);
}
// Pack
this->pack_padding = 15;
add_element(image);
add_element(text_stack);
}
user_list_dialog::user_list_dialog()
{
m_dim_background = std::make_unique<overlay_element>();
m_dim_background->set_size(1280, 720);
m_dim_background->back_color.a = 0.5f;
m_list = std::make_unique<list_view>(1240, 540);
m_list->set_pos(20, 85);
m_description = std::make_unique<label>();
m_description->set_font("Arial", 20);
m_description->set_pos(20, 37);
m_description->set_text("Select user");
m_description->auto_resize();
m_description->back_color.a = 0.f;
return_code = selection_code::canceled;
}
void user_list_dialog::on_button_pressed(pad_button button_press)
{
switch (button_press)
{
case pad_button::cross:
if (const usz index = static_cast<usz>(m_list->get_selected_index()); index < m_entry_ids.size())
{
return_code = static_cast<s32>(m_entry_ids[index]);
}
else
{
return_code = selection_code::error;
}
[[fallthrough]];
case pad_button::circle:
close(true, true);
break;
case pad_button::dpad_up:
m_list->select_previous();
break;
case pad_button::dpad_down:
m_list->select_next();
break;
case pad_button::L1:
m_list->select_previous(10);
break;
case pad_button::R1:
m_list->select_next(10);
break;
default:
rsx_log.trace("[ui] Button %d pressed", static_cast<u8>(button_press));
break;
}
}
compiled_resource user_list_dialog::get_compiled()
{
if (!visible)
{
return {};
}
compiled_resource result;
result.add(m_dim_background->get_compiled());
result.add(m_list->get_compiled());
result.add(m_description->get_compiled());
return result;
}
struct user_list_dialog_thread
{
static constexpr auto thread_name = "UserList Thread"sv;
};
error_code user_list_dialog::show(const std::string& title, u32 focused, const std::vector<u32>& user_ids, bool enable_overlay, std::function<void(s32 status)> on_close)
{
visible = false;
if (enable_overlay)
{
m_dim_background->back_color.a = 0.9f;
}
else
{
m_dim_background->back_color.a = 0.5f;
}
std::vector<u8> icon;
std::vector<std::unique_ptr<overlay_element>> entries;
const std::string home_dir = Emulator::GetHddDir() + "home/";
s32 selected_index = 0;
for (const auto& id : user_ids)
{
const std::string user_id = fmt::format("%08d", id);
if (const fs::file file{home_dir + user_id + "/localusername"})
{
if (id == focused)
{
selected_index = static_cast<s32>(entries.size());
}
// Let's assume there are 26 avatar pngs (like in my installation)
const std::string avatar_path = g_cfg.vfs.get_dev_flash() + fmt::format("vsh/resource/explore/user/%03d.png", id % 26);
const std::string username = file.to_string();
std::unique_ptr<overlay_element> entry = std::make_unique<user_list_entry>(username, user_id, avatar_path);
entries.emplace_back(std::move(entry));
m_entry_ids.emplace_back(id);
}
}
for (auto& entry : entries)
{
m_list->add_entry(entry);
}
if (m_list->m_items.empty())
{
m_list->set_cancel_only(true);
}
else
{
// Only select an entry if there are entries available
m_list->select_entry(selected_index);
}
m_description->set_text(title);
m_description->auto_resize();
this->on_close = std::move(on_close);
visible = true;
g_fxo->get<named_thread<user_list_dialog_thread>>()([&, tbit = alloc_thread_bit()]()
{
g_thread_bit = tbit;
auto ref = g_fxo->get<display_manager>().get(uid);
if (const auto error = run_input_loop())
{
rsx_log.error("Dialog input loop exited with error code=%d", error);
}
thread_bits &= ~tbit;
thread_bits.notify_all();
});
return CELL_OK;
}
} // namespace overlays
} // namespace RSX

View File

@ -0,0 +1,37 @@
#pragma once
#include "overlays.h"
#include "Emu/Cell/ErrorCodes.h"
namespace rsx
{
namespace overlays
{
struct user_list_dialog : public user_interface
{
private:
struct user_list_entry : horizontal_layout
{
private:
std::unique_ptr<image_info> icon_data;
public:
user_list_entry(const std::string& username, const std::string& user_id, const std::string& avatar_path);
};
std::vector<u32> m_entry_ids;
std::unique_ptr<overlay_element> m_dim_background;
std::unique_ptr<list_view> m_list;
std::unique_ptr<label> m_description;
public:
user_list_dialog();
void on_button_pressed(pad_button button_press) override;
compiled_resource get_compiled() override;
error_code show(const std::string& title, u32 focused, const std::vector<u32>& user_ids, bool enable_overlay, std::function<void(s32 status)> on_close);
};
}
}

View File

@ -72,6 +72,7 @@
<ClCompile Include="Emu\NP\rpcn_config.cpp" /> <ClCompile Include="Emu\NP\rpcn_config.cpp" />
<ClCompile Include="Emu\RSX\Common\texture_cache.cpp" /> <ClCompile Include="Emu\RSX\Common\texture_cache.cpp" />
<ClCompile Include="Emu\RSX\Overlays\overlay_osk_panel.cpp" /> <ClCompile Include="Emu\RSX\Overlays\overlay_osk_panel.cpp" />
<ClCompile Include="Emu\RSX\Overlays\overlay_user_list_dialog.cpp" />
<ClCompile Include="Emu\RSX\Overlays\overlay_utils.cpp" /> <ClCompile Include="Emu\RSX\Overlays\overlay_utils.cpp" />
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.cpp" /> <ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.cpp" />
<ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog_native.cpp" /> <ClCompile Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog_native.cpp" />
@ -465,6 +466,7 @@
<ClInclude Include="Emu\RSX\Overlays\overlay_save_dialog.h" /> <ClInclude Include="Emu\RSX\Overlays\overlay_save_dialog.h" />
<ClInclude Include="Emu\RSX\Overlays\overlay_shader_compile_notification.h" /> <ClInclude Include="Emu\RSX\Overlays\overlay_shader_compile_notification.h" />
<ClInclude Include="Emu\RSX\Overlays\overlay_trophy_notification.h" /> <ClInclude Include="Emu\RSX\Overlays\overlay_trophy_notification.h" />
<ClInclude Include="Emu\RSX\Overlays\overlay_user_list_dialog.h" />
<ClInclude Include="Emu\RSX\Overlays\overlay_utils.h" /> <ClInclude Include="Emu\RSX\Overlays\overlay_utils.h" />
<ClInclude Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.h" /> <ClInclude Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog.h" />
<ClInclude Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog_native.h" /> <ClInclude Include="Emu\RSX\Overlays\Shaders\shader_loading_dialog_native.h" />

View File

@ -722,9 +722,6 @@
<ClCompile Include="Emu\Cell\PPUTranslator.cpp"> <ClCompile Include="Emu\Cell\PPUTranslator.cpp">
<Filter>Emu\Cell</Filter> <Filter>Emu\Cell</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Emu\Cell\timers.hpp">
<Filter>Emu\Cell</Filter>
</ClCompile>
<ClCompile Include="Emu\CPU\CPUTranslator.cpp"> <ClCompile Include="Emu\CPU\CPUTranslator.cpp">
<Filter>Emu\CPU</Filter> <Filter>Emu\CPU</Filter>
</ClCompile> </ClCompile>
@ -992,6 +989,9 @@
<ClCompile Include="Emu\Io\Turntable.cpp"> <ClCompile Include="Emu\Io\Turntable.cpp">
<Filter>Emu\Io</Filter> <Filter>Emu\Io</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Emu\RSX\Overlays\overlay_user_list_dialog.cpp">
<Filter>Emu\GPU\RSX\Overlays</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Crypto\aes.h"> <ClInclude Include="Crypto\aes.h">
@ -1933,6 +1933,12 @@
<ClInclude Include="Loader\mself.hpp"> <ClInclude Include="Loader\mself.hpp">
<Filter>Loader</Filter> <Filter>Loader</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Emu\Cell\timers.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\Overlays\overlay_user_list_dialog.h">
<Filter>Emu\GPU\RSX\Overlays</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl"> <None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl">