From f1218ad9f5ad5badad89021b31807227d7d40ee4 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Wed, 4 Sep 2024 21:18:11 -0300 Subject: [PATCH] system/MsgDialog: copy all ui state to local memory handles when game release memory before close extracted all UI code to it's own file use single window instead of creating new one every single dialogOpen --- src/core/libraries/system/msgdialog.cpp | 30 ++-- src/core/libraries/system/msgdialog.h | 131 +-------------- src/core/libraries/system/msgdialog_ui.cpp | 126 +++++++++------ src/core/libraries/system/msgdialog_ui.h | 177 +++++++++++++++++++++ src/imgui/imgui_std.h | 10 +- 5 files changed, 280 insertions(+), 194 deletions(-) create mode 100644 src/core/libraries/system/msgdialog_ui.h diff --git a/src/core/libraries/system/msgdialog.cpp b/src/core/libraries/system/msgdialog.cpp index c41485b68..f7449cd9f 100644 --- a/src/core/libraries/system/msgdialog.cpp +++ b/src/core/libraries/system/msgdialog.cpp @@ -6,10 +6,10 @@ #include "common/assert.h" #include "common/logging/log.h" -#include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/system/msgdialog.h" #include "imgui_internal.h" +#include "msgdialog_ui.h" namespace Libraries::MsgDialog { @@ -18,8 +18,8 @@ using CommonDialog::Result; using CommonDialog::Status; static auto g_status = Status::NONE; -static Param g_param{}; -static MsgDialogResult g_result{}; +static MsgDialogState g_state{}; +static DialogResult g_result{}; static MsgDialogUi g_msg_dialog_ui; Error PS4_SYSV_ABI sceMsgDialogClose() { @@ -30,7 +30,7 @@ Error PS4_SYSV_ABI sceMsgDialogClose() { return Error::OK; } -Error PS4_SYSV_ABI sceMsgDialogGetResult(MsgDialogResult* result) { +Error PS4_SYSV_ABI sceMsgDialogGetResult(DialogResult* result) { if (g_status != Status::FINISHED) { return Error::NOT_FINISHED; } @@ -66,19 +66,19 @@ Error PS4_SYSV_ABI sceMsgDialogInitialize() { return Error::OK; } -Error PS4_SYSV_ABI sceMsgDialogOpen(const Param* param) { +Error PS4_SYSV_ABI sceMsgDialogOpen(const OrbisParam* param) { if (g_status != Status::INITIALIZED && g_status != Status::FINISHED) { return Error::INVALID_STATE; } if (param == nullptr) { return Error::ARG_NULL; } - ASSERT(param->size == sizeof(Param)); + ASSERT(param->size == sizeof(OrbisParam)); ASSERT(param->baseParam.size == sizeof(CommonDialog::BaseParam)); - g_status = Status::RUNNING; - g_param = *param; g_result = {}; - g_msg_dialog_ui = MsgDialogUi(&g_param, &g_status, &g_result); + g_state = MsgDialogState{*param}; + g_status = Status::RUNNING; + g_msg_dialog_ui = MsgDialogUi(&g_state, &g_status, &g_result); return Error::OK; } @@ -86,13 +86,13 @@ Error PS4_SYSV_ABI sceMsgDialogProgressBarInc(OrbisMsgDialogProgressBarTarget ta if (g_status != Status::RUNNING) { return Error::NOT_RUNNING; } - if (g_param.mode != MsgDialogMode::PROGRESS_BAR) { + if (g_state.GetMode() != MsgDialogMode::PROGRESS_BAR) { return Error::NOT_SUPPORTED; } if (target != OrbisMsgDialogProgressBarTarget::DEFAULT) { return Error::PARAM_INVALID; } - g_msg_dialog_ui.SetProgressBarValue(delta, true); + g_state.GetState().progress += delta; return Error::OK; } @@ -101,13 +101,13 @@ Error PS4_SYSV_ABI sceMsgDialogProgressBarSetMsg(OrbisMsgDialogProgressBarTarget if (g_status != Status::RUNNING) { return Error::NOT_RUNNING; } - if (g_param.mode != MsgDialogMode::PROGRESS_BAR) { + if (g_state.GetMode() != MsgDialogMode::PROGRESS_BAR) { return Error::NOT_SUPPORTED; } if (target != OrbisMsgDialogProgressBarTarget::DEFAULT) { return Error::PARAM_INVALID; } - g_param.progBarParam->msg = msg; + g_state.GetState().msg = msg; return Error::OK; } @@ -116,13 +116,13 @@ Error PS4_SYSV_ABI sceMsgDialogProgressBarSetValue(OrbisMsgDialogProgressBarTarg if (g_status != Status::RUNNING) { return Error::NOT_RUNNING; } - if (g_param.mode != MsgDialogMode::PROGRESS_BAR) { + if (g_state.GetMode() != MsgDialogMode::PROGRESS_BAR) { return Error::NOT_SUPPORTED; } if (target != OrbisMsgDialogProgressBarTarget::DEFAULT) { return Error::PARAM_INVALID; } - g_msg_dialog_ui.SetProgressBarValue(value, false); + g_state.GetState().progress = value; return Error::OK; } diff --git a/src/core/libraries/system/msgdialog.h b/src/core/libraries/system/msgdialog.h index d2a34396c..b8a1f3f07 100644 --- a/src/core/libraries/system/msgdialog.h +++ b/src/core/libraries/system/msgdialog.h @@ -3,10 +3,7 @@ #pragma once -#include "common/fixed_value.h" -#include "common/types.h" #include "core/libraries/system/commondialog.h" -#include "imgui/imgui_layer.h" namespace Core::Loader { class SymbolsResolver; @@ -14,133 +11,15 @@ class SymbolsResolver; namespace Libraries::MsgDialog { -using OrbisUserServiceUserId = s32; - -enum class MsgDialogMode : u32 { - USER_MSG = 1, - PROGRESS_BAR = 2, - SYSTEM_MSG = 3, -}; - -enum class ButtonId : u32 { - INVALID = 0, - OK = 1, - YES = 1, - NO = 2, - BUTTON1 = 1, - BUTTON2 = 2, -}; - -enum class ButtonType : u32 { - OK = 0, - YESNO = 1, - NONE = 2, - OK_CANCEL = 3, - WAIT = 5, - WAIT_CANCEL = 6, - YESNO_FOCUS_NO = 7, - OK_CANCEL_FOCUS_CANCEL = 8, - TWO_BUTTONS = 9, -}; - -enum class ProgressBarType : u32 { - PERCENTAGE = 0, - PERCENTAGE_CANCEL = 1, -}; - -enum class SystemMessageType : u32 { - TRC_EMPTY_STORE = 0, - TRC_PSN_CHAT_RESTRICTION = 1, - TRC_PSN_UGC_RESTRICTION = 2, - CAMERA_NOT_CONNECTED = 4, - WARNING_PROFILE_PICTURE_AND_NAME_NOT_SHARED = 5, -}; - -enum class OrbisMsgDialogProgressBarTarget : u32 { - DEFAULT = 0, -}; - -struct ButtonsParam { - const char* msg1{}; - const char* msg2{}; - std::array reserved{}; -}; - -struct UserMessageParam { - ButtonType buttonType{}; - s32 : 32; - const char* msg{}; - ButtonsParam* buttonsParam{}; - std::array reserved{}; -}; - -struct ProgressBarParam { - ProgressBarType barType{}; - s32 : 32; - const char* msg{}; - std::array reserved{}; -}; - -struct SystemMessageParam { - SystemMessageType sysMsgType{}; - std::array reserved{}; -}; - -struct Param { - CommonDialog::BaseParam baseParam; - std::size_t size; - MsgDialogMode mode; - s32 : 32; - UserMessageParam* userMsgParam; - ProgressBarParam* progBarParam; - SystemMessageParam* sysMsgParam; - OrbisUserServiceUserId userId; - std::array reserved; - s32 : 32; -}; - -struct MsgDialogResult { - FixedValue mode{}; - CommonDialog::Result result{CommonDialog::Result::OK}; - ButtonId buttonId{ButtonId::INVALID}; - std::array reserved{}; -}; - -class MsgDialogUi final : public ImGui::Layer { - s32 dialog_unique_id; - const Param* param{}; - CommonDialog::Status* status{}; - MsgDialogResult* result{}; - u32 progress_bar_value{}; - - void DrawUser(); - void DrawProgressBar(); - void DrawSystemMessage(); - -public: - explicit MsgDialogUi(const Param* param = nullptr, CommonDialog::Status* status = nullptr, - MsgDialogResult* result = nullptr); - ~MsgDialogUi() override; - MsgDialogUi(const MsgDialogUi& other) = delete; - MsgDialogUi(MsgDialogUi&& other) noexcept; - MsgDialogUi& operator=(MsgDialogUi other); - - void Finish(ButtonId buttonId, CommonDialog::Result r = CommonDialog::Result::OK); - - void SetProgressBarValue(u32 value, bool increment); - - void Draw() override; - - bool ShouldGrabGamepad() override { - return true; - } -}; +struct DialogResult; +struct OrbisParam; +enum class OrbisMsgDialogProgressBarTarget : u32; CommonDialog::Error PS4_SYSV_ABI sceMsgDialogClose(); -CommonDialog::Error PS4_SYSV_ABI sceMsgDialogGetResult(MsgDialogResult* result); +CommonDialog::Error PS4_SYSV_ABI sceMsgDialogGetResult(DialogResult* result); CommonDialog::Status PS4_SYSV_ABI sceMsgDialogGetStatus(); CommonDialog::Error PS4_SYSV_ABI sceMsgDialogInitialize(); -CommonDialog::Error PS4_SYSV_ABI sceMsgDialogOpen(const Param* param); +CommonDialog::Error PS4_SYSV_ABI sceMsgDialogOpen(const OrbisParam* param); CommonDialog::Error PS4_SYSV_ABI sceMsgDialogProgressBarInc(OrbisMsgDialogProgressBarTarget, u32 delta); CommonDialog::Error PS4_SYSV_ABI sceMsgDialogProgressBarSetMsg(OrbisMsgDialogProgressBarTarget, diff --git a/src/core/libraries/system/msgdialog_ui.cpp b/src/core/libraries/system/msgdialog_ui.cpp index d9062fac5..d69754fe2 100644 --- a/src/core/libraries/system/msgdialog_ui.cpp +++ b/src/core/libraries/system/msgdialog_ui.cpp @@ -2,10 +2,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include "imgui/imgui_std.h" - #include "common/assert.h" -#include "msgdialog.h" +#include "imgui/imgui_std.h" +#include "msgdialog_ui.h" using namespace ImGui; using namespace Libraries::CommonDialog; @@ -34,23 +33,65 @@ static_assert(std::size(user_button_texts) == static_cast(ButtonType::TWO_B static void DrawCenteredText(const char* text) { const auto ws = GetWindowSize(); - SetCursorPosY(ws.y / 2.0f - 50.0f); + const auto text_size = CalcTextSize(text, nullptr, false, ws.x - 40.0f); PushTextWrapPos(ws.x - 30.0f); - SetCursorPosX((ws.x - CalcTextSize(text, nullptr, false, ws.x - 40.0f).x) / 2.0f); + SetCursorPos({ + (ws.x - text_size.x) / 2.0f, + (ws.y - text_size.y) / 2.0f - 50.0f, + }); Text("%s", text); PopTextWrapPos(); } +MsgDialogState::MsgDialogState(const OrbisParam& param) { + this->mode = param.mode; + switch (mode) { + case MsgDialogMode::USER_MSG: { + ASSERT(param.userMsgParam); + const auto& v = *param.userMsgParam; + auto state = UserState{ + .type = v.buttonType, + .msg = std::string(v.msg), + }; + if (v.buttonType == ButtonType::TWO_BUTTONS) { + ASSERT(v.buttonsParam); + state.btn_param1 = std::string(v.buttonsParam->msg1); + state.btn_param2 = std::string(v.buttonsParam->msg2); + } + this->state = state; + } break; + case MsgDialogMode::PROGRESS_BAR: { + ASSERT(param.progBarParam); + const auto& v = *param.progBarParam; + this->state = ProgressState{ + .type = v.barType, + .msg = std::string(v.msg), + .progress = 0, + }; + } break; + case MsgDialogMode::SYSTEM_MSG: { + ASSERT(param.sysMsgParam); + const auto& v = *param.sysMsgParam; + this->state = SystemState{ + .type = v.sysMsgType, + }; + } break; + default: + UNREACHABLE_MSG("Unknown dialog mode"); + } +} + void MsgDialogUi::DrawUser() { - const auto& [button_type, msg, btn_param, _] = *param->userMsgParam; + const auto& [button_type, msg, btn_param1, btn_param2] = + state->GetState(); const auto ws = GetWindowSize(); - DrawCenteredText(msg); + DrawCenteredText(msg.c_str()); ASSERT(button_type <= ButtonType::TWO_BUTTONS); auto [count, text1, text2] = user_button_texts[static_cast(button_type)]; if (count == 0xFF) { // TWO_BUTTONS -> User defined message count = 2; - text1 = btn_param->msg1; - text2 = btn_param->msg2; + text1 = btn_param1.c_str(); + text2 = btn_param2.c_str(); } const bool focus_first = button_type < ButtonType::YESNO_FOCUS_NO; SetCursorPos({ @@ -74,8 +115,8 @@ void MsgDialogUi::DrawUser() { break; } } - if (!focus_first) { - SetItemDefaultNav(); + if (first_render && !focus_first) { + SetItemCurrentNavFocus(); } PopID(); SameLine(); @@ -84,8 +125,8 @@ void MsgDialogUi::DrawUser() { if (Button(text1, BUTTON_SIZE)) { Finish(ButtonId::BUTTON1); } - if (focus_first) { - SetItemDefaultNav(); + if (first_render && focus_first) { + SetItemCurrentNavFocus(); } PopID(); SameLine(); @@ -94,25 +135,29 @@ void MsgDialogUi::DrawUser() { } void MsgDialogUi::DrawProgressBar() { - const auto& [bar_type, msg, _] = *param->progBarParam; - DrawCenteredText(msg); + const auto& [bar_type, msg, progress_bar_value] = + state->GetState(); + DrawCenteredText(msg.c_str()); const auto ws = GetWindowSize(); SetCursorPos({ ws.x * ((1 - PROGRESS_BAR_WIDTH) / 2.0f), ws.y - 10.0f - BUTTON_SIZE.y, }); - bool has_cancel = bar_type == ProgressBarType::PERCENTAGE_CANCEL; + const bool has_cancel = bar_type == ProgressBarType::PERCENTAGE_CANCEL; float bar_width = PROGRESS_BAR_WIDTH * ws.x; if (has_cancel) { bar_width -= BUTTON_SIZE.x - 10.0f; } BeginGroup(); - ProgressBar((float)progress_bar_value / 100.0f, {bar_width, BUTTON_SIZE.y}); + ProgressBar(static_cast(progress_bar_value) / 100.0f, {bar_width, BUTTON_SIZE.y}); if (has_cancel) { SameLine(); if (Button("Cancel", BUTTON_SIZE)) { Finish(ButtonId::INVALID, Result::USER_CANCELED); } + if (first_render) { + SetItemCurrentNavFocus(); + } } EndGroup(); } @@ -132,7 +177,7 @@ static_assert(std::size(system_message_texts) == void MsgDialogUi::DrawSystemMessage() { // TODO: Implement go to settings & user profile - const auto& [msg_type, _] = *param->sysMsgParam; + const auto& [msg_type] = state->GetState(); ASSERT(msg_type <= SystemMessageType::WARNING_PROFILE_PICTURE_AND_NAME_NOT_SHARED); auto [msg] = system_message_texts[static_cast(msg_type)]; DrawCenteredText(msg); @@ -144,15 +189,15 @@ void MsgDialogUi::DrawSystemMessage() { if (Button("OK", BUTTON_SIZE)) { Finish(ButtonId::OK); } + if (first_render) { + SetItemCurrentNavFocus(); + } } -MsgDialogUi::MsgDialogUi(const Param* param, Status* status, MsgDialogResult* result) - : dialog_unique_id([] { - static s32 last_id = 0; - return ++last_id; - }()), - param(param), status(status), result(result) { +MsgDialogUi::MsgDialogUi(MsgDialogState* state, Status* status, DialogResult* result) + : state(state), status(status), result(result) { if (status && *status == Status::RUNNING) { + first_render = true; AddLayer(this); } } @@ -160,20 +205,18 @@ MsgDialogUi::~MsgDialogUi() { Finish(ButtonId::INVALID); } MsgDialogUi::MsgDialogUi(MsgDialogUi&& other) noexcept - : Layer(other), dialog_unique_id(other.dialog_unique_id), param(other.param), - status(other.status), result(other.result) { - other.dialog_unique_id = 0; - other.param = nullptr; + : Layer(other), state(other.state), status(other.status), result(other.result) { + other.state = nullptr; other.status = nullptr; other.result = nullptr; } MsgDialogUi& MsgDialogUi::operator=(MsgDialogUi other) { using std::swap; - swap(dialog_unique_id, other.dialog_unique_id); - swap(param, other.param); + swap(state, other.state); swap(status, other.status); swap(result, other.result); if (status && *status == Status::RUNNING) { + first_render = true; AddLayer(this); } return *this; @@ -187,20 +230,12 @@ void MsgDialogUi::Finish(ButtonId buttonId, Result r) { if (status) { *status = Status::FINISHED; } - param = nullptr; + state = nullptr; status = nullptr; result = nullptr; RemoveLayer(this); } -void MsgDialogUi::SetProgressBarValue(u32 value, bool increment) { - if (increment) { - progress_bar_value += value; - } else { - progress_bar_value = value; - } -} - void MsgDialogUi::Draw() { if (status == nullptr || *status != Status::RUNNING) { return; @@ -218,14 +253,9 @@ void MsgDialogUi::Draw() { SetNextWindowCollapsed(false); KeepNavHighlight(); // Hack to allow every dialog to have a unique window -#define TITLE "Message Dialog##MD" - char id[sizeof(TITLE) + sizeof(int)] = TITLE "\0\0\0\0"; - *reinterpret_cast(&id[sizeof(TITLE) - 1]) = - dialog_unique_id | - 0x80808080; // keep the MSB set to extend the string length (null terminated) -#undef TITLE - if (Begin(id, nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) { - switch (param->mode) { + if (Begin("Message Dialog##MessageDialog", nullptr, + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) { + switch (state->GetMode()) { case MsgDialogMode::USER_MSG: DrawUser(); break; @@ -238,4 +268,6 @@ void MsgDialogUi::Draw() { } End(); } + + first_render = false; } \ No newline at end of file diff --git a/src/core/libraries/system/msgdialog_ui.h b/src/core/libraries/system/msgdialog_ui.h new file mode 100644 index 000000000..845abdc43 --- /dev/null +++ b/src/core/libraries/system/msgdialog_ui.h @@ -0,0 +1,177 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/fixed_value.h" +#include "common/types.h" +#include "core/libraries/system/commondialog.h" +#include "imgui/imgui_layer.h" + +namespace Libraries::MsgDialog { + +using OrbisUserServiceUserId = s32; + +enum class MsgDialogMode : u32 { + USER_MSG = 1, + PROGRESS_BAR = 2, + SYSTEM_MSG = 3, +}; + +enum class ButtonId : u32 { + INVALID = 0, + OK = 1, + YES = 1, + NO = 2, + BUTTON1 = 1, + BUTTON2 = 2, +}; + +enum class ButtonType : u32 { + OK = 0, + YESNO = 1, + NONE = 2, + OK_CANCEL = 3, + WAIT = 5, + WAIT_CANCEL = 6, + YESNO_FOCUS_NO = 7, + OK_CANCEL_FOCUS_CANCEL = 8, + TWO_BUTTONS = 9, +}; + +enum class ProgressBarType : u32 { + PERCENTAGE = 0, + PERCENTAGE_CANCEL = 1, +}; + +enum class SystemMessageType : u32 { + TRC_EMPTY_STORE = 0, + TRC_PSN_CHAT_RESTRICTION = 1, + TRC_PSN_UGC_RESTRICTION = 2, + CAMERA_NOT_CONNECTED = 4, + WARNING_PROFILE_PICTURE_AND_NAME_NOT_SHARED = 5, +}; + +enum class OrbisMsgDialogProgressBarTarget : u32 { + DEFAULT = 0, +}; + +struct ButtonsParam { + const char* msg1{}; + const char* msg2{}; + std::array reserved{}; +}; + +struct UserMessageParam { + ButtonType buttonType{}; + s32 : 32; + const char* msg{}; + ButtonsParam* buttonsParam{}; + std::array reserved{}; +}; + +struct ProgressBarParam { + ProgressBarType barType{}; + s32 : 32; + const char* msg{}; + std::array reserved{}; +}; + +struct SystemMessageParam { + SystemMessageType sysMsgType{}; + std::array reserved{}; +}; + +struct OrbisParam { + CommonDialog::BaseParam baseParam; + std::size_t size; + MsgDialogMode mode; + s32 : 32; + UserMessageParam* userMsgParam; + ProgressBarParam* progBarParam; + SystemMessageParam* sysMsgParam; + OrbisUserServiceUserId userId; + std::array reserved; + s32 : 32; +}; + +struct DialogResult { + FixedValue mode{}; + CommonDialog::Result result{CommonDialog::Result::OK}; + ButtonId buttonId{ButtonId::INVALID}; + std::array reserved{}; +}; + +// State is used to copy all the data from the param struct +class MsgDialogState { +public: + struct UserState { + ButtonType type{}; + std::string msg{}; + std::string btn_param1{}; + std::string btn_param2{}; + }; + struct ProgressState { + ProgressBarType type{}; + std::string msg{}; + u32 progress{}; + }; + struct SystemState { + SystemMessageType type{}; + }; + +private: + OrbisUserServiceUserId user_id{}; + MsgDialogMode mode{}; + std::variant state{std::monostate{}}; + +public: + explicit MsgDialogState(const OrbisParam& param); + MsgDialogState() = default; + + [[nodiscard]] OrbisUserServiceUserId GetUserId() const { + return user_id; + } + + [[nodiscard]] MsgDialogMode GetMode() const { + return mode; + } + + template + [[nodiscard]] T& GetState() { + return std::get(state); + } +}; + +class MsgDialogUi final : public ImGui::Layer { + bool first_render{false}; + MsgDialogState* state{}; + CommonDialog::Status* status{}; + DialogResult* result{}; + + void DrawUser(); + void DrawProgressBar(); + void DrawSystemMessage(); + +public: + explicit MsgDialogUi(MsgDialogState* state = nullptr, CommonDialog::Status* status = nullptr, + DialogResult* result = nullptr); + ~MsgDialogUi() override; + MsgDialogUi(const MsgDialogUi& other) = delete; + MsgDialogUi(MsgDialogUi&& other) noexcept; + MsgDialogUi& operator=(MsgDialogUi other); + + void Finish(ButtonId buttonId, CommonDialog::Result r = CommonDialog::Result::OK); + + void SetProgressBarValue(u32 value, bool increment); + + void Draw() override; + + bool ShouldGrabGamepad() override { + return true; + } +}; + +}; // namespace Libraries::MsgDialog \ No newline at end of file diff --git a/src/imgui/imgui_std.h b/src/imgui/imgui_std.h index 496de0e0a..6d97cc11b 100644 --- a/src/imgui/imgui_std.h +++ b/src/imgui/imgui_std.h @@ -18,12 +18,10 @@ inline void KeepNavHighlight() { GetCurrentContext()->NavDisableHighlight = false; } -inline void SetItemDefaultNav() { - if (IsWindowAppearing()) { - const auto ctx = GetCurrentContext(); - SetFocusID(ctx->LastItemData.ID, ctx->CurrentWindow); - ctx->NavInitResult.Clear(); - } +inline void SetItemCurrentNavFocus() { + const auto ctx = GetCurrentContext(); + SetFocusID(ctx->LastItemData.ID, ctx->CurrentWindow); + ctx->NavInitResult.Clear(); } } // namespace ImGui