Qt: add --no-gui mode

This commit is contained in:
Megamouse 2019-08-25 11:51:13 +02:00
parent 7cf037bd49
commit 432364cb04
14 changed files with 156 additions and 108 deletions

View file

@ -1708,7 +1708,8 @@ void Emulator::Stop(bool restart)
return; return;
} }
const bool do_exit = !restart && !m_force_boot && g_cfg.misc.autoexit; const bool full_stop = !restart && !m_force_boot;
const bool do_exit = full_stop && g_cfg.misc.autoexit;
LOG_NOTICE(GENERAL, "Stopping emulator..."); LOG_NOTICE(GENERAL, "Stopping emulator...");
@ -1735,10 +1736,14 @@ void Emulator::Stop(bool restart)
if (do_exit) if (do_exit)
{ {
GetCallbacks().exit(); GetCallbacks().exit(true);
} }
else else
{ {
if (full_stop)
{
GetCallbacks().exit(false);
}
Init(); Init();
} }

View file

@ -213,7 +213,7 @@ struct EmuCallbacks
std::function<void()> on_resume; std::function<void()> on_resume;
std::function<void()> on_stop; std::function<void()> on_stop;
std::function<void()> on_ready; std::function<void()> on_ready;
std::function<void()> exit; std::function<void(bool)> exit; // (force_quit) close RPCS3
std::function<void(const std::string&)> reset_pads; std::function<void(const std::string&)> reset_pads;
std::function<void(bool)> enable_pads; std::function<void(bool)> enable_pads;
std::function<void(s32, s32)> handle_taskbar_progress; // (type, value) type: 0 for reset, 1 for increment, 2 for set_limit std::function<void(s32, s32)> handle_taskbar_progress; // (type, value) type: 0 for reset, 1 for increment, 2 for set_limit

View file

