From 1a9ff8fe67600a9962610e89413623fd0072efc7 Mon Sep 17 00:00:00 2001 From: Adrian <78108584+AdrianCassar@users.noreply.github.com> Date: Tue, 27 Aug 2024 18:15:30 +0100 Subject: [PATCH] [App] Add notifications for hotkeys & XNotifyQueueUI --- src/xenia/app/emulator_window.cc | 60 +++++++++++++++--- src/xenia/base/logging.cc | 1 + src/xenia/base/logging.h | 1 + src/xenia/hid/input_system.cc | 2 + src/xenia/hid/input_system.h | 2 + src/xenia/kernel/xam/xam_ui.cc | 19 ++---- src/xenia/ui/imgui_guest_notification.cc | 77 ++++++++++++++++++++++++ src/xenia/ui/imgui_guest_notification.h | 11 ++++ 8 files changed, 151 insertions(+), 22 deletions(-) diff --git a/src/xenia/app/emulator_window.cc b/src/xenia/app/emulator_window.cc index 97585d46f..22c0fc702 100644 --- a/src/xenia/app/emulator_window.cc +++ b/src/xenia/app/emulator_window.cc @@ -1606,6 +1606,9 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey( } } + std::string notificationTitle = ""; + std::string notificationDesc = ""; + EmulatorWindow::ControllerHotKey button_combination = it->second; switch (button_combination.function) { @@ -1630,45 +1633,81 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey( GpuClearCaches(); } + notificationTitle = "Toggle Clear Memory Page State"; + notificationDesc = + cvars::clear_memory_page_state ? "Enabled" : "Disabled"; + // Extra Sleep xe::threading::Sleep(delay); break; case ButtonFunctions::ReadbackResolve: ToggleGPUSetting(gpu_cvar::ReadbackResolve); + notificationTitle = "Toggle Readback Resolve"; + notificationDesc = cvars::d3d12_readback_resolve ? "Enabled" : "Disabled"; + // Extra Sleep xe::threading::Sleep(delay); break; case ButtonFunctions::CpuTimeScalarSetHalf: CpuTimeScalarSetHalf(); + + notificationTitle = "Time Scalar"; + notificationDesc = + fmt::format("Decreased to {}", Clock::guest_time_scalar()); break; case ButtonFunctions::CpuTimeScalarSetDouble: CpuTimeScalarSetDouble(); + + notificationTitle = "Time Scalar"; + notificationDesc = + fmt::format("Increased to {}", Clock::guest_time_scalar()); break; case ButtonFunctions::CpuTimeScalarReset: CpuTimeScalarReset(); + + notificationTitle = "Time Scalar"; + notificationDesc = fmt::format("Reset to {}", Clock::guest_time_scalar()); break; case ButtonFunctions::ClearGPUCache: GpuClearCaches(); - // Extra Sleep - xe::threading::Sleep(delay); - break; - case ButtonFunctions::ToggleControllerVibration: - ToggleControllerVibration(); + notificationTitle = "Clear GPU Cache"; + notificationDesc = "Complete"; // Extra Sleep xe::threading::Sleep(delay); break; + case ButtonFunctions::ToggleControllerVibration: { + ToggleControllerVibration(); + + bool vibration = false; + + auto input_sys = emulator()->input_system(); + if (input_sys) { + vibration = input_sys->GetVibrationCvar(); + } + + notificationTitle = "Toggle Controller Vibration"; + notificationDesc = vibration ? "Enabled" : "Disabled"; + + // Extra Sleep + xe::threading::Sleep(delay); + } break; case ButtonFunctions::IncTitleSelect: selected_title_index++; break; case ButtonFunctions::DecTitleSelect: selected_title_index--; break; - case ButtonFunctions::ToggleLogging: + case ButtonFunctions::ToggleLogging: { logging::internal::ToggleLogLevel(); - break; + + notificationTitle = "Toggle Logging"; + + LogLevel level = static_cast(logging::internal::GetLogLevel()); + notificationDesc = level == LogLevel::Disabled ? "Disabled" : "Enabled"; + } break; case ButtonFunctions::Unknown: default: break; @@ -1704,6 +1743,13 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey( title); } + if (!notificationTitle.empty()) { + app_context_.CallInUIThread([&]() { + new xe::ui::HostNotificationWindow(imgui_drawer(), notificationTitle, + notificationDesc, 0); + }); + } + xe::threading::Sleep(delay); return it->second; diff --git a/src/xenia/base/logging.cc b/src/xenia/base/logging.cc index eec0fb93b..fcc149fd7 100644 --- a/src/xenia/base/logging.cc +++ b/src/xenia/base/logging.cc @@ -482,6 +482,7 @@ bool logging::internal::ShouldLog(LogLevel log_level, uint32_t log_mask) { return static_cast(log_level) <= cvars::log_level && (log_mask & cvars::log_mask) == 0; } +uint32_t logging::internal::GetLogLevel() { return cvars::log_level; } std::pair logging::internal::GetThreadBuffer() { return {thread_log_buffer_, sizeof(thread_log_buffer_)}; diff --git a/src/xenia/base/logging.h b/src/xenia/base/logging.h index 9401a59de..d0030219e 100644 --- a/src/xenia/base/logging.h +++ b/src/xenia/base/logging.h @@ -86,6 +86,7 @@ void ToggleLogLevel(); bool ShouldLog(LogLevel log_level, uint32_t log_mask = xe::LogSrc::Uncategorized); +uint32_t GetLogLevel(); std::pair GetThreadBuffer(); XE_NOALIAS void AppendLogLine(LogLevel log_level, const char prefix_char, size_t written); diff --git a/src/xenia/hid/input_system.cc b/src/xenia/hid/input_system.cc index 91a466886..878efb9a5 100644 --- a/src/xenia/hid/input_system.cc +++ b/src/xenia/hid/input_system.cc @@ -140,6 +140,8 @@ X_RESULT InputSystem::GetKeystroke(uint32_t user_index, uint32_t flags, return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED; } +bool InputSystem::GetVibrationCvar() { return cvars::vibration; } + void InputSystem::ToggleVibration() { OVERRIDE_bool(vibration, !cvars::vibration); // Send instant update to vibration state to prevent awaiting for next tick. diff --git a/src/xenia/hid/input_system.h b/src/xenia/hid/input_system.h index 998cbf540..fc12e1f0b 100644 --- a/src/xenia/hid/input_system.h +++ b/src/xenia/hid/input_system.h @@ -47,6 +47,8 @@ class InputSystem { X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags, X_INPUT_KEYSTROKE* out_keystroke); + bool GetVibrationCvar(); + void ToggleVibration(); const std::bitset GetConnectedSlots() const { diff --git a/src/xenia/kernel/xam/xam_ui.cc b/src/xenia/kernel/xam/xam_ui.cc index 40b372f2e..3311e5635 100644 --- a/src/xenia/kernel/xam/xam_ui.cc +++ b/src/xenia/kernel/xam/xam_ui.cc @@ -18,6 +18,7 @@ #include "xenia/kernel/xam/xam_private.h" #include "xenia/ui/imgui_dialog.h" #include "xenia/ui/imgui_drawer.h" +#include "xenia/ui/imgui_guest_notification.h" #include "xenia/ui/window.h" #include "xenia/ui/windowed_app_context.h" #include "xenia/xbox.h" @@ -398,8 +399,8 @@ dword_result_t XNotifyQueueUI_entry(dword_t exnq, dword_t dwUserIndex, qword_t qwAreas, lpu16string_t displayText_ptr, lpvoid_t contextData) { - std::string title_text = "XNotifyQueueUI"; std::string displayText = ""; + const uint8_t position_id = static_cast(qwAreas); if (displayText_ptr) { displayText = xe::to_utf8(displayText_ptr.value()); @@ -407,23 +408,11 @@ dword_result_t XNotifyQueueUI_entry(dword_t exnq, dword_t dwUserIndex, XELOGI("XNotifyQueueUI: {}", displayText); - if (cvars::headless) { - return X_ERROR_SUCCESS; - } - - auto close = [](MessageBoxDialog* dialog) -> X_RESULT { - dialog->chosen_button(); - return X_ERROR_SUCCESS; - }; - - std::vector buttons(1, "OK"); - const Emulator* emulator = kernel_state()->emulator(); ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer(); - xeXamDispatchDialog( - new MessageBoxDialog(imgui_drawer, title_text, displayText, buttons, 0), - close, false); + new xe::ui::XNotifyWindow(imgui_drawer, "", displayText, dwUserIndex, + position_id); // XNotifyQueueUI -> XNotifyQueueUIEx -> XMsgProcessRequest -> // XMsgStartIORequestEx & XMsgInProcessCall diff --git a/src/xenia/ui/imgui_guest_notification.cc b/src/xenia/ui/imgui_guest_notification.cc index 1e66c8f1d..53180587e 100644 --- a/src/xenia/ui/imgui_guest_notification.cc +++ b/src/xenia/ui/imgui_guest_notification.cc @@ -180,5 +180,82 @@ void AchievementNotificationWindow::OnDraw(ImGuiIO& io) { ImGui::End(); } +void XNotifyWindow::OnDraw(ImGuiIO& io) { + UpdateNotificationState(); + + if (IsNotificationExpired()) { + delete this; + return; + } + + const std::string longest_notification_text_line = + GetTitle().size() > GetDescription().size() ? GetTitle().c_str() + : GetDescription().c_str(); + + const ImVec2 screen_size = io.DisplaySize; + const float window_scale = + std::fminf(screen_size.x / default_drawing_resolution.x, + screen_size.y / default_drawing_resolution.y); + const float font_scale = default_font_size / io.Fonts->Fonts[0]->FontSize; + const ImVec2 text_size = io.Fonts->Fonts[0]->CalcTextSizeA( + default_font_size * default_notification_text_scale * window_scale, + FLT_MAX, -1.0f, longest_notification_text_line.c_str()); + + const ImVec2 final_notification_size = + CalculateNotificationSize(text_size, window_scale); + + const ImVec2 notification_position = CalculateNotificationScreenPosition( + screen_size, final_notification_size, GetPositionId()); + + if (isnan(notification_position.x) || isnan(notification_position.y)) { + return; + } + + ImVec2 current_notification_size = final_notification_size; + current_notification_size.x *= notification_draw_progress_; + current_notification_size.x = std::floorf(current_notification_size.x); + + // Initialize position and window size + ImGui::SetNextWindowSize(current_notification_size); + ImGui::SetNextWindowPos(notification_position); + + // Set new window style before drawing window + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, + default_notification_rounding * window_scale); + ImGui::PushStyleColor(ImGuiCol_WindowBg, + default_notification_background_color); + ImGui::PushStyleColor(ImGuiCol_Separator, + default_notification_background_color); + ImGui::PushStyleColor(ImGuiCol_Border, default_notification_border_color); + + ImGui::Begin("Notification Window", NULL, NOTIFY_TOAST_FLAGS); + { + ImGui::SetWindowFontScale(default_notification_text_scale * font_scale * + window_scale); + // Set offset to image to prevent it from being right on border. + ImGui::SetCursorPos(ImVec2(final_notification_size.x * 0.005f, + final_notification_size.y * 0.05f)); + // Elements of window + ImGui::Image(reinterpret_cast( + GetDrawer()->GetNotificationIcon(GetUserIndex())), + ImVec2(default_notification_icon_size.x * window_scale, + default_notification_icon_size.y * window_scale)); + + // Set offset to image to prevent it from being right on border. + // ImGui::SetCursorPos(ImVec2(final_notification_size.x * 0.1f, + // final_notification_size.y * 0.2f)); + + ImGui::SameLine(); + if (notification_draw_progress_ > 0.5f) { + ImGui::TextColored(white_color, GetDescription().c_str()); + } + } + // Restore previous style + ImGui::PopStyleVar(); + ImGui::PopStyleColor(3); + + ImGui::End(); +} + } // namespace ui } // namespace xe \ No newline at end of file diff --git a/src/xenia/ui/imgui_guest_notification.h b/src/xenia/ui/imgui_guest_notification.h index 79b229cc4..c5428215c 100644 --- a/src/xenia/ui/imgui_guest_notification.h +++ b/src/xenia/ui/imgui_guest_notification.h @@ -76,6 +76,17 @@ class AchievementNotificationWindow final : ImGuiGuestNotification { void OnDraw(ImGuiIO& io) override; }; +class XNotifyWindow final : ImGuiGuestNotification { + public: + XNotifyWindow(ui::ImGuiDrawer* imgui_drawer, std::string title, + std::string description, uint8_t user_index, + uint8_t position_id = 0) + : ImGuiGuestNotification(imgui_drawer, title, description, user_index, + position_id) {}; + + void OnDraw(ImGuiIO& io) override; +}; + } // namespace ui } // namespace xe