From ad57ab8fee35fc15837ec082277a3fd5b7fcd2ae Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Wed, 4 Sep 2024 12:49:00 -0300 Subject: [PATCH] system/MsgDialog: RAII for MsgDialog ui --- CMakeLists.txt | 1 + src/core/libraries/system/msgdialog.cpp | 239 +-------------------- src/core/libraries/system/msgdialog.h | 117 +++++++++- src/core/libraries/system/msgdialog_ui.cpp | 173 +++++++++++++++ 4 files changed, 297 insertions(+), 233 deletions(-) create mode 100644 src/core/libraries/system/msgdialog_ui.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e647a262..d1413b156 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -204,6 +204,7 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/system/commondialog.h src/core/libraries/system/msgdialog.cpp src/core/libraries/system/msgdialog.h + src/core/libraries/system/msgdialog_ui.cpp src/core/libraries/system/posix.cpp src/core/libraries/system/posix.h src/core/libraries/save_data/error_codes.h diff --git a/src/core/libraries/system/msgdialog.cpp b/src/core/libraries/system/msgdialog.cpp index da2e2183d..09b8f02ab 100644 --- a/src/core/libraries/system/msgdialog.cpp +++ b/src/core/libraries/system/msgdialog.cpp @@ -9,8 +9,6 @@ #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "core/libraries/system/msgdialog.h" -#include "imgui/imgui_layer.h" -#include "imgui/imgui_std.h" #include "imgui_internal.h" namespace Libraries::MsgDialog { @@ -19,240 +17,16 @@ using CommonDialog::Error; using CommonDialog::Result; using CommonDialog::Status; -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, -}; - -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{}; - Result result{Result::OK}; - ButtonId buttonId{ButtonId::INVALID}; - std::array reserved{}; -}; - static auto g_status = Status::NONE; +static Param g_param{}; static MsgDialogResult g_result{}; - -struct { - int count = 0; - const char* text1; - const char* text2; -} static constexpr button_texts[] = { - {1, "OK"}, // 0 OK - {2, "Yes", "No"}, // 1 YESNO - {0}, // 2 NONE - {2, "OK", "Cancel"}, // 3 OK_CANCEL - {}, // 4 !!NOP - {1, "Wait"}, // 5 WAIT - {2, "Wait", "Cancel"}, // 6 WAIT_CANCEL - {2, "Yes", "No"}, // 7 YESNO_FOCUS_NO - {2, "OK", "Cancel"}, // 8 OK_CANCEL_FOCUS_CANCEL - {0xFF}, // 9 TWO_BUTTONS -}; -static_assert(std::size(button_texts) == static_cast(ButtonType::TWO_BUTTONS) + 1); - -static void Finish(ButtonId buttonId); - -namespace { -using namespace ImGui; -class MsgDialogGui final : public Layer { - const Param* param{}; - s32 dialog_unique_id{}; - - void DrawUser(const UserMessageParam& user_message_param) { - constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f}; - - const auto ws = GetWindowSize(); - SetCursorPosY(ws.y / 2.0f - 50.0f); - PushTextWrapPos(ws.x - 30.0f); - SetCursorPosX( - (ws.x - CalcTextSize(user_message_param.msg, nullptr, false, ws.x - 40.0f).x) / 2.0f); - Text("%s", user_message_param.msg); - PopTextWrapPos(); - const auto button_type = user_message_param.buttonType; - ASSERT(user_message_param.buttonType <= ButtonType::TWO_BUTTONS); - auto [count, text1, text2] = button_texts[static_cast(button_type)]; - if (count == 0xFF) { // TWO_BUTTONS -> User defined message - count = 2; - text1 = user_message_param.buttonsParam->msg1; - text2 = user_message_param.buttonsParam->msg2; - } - const bool focus_first = button_type < ButtonType::YESNO_FOCUS_NO; - SetCursorPos({ - ws.x / 2.0f - BUTTON_SIZE.x / 2.0f * static_cast(count), - ws.y - 10.0f - BUTTON_SIZE.y, - }); - BeginGroup(); - if (count > 0) { - // First button at the right, so we render the second button first - if (count == 2) { - PushID(2); - if (Button(text2, BUTTON_SIZE)) { - Finish(ButtonId::BUTTON2); - } - if (!focus_first) { - SetItemDefaultNav(); - } - PopID(); - SameLine(); - } - PushID(1); - if (Button(text1, BUTTON_SIZE)) { - Finish(ButtonId::BUTTON1); - } - if (focus_first) { - SetItemDefaultNav(); - } - PopID(); - SameLine(); - } - EndGroup(); - } - - void DrawProgressBar(const ProgressBarParam& progress_bar_param) {} - - void DrawSystemMessage(const SystemMessageParam& system_message_param) {} - -public: - void Draw() override { - if (g_status != Status::RUNNING) { - return; - } - const auto& io = GetIO(); - - const ImVec2 window_size{ - std::min(io.DisplaySize.x, 500.0f), - std::min(io.DisplaySize.y, 300.0f), - }; - - CentralizeWindow(); - SetNextWindowSize(window_size); - SetNextWindowFocus(); - 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) { - case MsgDialogMode::USER_MSG: - DrawUser(*param->userMsgParam); - break; - case MsgDialogMode::PROGRESS_BAR: - DrawProgressBar(*param->progBarParam); - break; - case MsgDialogMode::SYSTEM_MSG: - DrawSystemMessage(*param->sysMsgParam); - break; - } - End(); - } - } - - bool ShouldGrabGamepad() override { - return true; - } - - explicit MsgDialogGui(const Param* param = nullptr) - : param(param), dialog_unique_id([] { - static s32 last_id = 0; - return last_id++; - }()) {} -} g_msg_dialog_gui; -} // namespace - -static void Finish(ButtonId buttonId) { - g_result.buttonId = buttonId; - g_status = Status::FINISHED; - Layer::RemoveLayer(&g_msg_dialog_gui); -} +static MsgDialogUi g_msg_dialog_ui; Error PS4_SYSV_ABI sceMsgDialogClose() { if (g_status != Status::RUNNING) { return Error::NOT_RUNNING; } - Finish(ButtonId::INVALID); + g_msg_dialog_ui.Finish(ButtonId::INVALID); return Error::OK; } @@ -296,12 +70,15 @@ Error PS4_SYSV_ABI sceMsgDialogOpen(const Param* 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->baseParam.size == sizeof(CommonDialog::BaseParam)); g_status = Status::RUNNING; + g_param = *param; g_result = {}; - g_msg_dialog_gui = MsgDialogGui(param); - Layer::AddLayer(&g_msg_dialog_gui); + g_msg_dialog_ui = MsgDialogUi(&g_param, &g_status, &g_result); return Error::OK; } diff --git a/src/core/libraries/system/msgdialog.h b/src/core/libraries/system/msgdialog.h index fbf525f75..368445132 100644 --- a/src/core/libraries/system/msgdialog.h +++ b/src/core/libraries/system/msgdialog.h @@ -6,6 +6,7 @@ #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; @@ -13,8 +14,120 @@ class SymbolsResolver; namespace Libraries::MsgDialog { -struct MsgDialogResult; -struct Param; +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, +}; + +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{}; + + 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); + + void Draw() override; + + bool ShouldGrabGamepad() override { + return true; + } +}; CommonDialog::Error PS4_SYSV_ABI sceMsgDialogClose(); CommonDialog::Error PS4_SYSV_ABI sceMsgDialogGetResult(MsgDialogResult* result); diff --git a/src/core/libraries/system/msgdialog_ui.cpp b/src/core/libraries/system/msgdialog_ui.cpp new file mode 100644 index 000000000..693955e9a --- /dev/null +++ b/src/core/libraries/system/msgdialog_ui.cpp @@ -0,0 +1,173 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "imgui/imgui_std.h" + +#include "common/assert.h" +#include "msgdialog.h" + +using namespace ImGui; +using namespace Libraries::CommonDialog; +using namespace Libraries::MsgDialog; + +static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f}; + +struct { + int count = 0; + const char* text1; + const char* text2; +} static constexpr user_button_texts[] = { + {1, "OK"}, // 0 OK + {2, "Yes", "No"}, // 1 YESNO + {0}, // 2 NONE + {2, "OK", "Cancel"}, // 3 OK_CANCEL + {}, // 4 !!NOP + {1, "Wait"}, // 5 WAIT + {2, "Wait", "Cancel"}, // 6 WAIT_CANCEL + {2, "Yes", "No"}, // 7 YESNO_FOCUS_NO + {2, "OK", "Cancel"}, // 8 OK_CANCEL_FOCUS_CANCEL + {0xFF}, // 9 TWO_BUTTONS +}; +static_assert(std::size(user_button_texts) == static_cast(ButtonType::TWO_BUTTONS) + 1); + +static void DrawCenteredText(const char* text) { + const auto ws = GetWindowSize(); + SetCursorPosY(ws.y / 2.0f - 50.0f); + PushTextWrapPos(ws.x - 30.0f); + SetCursorPosX((ws.x - CalcTextSize(text, nullptr, false, ws.x - 40.0f).x) / 2.0f); + Text("%s", text); + PopTextWrapPos(); +} + +void MsgDialogUi::DrawUser() { + const auto& [button_type, msg, btn_param, _] = *param->userMsgParam; + const auto ws = GetWindowSize(); + DrawCenteredText(msg); + 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; + } + const bool focus_first = button_type < ButtonType::YESNO_FOCUS_NO; + SetCursorPos({ + ws.x / 2.0f - BUTTON_SIZE.x / 2.0f * static_cast(count), + ws.y - 10.0f - BUTTON_SIZE.y, + }); + BeginGroup(); + if (count > 0) { + // First button at the right, so we render the second button first + if (count == 2) { + PushID(2); + if (Button(text2, BUTTON_SIZE)) { + Finish(ButtonId::BUTTON2); + } + if (!focus_first) { + SetItemDefaultNav(); + } + PopID(); + SameLine(); + } + PushID(1); + if (Button(text1, BUTTON_SIZE)) { + Finish(ButtonId::BUTTON1); + } + if (focus_first) { + SetItemDefaultNav(); + } + PopID(); + SameLine(); + } + EndGroup(); +} + +void MsgDialogUi::DrawProgressBar() {} + +void MsgDialogUi::DrawSystemMessage() {} + +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) { + if (status && *status == Status::RUNNING) { + AddLayer(this); + } +} +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; + 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(status, other.status); + swap(result, other.result); + if (status && *status == Status::RUNNING) { + AddLayer(this); + } + return *this; +} + +void MsgDialogUi::Finish(ButtonId buttonId) { + if (result) { + result->buttonId = buttonId; + } + if (status) { + *status = Status::FINISHED; + } + param = nullptr; + status = nullptr; + result = nullptr; + RemoveLayer(this); +} + +void MsgDialogUi::Draw() { + if (status == nullptr || *status != Status::RUNNING) { + return; + } + const auto& io = GetIO(); + + const ImVec2 window_size{ + std::min(io.DisplaySize.x, 500.0f), + std::min(io.DisplaySize.y, 300.0f), + }; + + CentralizeWindow(); + SetNextWindowSize(window_size); + SetNextWindowFocus(); + 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) { + case MsgDialogMode::USER_MSG: + DrawUser(); + break; + case MsgDialogMode::PROGRESS_BAR: + DrawProgressBar(); + break; + case MsgDialogMode::SYSTEM_MSG: + DrawSystemMessage(); + break; + } + End(); + } +} \ No newline at end of file