From cc99f5260600855e14c9f666997c76ab7f2d13ac Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Wed, 25 Sep 2024 00:48:27 -0300 Subject: [PATCH] Error dialog implementation (#1062) --- src/core/libraries/dialogs/error_dialog.cpp | 191 ++++++++++++++++---- src/core/libraries/dialogs/error_dialog.h | 27 +-- 2 files changed, 168 insertions(+), 50 deletions(-) diff --git a/src/core/libraries/dialogs/error_dialog.cpp b/src/core/libraries/dialogs/error_dialog.cpp index 7df9b1c83..b122e2d0a 100644 --- a/src/core/libraries/dialogs/error_dialog.cpp +++ b/src/core/libraries/dialogs/error_dialog.cpp @@ -1,40 +1,166 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include + +#include "common/assert.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" -#include "error_codes.h" +#include "core/libraries/system/commondialog.h" #include "error_dialog.h" +#include "imgui/imgui_layer.h" +#include "imgui/imgui_std.h" + +static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f}; namespace Libraries::ErrorDialog { -static OrbisErrorDialogStatus g_error_dlg_status = - OrbisErrorDialogStatus::ORBIS_ERROR_DIALOG_STATUS_NONE; +using CommonDialog::Error; +using CommonDialog::Result; +using CommonDialog::Status; -int PS4_SYSV_ABI sceErrorDialogClose() { - g_error_dlg_status = OrbisErrorDialogStatus::ORBIS_ERROR_DIALOG_STATUS_FINISHED; - return ORBIS_OK; -} +class ErrorDialogUi final : public ImGui::Layer { + bool first_render{false}; -OrbisErrorDialogStatus PS4_SYSV_ABI sceErrorDialogGetStatus() { - return g_error_dlg_status; -} + Status* status{nullptr}; + std::string err_message{}; -int PS4_SYSV_ABI sceErrorDialogInitialize(OrbisErrorDialogParam* param) { - if (g_error_dlg_status == OrbisErrorDialogStatus::ORBIS_ERROR_DIALOG_STATUS_INITIALIZED) { - LOG_ERROR(Lib_ErrorDialog, "Error dialog is already at init mode"); - return ORBIS_ERROR_DIALOG_ERROR_ALREADY_INITIALIZED; +public: + explicit ErrorDialogUi(Status* status = nullptr, std::string err_message = "") + : status(status), err_message(std::move(err_message)) { + if (status && *status == Status::RUNNING) { + first_render = true; + AddLayer(this); + } } - g_error_dlg_status = OrbisErrorDialogStatus::ORBIS_ERROR_DIALOG_STATUS_INITIALIZED; - return ORBIS_OK; + ~ErrorDialogUi() override { + Finish(); + } + ErrorDialogUi(const ErrorDialogUi& other) = delete; + ErrorDialogUi(ErrorDialogUi&& other) noexcept + : Layer(other), status(other.status), err_message(std::move(other.err_message)) { + other.status = nullptr; + } + ErrorDialogUi& operator=(ErrorDialogUi other) { + using std::swap; + swap(status, other.status); + swap(err_message, other.err_message); + if (status && *status == Status::RUNNING) { + first_render = true; + AddLayer(this); + } + return *this; + } + + void Finish() { + if (status) { + *status = Status::FINISHED; + } + status = nullptr; + RemoveLayer(this); + } + + void Draw() override { + using namespace ImGui; + 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); + SetNextWindowCollapsed(false); + if (first_render || !io.NavActive) { + SetNextWindowFocus(); + } + KeepNavHighlight(); + if (Begin("Error Dialog##ErrorDialog", nullptr, + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) { + const auto ws = GetWindowSize(); + + DrawPrettyBackground(); + const char* begin = &err_message.front(); + const char* end = &err_message.back() + 1; + SetWindowFontScale(1.3f); + DrawCenteredText(begin, end, + GetContentRegionAvail() - ImVec2{0.0f, 15.0f + BUTTON_SIZE.y}); + SetWindowFontScale(1.0f); + + SetCursorPos({ + ws.x / 2.0f - BUTTON_SIZE.x / 2.0f, + ws.y - 10.0f - BUTTON_SIZE.y, + }); + if (Button("OK", BUTTON_SIZE)) { + Finish(); + } + if (first_render) { + SetItemCurrentNavFocus(); + } + } + End(); + + first_render = false; + } +}; + +static auto g_status = Status::NONE; +static ErrorDialogUi g_dialog_ui; + +struct Param { + s32 size; + s32 errorCode; + OrbisUserServiceUserId userId; + s32 _reserved; +}; + +Error PS4_SYSV_ABI sceErrorDialogClose() { + LOG_DEBUG(Lib_ErrorDialog, "called"); + if (g_status != Status::RUNNING) { + return Error::NOT_RUNNING; + } + g_dialog_ui.Finish(); + return Error::OK; } -int PS4_SYSV_ABI sceErrorDialogOpen(OrbisErrorDialogParam* param) { - LOG_ERROR(Lib_ErrorDialog, "size = {} errorcode = {:#x} userid = {}", param->size, - param->errorCode, param->userId); - g_error_dlg_status = OrbisErrorDialogStatus::ORBIS_ERROR_DIALOG_STATUS_RUNNING; - return ORBIS_OK; +Status PS4_SYSV_ABI sceErrorDialogGetStatus() { + LOG_TRACE(Lib_ErrorDialog, "called status={}", magic_enum::enum_name(g_status)); + return g_status; +} + +Error PS4_SYSV_ABI sceErrorDialogInitialize() { + LOG_DEBUG(Lib_ErrorDialog, "called"); + if (g_status != Status::NONE) { + return Error::ALREADY_INITIALIZED; + } + g_status = Status::INITIALIZED; + return Error::OK; +} + +Error PS4_SYSV_ABI sceErrorDialogOpen(const Param* param) { + if (g_status != Status::INITIALIZED && g_status != Status::FINISHED) { + LOG_INFO(Lib_ErrorDialog, "called without initialize"); + return Error::INVALID_STATE; + } + if (param == nullptr) { + LOG_DEBUG(Lib_ErrorDialog, "called param:(NULL)"); + return Error::ARG_NULL; + } + const auto err = static_cast(param->errorCode); + LOG_DEBUG(Lib_ErrorDialog, "called param->errorCode = {:#x}", err); + ASSERT(param->size == sizeof(Param)); + + const std::string err_message = fmt::format("An error has occurred. \nCode: {:#X}", err); + g_status = Status::RUNNING; + g_dialog_ui = ErrorDialogUi{&g_status, err_message}; + return Error::OK; } int PS4_SYSV_ABI sceErrorDialogOpenDetail() { @@ -47,20 +173,21 @@ int PS4_SYSV_ABI sceErrorDialogOpenWithReport() { return ORBIS_OK; } -int PS4_SYSV_ABI sceErrorDialogTerminate() { - if (g_error_dlg_status == OrbisErrorDialogStatus::ORBIS_ERROR_DIALOG_STATUS_NONE) { - LOG_ERROR(Lib_ErrorDialog, "Error dialog hasn't initialized"); - return ORBIS_ERROR_DIALOG_ERROR_NOT_INITIALIZED; +Error PS4_SYSV_ABI sceErrorDialogTerminate() { + LOG_DEBUG(Lib_ErrorDialog, "called"); + if (g_status == Status::RUNNING) { + sceErrorDialogClose(); } - g_error_dlg_status = OrbisErrorDialogStatus::ORBIS_ERROR_DIALOG_STATUS_NONE; - return ORBIS_OK; + if (g_status == Status::NONE) { + return Error::NOT_INITIALIZED; + } + g_status = Status::NONE; + return Error::OK; } -OrbisErrorDialogStatus PS4_SYSV_ABI sceErrorDialogUpdateStatus() { - // TODO when imgui dialog is done this will loop until ORBIS_ERROR_DIALOG_STATUS_FINISHED - // This should be done calling sceErrorDialogClose but since we don't have a dialog we finish it - // here - return OrbisErrorDialogStatus::ORBIS_ERROR_DIALOG_STATUS_FINISHED; +Status PS4_SYSV_ABI sceErrorDialogUpdateStatus() { + LOG_TRACE(Lib_ErrorDialog, "called status={}", magic_enum::enum_name(g_status)); + return g_status; } void RegisterlibSceErrorDialog(Core::Loader::SymbolsResolver* sym) { diff --git a/src/core/libraries/dialogs/error_dialog.h b/src/core/libraries/dialogs/error_dialog.h index e491b2eee..3e6651d4a 100644 --- a/src/core/libraries/dialogs/error_dialog.h +++ b/src/core/libraries/dialogs/error_dialog.h @@ -4,34 +4,25 @@ #pragma once #include "common/types.h" +#include "core/libraries/system/commondialog.h" namespace Core::Loader { class SymbolsResolver; } namespace Libraries::ErrorDialog { -enum OrbisErrorDialogStatus { - ORBIS_ERROR_DIALOG_STATUS_NONE = 0, - ORBIS_ERROR_DIALOG_STATUS_INITIALIZED = 1, - ORBIS_ERROR_DIALOG_STATUS_RUNNING = 2, - ORBIS_ERROR_DIALOG_STATUS_FINISHED = 3 -}; +using OrbisUserServiceUserId = s32; -struct OrbisErrorDialogParam { - s32 size; - u32 errorCode; - s32 userId; - s32 reserved; -}; +struct Param; -int PS4_SYSV_ABI sceErrorDialogClose(); -OrbisErrorDialogStatus PS4_SYSV_ABI sceErrorDialogGetStatus(); -int PS4_SYSV_ABI sceErrorDialogInitialize(OrbisErrorDialogParam* param); -int PS4_SYSV_ABI sceErrorDialogOpen(OrbisErrorDialogParam* param); +CommonDialog::Error PS4_SYSV_ABI sceErrorDialogClose(); +CommonDialog::Status PS4_SYSV_ABI sceErrorDialogGetStatus(); +CommonDialog::Error PS4_SYSV_ABI sceErrorDialogInitialize(); +CommonDialog::Error PS4_SYSV_ABI sceErrorDialogOpen(const Param* param); int PS4_SYSV_ABI sceErrorDialogOpenDetail(); int PS4_SYSV_ABI sceErrorDialogOpenWithReport(); -int PS4_SYSV_ABI sceErrorDialogTerminate(); -OrbisErrorDialogStatus PS4_SYSV_ABI sceErrorDialogUpdateStatus(); +CommonDialog::Error PS4_SYSV_ABI sceErrorDialogTerminate(); +CommonDialog::Status PS4_SYSV_ABI sceErrorDialogUpdateStatus(); void RegisterlibSceErrorDialog(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::ErrorDialog \ No newline at end of file