2015-11-27 01:33:07 -07:00
|
|
|
// Copyright 2015 Dolphin Emulator Project
|
2021-07-04 19:22:19 -06:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2015-11-27 01:33:07 -07:00
|
|
|
|
2018-04-29 14:20:03 -06:00
|
|
|
#ifdef _WIN32
|
2020-09-21 03:23:06 -06:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
2018-04-29 14:20:03 -06:00
|
|
|
#include <Windows.h>
|
|
|
|
#include <cstdio>
|
|
|
|
#endif
|
|
|
|
|
2023-01-20 00:50:39 -07:00
|
|
|
#ifdef __linux__
|
|
|
|
#include <cstdlib>
|
|
|
|
#endif
|
|
|
|
|
2017-06-14 06:31:30 -06:00
|
|
|
#include <OptionParser.h>
|
2016-05-11 19:18:30 -06:00
|
|
|
#include <QAbstractEventDispatcher>
|
2015-11-27 01:33:07 -07:00
|
|
|
#include <QApplication>
|
2017-05-20 15:41:02 -06:00
|
|
|
#include <QObject>
|
2017-10-05 10:20:18 -06:00
|
|
|
#include <QPushButton>
|
2018-03-17 18:22:05 -06:00
|
|
|
#include <QWidget>
|
2015-11-27 01:33:07 -07:00
|
|
|
|
2020-07-20 03:22:53 -06:00
|
|
|
#include "Common/Config/Config.h"
|
2017-07-25 22:11:47 -06:00
|
|
|
#include "Common/MsgHandler.h"
|
2020-03-31 12:51:34 -06:00
|
|
|
#include "Common/ScopeGuard.h"
|
2019-03-04 12:49:00 -07:00
|
|
|
|
2017-10-01 16:09:07 -06:00
|
|
|
#include "Core/Boot/Boot.h"
|
2020-07-20 03:22:53 -06:00
|
|
|
#include "Core/Config/MainSettings.h"
|
2015-11-27 01:33:07 -07:00
|
|
|
#include "Core/Core.h"
|
2020-09-15 04:13:10 -06:00
|
|
|
#include "Core/DolphinAnalytics.h"
|
2019-03-04 12:49:00 -07:00
|
|
|
|
2018-07-06 16:40:15 -06:00
|
|
|
#include "DolphinQt/Host.h"
|
|
|
|
#include "DolphinQt/MainWindow.h"
|
2019-03-04 12:49:00 -07:00
|
|
|
#include "DolphinQt/QtUtils/ModalMessageBox.h"
|
2018-07-06 16:40:15 -06:00
|
|
|
#include "DolphinQt/QtUtils/RunOnObject.h"
|
|
|
|
#include "DolphinQt/Resources.h"
|
|
|
|
#include "DolphinQt/Settings.h"
|
|
|
|
#include "DolphinQt/Translation.h"
|
|
|
|
#include "DolphinQt/Updater.h"
|
2019-03-04 12:49:00 -07:00
|
|
|
|
2017-06-14 06:31:30 -06:00
|
|
|
#include "UICommon/CommandLineParse.h"
|
2015-11-27 01:33:07 -07:00
|
|
|
#include "UICommon/UICommon.h"
|
|
|
|
|
2019-06-16 21:45:37 -06:00
|
|
|
static bool QtMsgAlertHandler(const char* caption, const char* text, bool yes_no,
|
|
|
|
Common::MsgType style)
|
2017-07-25 22:11:47 -06:00
|
|
|
{
|
2020-03-31 12:51:34 -06:00
|
|
|
const bool called_from_cpu_thread = Core::IsCPUThread();
|
2020-04-24 16:26:51 -06:00
|
|
|
const bool called_from_gpu_thread = Core::IsGPUThread();
|
2020-03-31 12:51:34 -06:00
|
|
|
|
2018-05-21 16:27:12 -06:00
|
|
|
std::optional<bool> r = RunOnObject(QApplication::instance(), [&] {
|
2020-04-25 02:35:14 -06:00
|
|
|
// If we were called from the CPU/GPU thread, set us as the CPU/GPU thread.
|
|
|
|
// This information is used in order to avoid deadlocks when calling e.g.
|
|
|
|
// Host::SetRenderFocus or Core::RunAsCPUThread. (Host::SetRenderFocus
|
|
|
|
// can get called automatically when a dialog steals the focus.)
|
|
|
|
|
2020-04-24 16:26:51 -06:00
|
|
|
Common::ScopeGuard cpu_scope_guard(&Core::UndeclareAsCPUThread);
|
|
|
|
Common::ScopeGuard gpu_scope_guard(&Core::UndeclareAsGPUThread);
|
|
|
|
|
|
|
|
if (!called_from_cpu_thread)
|
|
|
|
cpu_scope_guard.Dismiss();
|
|
|
|
if (!called_from_gpu_thread)
|
|
|
|
gpu_scope_guard.Dismiss();
|
|
|
|
|
2020-03-31 12:51:34 -06:00
|
|
|
if (called_from_cpu_thread)
|
|
|
|
Core::DeclareAsCPUThread();
|
2020-04-24 16:26:51 -06:00
|
|
|
if (called_from_gpu_thread)
|
|
|
|
Core::DeclareAsGPUThread();
|
2020-03-31 12:51:34 -06:00
|
|
|
|
2019-08-01 11:52:28 -06:00
|
|
|
ModalMessageBox message_box(QApplication::activeWindow(), Qt::ApplicationModal);
|
2017-07-25 22:11:47 -06:00
|
|
|
message_box.setWindowTitle(QString::fromUtf8(caption));
|
|
|
|
message_box.setText(QString::fromUtf8(text));
|
2017-10-05 10:20:18 -06:00
|
|
|
|
2017-07-25 22:11:47 -06:00
|
|
|
message_box.setStandardButtons(yes_no ? QMessageBox::Yes | QMessageBox::No : QMessageBox::Ok);
|
2019-06-16 21:45:37 -06:00
|
|
|
if (style == Common::MsgType::Warning)
|
2017-10-05 10:20:18 -06:00
|
|
|
message_box.addButton(QMessageBox::Ignore)->setText(QObject::tr("Ignore for this session"));
|
|
|
|
|
2017-07-25 22:11:47 -06:00
|
|
|
message_box.setIcon([&] {
|
|
|
|
switch (style)
|
|
|
|
{
|
2019-06-16 21:45:37 -06:00
|
|
|
case Common::MsgType::Information:
|
2017-07-25 22:11:47 -06:00
|
|
|
return QMessageBox::Information;
|
2019-06-16 21:45:37 -06:00
|
|
|
case Common::MsgType::Question:
|
2017-07-25 22:11:47 -06:00
|
|
|
return QMessageBox::Question;
|
2019-06-16 21:45:37 -06:00
|
|
|
case Common::MsgType::Warning:
|
2017-07-25 22:11:47 -06:00
|
|
|
return QMessageBox::Warning;
|
2019-06-16 21:45:37 -06:00
|
|
|
case Common::MsgType::Critical:
|
2017-07-25 22:11:47 -06:00
|
|
|
return QMessageBox::Critical;
|
|
|
|
}
|
|
|
|
// appease MSVC
|
|
|
|
return QMessageBox::NoIcon;
|
|
|
|
}());
|
|
|
|
|
2017-10-05 10:20:18 -06:00
|
|
|
const int button = message_box.exec();
|
|
|
|
if (button == QMessageBox::Yes)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (button == QMessageBox::Ignore)
|
2019-07-20 13:04:27 -06:00
|
|
|
{
|
2022-09-24 05:03:45 -06:00
|
|
|
Config::SetCurrent(Config::MAIN_USE_PANIC_HANDLERS, false);
|
2019-07-20 13:04:27 -06:00
|
|
|
return true;
|
|
|
|
}
|
2017-10-05 10:20:18 -06:00
|
|
|
|
|
|
|
return false;
|
2017-07-25 22:11:47 -06:00
|
|
|
});
|
2018-05-21 16:27:12 -06:00
|
|
|
if (r.has_value())
|
|
|
|
return *r;
|
|
|
|
return false;
|
2017-07-25 22:11:47 -06:00
|
|
|
}
|
|
|
|
|
2022-03-08 00:22:35 -07:00
|
|
|
#ifdef _WIN32
|
|
|
|
#define main app_main
|
|
|
|
#endif
|
|
|
|
|
2015-11-27 01:33:07 -07:00
|
|
|
int main(int argc, char* argv[])
|
|
|
|
{
|
2022-03-08 00:22:35 -07:00
|
|
|
#ifdef _WIN32
|
2018-04-29 14:20:03 -06:00
|
|
|
const bool console_attached = AttachConsole(ATTACH_PARENT_PROCESS) != FALSE;
|
|
|
|
HANDLE stdout_handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
if (console_attached && stdout_handle)
|
|
|
|
{
|
|
|
|
freopen("CONOUT$", "w", stdout);
|
|
|
|
freopen("CONOUT$", "w", stderr);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-05-20 16:33:38 -06:00
|
|
|
Host::GetInstance()->DeclareAsHostThread();
|
|
|
|
|
2020-08-04 13:18:32 -06:00
|
|
|
#ifdef __APPLE__
|
|
|
|
// On macOS, a command line option matching the format "-psn_X_XXXXXX" is passed when
|
|
|
|
// the application is launched for the first time. This is to set the "ProcessSerialNumber",
|
|
|
|
// something used by the legacy Process Manager from Carbon. optparse will fail if it finds
|
|
|
|
// this as it isn't a valid Dolphin command line option, so pretend like it doesn't exist
|
|
|
|
// if found.
|
|
|
|
if (strncmp(argv[argc - 1], "-psn", 4) == 0)
|
|
|
|
{
|
|
|
|
argc--;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2023-01-20 00:50:39 -07:00
|
|
|
#ifdef __linux__
|
|
|
|
// Qt 6.3+ has a bug which causes mouse inputs to not be registered in our XInput2 code.
|
|
|
|
// If we define QT_XCB_NO_XI2, Qt's xcb platform plugin no longer initializes its XInput
|
|
|
|
// code, which makes mouse inputs work again.
|
|
|
|
// For more information: https://bugs.dolphin-emu.org/issues/12913
|
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
|
2023-03-23 10:17:49 -06:00
|
|
|
setenv("QT_XCB_NO_XI2", "1", true);
|
2023-01-20 00:50:39 -07:00
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2023-05-02 12:15:54 -06:00
|
|
|
// setHighDpiScaleFactorRoundingPolicy was added in 5.14, but default behavior changed in 6.0
|
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
|
|
|
// Set to the previous default behavior
|
|
|
|
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
|
|
|
|
#else
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
|
|
|
#endif
|
|
|
|
|
2017-07-05 18:35:47 -06:00
|
|
|
QCoreApplication::setOrganizationName(QStringLiteral("Dolphin Emulator"));
|
|
|
|
QCoreApplication::setOrganizationDomain(QStringLiteral("dolphin-emu.org"));
|
2018-01-01 05:31:53 -07:00
|
|
|
QCoreApplication::setApplicationName(QStringLiteral("dolphin-emu"));
|
2017-05-30 14:42:21 -06:00
|
|
|
|
2023-04-19 10:40:12 -06:00
|
|
|
// QApplication will parse arguments and remove any it recognizes as targeting Qt
|
2015-11-27 01:33:07 -07:00
|
|
|
QApplication app(argc, argv);
|
2023-04-19 10:40:12 -06:00
|
|
|
|
|
|
|
auto parser = CommandLineParse::CreateParser(CommandLineParse::ParserOptions::IncludeGUIOptions);
|
|
|
|
const optparse::Values& options = CommandLineParse::ParseArguments(parser.get(), argc, argv);
|
|
|
|
const std::vector<std::string> args = parser->args();
|
2015-11-27 01:33:07 -07:00
|
|
|
|
2018-04-29 14:20:03 -06:00
|
|
|
#ifdef _WIN32
|
|
|
|
FreeConsole();
|
|
|
|
#endif
|
|
|
|
|
2017-06-14 06:31:30 -06:00
|
|
|
UICommon::SetUserDirectory(static_cast<const char*>(options.get("user")));
|
2015-11-27 01:33:07 -07:00
|
|
|
UICommon::CreateDirectories();
|
|
|
|
UICommon::Init();
|
|
|
|
Resources::Init();
|
2019-10-27 12:03:10 -06:00
|
|
|
Settings::Instance().SetBatchModeEnabled(options.is_set("batch"));
|
2015-11-27 01:33:07 -07:00
|
|
|
|
2017-07-25 22:11:47 -06:00
|
|
|
// Hook up alerts from core
|
2019-06-16 21:45:37 -06:00
|
|
|
Common::RegisterMsgAlertHandler(QtMsgAlertHandler);
|
2017-07-25 22:11:47 -06:00
|
|
|
|
2017-07-17 15:50:40 -06:00
|
|
|
// Hook up translations
|
|
|
|
Translation::Initialize();
|
|
|
|
|
2016-05-11 19:18:30 -06:00
|
|
|
// Whenever the event loop is about to go to sleep, dispatch the jobs
|
|
|
|
// queued in the Core first.
|
|
|
|
QObject::connect(QAbstractEventDispatcher::instance(), &QAbstractEventDispatcher::aboutToBlock,
|
|
|
|
&app, &Core::HostDispatchJobs);
|
|
|
|
|
2020-05-06 21:10:30 -06:00
|
|
|
std::optional<std::string> save_state_path;
|
|
|
|
if (options.is_set("save_state"))
|
|
|
|
{
|
|
|
|
save_state_path = static_cast<const char*>(options.get("save_state"));
|
|
|
|
}
|
|
|
|
|
2017-10-01 16:09:07 -06:00
|
|
|
std::unique_ptr<BootParameters> boot;
|
2019-10-27 12:03:10 -06:00
|
|
|
bool game_specified = false;
|
2017-11-26 10:32:28 -07:00
|
|
|
if (options.is_set("exec"))
|
|
|
|
{
|
2018-11-05 11:20:45 -07:00
|
|
|
const std::list<std::string> paths_list = options.all("exec");
|
|
|
|
const std::vector<std::string> paths{std::make_move_iterator(std::begin(paths_list)),
|
|
|
|
std::make_move_iterator(std::end(paths_list))};
|
2021-11-20 11:38:09 -07:00
|
|
|
boot = BootParameters::GenerateFromFile(
|
|
|
|
paths, BootSessionData(save_state_path, DeleteSavestateAfterBoot::No));
|
2019-10-27 12:03:10 -06:00
|
|
|
game_specified = true;
|
2017-11-26 10:32:28 -07:00
|
|
|
}
|
|
|
|
else if (options.is_set("nand_title"))
|
2017-10-01 16:09:07 -06:00
|
|
|
{
|
|
|
|
const std::string hex_string = static_cast<const char*>(options.get("nand_title"));
|
|
|
|
if (hex_string.length() == 16)
|
|
|
|
{
|
|
|
|
const u64 title_id = std::stoull(hex_string, nullptr, 16);
|
|
|
|
boot = std::make_unique<BootParameters>(BootParameters::NANDTitle{title_id});
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-03-04 12:49:00 -07:00
|
|
|
ModalMessageBox::critical(nullptr, QObject::tr("Error"), QObject::tr("Invalid title ID."));
|
2017-10-01 16:09:07 -06:00
|
|
|
}
|
2019-10-27 12:03:10 -06:00
|
|
|
game_specified = true;
|
2017-10-01 16:09:07 -06:00
|
|
|
}
|
2018-03-16 02:14:24 -06:00
|
|
|
else if (!args.empty())
|
|
|
|
{
|
2021-11-20 11:38:09 -07:00
|
|
|
boot = BootParameters::GenerateFromFile(
|
|
|
|
args.front(), BootSessionData(save_state_path, DeleteSavestateAfterBoot::No));
|
2019-10-27 12:03:10 -06:00
|
|
|
game_specified = true;
|
2018-03-16 02:14:24 -06:00
|
|
|
}
|
2017-10-01 16:09:07 -06:00
|
|
|
|
2018-03-20 00:41:47 -06:00
|
|
|
int retval;
|
2017-06-22 16:11:53 -06:00
|
|
|
|
2020-05-06 21:10:30 -06:00
|
|
|
if (save_state_path && !game_specified)
|
|
|
|
{
|
|
|
|
ModalMessageBox::critical(
|
|
|
|
nullptr, QObject::tr("Error"),
|
|
|
|
QObject::tr("A save state cannot be loaded without specifying a game to launch."));
|
|
|
|
retval = 1;
|
|
|
|
}
|
|
|
|
else if (Settings::Instance().IsBatchModeEnabled() && !game_specified)
|
2019-10-27 12:03:10 -06:00
|
|
|
{
|
|
|
|
ModalMessageBox::critical(
|
|
|
|
nullptr, QObject::tr("Error"),
|
|
|
|
QObject::tr("Batch mode cannot be used without specifying a game to launch."));
|
|
|
|
retval = 1;
|
|
|
|
}
|
2020-05-06 21:10:30 -06:00
|
|
|
else if (!boot && (Settings::Instance().IsBatchModeEnabled() || save_state_path))
|
2019-10-27 12:03:10 -06:00
|
|
|
{
|
|
|
|
// A game to launch was specified, but it was invalid.
|
|
|
|
// An error has already been shown by code above, so exit without showing another error.
|
|
|
|
retval = 1;
|
|
|
|
}
|
|
|
|
else
|
2016-07-12 23:52:45 -06:00
|
|
|
{
|
2019-06-23 11:26:07 -06:00
|
|
|
DolphinAnalytics::Instance().ReportDolphinStart("qt");
|
2017-05-20 15:41:02 -06:00
|
|
|
|
2019-03-27 07:26:17 -06:00
|
|
|
MainWindow win{std::move(boot), static_cast<const char*>(options.get("movie"))};
|
2021-04-28 10:15:53 -06:00
|
|
|
Settings::Instance().SetCurrentUserStyle(Settings::Instance().GetCurrentUserStyle());
|
2018-05-13 07:03:48 -06:00
|
|
|
win.Show();
|
2017-05-20 15:41:02 -06:00
|
|
|
|
|
|
|
#if defined(USE_ANALYTICS) && USE_ANALYTICS
|
2020-07-20 03:22:53 -06:00
|
|
|
if (!Config::Get(Config::MAIN_ANALYTICS_PERMISSION_ASKED))
|
2017-05-20 15:41:02 -06:00
|
|
|
{
|
2019-03-04 12:49:00 -07:00
|
|
|
ModalMessageBox analytics_prompt(&win);
|
2017-05-20 15:41:02 -06:00
|
|
|
|
|
|
|
analytics_prompt.setIcon(QMessageBox::Question);
|
|
|
|
analytics_prompt.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
2018-09-08 07:19:01 -06:00
|
|
|
analytics_prompt.setWindowTitle(QObject::tr("Allow Usage Statistics Reporting"));
|
2017-07-23 03:41:39 -06:00
|
|
|
analytics_prompt.setText(
|
|
|
|
QObject::tr("Do you authorize Dolphin to report information to Dolphin's developers?"));
|
2017-05-20 15:41:02 -06:00
|
|
|
analytics_prompt.setInformativeText(
|
|
|
|
QObject::tr("If authorized, Dolphin can collect data on its performance, "
|
|
|
|
"feature usage, and configuration, as well as data on your system's "
|
|
|
|
"hardware and operating system.\n\n"
|
|
|
|
"No private data is ever collected. This data helps us understand "
|
|
|
|
"how people and emulated games use Dolphin and prioritize our "
|
|
|
|
"efforts. It also helps us identify rare configurations that are "
|
|
|
|
"causing bugs, performance and stability issues.\n"
|
|
|
|
"This authorization can be revoked at any time through Dolphin's "
|
2017-07-23 03:41:39 -06:00
|
|
|
"settings."));
|
2017-05-20 15:41:02 -06:00
|
|
|
|
|
|
|
const int answer = analytics_prompt.exec();
|
|
|
|
|
2020-07-20 03:22:53 -06:00
|
|
|
Config::SetBase(Config::MAIN_ANALYTICS_PERMISSION_ASKED, true);
|
2018-03-23 16:25:17 -06:00
|
|
|
Settings::Instance().SetAnalyticsEnabled(answer == QMessageBox::Yes);
|
2017-05-20 15:41:02 -06:00
|
|
|
|
2019-06-23 11:26:07 -06:00
|
|
|
DolphinAnalytics::Instance().ReloadConfig();
|
2017-05-20 15:41:02 -06:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-08-11 12:58:04 -06:00
|
|
|
if (!Settings::Instance().IsBatchModeEnabled())
|
|
|
|
{
|
2021-12-27 11:09:47 -07:00
|
|
|
auto* updater = new Updater(&win, Config::Get(Config::MAIN_AUTOUPDATE_UPDATE_TRACK),
|
|
|
|
Config::Get(Config::MAIN_AUTOUPDATE_HASH_OVERRIDE));
|
2019-08-11 12:58:04 -06:00
|
|
|
updater->start();
|
|
|
|
}
|
2018-03-17 18:22:05 -06:00
|
|
|
|
2016-07-12 23:52:45 -06:00
|
|
|
retval = app.exec();
|
|
|
|
}
|
2015-11-27 01:33:07 -07:00
|
|
|
|
|
|
|
Core::Shutdown();
|
|
|
|
UICommon::Shutdown();
|
|
|
|
Host::GetInstance()->deleteLater();
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
2022-03-08 00:22:35 -07:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPWSTR, _In_ int)
|
|
|
|
{
|
2023-05-16 12:23:21 -06:00
|
|
|
std::vector<std::string> args = Common::CommandLineToUtf8Argv(GetCommandLineW());
|
2022-03-08 00:22:35 -07:00
|
|
|
const int argc = static_cast<int>(args.size());
|
|
|
|
std::vector<char*> argv(args.size());
|
|
|
|
for (size_t i = 0; i < args.size(); ++i)
|
|
|
|
argv[i] = args[i].data();
|
|
|
|
|
|
|
|
return main(argc, argv.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef main
|
|
|
|
#endif
|