[App] Add notifications for hotkeys & XNotifyQueueUI

This commit is contained in:
Adrian 2024-08-27 18:15:30 +01:00 committed by Radosław Gliński
parent 9c8a575b76
commit 1a9ff8fe67
8 changed files with 151 additions and 22 deletions

View File

@ -1606,6 +1606,9 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
} }
} }
std::string notificationTitle = "";
std::string notificationDesc = "";
EmulatorWindow::ControllerHotKey button_combination = it->second; EmulatorWindow::ControllerHotKey button_combination = it->second;
switch (button_combination.function) { switch (button_combination.function) {
@ -1630,45 +1633,81 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
GpuClearCaches(); GpuClearCaches();
} }
notificationTitle = "Toggle Clear Memory Page State";
notificationDesc =
cvars::clear_memory_page_state ? "Enabled" : "Disabled";
// Extra Sleep // Extra Sleep
xe::threading::Sleep(delay); xe::threading::Sleep(delay);
break; break;
case ButtonFunctions::ReadbackResolve: case ButtonFunctions::ReadbackResolve:
ToggleGPUSetting(gpu_cvar::ReadbackResolve); ToggleGPUSetting(gpu_cvar::ReadbackResolve);
notificationTitle = "Toggle Readback Resolve";
notificationDesc = cvars::d3d12_readback_resolve ? "Enabled" : "Disabled";
// Extra Sleep // Extra Sleep
xe::threading::Sleep(delay); xe::threading::Sleep(delay);
break; break;
case ButtonFunctions::CpuTimeScalarSetHalf: case ButtonFunctions::CpuTimeScalarSetHalf:
CpuTimeScalarSetHalf(); CpuTimeScalarSetHalf();
notificationTitle = "Time Scalar";
notificationDesc =
fmt::format("Decreased to {}", Clock::guest_time_scalar());
break; break;
case ButtonFunctions::CpuTimeScalarSetDouble: case ButtonFunctions::CpuTimeScalarSetDouble:
CpuTimeScalarSetDouble(); CpuTimeScalarSetDouble();
notificationTitle = "Time Scalar";
notificationDesc =
fmt::format("Increased to {}", Clock::guest_time_scalar());
break; break;
case ButtonFunctions::CpuTimeScalarReset: case ButtonFunctions::CpuTimeScalarReset:
CpuTimeScalarReset(); CpuTimeScalarReset();
notificationTitle = "Time Scalar";
notificationDesc = fmt::format("Reset to {}", Clock::guest_time_scalar());
break; break;
case ButtonFunctions::ClearGPUCache: case ButtonFunctions::ClearGPUCache:
GpuClearCaches(); GpuClearCaches();
// Extra Sleep notificationTitle = "Clear GPU Cache";
xe::threading::Sleep(delay); notificationDesc = "Complete";
break;
case ButtonFunctions::ToggleControllerVibration:
ToggleControllerVibration();
// Extra Sleep // Extra Sleep
xe::threading::Sleep(delay); xe::threading::Sleep(delay);
break; 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: case ButtonFunctions::IncTitleSelect:
selected_title_index++; selected_title_index++;
break; break;
case ButtonFunctions::DecTitleSelect: case ButtonFunctions::DecTitleSelect:
selected_title_index--; selected_title_index--;
break; break;
case ButtonFunctions::ToggleLogging: case ButtonFunctions::ToggleLogging: {
logging::internal::ToggleLogLevel(); logging::internal::ToggleLogLevel();
break;
notificationTitle = "Toggle Logging";
LogLevel level = static_cast<LogLevel>(logging::internal::GetLogLevel());
notificationDesc = level == LogLevel::Disabled ? "Disabled" : "Enabled";
} break;
case ButtonFunctions::Unknown: case ButtonFunctions::Unknown:
default: default:
break; break;
@ -1704,6 +1743,13 @@ EmulatorWindow::ControllerHotKey EmulatorWindow::ProcessControllerHotkey(
title); title);
} }
if (!notificationTitle.empty()) {
app_context_.CallInUIThread([&]() {
new xe::ui::HostNotificationWindow(imgui_drawer(), notificationTitle,
notificationDesc, 0);
});
}
xe::threading::Sleep(delay); xe::threading::Sleep(delay);
return it->second; return it->second;

View File

