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