@ -9,8 +9,10 @@ headless_application::headless_application(int& argc, char** argv) : QCoreApplic
{ {
} }
void headless_application::Init() void headless_application::Init(const bool show_gui)
{ {
Q_UNUSED(show_gui);
// Force init the emulator // Force init the emulator
InitializeEmulator("1", true); // TODO: get user from cli args if possible InitializeEmulator("1", true); // TODO: get user from cli args if possible
@ -32,9 +34,12 @@ void headless_application::InitializeCallbacks()
{ {
EmuCallbacks callbacks = CreateCallbacks(); EmuCallbacks callbacks = CreateCallbacks();
callbacks.exit = [this]() callbacks.exit = [this](bool force_quit)
{
if (force_quit)
{ {
quit(); quit();
}
}; };
callbacks.call_after = [=](std::function<void()> func) callbacks.call_after = [=](std::function<void()> func)
{ {

View file

@ -17,7 +17,7 @@ public:
headless_application(int& argc, char** argv); headless_application(int& argc, char** argv);
/** Call this method before calling app.exec */ /** Call this method before calling app.exec */
void Init() override; void Init(const bool show_gui = false) override;
private: private:
void InitializeCallbacks(); void InitializeCallbacks();

View file

@ -1,4 +1,4 @@
// Qt5.10+ frontend implementation for rpcs3. Known to work on Windows, Linux, Mac // Qt5.10+ frontend implementation for rpcs3. Known to work on Windows, Linux, Mac
// by Sacha Refshauge, Megamouse and flash-fire // by Sacha Refshauge, Megamouse and flash-fire
#include <QApplication> #include <QApplication>
@ -97,12 +97,13 @@ static semaphore<> s_qt_mutex{};
std::abort(); std::abort();
} }
const char* ARG_HEADLESS = "headless"; const char* arg_headless = "headless";
const char* ARG_HI_DPI = "hidpi"; const char* arg_no_gui = "no-gui";
const char* arg_high_dpi = "hidpi";
QCoreApplication* createApplication(int& argc, char* argv[]) QCoreApplication* createApplication(int& argc, char* argv[])
{ {
const std::string headless("--" + std::string(ARG_HEADLESS)); const std::string headless("--" + std::string(arg_headless));
for (int i = 1; i < argc; ++i) for (int i = 1; i < argc; ++i)
if (!strcmp(headless.c_str(), argv[i])) if (!strcmp(headless.c_str(), argv[i]))
return new headless_application(argc, argv); return new headless_application(argc, argv);
@ -138,8 +139,9 @@ int main(int argc, char** argv)
const QCommandLineOption helpOption = parser.addHelpOption(); const QCommandLineOption helpOption = parser.addHelpOption();
const QCommandLineOption versionOption = parser.addVersionOption(); const QCommandLineOption versionOption = parser.addVersionOption();
parser.addOption(QCommandLineOption(ARG_HEADLESS, "Run RPCS3 in headless mode.")); parser.addOption(QCommandLineOption(arg_headless, "Run RPCS3 in headless mode."));
parser.addOption(QCommandLineOption(ARG_HI_DPI, "Enables Qt High Dpi Scaling.", "enabled", "1")); parser.addOption(QCommandLineOption(arg_no_gui, "Run RPCS3 without its GUI."));
parser.addOption(QCommandLineOption(arg_high_dpi, "Enables Qt High Dpi Scaling.", "enabled", "1"));
parser.process(app->arguments()); parser.process(app->arguments());
// Don't start up the full rpcs3 gui if we just want the version or help. // Don't start up the full rpcs3 gui if we just want the version or help.
@ -149,14 +151,15 @@ int main(int argc, char** argv)
if (auto gui_app = qobject_cast<gui_application*>(app.data())) if (auto gui_app = qobject_cast<gui_application*>(app.data()))
{ {
// Set QT_AUTO_SCREEN_SCALE_FACTOR from environment. Defaults to cli argument, which defaults to 1. // Set QT_AUTO_SCREEN_SCALE_FACTOR from environment. Defaults to cli argument, which defaults to 1.
const bool use_high_dpi = "1" == qEnvironmentVariable("QT_AUTO_SCREEN_SCALE_FACTOR", parser.value(ARG_HI_DPI)); const bool use_high_dpi = "1" == qEnvironmentVariable("QT_AUTO_SCREEN_SCALE_FACTOR", parser.value(arg_high_dpi));
const bool show_gui = !parser.isSet(arg_no_gui);
app->setAttribute(use_high_dpi ? Qt::AA_EnableHighDpiScaling : Qt::AA_DisableHighDpiScaling); app->setAttribute(use_high_dpi ? Qt::AA_EnableHighDpiScaling : Qt::AA_DisableHighDpiScaling);
app->setAttribute(Qt::AA_UseHighDpiPixmaps); app->setAttribute(Qt::AA_UseHighDpiPixmaps);
app->setAttribute(Qt::AA_DisableWindowContextHelpButton); app->setAttribute(Qt::AA_DisableWindowContextHelpButton);
app->setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); app->setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
gui_app->Init(); gui_app->Init(show_gui);
} }
else if (auto headless_app = qobject_cast<headless_application*>(app.data())) else if (auto headless_app = qobject_cast<headless_application*>(app.data()))
{ {

View file

@ -7,7 +7,7 @@
class main_application class main_application
{ {
public: public:
virtual void Init() = 0; virtual void Init(const bool show_gui = false) = 0;
static bool InitializeEmulator(const std::string& user, bool force_init); static bool InitializeEmulator(const std::string& user, bool force_init);

View file

@ -13,14 +13,25 @@
#include "osk_dialog_frame.h" #include "osk_dialog_frame.h"
#include "stylesheets.h" #include "stylesheets.h"
#include <QScreen>
gui_application::gui_application(int& argc, char** argv) : QApplication(argc, argv) gui_application::gui_application(int& argc, char** argv) : QApplication(argc, argv)
{ {
} }
void gui_application::Init() gui_application::~gui_application()
{
#ifdef WITH_DISCORD_RPC
discord::shutdown();
#endif
}
void gui_application::Init(const bool show_gui)
{ {
setWindowIcon(QIcon(":/rpcs3.ico")); setWindowIcon(QIcon(":/rpcs3.ico"));
m_show_gui = show_gui;
m_emu_settings.reset(new emu_settings()); m_emu_settings.reset(new emu_settings());
m_gui_settings.reset(new gui_settings()); m_gui_settings.reset(new gui_settings());
@ -28,7 +39,10 @@ void gui_application::Init()
InitializeEmulator(m_gui_settings->GetCurrentUser().toStdString(), true); InitializeEmulator(m_gui_settings->GetCurrentUser().toStdString(), true);
// Create the main window // Create the main window
if (m_show_gui)
{
m_main_window = new main_window(m_gui_settings, m_emu_settings, nullptr); m_main_window = new main_window(m_gui_settings, m_emu_settings, nullptr);
}
// Create callbacks from the emulator, which reference the handlers. // Create callbacks from the emulator, which reference the handlers.
InitializeCallbacks(); InitializeCallbacks();
@ -36,13 +50,17 @@ void gui_application::Init()
// Create connects to propagate events throughout Gui. // Create connects to propagate events throughout Gui.
InitializeConnects(); InitializeConnects();
if (m_main_window)
{
m_main_window->Init(); m_main_window->Init();
}
if (m_gui_settings->GetValue(gui::ib_show_welcome).toBool()) if (m_gui_settings->GetValue(gui::ib_show_welcome).toBool())
{ {
welcome_dialog* welcome = new welcome_dialog(); welcome_dialog* welcome = new welcome_dialog();
welcome->exec(); welcome->exec();
} }
#ifdef WITH_DISCORD_RPC #ifdef WITH_DISCORD_RPC
// Discord Rich Presence Integration // Discord Rich Presence Integration
if (m_gui_settings->GetValue(gui::m_richPresence).toBool()) if (m_gui_settings->GetValue(gui::m_richPresence).toBool())
@ -54,6 +72,8 @@ void gui_application::Init()
void gui_application::InitializeConnects() void gui_application::InitializeConnects()
{ {
if (m_main_window)
{
connect(m_main_window, &main_window::RequestGlobalStylesheetChange, this, &gui_application::OnChangeStyleSheetRequest); connect(m_main_window, &main_window::RequestGlobalStylesheetChange, this, &gui_application::OnChangeStyleSheetRequest);
connect(this, &gui_application::OnEmulatorRun, m_main_window, &main_window::OnEmuRun); connect(this, &gui_application::OnEmulatorRun, m_main_window, &main_window::OnEmuRun);
@ -61,6 +81,26 @@ void gui_application::InitializeConnects()
connect(this, &gui_application::OnEmulatorPause, m_main_window, &main_window::OnEmuPause); connect(this, &gui_application::OnEmulatorPause, m_main_window, &main_window::OnEmuPause);
connect(this, &gui_application::OnEmulatorResume, m_main_window, &main_window::OnEmuResume); connect(this, &gui_application::OnEmulatorResume, m_main_window, &main_window::OnEmuResume);
connect(this, &gui_application::OnEmulatorReady, m_main_window, &main_window::OnEmuReady); connect(this, &gui_application::OnEmulatorReady, m_main_window, &main_window::OnEmuReady);
}
#ifdef WITH_DISCORD_RPC
connect(this, &gui_application::OnEmulatorRun, [this]()
{
// Discord Rich Presence Integration
if (m_gui_settings->GetValue(gui::m_richPresence).toBool())
{
discord::update_presence(Emu.GetTitleID(), Emu.GetTitle());
}
});
connect(this, &gui_application::OnEmulatorStop, [this]()
{
// Discord Rich Presence Integration
if (m_gui_settings->GetValue(gui::m_richPresence).toBool())
{
discord::update_presence(m_gui_settings->GetValue(gui::m_discordState).toString().toStdString());
}
});
#endif
qRegisterMetaType<std::function<void()>>("std::function<void()>"); qRegisterMetaType<std::function<void()>>("std::function<void()>");
connect(this, &gui_application::RequestCallAfter, this, &gui_application::HandleCallAfter); connect(this, &gui_application::RequestCallAfter, this, &gui_application::HandleCallAfter);
@ -80,7 +120,9 @@ std::unique_ptr<gs_frame> gui_application::get_gs_frame()
h = m_gui_settings->GetValue(gui::gs_height).toInt(); h = m_gui_settings->GetValue(gui::gs_height).toInt();
} }
auto frame_geometry = gui::utils::create_centered_window_geometry(m_main_window->geometry(), w, h); const auto screen_geometry = m_main_window ? m_main_window->geometry() : primaryScreen()->geometry();
const auto frame_geometry = gui::utils::create_centered_window_geometry(screen_geometry, w, h);
const auto app_icon = m_main_window ? m_main_window->GetAppIcon() : gui::utils::get_app_icon_from_path(Emu.GetBoot(), Emu.GetTitleID());
gs_frame* frame; gs_frame* frame;
@ -88,23 +130,23 @@ std::unique_ptr<gs_frame> gui_application::get_gs_frame()
{ {
case video_renderer::null: case video_renderer::null:
{ {
frame = new gs_frame("Null", frame_geometry, m_main_window->GetAppIcon(), m_gui_settings); frame = new gs_frame("Null", frame_geometry, app_icon, m_gui_settings);
break; break;
} }
case video_renderer::opengl: case video_renderer::opengl:
{ {
frame = new gl_gs_frame(frame_geometry, m_main_window->GetAppIcon(), m_gui_settings); frame = new gl_gs_frame(frame_geometry, app_icon, m_gui_settings);
break; break;
} }
case video_renderer::vulkan: case video_renderer::vulkan:
{ {
frame = new gs_frame("Vulkan", frame_geometry, m_main_window->GetAppIcon(), m_gui_settings); frame = new gs_frame("Vulkan", frame_geometry, app_icon, m_gui_settings);
break; break;
} }
#ifdef _MSC_VER #ifdef _MSC_VER
case video_renderer::dx12: case video_renderer::dx12:
{ {
frame = new gs_frame("DirectX 12", frame_geometry, m_main_window->GetAppIcon(), m_gui_settings); frame = new gs_frame("DirectX 12", frame_geometry, app_icon, m_gui_settings);
break; break;
} }
#endif #endif
@ -112,6 +154,7 @@ std::unique_ptr<gs_frame> gui_application::get_gs_frame()
} }
m_game_window = frame; m_game_window = frame;
return std::unique_ptr<gs_frame>(frame); return std::unique_ptr<gs_frame>(frame);
} }
@ -120,9 +163,13 @@ void gui_application::InitializeCallbacks()
{ {
EmuCallbacks callbacks = CreateCallbacks(); EmuCallbacks callbacks = CreateCallbacks();
callbacks.exit = [this]() callbacks.exit = [this](bool force_quit)
{
// Close rpcs3 if closed in no-gui mode
if (force_quit || !m_main_window)
{ {
quit(); quit();
}
}; };
callbacks.call_after = [=](std::function<void()> func) callbacks.call_after = [=](std::function<void()> func)
{ {
@ -130,7 +177,7 @@ void gui_application::InitializeCallbacks()
}; };
callbacks.get_gs_frame = [this]() -> std::unique_ptr<GSFrameBase> { return get_gs_frame(); }; callbacks.get_gs_frame = [this]() -> std::unique_ptr<GSFrameBase> { return get_gs_frame(); };
callbacks.get_msg_dialog = [this]() -> std::shared_ptr<MsgDialogBase> { return std::make_shared<msg_dialog_frame>(m_main_window->windowHandle()); }; callbacks.get_msg_dialog = [this]() -> std::shared_ptr<MsgDialogBase> { return std::make_shared<msg_dialog_frame>(); };
callbacks.get_osk_dialog = []() -> std::shared_ptr<OskDialogBase> { return std::make_shared<osk_dialog_frame>(); }; callbacks.get_osk_dialog = []() -> std::shared_ptr<OskDialogBase> { return std::make_shared<osk_dialog_frame>(); };
callbacks.get_save_dialog = []() -> std::unique_ptr<SaveDialogBase> { return std::make_unique<save_data_dialog>(); }; callbacks.get_save_dialog = []() -> std::unique_ptr<SaveDialogBase> { return std::make_unique<save_data_dialog>(); };
callbacks.get_trophy_notification_dialog = [this]() -> std::unique_ptr<TrophyNotificationBase> { return std::make_unique<trophy_notification_helper>(m_game_window); }; callbacks.get_trophy_notification_dialog = [this]() -> std::unique_ptr<TrophyNotificationBase> { return std::make_unique<trophy_notification_helper>(m_game_window); };
@ -216,7 +263,11 @@ void gui_application::OnChangeStyleSheetRequest(const QString& path)
} }
gui::stylesheet = styleSheet(); gui::stylesheet = styleSheet();
if (m_main_window)
{
m_main_window->RepaintGui(); m_main_window->RepaintGui();
}
} }
/** /**

View file

@ -21,9 +21,10 @@ class gui_application : public QApplication, public main_application
Q_OBJECT Q_OBJECT
public: public:
gui_application(int& argc, char** argv); gui_application(int& argc, char** argv);
~gui_application();
/** Call this method before calling app.exec */ /** Call this method before calling app.exec */
void Init() override; void Init(const bool show_gui = true) override;
std::unique_ptr<gs_frame> get_gs_frame(); std::unique_ptr<gs_frame> get_gs_frame();
@ -41,6 +42,8 @@ private:
std::shared_ptr<emu_settings> m_emu_settings; std::shared_ptr<emu_settings> m_emu_settings;
std::shared_ptr<gui_settings> m_gui_settings; std::shared_ptr<gui_settings> m_gui_settings;
bool m_show_gui = true;
private Q_SLOTS: private Q_SLOTS:
void OnChangeStyleSheetRequest(const QString& path); void OnChangeStyleSheetRequest(const QString& path);

View file

@ -8,10 +8,6 @@
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QMimeData> #include <QMimeData>
#ifdef WITH_DISCORD_RPC
#include "_discord_utils.h"
#endif
#include "qt_utils.h" #include "qt_utils.h"
#include "vfs_dialog.h" #include "vfs_dialog.h"
#include "save_manager_dialog.h" #include "save_manager_dialog.h"
@ -66,9 +62,6 @@ main_window::main_window(std::shared_ptr<gui_settings> guiSettings, std::shared_
main_window::~main_window() main_window::~main_window()
{ {
delete ui; delete ui;
#ifdef WITH_DISCORD_RPC
discord::shutdown();
#endif
} }
/* An init method is used so that RPCS3App can create the necessary connects before calling init (specifically the stylesheet connect). /* An init method is used so that RPCS3App can create the necessary connects before calling init (specifically the stylesheet connect).
@ -186,50 +179,6 @@ QIcon main_window::GetAppIcon()
return m_appIcon; return m_appIcon;
} }
// loads the appIcon from path and embeds it centered into an empty square icon
void main_window::SetAppIconFromPath(const std::string& path, const std::string& title_id)
{
// get Icon for the gs_frame from path. this handles presumably all possible use cases
const QString qpath = qstr(path);
const std::string path_list[] = { path, sstr(qpath.section("/", 0, -2)), sstr(qpath.section("/", 0, -3)) };
for (const std::string& pth : path_list)
{
if (!fs::is_dir(pth))
{
continue;
}
const std::string sfo_dir = Emulator::GetSfoDirFromGamePath(pth, Emu.GetUsr(), title_id);
const std::string ico = sfo_dir + "/ICON0.PNG";
if (fs::is_file(ico))
{
// load the image from path. It will most likely be a rectangle
QImage source = QImage(qstr(ico));
int edgeMax = std::max(source.width(), source.height());
// create a new transparent image with square size and same format as source (maybe handle other formats than RGB32 as well?)
QImage::Format format = source.format() == QImage::Format_RGB32 ? QImage::Format_ARGB32 : source.format();
QImage dest = QImage(edgeMax, edgeMax, format);
dest.fill(QColor("transparent"));
// get the location to draw the source image centered within the dest image.
QPoint destPos = source.width() > source.height() ? QPoint(0, (source.width() - source.height()) / 2) : QPoint((source.height() - source.width()) / 2, 0);
// Paint the source into/over the dest
QPainter painter(&dest);
painter.drawImage(destPos, source);
painter.end();
// set Icon
m_appIcon = QIcon(QPixmap::fromImage(dest));
return;
}
}
// if nothing was found reset the icon to default
m_appIcon = QApplication::windowIcon();
}
void main_window::ResizeIcons(int index) void main_window::ResizeIcons(int index)
{ {
if (ui->sizeSlider->value() != index) if (ui->sizeSlider->value() != index)
@ -289,7 +238,8 @@ void main_window::Boot(const std::string& path, const std::string& title_id, boo
} }
} }
SetAppIconFromPath(path, title_id); m_appIcon = gui::utils::get_app_icon_from_path(path, title_id);
Emu.SetForceBoot(true); Emu.SetForceBoot(true);
Emu.Stop(); Emu.Stop();
@ -844,14 +794,6 @@ void main_window::OnEmuRun()
ui->toolbar_start->setText(tr("Pause")); ui->toolbar_start->setText(tr("Pause"));
ui->toolbar_start->setToolTip(tr("Pause emulation")); ui->toolbar_start->setToolTip(tr("Pause emulation"));
EnableMenus(true); EnableMenus(true);
#ifdef WITH_DISCORD_RPC
// Discord Rich Presence Integration
if (guiSettings->GetValue(gui::m_richPresence).toBool())
{
discord::update_presence(Emu.GetTitleID(), Emu.GetTitle());
}
#endif
} }
void main_window::OnEmuResume() void main_window::OnEmuResume()
@ -910,14 +852,6 @@ void main_window::OnEmuStop()
ui->toolbar_start->setToolTip(Emu.IsReady() ? tr("Start emulation") : tr("Resume emulation")); ui->toolbar_start->setToolTip(Emu.IsReady() ? tr("Start emulation") : tr("Resume emulation"));
} }
ui->actionManage_Users->setEnabled(true); ui->actionManage_Users->setEnabled(true);
#ifdef WITH_DISCORD_RPC
// Discord Rich Presence Integration
if (guiSettings->GetValue(gui::m_richPresence).toBool())
{
discord::update_presence(sstr(guiSettings->GetValue(gui::m_discordState).toString()));
}
#endif
} }
void main_window::OnEmuReady() void main_window::OnEmuReady()

View file

@ -108,7 +108,6 @@ protected:
void dragEnterEvent(QDragEnterEvent* event) override; void dragEnterEvent(QDragEnterEvent* event) override;
void dragMoveEvent(QDragMoveEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override;
void dragLeaveEvent(QDragLeaveEvent* event) override; void dragLeaveEvent(QDragLeaveEvent* event) override;
void SetAppIconFromPath(const std::string& path, const std::string& title_id = "");
private: private:
void RepaintToolBarIcons(); void RepaintToolBarIcons();

View file

@ -161,7 +161,7 @@ void msg_dialog_frame::Close(bool success)
} }
} }
msg_dialog_frame::msg_dialog_frame(QWindow* taskbarTarget) : m_taskbarTarget(taskbarTarget) {} msg_dialog_frame::msg_dialog_frame() {}
msg_dialog_frame::~msg_dialog_frame() msg_dialog_frame::~msg_dialog_frame()
{ {

View file

@ -45,12 +45,10 @@ private:
QProgressBar* m_gauge1 = nullptr; QProgressBar* m_gauge1 = nullptr;
QProgressBar* m_gauge2 = nullptr; QProgressBar* m_gauge2 = nullptr;
QWindow* m_taskbarTarget; // Window which will be targeted by custom taskbars.
int m_gauge_max = 0; int m_gauge_max = 0;
public: public:
msg_dialog_frame(QWindow* taskbarTarget); msg_dialog_frame();
~msg_dialog_frame(); ~msg_dialog_frame();
virtual void Create(const std::string& msg, const std::string& title = "") override; virtual void Create(const std::string& msg, const std::string& title = "") override;
virtual void Close(bool success) override; virtual void Close(bool success) override;

View file

@ -6,6 +6,11 @@
#include <QPainter> #include <QPainter>
#include <QScreen> #include <QScreen>
#include "Emu/System.h"
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
constexpr auto qstr = QString::fromStdString;
namespace gui namespace gui
{ {
namespace utils namespace utils
@ -227,5 +232,47 @@ namespace gui
canvas->ensurePolished(); canvas->ensurePolished();
canvas->show(); canvas->show();
} }
// Loads the app icon from path and embeds it centered into an empty square icon
QIcon get_app_icon_from_path(const std::string& path, const std::string& title_id)
{
// get Icon for the gs_frame from path. this handles presumably all possible use cases
const QString qpath = qstr(path);
const std::string path_list[] = { path, sstr(qpath.section("/", 0, -2)), sstr(qpath.section("/", 0, -3)) };
for (const std::string& pth : path_list)
{
if (!fs::is_dir(pth))
{
continue;
}
const std::string sfo_dir = Emulator::GetSfoDirFromGamePath(pth, Emu.GetUsr(), title_id);
const std::string ico = sfo_dir + "/ICON0.PNG";
if (fs::is_file(ico))
{
// load the image from path. It will most likely be a rectangle
QImage source = QImage(qstr(ico));
int edgeMax = std::max(source.width(), source.height());
// create a new transparent image with square size and same format as source (maybe handle other formats than RGB32 as well?)
QImage::Format format = source.format() == QImage::Format_RGB32 ? QImage::Format_ARGB32 : source.format();
QImage dest = QImage(edgeMax, edgeMax, format);
dest.fill(QColor("transparent"));
// get the location to draw the source image centered within the dest image.
QPoint destPos = source.width() > source.height() ? QPoint(0, (source.width() - source.height()) / 2) : QPoint((source.height() - source.width()) / 2, 0);
// Paint the source into/over the dest
QPainter painter(&dest);
painter.drawImage(destPos, source);
painter.end();
return QIcon(QPixmap::fromImage(dest));
}
}
// if nothing was found reset the icon to default
return QApplication::windowIcon();
}
} // utils } // utils
} // gui } // gui

View file

@ -47,5 +47,8 @@ namespace gui
// Opens an image in a new window with original size // Opens an image in a new window with original size
void show_windowed_image(const QImage& img, const QString& title = ""); void show_windowed_image(const QImage& img, const QString& title = "");
// Loads the app icon from path and embeds it centered into an empty square icon
QIcon get_app_icon_from_path(const std::string& path, const std::string& title_id);
} // utils } // utils
} // gui } // gui