From f76d05a3d57d307584265cac3c5d726bcc3c4b3b Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Tue, 7 May 2024 17:50:36 +0200 Subject: [PATCH] achievements: leaderboard notifications. detailed toast message Draw achievement notifications using imgui drawlist api Fixes for insets Issue #761 --- core/achievements/achievements.cpp | 64 +++++++++- core/emulator.cpp | 2 +- core/hw/naomi/printer.cpp | 5 +- core/rend/gui.cpp | 8 +- core/rend/gui.h | 2 +- core/rend/gui_achievements.cpp | 193 ++++++++++++++++++++--------- core/rend/gui_achievements.h | 7 +- core/rend/gui_util.cpp | 26 +++- core/rend/gui_util.h | 1 + shell/libretro/libretro.cpp | 4 +- 10 files changed, 231 insertions(+), 81 deletions(-) diff --git a/core/achievements/achievements.cpp b/core/achievements/achievements.cpp index 431839356..e5db54f7c 100644 --- a/core/achievements/achievements.cpp +++ b/core/achievements/achievements.cpp @@ -89,6 +89,12 @@ private: void handleShowAchievementProgress(const rc_client_event_t *event); void handleHideAchievementProgress(const rc_client_event_t *event); void handleUpdateAchievementProgress(const rc_client_event_t *event); + void handleLeaderboardStarted(const rc_client_event_t *event); + void handleLeaderboardFailed(const rc_client_event_t *event); + void handleLeaderboardSubmitted(const rc_client_event_t *event); + void handleShowLeaderboardTracker(const rc_client_event_t *event); + void handleHideLeaderboardTracker(const rc_client_event_t *event); + void handleUpdateLeaderboardTracker(const rc_client_event_t *event); static void emuEventCallback(Event event, void *arg); rc_client_t *rc_client = nullptr; @@ -506,16 +512,26 @@ void Achievements::clientEventHandler(const rc_client_event_t* event, rc_client_ achievements->handleUpdateAchievementProgress(event); break; -/* - TODO case RC_CLIENT_EVENT_LEADERBOARD_STARTED: + achievements->handleLeaderboardStarted(event); + break; case RC_CLIENT_EVENT_LEADERBOARD_FAILED: + achievements->handleLeaderboardFailed(event); + break; case RC_CLIENT_EVENT_LEADERBOARD_SUBMITTED: - case RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD: + achievements->handleLeaderboardSubmitted(event); + break; case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_SHOW: + achievements->handleShowLeaderboardTracker(event); + break; case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_HIDE: + achievements->handleHideLeaderboardTracker(event); + break; case RC_CLIENT_EVENT_LEADERBOARD_TRACKER_UPDATE: -*/ + achievements->handleUpdateLeaderboardTracker(event); + break; +// TODO case RC_CLIENT_EVENT_LEADERBOARD_SCOREBOARD: + case RC_CLIENT_EVENT_DISCONNECTED: notifyError("RetroAchievements disconnected"); break; @@ -578,6 +594,46 @@ void Achievements::handleAchievementChallengeIndicatorHideEvent(const rc_client_ } } +void Achievements::handleLeaderboardStarted(const rc_client_event_t *event) +{ + const rc_client_leaderboard_t *leaderboard = event->leaderboard; + INFO_LOG(COMMON, "RA: Leaderboard started: %s", leaderboard->title); + std::string text = "Leaderboard " + std::string(leaderboard->title) + " started"; + notifier.notify(Notification::Unlocked, "", text, leaderboard->description); +} +void Achievements::handleLeaderboardFailed(const rc_client_event_t *event) +{ + const rc_client_leaderboard_t *leaderboard = event->leaderboard; + INFO_LOG(COMMON, "RA: Leaderboard failed: %s", leaderboard->title); + std::string text = "Leaderboard " + std::string(leaderboard->title) + " failed"; + notifier.notify(Notification::Unlocked, "", text, leaderboard->description); +} +void Achievements::handleLeaderboardSubmitted(const rc_client_event_t *event) +{ + const rc_client_leaderboard_t *leaderboard = event->leaderboard; + INFO_LOG(COMMON, "RA: Leaderboard submitted: %s", leaderboard->title); + std::string text = "Leaderboard " + std::string(leaderboard->title) + " submitted"; + notifier.notify(Notification::Unlocked, "", text, leaderboard->description); +} +void Achievements::handleShowLeaderboardTracker(const rc_client_event_t *event) +{ + const rc_client_leaderboard_tracker_t *leaderboard = event->leaderboard_tracker; + DEBUG_LOG(COMMON, "RA: Show leaderboard[%d]: %s", leaderboard->id, leaderboard->display); + notifier.showLeaderboard(leaderboard->id, leaderboard->display); +} +void Achievements::handleHideLeaderboardTracker(const rc_client_event_t *event) +{ + const rc_client_leaderboard_tracker_t *leaderboard = event->leaderboard_tracker; + DEBUG_LOG(COMMON, "RA: Hide leaderboard[%d]: %s", leaderboard->id, leaderboard->display); + notifier.hideLeaderboard(leaderboard->id); +} +void Achievements::handleUpdateLeaderboardTracker(const rc_client_event_t *event) +{ + const rc_client_leaderboard_tracker_t *leaderboard = event->leaderboard_tracker; + DEBUG_LOG(COMMON, "RA: Update leaderboard[%d]: %s", leaderboard->id, leaderboard->display); + notifier.showLeaderboard(leaderboard->id, leaderboard->display); +} + void Achievements::handleGameCompleted(const rc_client_event_t *event) { const rc_client_game_t* game = rc_client_get_game_info(rc_client); diff --git a/core/emulator.cpp b/core/emulator.cpp index 7fa51ff9d..acb7c9b10 100644 --- a/core/emulator.cpp +++ b/core/emulator.cpp @@ -541,7 +541,7 @@ void Emulator::loadGame(const char *path, LoadProgress *progress) cheatManager.reset(settings.content.gameId); if (cheatManager.isWidescreen()) { - gui_display_notification("Widescreen cheat activated", 1000); + gui_display_notification("Widescreen cheat activated", 2000); config::ScreenStretching.override(134); // 4:3 -> 16:9 } // reload settings so that all settings can be overridden diff --git a/core/hw/naomi/printer.cpp b/core/hw/naomi/printer.cpp index df7551307..29eb0db81 100644 --- a/core/hw/naomi/printer.cpp +++ b/core/hw/naomi/printer.cpp @@ -830,8 +830,7 @@ private: std::string s = get_writable_data_path(settings.content.gameId + "-results.png"); bitmapWriter->save(s); bitmapWriter.reset(); - s = "Print out saved to " + s; - gui_display_notification(s.c_str(), 5000); + gui_display_notification("Print out saved", 5000, s.c_str()); NOTICE_LOG(NAOMI, "%s", s.c_str()); } break; @@ -1198,7 +1197,7 @@ std::string get_writable_data_path(const std::string& s) return "./" + s; } -void gui_display_notification(char const*, int) { +void gui_display_notification(char const*, int, char const*) { } int main(int argc, char *argv[]) diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index a6a0cef79..424c7ff2e 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -2971,7 +2971,7 @@ static void gui_display_settings() ImGui::End(); } -void gui_display_notification(const char *msg, int duration) +void gui_display_notification(const char *msg, int duration, const char *details) { if (gui_state != GuiState::Closed) { @@ -2980,7 +2980,7 @@ void gui_display_notification(const char *msg, int duration) osd_message_end = getTimeMs() + duration; } else { - toast.show(msg, "", duration); + toast.show(msg, details != nullptr ? details : "", duration); } } @@ -3465,7 +3465,7 @@ void gui_display_osd() const ScaledVec2 padding(5.f, 5.f); const ImVec2 size = largeFont->CalcTextSizeA(largeFont->FontSize, FLT_MAX, maxW, &message.front(), &message.back() + 1) + padding * 2.f; - ImVec2 pos(0, ImGui::GetIO().DisplaySize.y - size.y); + ImVec2 pos(insetLeft, ImGui::GetIO().DisplaySize.y - size.y); constexpr float alpha = 0.7f; const ImU32 bg_col = alphaOverride(0x00202020, alpha / 2.f); dl->AddRectFilled(pos, pos + size, bg_col, 0.f); @@ -3560,7 +3560,7 @@ void fatal_error(const char* text, ...) va_end(args); ERROR_LOG(COMMON, "%s", temp); - gui_display_notification(temp, 2000); + gui_display_notification("Fatal Error", 20000, temp); } extern bool subfolders_read; diff --git a/core/rend/gui.h b/core/rend/gui.h index d0ef7af84..593ba32b8 100644 --- a/core/rend/gui.h +++ b/core/rend/gui.h @@ -25,7 +25,7 @@ void gui_init(); void gui_initFonts(); void gui_open_settings(); void gui_display_ui(); -void gui_display_notification(const char *msg, int duration); +void gui_display_notification(const char *msg, int duration, const char *details = nullptr); void gui_display_osd(); void gui_display_profiler(); void gui_open_onboarding(); diff --git a/core/rend/gui_achievements.cpp b/core/rend/gui_achievements.cpp index e5d42fe63..f9aa436e1 100644 --- a/core/rend/gui_achievements.cpp +++ b/core/rend/gui_achievements.cpp @@ -28,6 +28,7 @@ #include extern ImFont *largeFont; +extern int insetLeft; namespace achievements { @@ -42,6 +43,7 @@ static constexpr u64 NEVER_ENDS = 1000000000000; void Notification::notify(Type type, const std::string& image, const std::string& text1, const std::string& text2, const std::string& text3) { + verify(type != Challenge && type != Leaderboard); std::lock_guard _(mutex); u64 now = getTimeMs(); if (type == Progress) @@ -98,6 +100,36 @@ void Notification::hideChallenge(const std::string& image) endTime = getTimeMs(); } +void Notification::showLeaderboard(u32 id, const std::string& text) +{ + std::lock_guard _(mutex); + auto it = leaderboards.find(id); + if (it == leaderboards.end()) + { + if (leaderboards.empty()) + { + this->type = Leaderboard; + startTime = getTimeMs(); + endTime = NEVER_ENDS; + } + leaderboards[id] = text; + } + else { + it->second = text; + } +} + +void Notification::hideLeaderboard(u32 id) +{ + std::lock_guard _(mutex); + auto it = leaderboards.find(id); + if (it == leaderboards.end()) + return; + leaderboards.erase(it); + if (this->type == Leaderboard && leaderboards.empty()) + endTime = getTimeMs(); +} + bool Notification::draw() { std::lock_guard _(mutex); @@ -106,7 +138,14 @@ bool Notification::draw() u64 now = getTimeMs(); if (now > endTime + END_ANIM_TIME) { - if (!challenges.empty()) + if (!leaderboards.empty()) + { + // Show current leaderboards + type = Leaderboard; + startTime = getTimeMs(); + endTime = NEVER_ENDS; + } + else if (!challenges.empty()) { // Show current challenge indicators type = Challenge; @@ -120,78 +159,114 @@ bool Notification::draw() return false; } } + float alpha = 1.f; if (now > endTime) - { // Fade out - float alpha = (std::cos((now - endTime) / (float)END_ANIM_TIME * (float)M_PI) + 1.f) / 2.f; - ImGui::GetStyle().Alpha = alpha; - ImGui::SetNextWindowBgAlpha(alpha / 2.f); - } - else { - ImGui::SetNextWindowBgAlpha(0.5f); - } - float y = ImGui::GetIO().DisplaySize.y; + alpha = (std::cos((now - endTime) / (float)END_ANIM_TIME * (float)M_PI) + 1.f) / 2.f; + float animY = 0.f; if (now - startTime < START_ANIM_TIME) // Slide up - y += uiScaled(80.f) * (std::cos((now - startTime) / (float)START_ANIM_TIME * (float)M_PI) + 1.f) / 2.f; + animY = (std::cos((now - startTime) / (float)START_ANIM_TIME * (float)M_PI) + 1.f) / 2.f; - ImGui::SetNextWindowPos(ImVec2(0, y), ImGuiCond_Always, ImVec2(0.f, 1.f)); // Lower left corner + const ImVec2 padding = ImGui::GetStyle().WindowPadding; + ImDrawList *dl = ImGui::GetForegroundDrawList(); + const ImU32 bg_col = alphaOverride(ImGui::GetColorU32(ImGuiCol_WindowBg), alpha / 2.f); + const ImU32 borderCol = alphaOverride(ImGui::GetColorU32(ImGuiCol_Border), alpha); if (type == Challenge) { - ImGui::Begin("##achievement", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav - | ImGuiWindowFlags_NoInputs); - for (const auto& img : challenges) - { - img.draw(ScaledVec2(60.f, 60.f)); - ImGui::SameLine(); + const ScaledVec2 size(60.f, 60.f); + const ImVec2 spacing(ImGui::GetStyle().ItemSpacing.x, 0.f); + const ImVec2 totalSize = size * challenges.size() + spacing * (challenges.size() - 1) + padding * 2.f; + ImVec2 pos(insetLeft, ImGui::GetIO().DisplaySize.y - totalSize.y * (1.f - animY)); + dl->AddRectFilled(pos, pos + totalSize, bg_col, 0.f); + dl->AddRect(pos, pos + totalSize, borderCol, 0.f); + + pos += padding; + for (const auto& img : challenges) { + img.draw(dl, pos, size, alpha); + pos += spacing; + } + } + else if (type == Leaderboard) + { + ImFont *font = ImGui::GetFont(); + const ImVec2 padding = ImGui::GetStyle().FramePadding; + // iterate from the end + ImVec2 pos(insetLeft + padding.x, ImGui::GetIO().DisplaySize.y - padding.y); + for (auto it = leaderboards.rbegin(); it != leaderboards.rend(); ++it) + { + const std::string& text = it->second; + ImVec2 size = font->CalcTextSizeA(font->FontSize, FLT_MAX, -1.f, text.c_str()); + ImVec2 psize = size + padding * 2; + pos.y -= psize.y; + dl->AddRectFilled(pos, pos + psize, bg_col, 0.f); + ImVec2 tpos = pos + padding; + const ImU32 col = alphaOverride(0xffffff, alpha); + dl->AddText(font, font->FontSize, tpos, col, &text.front(), &text.back() + 1, FLT_MAX); + pos.y -= padding.y; } - ImGui::End(); } else { - ImGui::SetNextWindowSizeConstraints(ScaledVec2(80.f, 80.f) + ImVec2(ImGui::GetStyle().WindowPadding.x * 2, 0.f), ImVec2(FLT_MAX, FLT_MAX)); - const float winPaddingX = ImGui::GetStyle().WindowPadding.x; - ImguiStyleVar _(ImGuiStyleVar_WindowPadding, ImVec2{}); - - ImGui::Begin("##achievement", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav - | ImGuiWindowFlags_NoInputs); - ImTextureID imageId = image.getId(); - const bool hasPic = imageId != ImTextureID{}; - if (ImGui::BeginTable("achievementNotif", hasPic ? 2 : 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings)) + const float hspacing = ImGui::GetStyle().ItemSpacing.x; + const float vspacing = ImGui::GetStyle().ItemSpacing.y; + const ScaledVec2 imgSize = image.getId() != ImTextureID{} ? ScaledVec2(80.f, 80.f) : ScaledVec2(); + // text size + const float maxW = std::min(ImGui::GetIO().DisplaySize.x, uiScaled(640.f)) - padding.x + - (imgSize.x != 0.f ? imgSize.x + hspacing : padding.x); + ImFont *regularFont = ImGui::GetFont(); + ImVec2 textSize[3] {}; + ImVec2 totalSize(0.f, padding.y * 2); + for (size_t i = 0; i < std::size(text); i++) { - if (hasPic) - ImGui::TableSetupColumn("icon", ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("text", ImGuiTableColumnFlags_WidthStretch); - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - if (hasPic) - { - image.draw(ScaledVec2(80.f, 80.f)); - ImGui::TableSetColumnIndex(1); - } - - float w = largeFont->CalcTextSizeA(largeFont->FontSize, FLT_MAX, -1.f, text[0].c_str()).x; - w = std::max(w, ImGui::CalcTextSize(text[1].c_str()).x); - w = std::max(w, ImGui::CalcTextSize(text[2].c_str()).x) + winPaddingX * 2; - int lines = (int)!text[0].empty() + (int)!text[1].empty() + (int)!text[2].empty(); - ImguiStyleVar _(ImGuiStyleVar_WindowPadding, ImVec2{ hasPic ? 0.f : winPaddingX, (3 - lines) * ImGui::GetTextLineHeight() / 2 }); - if (ImGui::BeginChild("##text", ImVec2(w, 0), ImGuiChildFlags_AlwaysUseWindowPadding, ImGuiWindowFlags_None)) - { - ImGui::PushFont(largeFont); - ImGui::Text("%s", text[0].c_str()); - ImGui::PopFont(); - if (!text[1].empty()) - ImGui::TextColored(ImVec4(1, 1, 0, 0.7f), "%s", text[1].c_str()); - if (!text[2].empty()) - ImGui::TextColored(ImVec4(1, 1, 0, 0.7f), "%s", text[2].c_str()); - } - ImGui::EndChild(); - ImGui::EndTable(); + if (text[i].empty()) + continue; + const ImFont *font = i == 0 ? largeFont : regularFont; + textSize[i] = font->CalcTextSizeA(font->FontSize, FLT_MAX, maxW, text[i].c_str()); + totalSize.x = std::max(totalSize.x, textSize[i].x); + totalSize.y += textSize[i].y; + } + float topMargin = 0.f; + // image / left padding + if (imgSize.x != 0.f) + { + if (totalSize.y < imgSize.y) + topMargin = (imgSize.y - totalSize.y) / 2.f; + totalSize.x += imgSize.x + hspacing; + totalSize.y = std::max(totalSize.y, imgSize.y); + } + else { + totalSize.x += padding.x; + } + // right padding + totalSize.x += padding.x; + // border + totalSize += ImVec2(2.f, 2.f); + // draw background, border + ImVec2 pos(insetLeft, ImGui::GetIO().DisplaySize.y - totalSize.y * (1.f - animY)); + dl->AddRectFilled(pos, pos + totalSize, bg_col, 0.f); + dl->AddRect(pos, pos + totalSize, borderCol, 0.f); + + // draw image and text + pos += ImVec2(1.f, 1.f); // border + if (imgSize.x != 0.f) { + image.draw(dl, pos, imgSize, alpha); + pos.x += imgSize.x + hspacing; + } + else { + pos.x += padding.x; + } + pos.y += topMargin; + for (size_t i = 0; i < std::size(text); i++) + { + if (text[i].empty()) + continue; + const ImFont *font = i == 0 ? largeFont : regularFont; + const ImU32 col = alphaOverride(i == 0 ? 0xffffff : 0x00ffff, alpha); + dl->AddText(font, font->FontSize, pos, col, &text[i].front(), &text[i].back() + 1, maxW); + pos.y += textSize[i].y + vspacing; } - ImGui::End(); } - ImGui::GetStyle().Alpha = 1.f; return true; } diff --git a/core/rend/gui_achievements.h b/core/rend/gui_achievements.h index 733cc1a6c..30aa7c188 100644 --- a/core/rend/gui_achievements.h +++ b/core/rend/gui_achievements.h @@ -21,6 +21,7 @@ #include "gui_util.h" #include #include +#include namespace achievements { @@ -35,13 +36,16 @@ public: Unlocked, Progress, Mastery, - Challenge, + Challenge, // internal use + Leaderboard, // internal use Error }; void notify(Type type, const std::string& image, const std::string& text1, const std::string& text2 = {}, const std::string& text3 = {}); void showChallenge(const std::string& image); void hideChallenge(const std::string& image); + void showLeaderboard(u32 id, const std::string& text); + void hideLeaderboard(u32 id); bool draw(); private: @@ -52,6 +56,7 @@ private: std::string text[3]; std::mutex mutex; std::vector challenges; + std::map leaderboards; }; extern Notification notifier; diff --git a/core/rend/gui_util.cpp b/core/rend/gui_util.cpp index faf4316d7..8621fb231 100644 --- a/core/rend/gui_util.cpp +++ b/core/rend/gui_util.cpp @@ -36,6 +36,7 @@ static std::vector folderFiles; bool subfolders_read; extern int insetLeft, insetRight, insetTop, insetBottom; +extern ImFont *largeFont; void error_popup(); namespace hostfs @@ -728,6 +729,19 @@ void ImguiTexture::draw(const ImVec2& size, const ImVec4& tint_col, const ImVec4 } } +void ImguiTexture::draw(ImDrawList *drawList, const ImVec2& pos, const ImVec2& size, float alpha) const +{ + ImTextureID id = getId(); + if (id == ImTextureID{}) + return; + float ar = imguiDriver->getAspectRatio(id); + ImVec2 uv0, uv1; + setUV(ar, uv0, uv1); + ImVec2 pos_b = pos + size; + u32 col = alphaOverride(0xffffff, alpha); + drawList->AddImage(id, pos, pos_b, uv0, uv1, col); +} + bool ImguiTexture::button(const char* str_id, const ImVec2& image_size, const std::string& title, const ImVec4& bg_col, const ImVec4& tint_col) const { @@ -813,19 +827,19 @@ bool Toast::draw() // Fade out alpha = (std::cos((now - endTime) / (float)END_ANIM_TIME * (float)M_PI) + 1.f) / 2.f; - extern ImFont *largeFont; // FIXME ImFont *regularFont = ImGui::GetFont(); const float maxW = uiScaled(640.f); const ImVec2 titleSize = title.empty() ? ImVec2() : largeFont->CalcTextSizeA(largeFont->FontSize, FLT_MAX, maxW, &title.front(), &title.back() + 1); const ImVec2 msgSize = message.empty() ? ImVec2() : regularFont->CalcTextSizeA(regularFont->FontSize, FLT_MAX, maxW, &message.front(), &message.back() + 1); - const ScaledVec2 padding(10.f, 10.f); - const ScaledVec2 spacing(0.f, 5.f); - const ImVec2 totalSize = titleSize + spacing + msgSize + padding * 2.f; + const ScaledVec2 padding(5.f, 4.f); + const ScaledVec2 spacing(0.f, 2.f); + ImVec2 totalSize(std::max(titleSize.x, msgSize.x), titleSize.y + msgSize.y); + totalSize += padding * 2.f + spacing * (float)(!title.empty() && !message.empty()); const ImVec2 displaySize(ImGui::GetIO().DisplaySize); - ImVec2 pos(0.f, displaySize.y - totalSize.y); + ImVec2 pos(insetLeft, displaySize.y - totalSize.y); if (now - startTime < START_ANIM_TIME) // Slide up pos.y += totalSize.y * (std::cos((now - startTime) / (float)START_ANIM_TIME * (float)M_PI) + 1.f) / 2.f; @@ -840,7 +854,7 @@ bool Toast::draw() { const ImU32 col = alphaOverride(ImGui::GetColorU32(ImGuiCol_Text), alpha); dl->AddText(largeFont, largeFont->FontSize, pos, col, &title.front(), &title.back() + 1, maxW); - pos += spacing + ImVec2(0.f, titleSize.y); + pos.y += spacing.y + titleSize.y; } if (!message.empty()) { diff --git a/core/rend/gui_util.h b/core/rend/gui_util.h index 26e7ad3fb..c52f1956c 100644 --- a/core/rend/gui_util.h +++ b/core/rend/gui_util.h @@ -204,6 +204,7 @@ public: void draw(const ImVec2& size, const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec4& border_col = ImVec4(0, 0, 0, 0)) const; + void draw(ImDrawList *drawList, const ImVec2& pos, const ImVec2& size, float alpha = 1.f) const; bool button(const char* str_id, const ImVec2& image_size, const std::string& title = {}, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)) const; diff --git a/shell/libretro/libretro.cpp b/shell/libretro/libretro.cpp index a5af48220..4348b6748 100644 --- a/shell/libretro/libretro.cpp +++ b/shell/libretro/libretro.cpp @@ -196,7 +196,7 @@ static retro_rumble_interface rumble; static void refresh_devices(bool first_startup); static void init_disk_control_interface(); static bool read_m3u(const char *file); -void gui_display_notification(const char *msg, int duration); +void gui_display_notification(const char *msg, int duration, const char *details = nullptr); static void updateVibration(u32 port, float power, float inclination, u32 durationMs); static std::string game_data; @@ -3702,7 +3702,7 @@ static bool read_m3u(const char *file) return disk_index != 0; } -void gui_display_notification(const char *msg, int duration) +void gui_display_notification(const char *msg, int duration, const char *details) { retro_message retromsg; retromsg.msg = msg;