Error dialog implementation

This commit is contained in:
Vinicius Rangel 2024-09-24 19:30:25 -03:00
parent f79da986e3
commit 6bb67b71e2
No known key found for this signature in database
GPG key ID: A5B154D904B761D9
2 changed files with 168 additions and 50 deletions

View file

@ -1,40 +1,166 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <utility>
#include <imgui.h>
#include <magic_enum.hpp>
#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<u32>(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) {

View file

@ -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