diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 2405e8f9f7..15c258433d 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -44,6 +44,8 @@ #include "display_sleep_control.h" +#include "rpcs3_version.h" + #if defined(_WIN32) || defined(HAVE_VULKAN) #include "Emu/RSX/VK/VulkanAPI.h" #endif @@ -1865,6 +1867,126 @@ void Emulator::Stop(bool restart) } } +std::string Emulator::FormatTitle(double fps) const +{ + // Get version by substringing VersionNumber-buildnumber-commithash to get just the part before the dash + std::string version = rpcs3::get_version().to_string(); + const auto last_minus = version.find_last_of('-'); + + // Add branch and commit hash to version on frame unless it's master. + if (rpcs3::get_branch() != "master"sv && rpcs3::get_branch() != "HEAD"sv) + { + version = version.substr(0, ~last_minus ? last_minus + 9 : last_minus); + version += '-'; + version += rpcs3::get_branch(); + } + else + { + version = version.substr(0, last_minus); + } + + auto [title_format, life1] = g_cfg.misc.title_format.get(); + + // Parse title format string + std::string title_string; + + // Backward compatibility hack + std::size_t fmt_start = 0; + + if (g_cfg.misc.show_fps_in_title.get() == false) + { + if (title_format.starts_with("FPS: %F | ")) + { + // Remove "FPS" from the title if detected + fmt_start = 10; + } + } + + for (std::size_t i = fmt_start; i < title_format.size();) + { + const char c1 = title_format[i]; + + if (c1 == '\0') + { + break; + } + + switch (c1) + { + case '%': + { + const char c2 = title_format[i + 1]; + + if (c2 == '\0') + { + title_string += '%'; + i++; + continue; + } + + switch (c2) + { + case '%': + { + title_string += '%'; + break; + } + case 'T': + { + title_string += this->GetTitle(); + break; + } + case 't': + { + title_string += this->GetTitleID(); + break; + } + case 'R': + { + fmt::append(title_string, "%s", g_cfg.video.renderer.get()); + break; + } + case 'V': + { + title_string += version; + break; + } + case 'F': + { + if (g_cfg.misc.show_fps_in_title) + { + fmt::append(title_string, "%.2f", fps); + } + else + { + title_string += "Disabled"; + } + + break; + } + default: + { + title_string += '%'; + title_string += c2; + break; + } + } + + i += 2; + break; + } + default: + { + title_string += c1; + i += 1; + break; + } + } + } + + return title_string; +} + std::string cfg_root::node_vfs::get(const cfg::string& _cfg, const char* _def) const { auto [spath, sshared] = _cfg.get(); diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 092d5a5bf4..5208a0035f 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -377,6 +377,8 @@ public: bool HasGui() const { return m_has_gui; } void SetHasGui(bool has_gui) { m_has_gui = has_gui; } + + std::string FormatTitle(double fps) const; }; extern Emulator Emu; @@ -626,6 +628,7 @@ struct cfg_root : cfg::node cfg::_bool use_native_interface{ this, "Use native user interface", true }; cfg::string gdb_server{this, "GDB Server", "127.0.0.1:2345"}; cfg::_bool silence_all_logs{this, "Silence All Logs", false, false}; + cfg::string title_format{this, "Window Title Format", "FPS: %F | %R | %V | %T [%t]", true}; } misc{this}; diff --git a/rpcs3/rpcs3_version.cpp b/rpcs3/rpcs3_version.cpp index d24b00e274..1d6026a354 100644 --- a/rpcs3/rpcs3_version.cpp +++ b/rpcs3/rpcs3_version.cpp @@ -5,7 +5,7 @@ namespace rpcs3 { - std::string get_branch() + std::string_view get_branch() { return RPCS3_GIT_BRANCH; } diff --git a/rpcs3/rpcs3_version.h b/rpcs3/rpcs3_version.h index a1ef0e1dc4..28eca23008 100644 --- a/rpcs3/rpcs3_version.h +++ b/rpcs3/rpcs3_version.h @@ -5,7 +5,7 @@ namespace rpcs3 { - std::string get_branch(); + std::string_view get_branch(); std::pair get_commit_and_hash(); const utils::version& get_version(); } diff --git a/rpcs3/rpcs3qt/gl_gs_frame.cpp b/rpcs3/rpcs3qt/gl_gs_frame.cpp index 0e4ae726c3..5c2c602d4e 100644 --- a/rpcs3/rpcs3qt/gl_gs_frame.cpp +++ b/rpcs3/rpcs3qt/gl_gs_frame.cpp @@ -7,7 +7,7 @@ #include gl_gs_frame::gl_gs_frame(const QRect& geometry, const QIcon& appIcon, const std::shared_ptr& gui_settings) - : gs_frame("OpenGL", geometry, appIcon, gui_settings) + : gs_frame(geometry, appIcon, gui_settings) { setSurfaceType(QSurface::OpenGLSurface); diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 12f6b78a7e..153279a32e 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -13,8 +13,6 @@ #include #include -#include "rpcs3_version.h" - #include "png.h" #ifdef _WIN32 @@ -35,32 +33,12 @@ LOG_CHANNEL(screenshot); constexpr auto qstr = QString::fromStdString; -gs_frame::gs_frame(const QString& title, const QRect& geometry, const QIcon& appIcon, const std::shared_ptr& gui_settings) - : QWindow(), m_windowTitle(title), m_gui_settings(gui_settings) +gs_frame::gs_frame(const QRect& geometry, const QIcon& appIcon, const std::shared_ptr& gui_settings) + : QWindow(), m_gui_settings(gui_settings) { m_disable_mouse = gui_settings->GetValue(gui::gs_disableMouse).toBool(); - // Get version by substringing VersionNumber-buildnumber-commithash to get just the part before the dash - std::string version = rpcs3::get_version().to_string(); - version = version.substr(0 , version.find_last_of('-')); - - // Add branch and commit hash to version on frame unless it's master. - if ((rpcs3::get_branch().compare("master") != 0) && (rpcs3::get_branch().compare("HEAD") != 0)) - { - version = version + "-" + rpcs3::get_version().to_string().substr((rpcs3::get_version().to_string().find_last_of('-') + 1), 8) + "-" + rpcs3::get_branch(); - } - - m_windowTitle += qstr(" | " + version); - - if (!Emu.GetTitle().empty()) - { - m_windowTitle += qstr(" | " + Emu.GetTitle()); - } - - if (!Emu.GetTitleID().empty()) - { - m_windowTitle += qstr(" [" + Emu.GetTitleID() + ']'); - } + m_window_title = qstr(Emu.FormatTitle(0)); if (!appIcon.isNull()) { @@ -76,7 +54,7 @@ gs_frame::gs_frame(const QString& title, const QRect& geometry, const QIcon& app setMinimumWidth(160); setMinimumHeight(90); setGeometry(geometry); - setTitle(m_windowTitle); + setTitle(m_window_title); setVisibility(Hidden); create(); @@ -291,31 +269,22 @@ void gs_frame::flip(draw_context_t, bool /*skip_frame*/) { static Timer fps_t; - if (!m_show_fps_in_title && !g_cfg.misc.show_fps_in_title) - { - return; - } - ++m_frames; if (fps_t.GetElapsedTimeInSec() >= 0.5) { - QString fps_title; + QString new_title = qstr(Emu.FormatTitle(m_frames / fps_t.GetElapsedTimeInSec())); - if ((m_show_fps_in_title = g_cfg.misc.show_fps_in_title.get())) + if (new_title != m_window_title) { - fps_title = qstr(fmt::format("FPS: %.2f", m_frames / fps_t.GetElapsedTimeInSec())); + m_window_title = new_title; - if (!m_windowTitle.isEmpty()) + Emu.CallAfter([this, title = std::move(new_title)]() { - fps_title += " | "; - } + setTitle(title); + }); } - fps_title += m_windowTitle; - - Emu.CallAfter([this, title = std::move(fps_title)]() { setTitle(title); }); - m_frames = 0; fps_t.Start(); } diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index 1e9ffbbb61..90e764fee0 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -36,13 +36,11 @@ private: std::shared_ptr m_gui_settings; u64 m_frames = 0; - // display status of last title update, needed for dynamic changes of the fps setting - bool m_show_fps_in_title = false; - QString m_windowTitle; + QString m_window_title; bool m_disable_mouse; public: - gs_frame(const QString& title, const QRect& geometry, const QIcon& appIcon, const std::shared_ptr& gui_settings); + gs_frame(const QRect& geometry, const QIcon& appIcon, const std::shared_ptr& gui_settings); ~gs_frame(); draw_context_t make_context() override; diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index 1943694315..4c381538cf 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -233,19 +233,15 @@ std::unique_ptr gui_application::get_gs_frame() switch (video_renderer type = g_cfg.video.renderer) { - case video_renderer::null: - { - frame = new gs_frame("Null", frame_geometry, app_icon, m_gui_settings); - break; - } case video_renderer::opengl: { frame = new gl_gs_frame(frame_geometry, app_icon, m_gui_settings); break; } + case video_renderer::null: case video_renderer::vulkan: { - frame = new gs_frame("Vulkan", frame_geometry, app_icon, m_gui_settings); + frame = new gs_frame(frame_geometry, app_icon, m_gui_settings); break; } default: fmt::throw_exception("Invalid video renderer: %s" HERE, type);