2015-05-23 22:55:12 -06:00
|
|
|
// Copyright 2008 Dolphin Emulator Project
|
2015-05-17 17:08:10 -06:00
|
|
|
// Licensed under GPLv2+
|
2013-04-17 21:43:35 -06:00
|
|
|
// Refer to the license.txt file included.
|
2009-07-13 01:31:43 -06:00
|
|
|
|
2016-01-17 04:13:09 -07:00
|
|
|
#include <OptionParser.h>
|
2014-02-22 15:36:30 -07:00
|
|
|
#include <cstddef>
|
2014-02-17 03:18:15 -07:00
|
|
|
#include <cstdio>
|
2014-02-22 15:36:30 -07:00
|
|
|
#include <cstring>
|
2016-09-24 13:17:34 -06:00
|
|
|
#include <signal.h>
|
2014-03-12 13:33:41 -06:00
|
|
|
#include <string>
|
2016-05-11 19:18:30 -06:00
|
|
|
#include <thread>
|
2015-09-04 00:14:10 -06:00
|
|
|
#include <unistd.h>
|
2008-09-07 14:26:38 -06:00
|
|
|
|
2014-09-07 19:06:58 -06:00
|
|
|
#include "Common/CommonTypes.h"
|
2014-04-13 17:15:23 -06:00
|
|
|
#include "Common/Event.h"
|
2016-09-24 13:17:34 -06:00
|
|
|
#include "Common/Flag.h"
|
2014-06-05 17:29:54 -06:00
|
|
|
#include "Common/Logging/LogManager.h"
|
2016-06-24 02:43:46 -06:00
|
|
|
#include "Common/MsgHandler.h"
|
2009-05-15 02:55:46 -06:00
|
|
|
|
2016-06-17 18:43:59 -06:00
|
|
|
#include "Core/Analytics.h"
|
2017-05-27 07:43:40 -06:00
|
|
|
#include "Core/Boot/Boot.h"
|
2014-02-18 18:56:29 -07:00
|
|
|
#include "Core/BootManager.h"
|
|
|
|
#include "Core/ConfigManager.h"
|
|
|
|
#include "Core/Core.h"
|
2016-06-24 02:43:46 -06:00
|
|
|
#include "Core/Host.h"
|
2017-04-29 08:11:22 -06:00
|
|
|
#include "Core/IOS/IOS.h"
|
2017-01-18 05:50:28 -07:00
|
|
|
#include "Core/IOS/STM/STM.h"
|
2016-06-24 02:43:46 -06:00
|
|
|
#include "Core/State.h"
|
2014-02-18 18:56:29 -07:00
|
|
|
|
2016-01-17 04:13:09 -07:00
|
|
|
#include "UICommon/CommandLineParse.h"
|
2014-10-04 13:12:15 -06:00
|
|
|
#include "UICommon/UICommon.h"
|
|
|
|
|
2016-11-19 06:03:20 -07:00
|
|
|
#include "VideoCommon/RenderBase.h"
|
2014-02-18 18:56:29 -07:00
|
|
|
#include "VideoCommon/VideoBackendBase.h"
|
|
|
|
|
2014-08-06 19:24:42 -06:00
|
|
|
static bool rendererHasFocus = true;
|
2015-01-04 09:09:56 -07:00
|
|
|
static bool rendererIsFullscreen = false;
|
2016-09-24 13:17:34 -06:00
|
|
|
static Common::Flag s_running{true};
|
2016-10-01 13:35:29 -06:00
|
|
|
static Common::Flag s_shutdown_requested{false};
|
|
|
|
static Common::Flag s_tried_graceful_shutdown{false};
|
2016-09-24 13:17:34 -06:00
|
|
|
|
|
|
|
static void signal_handler(int)
|
|
|
|
{
|
|
|
|
const char message[] = "A signal was received. A second signal will force Dolphin to stop.\n";
|
|
|
|
if (write(STDERR_FILENO, message, sizeof(message)) < 0)
|
|
|
|
{
|
|
|
|
}
|
2016-10-01 13:35:29 -06:00
|
|
|
s_shutdown_requested.Set();
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace ProcessorInterface
|
|
|
|
{
|
|
|
|
void PowerButton_Tap();
|
2016-09-24 13:17:34 -06:00
|
|
|
}
|
2010-05-26 15:23:44 -06:00
|
|
|
|
2014-08-05 22:34:31 -06:00
|
|
|
class Platform
|
|
|
|
{
|
|
|
|
public:
|
2016-06-24 02:43:46 -06:00
|
|
|
virtual void Init() {}
|
|
|
|
virtual void SetTitle(const std::string& title) {}
|
|
|
|
virtual void MainLoop()
|
|
|
|
{
|
2016-09-24 13:17:34 -06:00
|
|
|
while (s_running.IsSet())
|
2016-06-24 02:43:46 -06:00
|
|
|
{
|
|
|
|
Core::HostDispatchJobs();
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
virtual void Shutdown() {}
|
|
|
|
virtual ~Platform() {}
|
2014-08-05 22:34:31 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
static Platform* platform;
|
|
|
|
|
2016-06-24 02:43:46 -06:00
|
|
|
void Host_NotifyMapLoaded()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void Host_RefreshDSPDebuggerWindow()
|
|
|
|
{
|
|
|
|
}
|
2008-09-07 14:26:38 -06:00
|
|
|
|
2014-08-06 19:24:42 -06:00
|
|
|
static Common::Event updateMainFrameEvent;
|
2010-03-16 07:18:52 -06:00
|
|
|
void Host_Message(int Id)
|
|
|
|
{
|
2016-06-24 02:43:46 -06:00
|
|
|
if (Id == WM_USER_STOP)
|
2016-09-24 13:17:34 -06:00
|
|
|
s_running.Clear();
|
2018-01-25 21:41:57 -07:00
|
|
|
if (Id == WM_USER_JOB_DISPATCH || Id == WM_USER_STOP)
|
2016-06-24 02:43:46 -06:00
|
|
|
updateMainFrameEvent.Set();
|
2010-03-16 07:18:52 -06:00
|
|
|
}
|
2008-09-07 14:26:38 -06:00
|
|
|
|
2016-01-26 06:35:17 -07:00
|
|
|
static void* s_window_handle = nullptr;
|
2011-02-12 14:25:49 -07:00
|
|
|
void* Host_GetRenderHandle()
|
|
|
|
{
|
2016-06-24 02:43:46 -06:00
|
|
|
return s_window_handle;
|
2011-02-12 14:25:49 -07:00
|
|
|
}
|
|
|
|
|
2014-08-05 22:44:21 -06:00
|
|
|
void Host_UpdateTitle(const std::string& title)
|
|
|
|
{
|
2016-06-24 02:43:46 -06:00
|
|
|
platform->SetTitle(title);
|
2014-08-05 22:44:21 -06:00
|
|
|
}
|
2010-04-18 21:06:18 -06:00
|
|
|
|
2016-06-24 02:43:46 -06:00
|
|
|
void Host_UpdateDisasmDialog()
|
|
|
|
{
|
|
|
|
}
|
2008-09-07 14:26:38 -06:00
|
|
|
|
2008-09-07 15:06:55 -06:00
|
|
|
void Host_UpdateMainFrame()
|
|
|
|
{
|
2016-06-24 02:43:46 -06:00
|
|
|
updateMainFrameEvent.Set();
|
2008-09-07 15:06:55 -06:00
|
|
|
}
|
2008-09-07 14:26:38 -06:00
|
|
|
|
2016-06-24 02:43:46 -06:00
|
|
|
void Host_RequestRenderWindowSize(int width, int height)
|
|
|
|
{
|
|
|
|
}
|
2014-07-16 07:53:33 -06:00
|
|
|
|
2017-07-12 01:11:29 -06:00
|
|
|
bool Host_UINeedsControllerState()
|
2014-07-16 08:24:40 -06:00
|
|
|
{
|
2016-06-24 02:43:46 -06:00
|
|
|
return false;
|
2014-07-16 08:24:40 -06:00
|
|
|
}
|
|
|
|
|
2010-04-11 19:33:10 -06:00
|
|
|
bool Host_RendererHasFocus()
|
|
|
|
{
|
2016-06-24 02:43:46 -06:00
|
|
|
return rendererHasFocus;
|
2010-04-11 19:33:10 -06:00
|
|
|
}
|
2008-09-07 14:26:38 -06:00
|
|
|
|
2015-01-04 09:09:56 -07:00
|
|
|
bool Host_RendererIsFullscreen()
|
|
|
|
{
|
2016-06-24 02:43:46 -06:00
|
|
|
return rendererIsFullscreen;
|
2015-01-04 09:09:56 -07:00
|
|
|
}
|
|
|
|
|
2016-06-26 00:06:23 -06:00
|
|
|
void Host_ShowVideoConfig(void*, const std::string&)
|
2016-06-24 02:43:46 -06:00
|
|
|
{
|
|
|
|
}
|
2014-08-02 00:21:03 -06:00
|
|
|
|
2016-11-10 09:55:21 -07:00
|
|
|
void Host_YieldToUI()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-06-25 09:35:24 -06:00
|
|
|
void Host_UpdateProgressDialog(const char* caption, int position, int total)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-01-19 09:11:07 -07:00
|
|
|
#if HAVE_X11
|
2018-01-19 14:31:44 -07:00
|
|
|
#include <X11/Xatom.h>
|
2017-06-13 03:46:13 -06:00
|
|
|
#include <X11/Xlib.h>
|
2014-08-05 22:34:31 -06:00
|
|
|
#include <X11/keysym.h>
|
2016-05-05 17:43:38 -06:00
|
|
|
#include "UICommon/X11Utils.h"
|
2018-01-19 14:31:44 -07:00
|
|
|
#ifndef HOST_NAME_MAX
|
|
|
|
#include <climits>
|
|
|
|
#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
|
|
|
|
#endif
|
2010-11-24 19:26:46 -07:00
|
|
|
|
2014-08-05 22:34:31 -06:00
|
|
|
class PlatformX11 : public Platform
|
|
|
|
{
|
2016-06-24 02:43:46 -06:00
|
|
|
Display* dpy;
|
|
|
|
Window win;
|
|
|
|
Cursor blankCursor = None;
|
2010-04-21 22:28:34 -06:00
|
|
|
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
2016-06-24 02:43:46 -06:00
|
|
|
X11Utils::XRRConfiguration* XRRConfig;
|
2010-04-21 22:28:34 -06:00
|
|
|
#endif
|
|
|
|
|
2016-06-24 02:43:46 -06:00
|
|
|
void Init() override
|
|
|
|
{
|
|
|
|
XInitThreads();
|
|
|
|
dpy = XOpenDisplay(nullptr);
|
|
|
|
if (!dpy)
|
|
|
|
{
|
|
|
|
PanicAlert("No X11 display found");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), SConfig::GetInstance().iRenderWindowXPos,
|
|
|
|
SConfig::GetInstance().iRenderWindowYPos,
|
|
|
|
SConfig::GetInstance().iRenderWindowWidth,
|
|
|
|
SConfig::GetInstance().iRenderWindowHeight, 0, 0, BlackPixel(dpy, 0));
|
2016-11-19 06:03:20 -07:00
|
|
|
XSelectInput(dpy, win, StructureNotifyMask | KeyPressMask | FocusChangeMask);
|
2016-06-24 02:43:46 -06:00
|
|
|
Atom wmProtocols[1];
|
|
|
|
wmProtocols[0] = XInternAtom(dpy, "WM_DELETE_WINDOW", True);
|
|
|
|
XSetWMProtocols(dpy, win, wmProtocols, 1);
|
2018-01-19 14:31:44 -07:00
|
|
|
pid_t pid = getpid();
|
|
|
|
XChangeProperty(dpy, win, XInternAtom(dpy, "_NET_WM_PID", False), XA_CARDINAL, 32,
|
|
|
|
PropModeReplace, reinterpret_cast<unsigned char*>(&pid), 1);
|
|
|
|
char host_name[HOST_NAME_MAX] = "";
|
|
|
|
if (!gethostname(host_name, sizeof(host_name)))
|
|
|
|
{
|
|
|
|
XTextProperty wmClientMachine = {reinterpret_cast<unsigned char*>(host_name), XA_STRING, 8,
|
|
|
|
strlen(host_name)};
|
|
|
|
XSetWMClientMachine(dpy, win, &wmClientMachine);
|
|
|
|
}
|
2016-06-24 02:43:46 -06:00
|
|
|
XMapRaised(dpy, win);
|
|
|
|
XFlush(dpy);
|
|
|
|
s_window_handle = (void*)win;
|
|
|
|
|
|
|
|
if (SConfig::GetInstance().bDisableScreenSaver)
|
|
|
|
X11Utils::InhibitScreensaver(dpy, win, true);
|
2014-08-05 22:34:31 -06:00
|
|
|
|
2010-04-21 22:28:34 -06:00
|
|
|
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
2016-06-24 02:43:46 -06:00
|
|
|
XRRConfig = new X11Utils::XRRConfiguration(dpy, win);
|
2010-04-21 22:28:34 -06:00
|
|
|
#endif
|
|
|
|
|
2016-06-24 02:43:46 -06:00
|
|
|
if (SConfig::GetInstance().bHideCursor)
|
|
|
|
{
|
|
|
|
// make a blank cursor
|
|
|
|
Pixmap Blank;
|
|
|
|
XColor DummyColor;
|
|
|
|
char ZeroData[1] = {0};
|
|
|
|
Blank = XCreateBitmapFromData(dpy, win, ZeroData, 1, 1);
|
|
|
|
blankCursor = XCreatePixmapCursor(dpy, Blank, Blank, &DummyColor, &DummyColor, 0, 0);
|
|
|
|
XFreePixmap(dpy, Blank);
|
|
|
|
XDefineCursor(dpy, win, blankCursor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetTitle(const std::string& string) override { XStoreName(dpy, win, string.c_str()); }
|
|
|
|
void MainLoop() override
|
|
|
|
{
|
|
|
|
bool fullscreen = SConfig::GetInstance().bFullscreen;
|
2016-11-19 06:03:20 -07:00
|
|
|
int last_window_width = SConfig::GetInstance().iRenderWindowWidth;
|
|
|
|
int last_window_height = SConfig::GetInstance().iRenderWindowHeight;
|
2016-06-24 02:43:46 -06:00
|
|
|
|
|
|
|
if (fullscreen)
|
|
|
|
{
|
|
|
|
rendererIsFullscreen = X11Utils::ToggleFullscreen(dpy, win);
|
2014-08-05 22:34:31 -06:00
|
|
|
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
2016-06-24 02:43:46 -06:00
|
|
|
XRRConfig->ToggleDisplayMode(True);
|
2014-08-05 22:34:31 -06:00
|
|
|
#endif
|
2016-06-24 02:43:46 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// The actual loop
|
2016-09-24 13:17:34 -06:00
|
|
|
while (s_running.IsSet())
|
2016-06-24 02:43:46 -06:00
|
|
|
{
|
2016-10-01 13:35:29 -06:00
|
|
|
if (s_shutdown_requested.TestAndClear())
|
|
|
|
{
|
2017-04-29 08:11:22 -06:00
|
|
|
const auto ios = IOS::HLE::GetIOS();
|
|
|
|
const auto stm = ios ? ios->GetDeviceByName("/dev/stm/eventhook") : nullptr;
|
2016-10-04 14:58:01 -06:00
|
|
|
if (!s_tried_graceful_shutdown.IsSet() && stm &&
|
2017-01-18 12:27:51 -07:00
|
|
|
std::static_pointer_cast<IOS::HLE::Device::STMEventHook>(stm)->HasHookInstalled())
|
2016-10-01 13:35:29 -06:00
|
|
|
{
|
|
|
|
ProcessorInterface::PowerButton_Tap();
|
|
|
|
s_tried_graceful_shutdown.Set();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
s_running.Clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-24 02:43:46 -06:00
|
|
|
XEvent event;
|
|
|
|
KeySym key;
|
|
|
|
for (int num_events = XPending(dpy); num_events > 0; num_events--)
|
|
|
|
{
|
|
|
|
XNextEvent(dpy, &event);
|
|
|
|
switch (event.type)
|
|
|
|
{
|
|
|
|
case KeyPress:
|
|
|
|
key = XLookupKeysym((XKeyEvent*)&event, 0);
|
|
|
|
if (key == XK_Escape)
|
2017-11-11 07:46:15 -07:00
|
|
|
{
|
|
|
|
s_shutdown_requested.Set();
|
|
|
|
}
|
|
|
|
else if (key == XK_F10)
|
2016-06-24 02:43:46 -06:00
|
|
|
{
|
2017-02-05 05:39:58 -07:00
|
|
|
if (Core::GetState() == Core::State::Running)
|
2016-06-24 02:43:46 -06:00
|
|
|
{
|
|
|
|
if (SConfig::GetInstance().bHideCursor)
|
|
|
|
XUndefineCursor(dpy, win);
|
2017-02-05 05:39:58 -07:00
|
|
|
Core::SetState(Core::State::Paused);
|
2016-06-24 02:43:46 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (SConfig::GetInstance().bHideCursor)
|
|
|
|
XDefineCursor(dpy, win, blankCursor);
|
2017-02-05 05:39:58 -07:00
|
|
|
Core::SetState(Core::State::Running);
|
2016-06-24 02:43:46 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((key == XK_Return) && (event.xkey.state & Mod1Mask))
|
|
|
|
{
|
|
|
|
fullscreen = !fullscreen;
|
|
|
|
X11Utils::ToggleFullscreen(dpy, win);
|
2010-04-21 22:28:34 -06:00
|
|
|
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
2016-06-24 02:43:46 -06:00
|
|
|
XRRConfig->ToggleDisplayMode(fullscreen);
|
2010-04-21 22:28:34 -06:00
|
|
|
#endif
|
2016-06-24 02:43:46 -06:00
|
|
|
}
|
|
|
|
else if (key >= XK_F1 && key <= XK_F8)
|
|
|
|
{
|
|
|
|
int slot_number = key - XK_F1 + 1;
|
|
|
|
if (event.xkey.state & ShiftMask)
|
|
|
|
State::Save(slot_number);
|
|
|
|
else
|
|
|
|
State::Load(slot_number);
|
|
|
|
}
|
|
|
|
else if (key == XK_F9)
|
|
|
|
Core::SaveScreenShot();
|
|
|
|
else if (key == XK_F11)
|
|
|
|
State::LoadLastSaved();
|
|
|
|
else if (key == XK_F12)
|
|
|
|
{
|
|
|
|
if (event.xkey.state & ShiftMask)
|
|
|
|
State::UndoLoadState();
|
|
|
|
else
|
|
|
|
State::UndoSaveState();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FocusIn:
|
|
|
|
rendererHasFocus = true;
|
2017-02-05 05:39:58 -07:00
|
|
|
if (SConfig::GetInstance().bHideCursor && Core::GetState() != Core::State::Paused)
|
2016-06-24 02:43:46 -06:00
|
|
|
XDefineCursor(dpy, win, blankCursor);
|
|
|
|
break;
|
|
|
|
case FocusOut:
|
|
|
|
rendererHasFocus = false;
|
|
|
|
if (SConfig::GetInstance().bHideCursor)
|
|
|
|
XUndefineCursor(dpy, win);
|
|
|
|
break;
|
|
|
|
case ClientMessage:
|
|
|
|
if ((unsigned long)event.xclient.data.l[0] == XInternAtom(dpy, "WM_DELETE_WINDOW", False))
|
2016-10-01 13:35:29 -06:00
|
|
|
s_shutdown_requested.Set();
|
2016-06-24 02:43:46 -06:00
|
|
|
break;
|
2016-11-19 06:03:20 -07:00
|
|
|
case ConfigureNotify:
|
|
|
|
{
|
|
|
|
if (last_window_width != event.xconfigure.width ||
|
|
|
|
last_window_height != event.xconfigure.height)
|
|
|
|
{
|
|
|
|
last_window_width = event.xconfigure.width;
|
|
|
|
last_window_height = event.xconfigure.height;
|
|
|
|
|
|
|
|
// We call Renderer::ChangeSurface here to indicate the size has changed,
|
|
|
|
// but pass the same window handle. This is needed for the Vulkan backend,
|
|
|
|
// otherwise it cannot tell that the window has been resized on some drivers.
|
|
|
|
if (g_renderer)
|
|
|
|
g_renderer->ChangeSurface(s_window_handle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2016-06-24 02:43:46 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!fullscreen)
|
|
|
|
{
|
|
|
|
Window winDummy;
|
|
|
|
unsigned int borderDummy, depthDummy;
|
|
|
|
XGetGeometry(dpy, win, &winDummy, &SConfig::GetInstance().iRenderWindowXPos,
|
|
|
|
&SConfig::GetInstance().iRenderWindowYPos,
|
|
|
|
(unsigned int*)&SConfig::GetInstance().iRenderWindowWidth,
|
|
|
|
(unsigned int*)&SConfig::GetInstance().iRenderWindowHeight, &borderDummy,
|
|
|
|
&depthDummy);
|
|
|
|
rendererIsFullscreen = false;
|
|
|
|
}
|
|
|
|
Core::HostDispatchJobs();
|
|
|
|
usleep(100000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Shutdown() override
|
|
|
|
{
|
2010-04-21 22:28:34 -06:00
|
|
|
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
2016-06-24 02:43:46 -06:00
|
|
|
delete XRRConfig;
|
2010-04-21 22:28:34 -06:00
|
|
|
#endif
|
2010-11-24 19:26:46 -07:00
|
|
|
|
2016-06-24 02:43:46 -06:00
|
|
|
if (SConfig::GetInstance().bHideCursor)
|
|
|
|
XFreeCursor(dpy, blankCursor);
|
2014-08-05 22:34:31 -06:00
|
|
|
|
2016-06-24 02:43:46 -06:00
|
|
|
XCloseDisplay(dpy);
|
|
|
|
}
|
2014-08-05 22:34:31 -06:00
|
|
|
};
|
2010-04-21 22:28:34 -06:00
|
|
|
#endif
|
2009-01-12 13:52:45 -07:00
|
|
|
|
2014-08-05 22:34:31 -06:00
|
|
|
static Platform* GetPlatform()
|
2010-06-03 22:59:07 -06:00
|
|
|
{
|
2017-04-15 20:23:19 -06:00
|
|
|
#if defined(USE_HEADLESS)
|
2016-06-24 02:43:46 -06:00
|
|
|
return new Platform();
|
2016-01-26 06:35:17 -07:00
|
|
|
#elif HAVE_X11
|
2016-06-24 02:43:46 -06:00
|
|
|
return new PlatformX11();
|
2011-03-08 16:25:37 -07:00
|
|
|
#endif
|
2016-06-24 02:43:46 -06:00
|
|
|
return nullptr;
|
2014-08-05 22:34:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char* argv[])
|
|
|
|
{
|
2016-01-17 04:13:09 -07:00
|
|
|
auto parser = CommandLineParse::CreateParser(CommandLineParse::ParserOptions::OmitGUIOptions);
|
|
|
|
optparse::Values& options = CommandLineParse::ParseArguments(parser.get(), argc, argv);
|
|
|
|
std::vector<std::string> args = parser->args();
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2017-10-01 16:09:07 -06:00
|
|
|
std::unique_ptr<BootParameters> boot;
|
2016-01-17 04:13:09 -07:00
|
|
|
if (options.is_set("exec"))
|
2016-06-24 02:43:46 -06:00
|
|
|
{
|
2017-10-01 16:09:07 -06:00
|
|
|
boot = BootParameters::GenerateFromFile(static_cast<const char*>(options.get("exec")));
|
|
|
|
}
|
|
|
|
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});
|
2016-06-24 02:43:46 -06:00
|
|
|
}
|
2016-01-17 04:13:09 -07:00
|
|
|
else if (args.size())
|
2016-06-24 02:43:46 -06:00
|
|
|
{
|
2017-10-01 16:09:07 -06:00
|
|
|
boot = BootParameters::GenerateFromFile(args.front());
|
2016-01-17 04:13:09 -07:00
|
|
|
args.erase(args.begin());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parser->print_help();
|
|
|
|
return 0;
|
2016-06-24 02:43:46 -06:00
|
|
|
}
|
|
|
|
|
2017-02-28 12:36:11 -07:00
|
|
|
std::string user_directory;
|
|
|
|
if (options.is_set("user"))
|
|
|
|
{
|
|
|
|
user_directory = static_cast<const char*>(options.get("user"));
|
|
|
|
}
|
|
|
|
|
2016-06-24 02:43:46 -06:00
|
|
|
platform = GetPlatform();
|
|
|
|
if (!platform)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "No platform found\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-02-28 12:36:11 -07:00
|
|
|
UICommon::SetUserDirectory(user_directory);
|
2016-06-24 02:43:46 -06:00
|
|
|
UICommon::Init();
|
|
|
|
|
2017-09-04 11:57:42 -06:00
|
|
|
Core::SetOnStateChangedCallback([](Core::State state) {
|
|
|
|
if (state == Core::State::Uninitialized)
|
|
|
|
s_running.Clear();
|
|
|
|
});
|
2016-06-24 02:43:46 -06:00
|
|
|
platform->Init();
|
|
|
|
|
2016-09-24 13:17:34 -06: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);
|
|
|
|
|
2016-06-24 02:43:46 -06:00
|
|
|
DolphinAnalytics::Instance()->ReportDolphinStart("nogui");
|
|
|
|
|
2017-10-01 16:09:07 -06:00
|
|
|
if (!BootManager::BootCore(std::move(boot)))
|
2016-06-24 02:43:46 -06:00
|
|
|
{
|
2017-10-01 16:09:07 -06:00
|
|
|
fprintf(stderr, "Could not boot the specified file\n");
|
2016-06-24 02:43:46 -06:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-09-24 13:17:34 -06:00
|
|
|
while (!Core::IsRunning() && s_running.IsSet())
|
2016-06-24 02:43:46 -06:00
|
|
|
{
|
|
|
|
Core::HostDispatchJobs();
|
|
|
|
updateMainFrameEvent.Wait();
|
|
|
|
}
|
|
|
|
|
2016-09-24 13:17:34 -06:00
|
|
|
if (s_running.IsSet())
|
2016-06-24 02:43:46 -06:00
|
|
|
platform->MainLoop();
|
|
|
|
Core::Stop();
|
|
|
|
|
|
|
|
Core::Shutdown();
|
|
|
|
platform->Shutdown();
|
|
|
|
UICommon::Shutdown();
|
|
|
|
|
|
|
|
delete platform;
|
|
|
|
|
|
|
|
return 0;
|
2008-09-07 14:26:38 -06:00
|
|
|
}
|