2015-05-24 06:55:12 +02:00
|
|
|
// Copyright 2008 Dolphin Emulator Project
|
2021-07-05 03:22:19 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2009-07-13 09:31:43 +02:00
|
|
|
|
2018-10-24 07:50:54 +02:00
|
|
|
#include "DolphinNoGUI/Platform.h"
|
|
|
|
|
2016-01-17 12:13:09 +01:00
|
|
|
#include <OptionParser.h>
|
2014-02-22 23:36:30 +01:00
|
|
|
#include <cstddef>
|
2014-02-17 11:18:15 +01:00
|
|
|
#include <cstdio>
|
2014-02-22 23:36:30 +01:00
|
|
|
#include <cstring>
|
2016-09-24 21:17:34 +02:00
|
|
|
#include <signal.h>
|
2014-03-12 20:33:41 +01:00
|
|
|
#include <string>
|
2020-12-27 23:11:22 +01:00
|
|
|
#include <vector>
|
|
|
|
|
2018-10-24 07:50:54 +02:00
|
|
|
#ifndef _WIN32
|
2015-09-04 08:14:10 +02:00
|
|
|
#include <unistd.h>
|
2019-11-26 05:25:19 +01:00
|
|
|
#else
|
|
|
|
#include <Windows.h>
|
2018-10-24 07:50:54 +02:00
|
|
|
#endif
|
2009-05-15 10:55:46 +02:00
|
|
|
|
2022-06-25 17:31:31 +02:00
|
|
|
#include "Common/ScopeGuard.h"
|
2019-11-26 05:25:19 +01:00
|
|
|
#include "Common/StringUtil.h"
|
2017-05-27 15:43:40 +02:00
|
|
|
#include "Core/Boot/Boot.h"
|
2014-02-19 02:56:29 +01:00
|
|
|
#include "Core/BootManager.h"
|
|
|
|
#include "Core/Core.h"
|
2020-09-15 12:13:10 +02:00
|
|
|
#include "Core/DolphinAnalytics.h"
|
2014-08-02 08:21:03 +02:00
|
|
|
#include "Core/Host.h"
|
2024-03-02 08:39:04 +01:00
|
|
|
#include "Core/System.h"
|
2014-02-19 02:56:29 +01:00
|
|
|
|
2016-01-17 12:13:09 +01:00
|
|
|
#include "UICommon/CommandLineParse.h"
|
2018-06-08 22:56:11 +02:00
|
|
|
#ifdef USE_DISCORD_PRESENCE
|
|
|
|
#include "UICommon/DiscordPresence.h"
|
|
|
|
#endif
|
2014-10-04 21:12:15 +02:00
|
|
|
#include "UICommon/UICommon.h"
|
|
|
|
|
2022-03-08 22:16:54 +01:00
|
|
|
#include "InputCommon/GCAdapter.h"
|
|
|
|
|
2014-02-19 02:56:29 +01:00
|
|
|
#include "VideoCommon/VideoBackendBase.h"
|
|
|
|
|
2018-10-24 07:50:54 +02:00
|
|
|
static std::unique_ptr<Platform> s_platform;
|
2016-09-24 21:17:34 +02:00
|
|
|
|
|
|
|
static void signal_handler(int)
|
|
|
|
{
|
|
|
|
const char message[] = "A signal was received. A second signal will force Dolphin to stop.\n";
|
2018-10-24 07:50:54 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
puts(message);
|
|
|
|
#else
|
2016-09-24 21:17:34 +02:00
|
|
|
if (write(STDERR_FILENO, message, sizeof(message)) < 0)
|
|
|
|
{
|
|
|
|
}
|
2018-10-24 07:50:54 +02:00
|
|
|
#endif
|
2016-10-01 21:35:29 +02:00
|
|
|
|
2018-10-24 07:50:54 +02:00
|
|
|
s_platform->RequestShutdown();
|
2016-09-24 21:17:34 +02:00
|
|
|
}
|
2010-05-26 23:23:44 +02:00
|
|
|
|
2020-12-27 23:11:22 +01:00
|
|
|
std::vector<std::string> Host_GetPreferredLocales()
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2024-03-17 07:05:56 +01:00
|
|
|
void Host_PPCSymbolsChanged()
|
2011-02-08 16:36:15 +01:00
|
|
|
{
|
|
|
|
}
|
2020-12-27 23:11:22 +01:00
|
|
|
|
2011-02-08 16:36:15 +01:00
|
|
|
void Host_RefreshDSPDebuggerWindow()
|
|
|
|
{
|
|
|
|
}
|
2008-09-07 22:26:38 +02:00
|
|
|
|
2019-03-18 17:30:33 +01:00
|
|
|
bool Host_UIBlocksControllerState()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-10-24 07:50:54 +02:00
|
|
|
static Common::Event s_update_main_frame_event;
|
2018-05-28 19:03:29 +02:00
|
|
|
void Host_Message(HostMessageID id)
|
2010-03-16 14:18:52 +01:00
|
|
|
{
|
2018-05-28 19:03:29 +02:00
|
|
|
if (id == HostMessageID::WMUserStop)
|
2018-10-24 07:50:54 +02:00
|
|
|
s_platform->Stop();
|
2010-03-16 14:18:52 +01:00
|
|
|
}
|
2008-09-07 22:26:38 +02:00
|
|
|
|
2014-08-06 06:44:21 +02:00
|
|
|
void Host_UpdateTitle(const std::string& title)
|
|
|
|
{
|
2018-10-24 07:50:54 +02:00
|
|
|
s_platform->SetTitle(title);
|
2014-08-06 06:44:21 +02:00
|
|
|
}
|
2010-04-19 05:06:18 +02:00
|
|
|
|
2008-09-07 22:26:38 +02:00
|
|
|
void Host_UpdateDisasmDialog()
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2008-09-07 22:26:38 +02:00
|
|
|
}
|
|
|
|
|
2024-10-24 04:30:22 +02:00
|
|
|
void Host_JitCacheInvalidation()
|
2024-04-16 06:52:40 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Host_JitProfileDataWiped()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2008-09-07 23:06:55 +02:00
|
|
|
void Host_UpdateMainFrame()
|
|
|
|
{
|
2018-10-24 07:50:54 +02:00
|
|
|
s_update_main_frame_event.Set();
|
2008-09-07 23:06:55 +02:00
|
|
|
}
|
2008-09-07 22:26:38 +02:00
|
|
|
|
2011-01-25 13:52:20 +01:00
|
|
|
void Host_RequestRenderWindowSize(int width, int height)
|
|
|
|
{
|
|
|
|
}
|
2014-07-16 15:53:33 +02:00
|
|
|
|
2010-04-12 03:33:10 +02:00
|
|
|
bool Host_RendererHasFocus()
|
|
|
|
{
|
2018-10-24 07:50:54 +02:00
|
|
|
return s_platform->IsWindowFocused();
|
2010-04-12 03:33:10 +02:00
|
|
|
}
|
2008-09-07 22:26:38 +02:00
|
|
|
|
2021-05-09 12:28:04 +02:00
|
|
|
bool Host_RendererHasFullFocus()
|
|
|
|
{
|
|
|
|
// Mouse capturing isn't implemented
|
|
|
|
return Host_RendererHasFocus();
|
|
|
|
}
|
|
|
|
|
2015-01-04 17:09:56 +01:00
|
|
|
bool Host_RendererIsFullscreen()
|
|
|
|
{
|
2018-10-24 07:50:54 +02:00
|
|
|
return s_platform->IsWindowFullscreen();
|
2015-01-04 17:09:56 +01:00
|
|
|
}
|
|
|
|
|
2024-05-27 01:50:12 +02:00
|
|
|
bool Host_TASInputHasFocus()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-11-10 17:55:21 +01:00
|
|
|
void Host_YieldToUI()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-02-14 03:31:31 +01:00
|
|
|
void Host_TitleChanged()
|
|
|
|
{
|
|
|
|
#ifdef USE_DISCORD_PRESENCE
|
|
|
|
Discord::UpdateDiscordPresence();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-08-03 06:46:11 +02:00
|
|
|
void Host_UpdateDiscordClientID(const std::string& client_id)
|
|
|
|
{
|
|
|
|
#ifdef USE_DISCORD_PRESENCE
|
|
|
|
Discord::UpdateClientID(client_id);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Host_UpdateDiscordPresenceRaw(const std::string& details, const std::string& state,
|
|
|
|
const std::string& large_image_key,
|
|
|
|
const std::string& large_image_text,
|
|
|
|
const std::string& small_image_key,
|
|
|
|
const std::string& small_image_text,
|
|
|
|
const int64_t start_timestamp, const int64_t end_timestamp,
|
|
|
|
const int party_size, const int party_max)
|
|
|
|
{
|
|
|
|
#ifdef USE_DISCORD_PRESENCE
|
|
|
|
return Discord::UpdateDiscordPresenceRaw(details, state, large_image_key, large_image_text,
|
|
|
|
small_image_key, small_image_text, start_timestamp,
|
|
|
|
end_timestamp, party_size, party_max);
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-07-04 13:09:46 +02:00
|
|
|
std::unique_ptr<GBAHostInterface> Host_CreateGBAHost(std::weak_ptr<HW::GBA::Core> core)
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-10-24 07:50:54 +02:00
|
|
|
static std::unique_ptr<Platform> GetPlatform(const optparse::Values& options)
|
2014-08-06 06:34:31 +02:00
|
|
|
{
|
2018-10-24 07:50:54 +02:00
|
|
|
std::string platform_name = static_cast<const char*>(options.get("platform"));
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-10-24 07:50:54 +02:00
|
|
|
#if HAVE_X11
|
|
|
|
if (platform_name == "x11" || platform_name.empty())
|
|
|
|
return Platform::CreateX11Platform();
|
2010-04-22 06:28:34 +02:00
|
|
|
#endif
|
2010-11-25 03:26:46 +01:00
|
|
|
|
2019-04-10 16:44:21 +02:00
|
|
|
#ifdef __linux__
|
|
|
|
if (platform_name == "fbdev" || platform_name.empty())
|
|
|
|
return Platform::CreateFBDevPlatform();
|
|
|
|
#endif
|
|
|
|
|
2019-11-26 05:25:19 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
if (platform_name == "win32" || platform_name.empty())
|
|
|
|
return Platform::CreateWin32Platform();
|
|
|
|
#endif
|
2023-02-02 01:00:12 +01:00
|
|
|
#ifdef __APPLE__
|
|
|
|
if (platform_name == "macos" || platform_name.empty())
|
|
|
|
return Platform::CreateMacOSPlatform();
|
|
|
|
#endif
|
2019-11-26 05:25:19 +01:00
|
|
|
|
2018-10-24 07:50:54 +02:00
|
|
|
if (platform_name == "headless" || platform_name.empty())
|
|
|
|
return Platform::CreateHeadlessPlatform();
|
2009-01-12 21:52:45 +01:00
|
|
|
|
2014-08-06 06:34:31 +02:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-03-08 08:22:35 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
#define main app_main
|
|
|
|
#endif
|
|
|
|
|
2014-08-06 06:34:31 +02:00
|
|
|
int main(int argc, char* argv[])
|
|
|
|
{
|
2023-06-02 18:00:51 +02:00
|
|
|
Core::DeclareAsHostThread();
|
|
|
|
|
2016-01-17 12:13:09 +01:00
|
|
|
auto parser = CommandLineParse::CreateParser(CommandLineParse::ParserOptions::OmitGUIOptions);
|
2018-10-24 07:50:54 +02:00
|
|
|
parser->add_option("-p", "--platform")
|
|
|
|
.action("store")
|
|
|
|
.help("Window platform to use [%choices]")
|
|
|
|
.choices({
|
|
|
|
"headless"
|
2019-04-10 16:44:21 +02:00
|
|
|
#ifdef __linux__
|
|
|
|
,
|
|
|
|
"fbdev"
|
|
|
|
#endif
|
2018-10-24 07:50:54 +02:00
|
|
|
#if HAVE_X11
|
|
|
|
,
|
|
|
|
"x11"
|
2019-11-26 05:25:19 +01:00
|
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
|
|
,
|
|
|
|
"win32"
|
2023-02-02 01:00:12 +01:00
|
|
|
#endif
|
|
|
|
#ifdef __APPLE__
|
|
|
|
,
|
|
|
|
"macos"
|
2018-10-24 07:50:54 +02:00
|
|
|
#endif
|
|
|
|
});
|
|
|
|
|
2016-01-17 12:13:09 +01:00
|
|
|
optparse::Values& options = CommandLineParse::ParseArguments(parser.get(), argc, argv);
|
|
|
|
std::vector<std::string> args = parser->args();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2020-05-07 05:10:30 +02: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-02 00:09:07 +02:00
|
|
|
std::unique_ptr<BootParameters> boot;
|
2020-05-07 05:10:30 +02:00
|
|
|
bool game_specified = false;
|
2016-01-17 12:13:09 +01:00
|
|
|
if (options.is_set("exec"))
|
2011-01-15 11:33:07 +01:00
|
|
|
{
|
2018-11-05 19:20:45 +01: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 19:38:09 +01:00
|
|
|
boot = BootParameters::GenerateFromFile(
|
|
|
|
paths, BootSessionData(save_state_path, DeleteSavestateAfterBoot::No));
|
2020-05-07 05:10:30 +02:00
|
|
|
game_specified = true;
|
2017-10-02 00:09:07 +02:00
|
|
|
}
|
|
|
|
else if (options.is_set("nand_title"))
|
|
|
|
{
|
|
|
|
const std::string hex_string = static_cast<const char*>(options.get("nand_title"));
|
|
|
|
if (hex_string.length() != 16)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Invalid title ID\n");
|
|
|
|
parser->print_help();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
const u64 title_id = std::stoull(hex_string, nullptr, 16);
|
|
|
|
boot = std::make_unique<BootParameters>(BootParameters::NANDTitle{title_id});
|
2011-01-15 11:33:07 +01:00
|
|
|
}
|
2016-01-17 12:13:09 +01:00
|
|
|
else if (args.size())
|
2011-01-15 11:33:07 +01:00
|
|
|
{
|
2021-11-20 19:38:09 +01:00
|
|
|
boot = BootParameters::GenerateFromFile(
|
|
|
|
args.front(), BootSessionData(save_state_path, DeleteSavestateAfterBoot::No));
|
2016-01-17 12:13:09 +01:00
|
|
|
args.erase(args.begin());
|
2020-05-07 05:10:30 +02:00
|
|
|
game_specified = true;
|
2016-01-17 12:13:09 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parser->print_help();
|
|
|
|
return 0;
|
2008-09-07 23:02:57 +02:00
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2017-02-28 20:36:11 +01:00
|
|
|
std::string user_directory;
|
|
|
|
if (options.is_set("user"))
|
|
|
|
user_directory = static_cast<const char*>(options.get("user"));
|
|
|
|
|
2018-10-24 07:50:54 +02:00
|
|
|
s_platform = GetPlatform(options);
|
|
|
|
if (!s_platform || !s_platform->Init())
|
2014-08-06 06:34:31 +02:00
|
|
|
{
|
2018-10-24 07:50:54 +02:00
|
|
|
fprintf(stderr, "No platform found, or failed to initialize.\n");
|
2014-08-06 06:34:31 +02:00
|
|
|
return 1;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2022-06-25 17:31:31 +02:00
|
|
|
const WindowSystemInfo wsi = s_platform->GetWindowSystemInfo();
|
|
|
|
|
|
|
|
UICommon::SetUserDirectory(user_directory);
|
|
|
|
UICommon::Init();
|
|
|
|
UICommon::InitControllers(wsi);
|
|
|
|
|
|
|
|
Common::ScopeGuard ui_common_guard([] {
|
|
|
|
UICommon::ShutdownControllers();
|
|
|
|
UICommon::Shutdown();
|
|
|
|
});
|
|
|
|
|
2020-05-07 05:10:30 +02:00
|
|
|
if (save_state_path && !game_specified)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "A save state cannot be loaded without specifying a game to launch.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-12-31 13:03:20 +01:00
|
|
|
Core::AddOnStateChangedCallback([](Core::State state) {
|
2017-09-04 19:57:42 +02:00
|
|
|
if (state == Core::State::Uninitialized)
|
2018-10-24 07:50:54 +02:00
|
|
|
s_platform->Stop();
|
2017-09-04 19:57:42 +02:00
|
|
|
});
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2019-11-26 05:25:19 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
signal(SIGINT, signal_handler);
|
|
|
|
signal(SIGTERM, signal_handler);
|
|
|
|
#else
|
2016-09-24 21:17:34 +02:00
|
|
|
// Shut down cleanly on SIGINT and SIGTERM
|
|
|
|
struct sigaction sa;
|
|
|
|
sa.sa_handler = signal_handler;
|
|
|
|
sigemptyset(&sa.sa_mask);
|
|
|
|
sa.sa_flags = SA_RESETHAND;
|
|
|
|
sigaction(SIGINT, &sa, nullptr);
|
|
|
|
sigaction(SIGTERM, &sa, nullptr);
|
2019-11-26 05:25:19 +01:00
|
|
|
#endif
|
2016-09-24 21:17:34 +02:00
|
|
|
|
2019-06-23 19:26:07 +02:00
|
|
|
DolphinAnalytics::Instance().ReportDolphinStart("nogui");
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2024-03-02 08:39:04 +01:00
|
|
|
if (!BootManager::BootCore(Core::System::GetInstance(), std::move(boot), wsi))
|
2014-08-06 06:34:31 +02:00
|
|
|
{
|
2017-10-02 00:09:07 +02:00
|
|
|
fprintf(stderr, "Could not boot the specified file\n");
|
2014-08-06 06:34:31 +02:00
|
|
|
return 1;
|
|
|
|
}
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2018-06-08 22:56:11 +02:00
|
|
|
#ifdef USE_DISCORD_PRESENCE
|
|
|
|
Discord::UpdateDiscordPresence();
|
|
|
|
#endif
|
|
|
|
|
2018-10-24 07:50:54 +02:00
|
|
|
s_platform->MainLoop();
|
2024-03-18 09:35:42 +01:00
|
|
|
Core::Stop(Core::System::GetInstance());
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2024-03-18 09:35:42 +01:00
|
|
|
Core::Shutdown(Core::System::GetInstance());
|
2018-10-24 07:50:54 +02:00
|
|
|
s_platform.reset();
|
2016-06-24 10:43:46 +02:00
|
|
|
|
2011-01-15 11:33:07 +01:00
|
|
|
return 0;
|
2008-09-07 22:26:38 +02:00
|
|
|
}
|
2022-03-08 08:22:35 +01:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
int wmain(int, wchar_t*[], wchar_t*[])
|
|
|
|
{
|
2023-05-16 20:23:21 +02:00
|
|
|
std::vector<std::string> args = Common::CommandLineToUtf8Argv(GetCommandLineW());
|
2022-03-08 08:22:35 +01: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
|