Keep sign-in dialogs active until user dismissal through shared system presentation

This commit is contained in:
nmzik
2026-10-03 16:55:47 +04:00
parent c3330caba2
commit 45f55413e0
5 changed files with 312 additions and 146 deletions
+36 -28
View File
@@ -29,9 +29,9 @@ namespace Libs::Graphics {
namespace {
namespace CoreIme = Libs::Ime;
namespace DialogIme = Libs::Dialog::ImeDialog;
namespace ErrorDialog = Libs::Dialog::ErrorDialog;
namespace CoreIme = Libs::Ime;
namespace DialogIme = Libs::Dialog::ImeDialog;
namespace SystemDialog = Libs::Dialog::SystemDialog;
namespace Ime {
@@ -104,7 +104,7 @@ bool HostQueueExternalInput(uint64_t generation, ExternalInput input) {
} // namespace Ime
enum class OverlayKind : uint8_t { None, Ime, Error };
enum class OverlayKind : uint8_t { None, Ime, Dialog };
struct OverlaySession {
OverlayKind kind = OverlayKind::None;
@@ -114,14 +114,14 @@ struct OverlaySession {
};
struct OverlaySnapshot {
OverlaySession session;
Ime::HostSnapshot ime;
ErrorDialog::HostSnapshot error;
OverlaySession session;
Ime::HostSnapshot ime;
SystemDialog::HostSnapshot dialog;
};
bool GetOverlaySnapshot(OverlaySnapshot* snapshot) {
if (ErrorDialog::GetHostSnapshot(&snapshot->error)) {
snapshot->session = {OverlayKind::Error, snapshot->error.generation};
if (SystemDialog::GetHostSnapshot(&snapshot->dialog)) {
snapshot->session = {OverlayKind::Dialog, snapshot->dialog.generation};
return true;
}
if (Ime::GetHostSnapshot(&snapshot->ime)) {
@@ -234,7 +234,7 @@ void RefreshVisibility() {
bool capture_keyboard = false;
bool text_input = false;
bool multiline = false;
if (snapshot.session.kind == OverlayKind::Error) {
if (snapshot.session.kind == OverlayKind::Dialog) {
capture_controller = true;
capture_keyboard = true;
} else if (snapshot.session.kind == OverlayKind::Ime) {
@@ -429,14 +429,14 @@ void InitializeSystemOverlayInput(SDL_Window* window) {
}
CoreIme::SetVisibilityCallback(OnCoreVisibilityChanged);
DialogIme::SetVisibilityCallback(OnDialogVisibilityChanged);
ErrorDialog::SetVisibilityCallback(RefreshVisibility);
SystemDialog::SetVisibilityCallback(RefreshVisibility);
RefreshVisibility();
}
void ShutdownSystemOverlayInput() {
CoreIme::SetVisibilityCallback(nullptr);
DialogIme::SetVisibilityCallback(nullptr);
ErrorDialog::SetVisibilityCallback(nullptr);
SystemDialog::SetVisibilityCallback(nullptr);
{
std::scoped_lock lock(g_visibility_mutex);
g_input_lifecycle_active = false;
@@ -466,9 +466,9 @@ void ShutdownSystemOverlayInput() {
SystemOverlayVisualState GetSystemOverlayVisualState() noexcept {
const auto core = CoreIme::GetVisualState();
const auto dialog = DialogIme::GetVisualState();
const auto error = ErrorDialog::GetVisualState();
return {core.active || dialog.active || error.active,
core.revision + dialog.revision + error.revision};
const auto system = SystemDialog::GetVisualState();
return {core.active || dialog.active || system.active,
core.revision + dialog.revision + system.revision};
}
bool ProcessSystemOverlayInput(const SDL_Event& event) {
@@ -521,10 +521,11 @@ bool ProcessSystemOverlayInput(const SDL_Event& event) {
if (keyboard_event && !g_input_keyboard) {
return false;
}
if (keyboard_event && session.kind == OverlayKind::Error) {
if (keyboard_event && session.kind == OverlayKind::Dialog) {
if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat &&
(event.key.key == SDLK_RETURN || event.key.key == SDLK_KP_ENTER)) {
ErrorDialog::HostAccept(generation);
(event.key.key == SDLK_RETURN || event.key.key == SDLK_KP_ENTER ||
event.key.key == SDLK_ESCAPE)) {
SystemDialog::HostClose(generation);
}
return true;
}
@@ -904,7 +905,7 @@ struct SystemOverlay::Impl {
ImGui::End();
}
void DrawError(const ErrorDialog::HostSnapshot& snapshot, vk::Extent2D extent) {
void DrawDialog(const SystemDialog::HostSnapshot& snapshot, vk::Extent2D extent) {
const ImVec2 display(static_cast<float>(extent.width), static_cast<float>(extent.height));
const float scale = std::max(std::min(display.x / 1280.0f, display.y / 720.0f), 0.5f);
ImGui::GetBackgroundDrawList()->AddRectFilled({0.0f, 0.0f}, display,
@@ -922,17 +923,23 @@ struct SystemOverlay::Impl {
constexpr ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_AlwaysAutoResize;
ImGui::Begin("##SystemError", nullptr, flags);
ImGui::TextUnformatted("Error");
const bool signin = snapshot.kind == SystemDialog::Kind::Signin;
ImGui::Begin("##SystemDialog", nullptr, flags);
ImGui::TextUnformatted(signin ? "Sign in to PlayStation Network" : "Error");
ImGui::Separator();
const auto error_code = static_cast<uint32_t>(snapshot.error_code);
ImGui::TextWrapped("%s", error_code == 0x80550006u
ImGui::TextWrapped("%s", signin ? "PlayStation Network sign-in is unavailable."
: error_code == 0x80550006u
? "You are not signed in to PlayStation Network."
: "An error has occurred.");
ImGui::TextDisabled("Error code: 0x%08X", error_code);
if (!signin) {
ImGui::TextDisabled("Error code: 0x%08X", error_code);
}
const float button_width = std::min(140.0f * scale, ImGui::GetContentRegionAvail().x);
ImGui::SetCursorPosX((ImGui::GetWindowSize().x - button_width) * 0.5f);
const bool accepted = ImGui::Button("OK", {button_width, 44.0f * scale});
const bool accepted =
ImGui::Button(signin ? "Cancel" : "OK", {button_width, 44.0f * scale}) ||
(signin && ImGui::IsKeyPressed(ImGuiKey_GamepadFaceRight, false));
if (focus_pending) {
ImGui::SetItemDefaultFocus();
focus_pending = false;
@@ -941,7 +948,7 @@ struct SystemOverlay::Impl {
ImGui::PopFont();
ImGui::PopStyleVar(2);
if (accepted) {
ErrorDialog::HostAccept(snapshot.generation);
SystemDialog::HostClose(snapshot.generation);
}
}
@@ -955,7 +962,8 @@ struct SystemOverlay::Impl {
if (session != snapshot.session) {
session = snapshot.session;
focus_pending = true;
shift = (snapshot.ime.option & Ime::OPTION_NO_AUTO_CAPITALIZE) == 0;
shift = session.kind == OverlayKind::Ime &&
(snapshot.ime.option & Ime::OPTION_NO_AUTO_CAPITALIZE) == 0;
symbol_mode = false;
panel_offset = {};
right_stick = {};
@@ -981,8 +989,8 @@ struct SystemOverlay::Impl {
ImGui::EndFrame();
return false;
}
if (snapshot.session.kind == OverlayKind::Error) {
DrawError(snapshot.error, frame_extent);
if (snapshot.session.kind == OverlayKind::Dialog) {
DrawDialog(snapshot.dialog, frame_extent);
} else {
DrawIme(snapshot.ime, frame_extent);
}
+122 -88
View File
@@ -14,6 +14,81 @@
// NOLINTNEXTLINE(modernize-concat-nested-namespaces)
namespace Libs::Dialog {
namespace {
constexpr int DIALOG_STATUS_RUNNING = 2;
constexpr int DIALOG_STATUS_FINISHED = 3;
struct DialogState {
int status = 0;
uint64_t generation = 0;
};
std::mutex g_dialog_mutex;
DialogState g_error;
DialogState g_signin;
uint64_t g_dialog_generation = 0;
uint64_t g_dialog_revision = 0;
int32_t g_error_code = 0;
void (*g_dialog_visibility_callback)() = nullptr;
void SetDialogStatus(DialogState& dialog, int status, std::unique_lock<std::mutex>& lock) {
const bool visibility_changed =
(dialog.status == DIALOG_STATUS_RUNNING) != (status == DIALOG_STATUS_RUNNING);
dialog.status = status;
if (visibility_changed) {
g_dialog_revision++;
if (status == DIALOG_STATUS_RUNNING) {
dialog.generation = ++g_dialog_generation;
}
}
const auto callback = visibility_changed ? g_dialog_visibility_callback : nullptr;
lock.unlock();
if (callback != nullptr) {
callback();
}
}
} // namespace
namespace SystemDialog {
bool GetHostSnapshot(HostSnapshot* snapshot) {
std::scoped_lock lock(g_dialog_mutex);
if (g_signin.status == DIALOG_STATUS_RUNNING) {
*snapshot = {Kind::Signin, g_signin.generation, 0};
return true;
}
if (g_error.status == DIALOG_STATUS_RUNNING) {
*snapshot = {Kind::Error, g_error.generation, g_error_code};
return true;
}
return false;
}
VisualState GetVisualState() noexcept {
std::scoped_lock lock(g_dialog_mutex);
const bool signin = g_signin.status == DIALOG_STATUS_RUNNING;
return {g_error.status == DIALOG_STATUS_RUNNING || signin, signin, g_dialog_revision};
}
void SetVisibilityCallback(void (*callback)()) {
std::scoped_lock lock(g_dialog_mutex);
g_dialog_visibility_callback = callback;
}
bool HostClose(uint64_t generation) {
std::unique_lock lock(g_dialog_mutex);
auto& dialog = g_signin.status == DIALOG_STATUS_RUNNING ? g_signin : g_error;
if (dialog.status != DIALOG_STATUS_RUNNING || generation != dialog.generation) {
return false;
}
SetDialogStatus(dialog, DIALOG_STATUS_FINISHED, lock);
return true;
}
} // namespace SystemDialog
namespace CommonDialog {
LIB_NAME("CommonDialog", "CommonDialog");
@@ -207,6 +282,7 @@ constexpr int SIGNIN_ERROR_NOT_INITIALIZED = static_cast<int>(0x81350001u);
constexpr int SIGNIN_ERROR_ALREADY_INITIALIZED = static_cast<int>(0x81350002u);
constexpr int SIGNIN_ERROR_PARAM_INVALID = static_cast<int>(0x81350003u);
constexpr int SIGNIN_ERROR_INVALID_STATE = static_cast<int>(0x81350005u);
constexpr int SIGNIN_ERROR_INVALID_USER_ID = static_cast<int>(0x81350007u);
struct SigninDialogResult {
int32_t result;
@@ -222,70 +298,78 @@ struct SigninDialogParam {
static_assert(sizeof(SigninDialogResult) == 16);
static_assert(sizeof(SigninDialogParam) == 16);
static int g_signin_status = SIGNIN_STATUS_NONE;
int KYTY_SYSV_ABI SigninDialogInitialize() {
PRINT_NAME();
if (g_signin_status != SIGNIN_STATUS_NONE) {
std::scoped_lock lock(g_dialog_mutex);
if (g_signin.status != SIGNIN_STATUS_NONE) {
return SIGNIN_ERROR_ALREADY_INITIALIZED;
}
g_signin_status = SIGNIN_STATUS_INITIALIZED;
g_signin.status = SIGNIN_STATUS_INITIALIZED;
return OK;
}
int KYTY_SYSV_ABI SigninDialogTerminate() {
PRINT_NAME();
if (g_signin_status == SIGNIN_STATUS_NONE) {
std::unique_lock lock(g_dialog_mutex);
if (g_signin.status == SIGNIN_STATUS_NONE) {
return SIGNIN_ERROR_NOT_INITIALIZED;
}
g_signin_status = SIGNIN_STATUS_NONE;
SetDialogStatus(g_signin, SIGNIN_STATUS_NONE, lock);
return OK;
}
int KYTY_SYSV_ABI SigninDialogOpen(const void* param) {
PRINT_NAME();
if (g_signin_status != SIGNIN_STATUS_INITIALIZED && g_signin_status != SIGNIN_STATUS_FINISHED) {
std::unique_lock lock(g_dialog_mutex);
if (g_signin.status == SIGNIN_STATUS_NONE) {
return SIGNIN_ERROR_NOT_INITIALIZED;
}
if (g_signin.status != SIGNIN_STATUS_INITIALIZED && g_signin.status != SIGNIN_STATUS_FINISHED) {
return SIGNIN_ERROR_INVALID_STATE;
}
if (param == nullptr ||
static_cast<const SigninDialogParam*>(param)->size != sizeof(SigninDialogParam)) {
if (param == nullptr) {
return SIGNIN_ERROR_PARAM_INVALID;
}
g_signin_status = SIGNIN_STATUS_RUNNING;
const auto* p = static_cast<const SigninDialogParam*>(param);
if (p->size != sizeof(SigninDialogParam) || p->reserved[0] != 0 || p->reserved[1] != 0) {
return SIGNIN_ERROR_PARAM_INVALID;
}
if (p->user_id != Config::GetUserId()) {
return SIGNIN_ERROR_INVALID_USER_ID;
}
SetDialogStatus(g_signin, SIGNIN_STATUS_RUNNING, lock);
return OK;
}
int KYTY_SYSV_ABI SigninDialogClose() {
PRINT_NAME();
if (g_signin_status == SIGNIN_STATUS_NONE) {
std::unique_lock lock(g_dialog_mutex);
if (g_signin.status == SIGNIN_STATUS_NONE) {
return SIGNIN_ERROR_NOT_INITIALIZED;
}
if (g_signin_status != SIGNIN_STATUS_RUNNING && g_signin_status != SIGNIN_STATUS_FINISHED) {
return SIGNIN_ERROR_INVALID_STATE;
}
g_signin_status = SIGNIN_STATUS_FINISHED;
SetDialogStatus(g_signin, SIGNIN_STATUS_FINISHED, lock);
return OK;
}
int KYTY_SYSV_ABI SigninDialogUpdateStatus() {
PRINT_NAME();
if (g_signin_status == SIGNIN_STATUS_RUNNING) {
g_signin_status = SIGNIN_STATUS_FINISHED;
}
return g_signin_status;
std::scoped_lock lock(g_dialog_mutex);
return g_signin.status;
}
int KYTY_SYSV_ABI SigninDialogGetStatus() {
PRINT_NAME();
return g_signin_status;
std::scoped_lock lock(g_dialog_mutex);
return g_signin.status;
}
int KYTY_SYSV_ABI SigninDialogGetResult(void* result) {
PRINT_NAME();
if (g_signin_status == SIGNIN_STATUS_NONE) {
std::scoped_lock lock(g_dialog_mutex);
if (g_signin.status == SIGNIN_STATUS_NONE) {
return SIGNIN_ERROR_NOT_INITIALIZED;
}
if (g_signin_status != SIGNIN_STATUS_FINISHED) {
if (g_signin.status != SIGNIN_STATUS_FINISHED) {
return SIGNIN_ERROR_INVALID_STATE;
}
if (result == nullptr) {
@@ -661,64 +745,15 @@ struct ErrorDialogParam {
static_assert(sizeof(ErrorDialogParam) == 16);
static std::mutex g_error_mutex;
static int g_error_status = STATUS_NONE;
static uint64_t g_error_generation = 0;
static uint64_t g_error_revision = 0;
static int32_t g_error_code = 0;
static void (*g_error_visibility_callback)() = nullptr;
static void SetStatus(int status, std::unique_lock<std::mutex>& lock) {
const bool visibility_changed =
(g_error_status == STATUS_RUNNING) != (status == STATUS_RUNNING);
g_error_status = status;
if (visibility_changed) {
g_error_revision++;
}
const auto callback = visibility_changed ? g_error_visibility_callback : nullptr;
lock.unlock();
if (callback != nullptr) {
callback();
}
}
bool GetHostSnapshot(HostSnapshot* snapshot) {
std::scoped_lock lock(g_error_mutex);
if (g_error_status != STATUS_RUNNING) {
return false;
}
*snapshot = {g_error_generation, g_error_code};
return true;
}
VisualState GetVisualState() noexcept {
std::scoped_lock lock(g_error_mutex);
return {g_error_status == STATUS_RUNNING, g_error_revision};
}
void SetVisibilityCallback(void (*callback)()) {
std::scoped_lock lock(g_error_mutex);
g_error_visibility_callback = callback;
}
bool HostAccept(uint64_t generation) {
std::unique_lock lock(g_error_mutex);
if (g_error_status != STATUS_RUNNING || generation != g_error_generation) {
return false;
}
SetStatus(STATUS_FINISHED, lock);
return true;
}
int KYTY_SYSV_ABI ErrorDialogInitialize() {
PRINT_NAME();
std::scoped_lock lock(g_error_mutex);
if (g_error_status != STATUS_NONE) {
std::scoped_lock lock(g_dialog_mutex);
if (g_error.status != STATUS_NONE) {
return ERROR_ALREADY_INITIALIZED;
}
g_error_status = STATUS_INITIALIZED;
g_error.status = STATUS_INITIALIZED;
return OK;
}
@@ -726,8 +761,8 @@ int KYTY_SYSV_ABI ErrorDialogInitialize() {
int KYTY_SYSV_ABI ErrorDialogOpen(const void* param) {
PRINT_NAME();
std::unique_lock lock(g_error_mutex);
if (g_error_status != STATUS_INITIALIZED && g_error_status != STATUS_FINISHED) {
std::unique_lock lock(g_dialog_mutex);
if (g_error.status != STATUS_INITIALIZED && g_error.status != STATUS_FINISHED) {
return ERROR_INVALID_STATE;
}
if (param == nullptr) {
@@ -745,8 +780,7 @@ int KYTY_SYSV_ABI ErrorDialogOpen(const void* param) {
p->size, static_cast<uint32_t>(p->error_code), p->user_id);
g_error_code = p->error_code;
g_error_generation++;
SetStatus(STATUS_RUNNING, lock);
SetDialogStatus(g_error, STATUS_RUNNING, lock);
return OK;
}
@@ -754,12 +788,12 @@ int KYTY_SYSV_ABI ErrorDialogOpen(const void* param) {
int KYTY_SYSV_ABI ErrorDialogClose() {
PRINT_NAME();
std::unique_lock lock(g_error_mutex);
if (g_error_status != STATUS_RUNNING) {
std::unique_lock lock(g_dialog_mutex);
if (g_error.status != STATUS_RUNNING) {
return ERROR_INVALID_STATE;
}
SetStatus(STATUS_FINISHED, lock);
SetDialogStatus(g_error, STATUS_FINISHED, lock);
return OK;
}
@@ -767,12 +801,12 @@ int KYTY_SYSV_ABI ErrorDialogClose() {
int KYTY_SYSV_ABI ErrorDialogTerminate() {
PRINT_NAME();
std::unique_lock lock(g_error_mutex);
if (g_error_status == STATUS_NONE) {
std::unique_lock lock(g_dialog_mutex);
if (g_error.status == STATUS_NONE) {
return ERROR_NOT_INITIALIZED;
}
SetStatus(STATUS_NONE, lock);
SetDialogStatus(g_error, STATUS_NONE, lock);
return OK;
}
@@ -780,15 +814,15 @@ int KYTY_SYSV_ABI ErrorDialogTerminate() {
int KYTY_SYSV_ABI ErrorDialogUpdateStatus() {
PRINT_NAME();
std::scoped_lock lock(g_error_mutex);
return g_error_status;
std::scoped_lock lock(g_dialog_mutex);
return g_error.status;
}
int KYTY_SYSV_ABI ErrorDialogGetStatus() {
PRINT_NAME();
std::scoped_lock lock(g_error_mutex);
return g_error_status;
std::scoped_lock lock(g_dialog_mutex);
return g_error.status;
}
} // namespace ErrorDialog
+10 -2
View File
@@ -68,22 +68,30 @@ int KYTY_SYSV_ABI MsgDialogProgressBarSetMsg(int target, const char* msg);
} // namespace MsgDialog
namespace ErrorDialog {
namespace SystemDialog {
enum class Kind { Error, Signin };
struct HostSnapshot {
Kind kind;
uint64_t generation;
int32_t error_code;
};
struct VisualState {
bool active;
bool background;
uint64_t revision;
};
bool GetHostSnapshot(HostSnapshot* snapshot);
VisualState GetVisualState() noexcept;
void SetVisibilityCallback(void (*callback)());
bool HostAccept(uint64_t generation);
bool HostClose(uint64_t generation);
} // namespace SystemDialog
namespace ErrorDialog {
int KYTY_SYSV_ABI ErrorDialogInitialize();
int KYTY_SYSV_ABI ErrorDialogOpen(const void* param);
+4 -2
View File
@@ -163,8 +163,10 @@ static int KYTY_SYSV_ABI SystemServiceGetStatus(SystemServiceStatus* status) {
return SYSTEM_SERVICE_ERROR_PARAMETER;
}
*status = SystemServiceStatus();
status->is_system_ui_overlaid = Dialog::ErrorDialog::GetVisualState().active;
const auto dialog = Dialog::SystemDialog::GetVisualState();
*status = SystemServiceStatus();
status->is_system_ui_overlaid = dialog.active;
status->is_in_background_execution = dialog.background;
return OK;
}