diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 2ca98b9072..68a6e8b67d 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -1708,7 +1708,8 @@ void Emulator::Stop(bool restart) 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..."); @@ -1735,10 +1736,14 @@ void Emulator::Stop(bool restart) if (do_exit) { - GetCallbacks().exit(); + GetCallbacks().exit(true); } else { + if (full_stop) + { + GetCallbacks().exit(false); + } Init(); } diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 597c9fa493..cf1dd40bea 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -213,7 +213,7 @@ struct EmuCallbacks std::function on_resume; std::function on_stop; std::function on_ready; - std::function exit; + std::function exit; // (force_quit) close RPCS3 std::function reset_pads; std::function enable_pads; std::function handle_taskbar_progress; // (type, value) type: 0 for reset, 1 for increment, 2 for set_limit diff --git a/rpcs3/headless_application.cpp b/rpcs3/headless_application.cpp index 85e208a42e..16703a85ac 100644 --- a/rpcs3/headless_application.cpp +++ b/rpcs3/headless_application.cpp @@ -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 InitializeEmulator("1", true); // TODO: get user from cli args if possible @@ -32,9 +34,12 @@ void headless_application::InitializeCallbacks() { EmuCallbacks callbacks = CreateCallbacks(); - callbacks.exit = [this]() + callbacks.exit = [this](bool force_quit) { - quit(); + if (force_quit) + { + quit(); + } }; callbacks.call_after = [=](std::function func) { diff --git a/rpcs3/headless_application.h b/rpcs3/headless_application.h index 0a4b66f8bb..a7d0a475b3 100644 --- a/rpcs3/headless_application.h +++ b/rpcs3/headless_application.h @@ -17,7 +17,7 @@ public: headless_application(int& argc, char** argv); /** Call this method before calling app.exec */ - void Init() override; + void Init(const bool show_gui = false) override; private: void InitializeCallbacks(); diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 2efbfed3cc..f969cdeac1 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -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 #include @@ -97,12 +97,13 @@ static semaphore<> s_qt_mutex{}; std::abort(); } -const char* ARG_HEADLESS = "headless"; -const char* ARG_HI_DPI = "hidpi"; +const char* arg_headless = "headless"; +const char* arg_no_gui = "no-gui"; +const char* arg_high_dpi = "hidpi"; 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) if (!strcmp(headless.c_str(), argv[i])) return new headless_application(argc, argv); @@ -138,8 +139,9 @@ int main(int argc, char** argv) const QCommandLineOption helpOption = parser.addHelpOption(); const QCommandLineOption versionOption = parser.addVersionOption(); - 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_headless, "Run RPCS3 in headless mode.")); + 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()); // 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(app.data())) { // 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(Qt::AA_UseHighDpiPixmaps); app->setAttribute(Qt::AA_DisableWindowContextHelpButton); app->setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); - gui_app->Init(); + gui_app->Init(show_gui); } else if (auto headless_app = qobject_cast(app.data())) { diff --git a/rpcs3/main_application.h b/rpcs3/main_application.h index 7f52d9d655..497bbc271b 100644 --- a/rpcs3/main_application.h +++ b/rpcs3/main_application.h @@ -7,7 +7,7 @@ class main_application { public: - virtual void Init() = 0; + virtual void Init(const bool show_gui = false) = 0; static bool InitializeEmulator(const std::string& user, bool force_init); diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index 2a89f24992..73551734fc 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -13,14 +13,25 @@ #include "osk_dialog_frame.h" #include "stylesheets.h" +#include + 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")); + m_show_gui = show_gui; + m_emu_settings.reset(new emu_settings()); m_gui_settings.reset(new gui_settings()); @@ -28,7 +39,10 @@ void gui_application::Init() InitializeEmulator(m_gui_settings->GetCurrentUser().toStdString(), true); // Create the main window - m_main_window = new main_window(m_gui_settings, m_emu_settings, nullptr); + if (m_show_gui) + { + m_main_window = new main_window(m_gui_settings, m_emu_settings, nullptr); + } // Create callbacks from the emulator, which reference the handlers. InitializeCallbacks(); @@ -36,13 +50,17 @@ void gui_application::Init() // Create connects to propagate events throughout Gui. InitializeConnects(); - m_main_window->Init(); + if (m_main_window) + { + m_main_window->Init(); + } if (m_gui_settings->GetValue(gui::ib_show_welcome).toBool()) { welcome_dialog* welcome = new welcome_dialog(); welcome->exec(); } + #ifdef WITH_DISCORD_RPC // Discord Rich Presence Integration if (m_gui_settings->GetValue(gui::m_richPresence).toBool()) @@ -54,13 +72,35 @@ void gui_application::Init() void gui_application::InitializeConnects() { - connect(m_main_window, &main_window::RequestGlobalStylesheetChange, this, &gui_application::OnChangeStyleSheetRequest); + if (m_main_window) + { + 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::OnEmulatorStop, m_main_window, &main_window::OnEmuStop); - 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::OnEmulatorReady, m_main_window, &main_window::OnEmuReady); + connect(this, &gui_application::OnEmulatorRun, m_main_window, &main_window::OnEmuRun); + connect(this, &gui_application::OnEmulatorStop, m_main_window, &main_window::OnEmuStop); + 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::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"); connect(this, &gui_application::RequestCallAfter, this, &gui_application::HandleCallAfter); @@ -80,7 +120,9 @@ std::unique_ptr gui_application::get_gs_frame() 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; @@ -88,23 +130,23 @@ std::unique_ptr gui_application::get_gs_frame() { 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; } 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; } 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; } #ifdef _MSC_VER 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; } #endif @@ -112,6 +154,7 @@ std::unique_ptr gui_application::get_gs_frame() } m_game_window = frame; + return std::unique_ptr(frame); } @@ -120,9 +163,13 @@ void gui_application::InitializeCallbacks() { EmuCallbacks callbacks = CreateCallbacks(); - callbacks.exit = [this]() + callbacks.exit = [this](bool force_quit) { - quit(); + // Close rpcs3 if closed in no-gui mode + if (force_quit || !m_main_window) + { + quit(); + } }; callbacks.call_after = [=](std::function func) { @@ -130,7 +177,7 @@ void gui_application::InitializeCallbacks() }; callbacks.get_gs_frame = [this]() -> std::unique_ptr { return get_gs_frame(); }; - callbacks.get_msg_dialog = [this]() -> std::shared_ptr { return std::make_shared(m_main_window->windowHandle()); }; + callbacks.get_msg_dialog = [this]() -> std::shared_ptr { return std::make_shared(); }; callbacks.get_osk_dialog = []() -> std::shared_ptr { return std::make_shared(); }; callbacks.get_save_dialog = []() -> std::unique_ptr { return std::make_unique(); }; callbacks.get_trophy_notification_dialog = [this]() -> std::unique_ptr { return std::make_unique(m_game_window); }; @@ -216,7 +263,11 @@ void gui_application::OnChangeStyleSheetRequest(const QString& path) } gui::stylesheet = styleSheet(); - m_main_window->RepaintGui(); + + if (m_main_window) + { + m_main_window->RepaintGui(); + } } /** diff --git a/rpcs3/rpcs3qt/gui_application.h b/rpcs3/rpcs3qt/gui_application.h index 55bc59fcad..b34c817db6 100644 --- a/rpcs3/rpcs3qt/gui_application.h +++ b/rpcs3/rpcs3qt/gui_application.h @@ -21,9 +21,10 @@ class gui_application : public QApplication, public main_application Q_OBJECT public: gui_application(int& argc, char** argv); + ~gui_application(); /** Call this method before calling app.exec */ - void Init() override; + void Init(const bool show_gui = true) override; std::unique_ptr get_gs_frame(); @@ -41,6 +42,8 @@ private: std::shared_ptr m_emu_settings; std::shared_ptr m_gui_settings; + bool m_show_gui = true; + private Q_SLOTS: void OnChangeStyleSheetRequest(const QString& path); diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 491b630825..91f533d2dc 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -8,10 +8,6 @@ #include #include -#ifdef WITH_DISCORD_RPC -#include "_discord_utils.h" -#endif - #include "qt_utils.h" #include "vfs_dialog.h" #include "save_manager_dialog.h" @@ -66,9 +62,6 @@ main_window::main_window(std::shared_ptr guiSettings, std::shared_ main_window::~main_window() { 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). @@ -186,50 +179,6 @@ QIcon main_window::GetAppIcon() 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) { 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.Stop(); @@ -844,14 +794,6 @@ void main_window::OnEmuRun() ui->toolbar_start->setText(tr("Pause")); ui->toolbar_start->setToolTip(tr("Pause emulation")); 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() @@ -910,14 +852,6 @@ void main_window::OnEmuStop() ui->toolbar_start->setToolTip(Emu.IsReady() ? tr("Start emulation") : tr("Resume emulation")); } 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() diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index f59dc99b6b..d112499e1f 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -108,7 +108,6 @@ protected: void dragEnterEvent(QDragEnterEvent* event) override; void dragMoveEvent(QDragMoveEvent* event) override; void dragLeaveEvent(QDragLeaveEvent* event) override; - void SetAppIconFromPath(const std::string& path, const std::string& title_id = ""); private: void RepaintToolBarIcons(); diff --git a/rpcs3/rpcs3qt/msg_dialog_frame.cpp b/rpcs3/rpcs3qt/msg_dialog_frame.cpp index faec60a601..cbce7ea609 100644 --- a/rpcs3/rpcs3qt/msg_dialog_frame.cpp +++ b/rpcs3/rpcs3qt/msg_dialog_frame.cpp @@ -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() { diff --git a/rpcs3/rpcs3qt/msg_dialog_frame.h b/rpcs3/rpcs3qt/msg_dialog_frame.h index 82682d3e76..648a18b7e4 100644 --- a/rpcs3/rpcs3qt/msg_dialog_frame.h +++ b/rpcs3/rpcs3qt/msg_dialog_frame.h @@ -45,12 +45,10 @@ private: QProgressBar* m_gauge1 = nullptr; QProgressBar* m_gauge2 = nullptr; - QWindow* m_taskbarTarget; // Window which will be targeted by custom taskbars. - int m_gauge_max = 0; public: - msg_dialog_frame(QWindow* taskbarTarget); + msg_dialog_frame(); ~msg_dialog_frame(); virtual void Create(const std::string& msg, const std::string& title = "") override; virtual void Close(bool success) override; diff --git a/rpcs3/rpcs3qt/qt_utils.cpp b/rpcs3/rpcs3qt/qt_utils.cpp index 4ff3c4480f..5650baa23a 100644 --- a/rpcs3/rpcs3qt/qt_utils.cpp +++ b/rpcs3/rpcs3qt/qt_utils.cpp @@ -6,6 +6,11 @@ #include #include +#include "Emu/System.h" + +inline std::string sstr(const QString& _in) { return _in.toStdString(); } +constexpr auto qstr = QString::fromStdString; + namespace gui { namespace utils @@ -227,5 +232,47 @@ namespace gui canvas->ensurePolished(); 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 } // gui diff --git a/rpcs3/rpcs3qt/qt_utils.h b/rpcs3/rpcs3qt/qt_utils.h index cf6aa498ea..8d1133bfd1 100644 --- a/rpcs3/rpcs3qt/qt_utils.h +++ b/rpcs3/rpcs3qt/qt_utils.h @@ -47,5 +47,8 @@ namespace gui // Opens an image in a new window with original size 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 } // gui