@ -482,6 +482,7 @@ bool logging::internal::ShouldLog(LogLevel log_level, uint32_t log_mask) {
return static_cast<int32_t>(log_level) <= cvars::log_level && return static_cast<int32_t>(log_level) <= cvars::log_level &&
(log_mask & cvars::log_mask) == 0; (log_mask & cvars::log_mask) == 0;
} }
uint32_t logging::internal::GetLogLevel() { return cvars::log_level; }
std::pair<char*, size_t> logging::internal::GetThreadBuffer() { std::pair<char*, size_t> logging::internal::GetThreadBuffer() {
return {thread_log_buffer_, sizeof(thread_log_buffer_)}; return {thread_log_buffer_, sizeof(thread_log_buffer_)};

View File

@ -86,6 +86,7 @@ void ToggleLogLevel();
bool ShouldLog(LogLevel log_level, bool ShouldLog(LogLevel log_level,
uint32_t log_mask = xe::LogSrc::Uncategorized); uint32_t log_mask = xe::LogSrc::Uncategorized);
uint32_t GetLogLevel();
std::pair<char*, size_t> GetThreadBuffer(); std::pair<char*, size_t> GetThreadBuffer();
XE_NOALIAS XE_NOALIAS
void AppendLogLine(LogLevel log_level, const char prefix_char, size_t written); void AppendLogLine(LogLevel log_level, const char prefix_char, size_t written);

View File

@ -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; return any_connected ? X_ERROR_EMPTY : X_ERROR_DEVICE_NOT_CONNECTED;
} }
bool InputSystem::GetVibrationCvar() { return cvars::vibration; }
void InputSystem::ToggleVibration() { void InputSystem::ToggleVibration() {
OVERRIDE_bool(vibration, !cvars::vibration); OVERRIDE_bool(vibration, !cvars::vibration);
// Send instant update to vibration state to prevent awaiting for next tick. // Send instant update to vibration state to prevent awaiting for next tick.

View File

@ -47,6 +47,8 @@ class InputSystem {
X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags, X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags,
X_INPUT_KEYSTROKE* out_keystroke); X_INPUT_KEYSTROKE* out_keystroke);
bool GetVibrationCvar();
void ToggleVibration(); void ToggleVibration();
const std::bitset<max_allowed_controllers> GetConnectedSlots() const { const std::bitset<max_allowed_controllers> GetConnectedSlots() const {

View File

@ -18,6 +18,7 @@
#include "xenia/kernel/xam/xam_private.h" #include "xenia/kernel/xam/xam_private.h"
#include "xenia/ui/imgui_dialog.h" #include "xenia/ui/imgui_dialog.h"
#include "xenia/ui/imgui_drawer.h" #include "xenia/ui/imgui_drawer.h"
#include "xenia/ui/imgui_guest_notification.h"
#include "xenia/ui/window.h" #include "xenia/ui/window.h"
#include "xenia/ui/windowed_app_context.h" #include "xenia/ui/windowed_app_context.h"
#include "xenia/xbox.h" #include "xenia/xbox.h"
@ -398,8 +399,8 @@ dword_result_t XNotifyQueueUI_entry(dword_t exnq, dword_t dwUserIndex,
qword_t qwAreas, qword_t qwAreas,
lpu16string_t displayText_ptr, lpu16string_t displayText_ptr,
lpvoid_t contextData) { lpvoid_t contextData) {
std::string title_text = "XNotifyQueueUI";
std::string displayText = ""; std::string displayText = "";
const uint8_t position_id = static_cast<uint8_t>(qwAreas);
if (displayText_ptr) { if (displayText_ptr) {
displayText = xe::to_utf8(displayText_ptr.value()); 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); 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<std::string> buttons(1, "OK");
const Emulator* emulator = kernel_state()->emulator(); const Emulator* emulator = kernel_state()->emulator();
ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer(); ui::ImGuiDrawer* imgui_drawer = emulator->imgui_drawer();
xeXamDispatchDialog<MessageBoxDialog>( new xe::ui::XNotifyWindow(imgui_drawer, "", displayText, dwUserIndex,
new MessageBoxDialog(imgui_drawer, title_text, displayText, buttons, 0), position_id);
close, false);
// XNotifyQueueUI -> XNotifyQueueUIEx -> XMsgProcessRequest -> // XNotifyQueueUI -> XNotifyQueueUIEx -> XMsgProcessRequest ->
// XMsgStartIORequestEx & XMsgInProcessCall // XMsgStartIORequestEx & XMsgInProcessCall

View File

@ -180,5 +180,82 @@ void AchievementNotificationWindow::OnDraw(ImGuiIO& io) {
ImGui::End(); 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<ImTextureID>(
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 ui
} // namespace xe } // namespace xe

View File

@ -76,6 +76,17 @@ class AchievementNotificationWindow final : ImGuiGuestNotification {
void OnDraw(ImGuiIO& io) override; 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 ui
} // namespace xe } // namespace xe