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;
}
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();
}

View file

@ -213,7 +213,7 @@ struct EmuCallbacks
std::function<void()> on_resume;
std::function<void()> on_stop;
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(bool)> enable_pads;
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
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<void()> func)
{

View file

@ -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();

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
#include <QApplication>
@ -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<gui_application*>(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<headless_application*>(app.data()))
{

View file

@ -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);

View file

@ -13,14 +13,25 @@
#include "osk_dialog_frame.h"
#include "stylesheets.h"
#include <QScreen>
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<void()>>("std::function<void()>");
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();
}
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<gs_frame> 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<gs_frame> gui_application::get_gs_frame()
}
m_game_window = frame;
return std::unique_ptr<gs_frame>(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<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_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_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); };
@ -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();
}
}
/**

View file

@ -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<gs_frame> get_gs_frame();
@ -41,6 +42,8 @@ private:
std::shared_ptr<emu_settings> m_emu_settings;
std::shared_ptr<gui_settings> m_gui_settings;
bool m_show_gui = true;
private Q_SLOTS:
void OnChangeStyleSheetRequest(const QString& path);

View file

@ -8,10 +8,6 @@
#include <QDesktopWidget>
#include <QMimeData>
#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<gui_settings> 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()

View file

@ -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();

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()
{

View file

@ -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;

View file

@ -6,6 +6,11 @@
#include <QPainter>
#include <QScreen>
#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

View file

@ -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