123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- // Copyright 2008 Dolphin Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #include "DolphinNoGUI/Platform.h"
- #include <OptionParser.h>
- #include <cstddef>
- #include <cstdio>
- #include <cstring>
- #include <signal.h>
- #include <string>
- #include <vector>
- #ifndef _WIN32
- #include <unistd.h>
- #else
- #include <Windows.h>
- #endif
- #include "Common/ScopeGuard.h"
- #include "Common/StringUtil.h"
- #include "Core/Boot/Boot.h"
- #include "Core/BootManager.h"
- #include "Core/Core.h"
- #include "Core/DolphinAnalytics.h"
- #include "Core/Host.h"
- #include "Core/System.h"
- #include "UICommon/CommandLineParse.h"
- #ifdef USE_DISCORD_PRESENCE
- #include "UICommon/DiscordPresence.h"
- #endif
- #include "UICommon/UICommon.h"
- #include "InputCommon/GCAdapter.h"
- #include "VideoCommon/VideoBackendBase.h"
- static std::unique_ptr<Platform> s_platform;
- static void signal_handler(int)
- {
- const char message[] = "A signal was received. A second signal will force Dolphin to stop.\n";
- #ifdef _WIN32
- puts(message);
- #else
- if (write(STDERR_FILENO, message, sizeof(message)) < 0)
- {
- }
- #endif
- s_platform->RequestShutdown();
- }
- std::vector<std::string> Host_GetPreferredLocales()
- {
- return {};
- }
- void Host_PPCSymbolsChanged()
- {
- }
- void Host_PPCBreakpointsChanged()
- {
- }
- void Host_RefreshDSPDebuggerWindow()
- {
- }
- bool Host_UIBlocksControllerState()
- {
- return false;
- }
- static Common::Event s_update_main_frame_event;
- void Host_Message(HostMessageID id)
- {
- if (id == HostMessageID::WMUserStop)
- s_platform->Stop();
- }
- void Host_UpdateTitle(const std::string& title)
- {
- s_platform->SetTitle(title);
- }
- void Host_UpdateDisasmDialog()
- {
- }
- void Host_JitCacheInvalidation()
- {
- }
- void Host_JitProfileDataWiped()
- {
- }
- void Host_UpdateMainFrame()
- {
- s_update_main_frame_event.Set();
- }
- void Host_RequestRenderWindowSize(int width, int height)
- {
- }
- bool Host_RendererHasFocus()
- {
- return s_platform->IsWindowFocused();
- }
- bool Host_RendererHasFullFocus()
- {
- // Mouse capturing isn't implemented
- return Host_RendererHasFocus();
- }
- bool Host_RendererIsFullscreen()
- {
- return s_platform->IsWindowFullscreen();
- }
- bool Host_TASInputHasFocus()
- {
- return false;
- }
- void Host_YieldToUI()
- {
- }
- void Host_TitleChanged()
- {
- #ifdef USE_DISCORD_PRESENCE
- Discord::UpdateDiscordPresence();
- #endif
- }
- 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
- }
- std::unique_ptr<GBAHostInterface> Host_CreateGBAHost(std::weak_ptr<HW::GBA::Core> core)
- {
- return nullptr;
- }
- static std::unique_ptr<Platform> GetPlatform(const optparse::Values& options)
- {
- std::string platform_name = static_cast<const char*>(options.get("platform"));
- #if HAVE_X11
- if (platform_name == "x11" || platform_name.empty())
- return Platform::CreateX11Platform();
- #endif
- #ifdef __linux__
- if (platform_name == "fbdev" || platform_name.empty())
- return Platform::CreateFBDevPlatform();
- #endif
- #ifdef _WIN32
- if (platform_name == "win32" || platform_name.empty())
- return Platform::CreateWin32Platform();
- #endif
- #ifdef __APPLE__
- if (platform_name == "macos" || platform_name.empty())
- return Platform::CreateMacOSPlatform();
- #endif
- if (platform_name == "headless" || platform_name.empty())
- return Platform::CreateHeadlessPlatform();
- return nullptr;
- }
- #ifdef _WIN32
- #define main app_main
- #endif
- int main(int argc, char* argv[])
- {
- Core::DeclareAsHostThread();
- auto parser = CommandLineParse::CreateParser(CommandLineParse::ParserOptions::OmitGUIOptions);
- parser->add_option("-p", "--platform")
- .action("store")
- .help("Window platform to use [%choices]")
- .choices({
- "headless"
- #ifdef __linux__
- ,
- "fbdev"
- #endif
- #if HAVE_X11
- ,
- "x11"
- #endif
- #ifdef _WIN32
- ,
- "win32"
- #endif
- #ifdef __APPLE__
- ,
- "macos"
- #endif
- });
- optparse::Values& options = CommandLineParse::ParseArguments(parser.get(), argc, argv);
- std::vector<std::string> args = parser->args();
- std::optional<std::string> save_state_path;
- if (options.is_set("save_state"))
- {
- save_state_path = static_cast<const char*>(options.get("save_state"));
- }
- std::unique_ptr<BootParameters> boot;
- bool game_specified = false;
- if (options.is_set("exec"))
- {
- 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))};
- boot = BootParameters::GenerateFromFile(
- paths, BootSessionData(save_state_path, DeleteSavestateAfterBoot::No));
- game_specified = true;
- }
- 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});
- }
- else if (args.size())
- {
- boot = BootParameters::GenerateFromFile(
- args.front(), BootSessionData(save_state_path, DeleteSavestateAfterBoot::No));
- args.erase(args.begin());
- game_specified = true;
- }
- else
- {
- parser->print_help();
- return 0;
- }
- std::string user_directory;
- if (options.is_set("user"))
- user_directory = static_cast<const char*>(options.get("user"));
- s_platform = GetPlatform(options);
- if (!s_platform || !s_platform->Init())
- {
- fprintf(stderr, "No platform found, or failed to initialize.\n");
- return 1;
- }
- const WindowSystemInfo wsi = s_platform->GetWindowSystemInfo();
- UICommon::SetUserDirectory(user_directory);
- UICommon::Init();
- UICommon::InitControllers(wsi);
- Common::ScopeGuard ui_common_guard([] {
- UICommon::ShutdownControllers();
- UICommon::Shutdown();
- });
- if (save_state_path && !game_specified)
- {
- fprintf(stderr, "A save state cannot be loaded without specifying a game to launch.\n");
- return 1;
- }
- Core::AddOnStateChangedCallback([](Core::State state) {
- if (state == Core::State::Uninitialized)
- s_platform->Stop();
- });
- #ifdef _WIN32
- signal(SIGINT, signal_handler);
- signal(SIGTERM, signal_handler);
- #else
- // 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);
- #endif
- DolphinAnalytics::Instance().ReportDolphinStart("nogui");
- if (!BootManager::BootCore(Core::System::GetInstance(), std::move(boot), wsi))
- {
- fprintf(stderr, "Could not boot the specified file\n");
- return 1;
- }
- #ifdef USE_DISCORD_PRESENCE
- Discord::UpdateDiscordPresence();
- #endif
- s_platform->MainLoop();
- Core::Stop(Core::System::GetInstance());
- Core::Shutdown(Core::System::GetInstance());
- s_platform.reset();
- return 0;
- }
- #ifdef _WIN32
- int wmain(int, wchar_t*[], wchar_t*[])
- {
- std::vector<std::string> args = Common::CommandLineToUtf8Argv(GetCommandLineW());
- 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
|