2015-05-24 06:55:12 +02:00
|
|
|
// Copyright 2008 Dolphin Emulator Project
|
2015-05-18 01:08:10 +02:00
|
|
|
// Licensed under GPLv2+
|
2013-04-18 05:43:35 +02:00
|
|
|
// Refer to the license.txt file included.
|
2009-07-13 09:31:43 +02:00
|
|
|
|
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>
|
2016-05-12 03:18:30 +02:00
|
|
|
#include <thread>
|
2015-09-04 08:14:10 +02:00
|
|
|
#include <unistd.h>
|
2008-09-07 22:26:38 +02:00
|
|
|
|
2014-09-08 03:06:58 +02:00
|
|
|
#include "Common/CommonTypes.h"
|
2014-04-14 01:15:23 +02:00
|
|
|
#include "Common/Event.h"
|
2016-09-24 21:17:34 +02:00
|
|
|
#include "Common/Flag.h"
|
2014-06-06 01:29:54 +02:00
|
|
|
#include "Common/Logging/LogManager.h"
|
2016-06-24 10:43:46 +02:00
|
|
|
#include "Common/MsgHandler.h"
|
2009-05-15 10:55:46 +02:00
|
|
|
|
2016-06-18 02:43:59 +02:00
|
|
|
#include "Core/Analytics.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/ConfigManager.h"
|
|
|
|
#include "Core/Core.h"
|
2016-06-24 10:43:46 +02:00
|
|
|
#include "Core/Host.h"
|
2017-04-29 16:11:22 +02:00
|
|
|
#include "Core/IOS/IOS.h"
|
2017-01-18 13:50:28 +01:00
|
|
|
#include "Core/IOS/STM/STM.h"
|
2016-06-24 10:43:46 +02:00
|
|
|
#include "Core/State.h"
|
2014-02-19 02:56:29 +01:00
|
|
|
|
2016-01-17 12:13:09 +01:00
|
|
|
#include "UICommon/CommandLineParse.h"
|
2014-10-04 21:12:15 +02:00
|
|
|
#include "UICommon/UICommon.h"
|
|
|
|
|
2016-11-19 14:03:20 +01:00
|
|
|
#include "VideoCommon/RenderBase.h"
|
2014-02-19 02:56:29 +01:00
|
|
|
#include "VideoCommon/VideoBackendBase.h"
|
|
|
|
|
2014-08-07 03:24:42 +02:00
|
|
|
static bool rendererHasFocus = true;
|
2015-01-04 17:09:56 +01:00
|
|
|
static bool rendererIsFullscreen = false;
|
2016-09-24 21:17:34 +02:00
|
|
|
static Common::Flag s_running{true};
|
2016-10-01 21:35:29 +02:00
|
|
|
static Common::Flag s_shutdown_requested{false};
|
|
|
|
static Common::Flag s_tried_graceful_shutdown{false};
|
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";
|
|
|
|
if (write(STDERR_FILENO, message, sizeof(message)) < 0)
|
|
|
|
{
|
|
|
|
}
|
2016-10-01 21:35:29 +02:00
|
|
|
s_shutdown_requested.Set();
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace ProcessorInterface
|
|
|
|
{
|
|
|
|
void PowerButton_Tap();
|
2016-09-24 21:17:34 +02:00
|
|
|
}
|
2010-05-26 23:23:44 +02:00
|
|
|
|
2014-08-06 06:34:31 +02:00
|
|
|
class Platform
|
|
|
|
{
|
|
|
|
public:
|
2016-06-24 10:43:46 +02:00
|
|
|
virtual void Init() {}
|
|
|
|
virtual void SetTitle(const std::string& title) {}
|
|
|
|
virtual void MainLoop()
|
|
|
|
{
|
2016-09-24 21:17:34 +02:00
|
|
|
while (s_running.IsSet())
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
|
|
|
Core::HostDispatchJobs();
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
virtual void Shutdown() {}
|
|
|
|
virtual ~Platform() {}
|
2014-08-06 06:34:31 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static Platform* platform;
|
|
|
|
|
2016-06-24 10:43:46 +02:00
|
|
|
void Host_NotifyMapLoaded()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void Host_RefreshDSPDebuggerWindow()
|
|
|
|
{
|
|
|
|
}
|
2008-09-07 22:26:38 +02:00
|
|
|
|
2014-08-07 03:24:42 +02:00
|
|
|
static Common::Event updateMainFrameEvent;
|
2010-03-16 14:18:52 +01:00
|
|
|
void Host_Message(int Id)
|
|
|
|
{
|
2016-06-24 10:43:46 +02:00
|
|
|
if (Id == WM_USER_STOP)
|
2016-09-24 21:17:34 +02:00
|
|
|
s_running.Clear();
|
2018-01-26 05:41:57 +01:00
|
|
|
if (Id == WM_USER_JOB_DISPATCH || Id == WM_USER_STOP)
|
2016-06-24 10:43:46 +02:00
|
|
|
updateMainFrameEvent.Set();
|
2010-03-16 14:18:52 +01:00
|
|
|
}
|
2008-09-07 22:26:38 +02:00
|
|
|
|
2016-01-26 14:35:17 +01:00
|
|
|
static void* s_window_handle = nullptr;
|
2011-02-12 22:25:49 +01:00
|
|
|
void* Host_GetRenderHandle()
|
|
|
|
{
|
2016-06-24 10:43:46 +02:00
|
|
|
return s_window_handle;
|
2011-02-12 22:25:49 +01:00
|
|
|
}
|
|
|
|
|
2014-08-06 06:44:21 +02:00
|
|
|
void Host_UpdateTitle(const std::string& title)
|
|
|
|
{
|
2016-06-24 10:43:46 +02:00
|
|
|
platform->SetTitle(title);
|
2014-08-06 06:44:21 +02:00
|
|
|
}
|
2010-04-19 05:06:18 +02:00
|
|
|
|
2016-06-24 10:43:46 +02:00
|
|
|
void Host_UpdateDisasmDialog()
|
|
|
|
{
|
|
|
|
}
|
2008-09-07 22:26:38 +02:00
|
|
|
|
2008-09-07 23:06:55 +02:00
|
|
|
void Host_UpdateMainFrame()
|
|
|
|
{
|
2016-06-24 10:43:46 +02:00
|
|
|
updateMainFrameEvent.Set();
|
2008-09-07 23:06:55 +02:00
|
|
|
}
|
2008-09-07 22:26:38 +02:00
|
|
|
|
2016-06-24 10:43:46 +02:00
|
|
|
void Host_RequestRenderWindowSize(int width, int height)
|
|
|
|
{
|
|
|
|
}
|
2014-07-16 15:53:33 +02:00
|
|
|
|
2017-07-12 09:11:29 +02:00
|
|
|
bool Host_UINeedsControllerState()
|
2014-07-16 16:24:40 +02:00
|
|
|
{
|
2016-06-24 10:43:46 +02:00
|
|
|
return false;
|
2014-07-16 16:24:40 +02:00
|
|
|
}
|
|
|
|
|
2010-04-12 03:33:10 +02:00
|
|
|
bool Host_RendererHasFocus()
|
|
|
|
{
|
2016-06-24 10:43:46 +02:00
|
|
|
return rendererHasFocus;
|
2010-04-12 03:33:10 +02:00
|
|
|
}
|
2008-09-07 22:26:38 +02:00
|
|
|
|
2015-01-04 17:09:56 +01:00
|
|
|
bool Host_RendererIsFullscreen()
|
|
|
|
{
|
2016-06-24 10:43:46 +02:00
|
|
|
return rendererIsFullscreen;
|
2015-01-04 17:09:56 +01:00
|
|
|
}
|
|
|
|
|
2016-06-26 08:06:23 +02:00
|
|
|
void Host_ShowVideoConfig(void*, const std::string&)
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
|
|
|
}
|
2014-08-02 08:21:03 +02:00
|
|
|
|
2016-11-10 17:55:21 +01:00
|
|
|
void Host_YieldToUI()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-06-25 17:35:24 +02:00
|
|
|
void Host_UpdateProgressDialog(const char* caption, int position, int total)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-01-19 17:11:07 +01:00
|
|
|
#if HAVE_X11
|
2018-01-19 22:31:44 +01:00
|
|
|
#include <X11/Xatom.h>
|
2017-06-13 11:46:13 +02:00
|
|
|
#include <X11/Xlib.h>
|
2014-08-06 06:34:31 +02:00
|
|
|
#include <X11/keysym.h>
|
2016-05-06 01:43:38 +02:00
|
|
|
#include "UICommon/X11Utils.h"
|
2018-01-19 22:31:44 +01:00
|
|
|
#ifndef HOST_NAME_MAX
|
|
|
|
#include <climits>
|
|
|
|
#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
|
|
|
|
#endif
|
2010-11-25 03:26:46 +01:00
|
|
|
|
2014-08-06 06:34:31 +02:00
|
|
|
class PlatformX11 : public Platform
|
|
|
|
{
|
2016-06-24 10:43:46 +02:00
|
|
|
Display* dpy;
|
|
|
|
Window win;
|
|
|
|
Cursor blankCursor = None;
|
2010-04-22 06:28:34 +02:00
|
|
|
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
2016-06-24 10:43:46 +02:00
|
|
|
X11Utils::XRRConfiguration* XRRConfig;
|
2010-04-22 06:28:34 +02:00
|
|
|
#endif
|
|
|
|
|
2016-06-24 10:43:46 +02: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 14:03:20 +01:00
|
|
|
XSelectInput(dpy, win, StructureNotifyMask | KeyPressMask | FocusChangeMask);
|
2016-06-24 10:43:46 +02:00
|
|
|
Atom wmProtocols[1];
|
|
|
|
wmProtocols[0] = XInternAtom(dpy, "WM_DELETE_WINDOW", True);
|
|
|
|
XSetWMProtocols(dpy, win, wmProtocols, 1);
|
2018-01-19 22:31:44 +01: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 10:43:46 +02:00
|
|
|
XMapRaised(dpy, win);
|
|
|
|
XFlush(dpy);
|
|
|
|
s_window_handle = (void*)win;
|
|
|
|
|
|
|
|
if (SConfig::GetInstance().bDisableScreenSaver)
|
2018-04-14 16:48:49 +02:00
|
|
|
X11Utils::InhibitScreensaver(win, true);
|
2014-08-06 06:34:31 +02:00
|
|
|
|
2010-04-22 06:28:34 +02:00
|
|
|
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
2016-06-24 10:43:46 +02:00
|
|
|
XRRConfig = new X11Utils::XRRConfiguration(dpy, win);
|
2010-04-22 06:28:34 +02:00
|
|
|
#endif
|
|
|
|
|
2016-06-24 10:43:46 +02: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 14:03:20 +01:00
|
|
|
int last_window_width = SConfig::GetInstance().iRenderWindowWidth;
|
|
|
|
int last_window_height = SConfig::GetInstance().iRenderWindowHeight;
|
2016-06-24 10:43:46 +02:00
|
|
|
|
|
|
|
if (fullscreen)
|
|
|
|
{
|
|
|
|
rendererIsFullscreen = X11Utils::ToggleFullscreen(dpy, win);
|
2014-08-06 06:34:31 +02:00
|
|
|
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
2016-06-24 10:43:46 +02:00
|
|
|
XRRConfig->ToggleDisplayMode(True);
|
2014-08-06 06:34:31 +02:00
|
|
|
#endif
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// The actual loop
|
2016-09-24 21:17:34 +02:00
|
|
|
while (s_running.IsSet())
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2016-10-01 21:35:29 +02:00
|
|
|
if (s_shutdown_requested.TestAndClear())
|
|
|
|
{
|
2017-04-29 16:11:22 +02:00
|
|
|
const auto ios = IOS::HLE::GetIOS();
|
|
|
|
const auto stm = ios ? ios->GetDeviceByName("/dev/stm/eventhook") : nullptr;
|
2016-10-04 22:58:01 +02:00
|
|
|
if (!s_tried_graceful_shutdown.IsSet() && stm &&
|
2017-01-18 20:27:51 +01:00
|
|
|
std::static_pointer_cast<IOS::HLE::Device::STMEventHook>(stm)->HasHookInstalled())
|
2016-10-01 21:35:29 +02:00
|
|
|
{
|
|
|
|
ProcessorInterface::PowerButton_Tap();
|
|
|
|
s_tried_graceful_shutdown.Set();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
s_running.Clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-24 10:43:46 +02: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 15:46:15 +01:00
|
|
|
{
|
|
|
|
s_shutdown_requested.Set();
|
|
|
|
}
|
|
|
|
else if (key == XK_F10)
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2017-02-05 13:39:58 +01:00
|
|
|
if (Core::GetState() == Core::State::Running)
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
|
|
|
if (SConfig::GetInstance().bHideCursor)
|
|
|
|
XUndefineCursor(dpy, win);
|
2017-02-05 13:39:58 +01:00
|
|
|
Core::SetState(Core::State::Paused);
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (SConfig::GetInstance().bHideCursor)
|
|
|
|
XDefineCursor(dpy, win, blankCursor);
|
2017-02-05 13:39:58 +01:00
|
|
|
Core::SetState(Core::State::Running);
|
2016-06-24 10:43:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((key == XK_Return) && (event.xkey.state & Mod1Mask))
|
|
|
|
{
|
|
|
|
fullscreen = !fullscreen;
|
|
|
|
X11Utils::ToggleFullscreen(dpy, win);
|
2010-04-22 06:28:34 +02:00
|
|
|
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
2016-06-24 10:43:46 +02:00
|
|
|
XRRConfig->ToggleDisplayMode(fullscreen);
|
2010-04-22 06:28:34 +02:00
|
|
|
#endif
|
2016-06-24 10:43:46 +02: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 13:39:58 +01:00
|
|
|
if (SConfig::GetInstance().bHideCursor && Core::GetState() != Core::State::Paused)
|
2016-06-24 10:43:46 +02: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 21:35:29 +02:00
|
|
|
s_shutdown_requested.Set();
|
2016-06-24 10:43:46 +02:00
|
|
|
break;
|
2016-11-19 14:03:20 +01: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;
|
|
|
|
if (g_renderer)
|
2018-01-26 07:23:24 +01:00
|
|
|
g_renderer->ResizeSurface(last_window_width, last_window_height);
|
2016-11-19 14:03:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2016-06-24 10:43:46 +02: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-22 06:28:34 +02:00
|
|
|
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
2016-06-24 10:43:46 +02:00
|
|
|
delete XRRConfig;
|
2010-04-22 06:28:34 +02:00
|
|
|
#endif
|
2010-11-25 03:26:46 +01:00
|
|
|
|
2016-06-24 10:43:46 +02:00
|
|
|
if (SConfig::GetInstance().bHideCursor)
|
|
|
|
XFreeCursor(dpy, blankCursor);
|
2014-08-06 06:34:31 +02:00
|
|
|
|
2016-06-24 10:43:46 +02:00
|
|
|
XCloseDisplay(dpy);
|
|
|
|
}
|
2014-08-06 06:34:31 +02:00
|
|
|
};
|
2010-04-22 06:28:34 +02:00
|
|
|
#endif
|
2009-01-12 21:52:45 +01:00
|
|
|
|
2014-08-06 06:34:31 +02:00
|
|
|
static Platform* GetPlatform()
|
2010-06-04 06:59:07 +02:00
|
|
|
{
|
2017-04-16 04:23:19 +02:00
|
|
|
#if defined(USE_HEADLESS)
|
2016-06-24 10:43:46 +02:00
|
|
|
return new Platform();
|
2016-01-26 14:35:17 +01:00
|
|
|
#elif HAVE_X11
|
2016-06-24 10:43:46 +02:00
|
|
|
return new PlatformX11();
|
2011-03-09 00:25:37 +01:00
|
|
|
#endif
|
2016-06-24 10:43:46 +02:00
|
|
|
return nullptr;
|
2014-08-06 06:34:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char* argv[])
|
|
|
|
{
|
2016-01-17 12:13:09 +01: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 10:43:46 +02:00
|
|
|
|
2017-10-02 00:09:07 +02:00
|
|
|
std::unique_ptr<BootParameters> boot;
|
2016-01-17 12:13:09 +01:00
|
|
|
if (options.is_set("exec"))
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2017-10-02 00:09:07 +02: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 10:43:46 +02:00
|
|
|
}
|
2016-01-17 12:13:09 +01:00
|
|
|
else if (args.size())
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2017-10-02 00:09:07 +02:00
|
|
|
boot = BootParameters::GenerateFromFile(args.front());
|
2016-01-17 12:13:09 +01:00
|
|
|
args.erase(args.begin());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
parser->print_help();
|
|
|
|
return 0;
|
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"));
|
|
|
|
}
|
|
|
|
|
2016-06-24 10:43:46 +02:00
|
|
|
platform = GetPlatform();
|
|
|
|
if (!platform)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "No platform found\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-02-28 20:36:11 +01:00
|
|
|
UICommon::SetUserDirectory(user_directory);
|
2016-06-24 10:43:46 +02:00
|
|
|
UICommon::Init();
|
|
|
|
|
2017-09-04 19:57:42 +02:00
|
|
|
Core::SetOnStateChangedCallback([](Core::State state) {
|
|
|
|
if (state == Core::State::Uninitialized)
|
|
|
|
s_running.Clear();
|
|
|
|
});
|
2016-06-24 10:43:46 +02:00
|
|
|
platform->Init();
|
|
|
|
|
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);
|
|
|
|
|
2016-06-24 10:43:46 +02:00
|
|
|
DolphinAnalytics::Instance()->ReportDolphinStart("nogui");
|
|
|
|
|
2017-10-02 00:09:07 +02:00
|
|
|
if (!BootManager::BootCore(std::move(boot)))
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
2017-10-02 00:09:07 +02:00
|
|
|
fprintf(stderr, "Could not boot the specified file\n");
|
2016-06-24 10:43:46 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-09-24 21:17:34 +02:00
|
|
|
while (!Core::IsRunning() && s_running.IsSet())
|
2016-06-24 10:43:46 +02:00
|
|
|
{
|
|
|
|
Core::HostDispatchJobs();
|
|
|
|
updateMainFrameEvent.Wait();
|
|
|
|
}
|
|
|
|
|
2016-09-24 21:17:34 +02:00
|
|
|
if (s_running.IsSet())
|
2016-06-24 10:43:46 +02:00
|
|
|
platform->MainLoop();
|
|
|
|
Core::Stop();
|
|
|
|
|
|
|
|
Core::Shutdown();
|
|
|
|
platform->Shutdown();
|
|
|
|
UICommon::Shutdown();
|
|
|
|
|
|
|
|
delete platform;
|
|
|
|
|
|
|
|
return 0;
|
2008-09-07 22:26:38 +02:00
|
|
|
}
|