mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-22 13:49:53 -06:00
Moved NoGUI out of DolphinWx.
This commit is contained in:
455
Source/Core/DolphinNoGUI/MainNoGUI.cpp
Normal file
455
Source/Core/DolphinNoGUI/MainNoGUI.cpp
Normal file
@ -0,0 +1,455 @@
|
||||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <OptionParser.h>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <signal.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Event.h"
|
||||
#include "Common/Flag.h"
|
||||
#include "Common/Logging/LogManager.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
|
||||
#include "Core/Analytics.h"
|
||||
#include "Core/BootManager.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/Wiimote.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/IOS/IPC.h"
|
||||
#include "Core/IOS/STM/STM.h"
|
||||
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
|
||||
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
|
||||
#include "Core/State.h"
|
||||
|
||||
#include "UICommon/CommandLineParse.h"
|
||||
#include "UICommon/UICommon.h"
|
||||
|
||||
#include "VideoCommon/RenderBase.h"
|
||||
#include "VideoCommon/VideoBackendBase.h"
|
||||
|
||||
static bool rendererHasFocus = true;
|
||||
static bool rendererIsFullscreen = false;
|
||||
static Common::Flag s_running{true};
|
||||
static Common::Flag s_shutdown_requested{false};
|
||||
static Common::Flag s_tried_graceful_shutdown{false};
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
s_shutdown_requested.Set();
|
||||
}
|
||||
|
||||
namespace ProcessorInterface
|
||||
{
|
||||
void PowerButton_Tap();
|
||||
}
|
||||
|
||||
class Platform
|
||||
{
|
||||
public:
|
||||
virtual void Init() {}
|
||||
virtual void SetTitle(const std::string& title) {}
|
||||
virtual void MainLoop()
|
||||
{
|
||||
while (s_running.IsSet())
|
||||
{
|
||||
Core::HostDispatchJobs();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
virtual void Shutdown() {}
|
||||
virtual ~Platform() {}
|
||||
};
|
||||
|
||||
static Platform* platform;
|
||||
|
||||
void Host_NotifyMapLoaded()
|
||||
{
|
||||
}
|
||||
void Host_RefreshDSPDebuggerWindow()
|
||||
{
|
||||
}
|
||||
|
||||
static Common::Event updateMainFrameEvent;
|
||||
void Host_Message(int Id)
|
||||
{
|
||||
if (Id == WM_USER_STOP)
|
||||
{
|
||||
s_running.Clear();
|
||||
updateMainFrameEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
static void* s_window_handle = nullptr;
|
||||
void* Host_GetRenderHandle()
|
||||
{
|
||||
return s_window_handle;
|
||||
}
|
||||
|
||||
void Host_UpdateTitle(const std::string& title)
|
||||
{
|
||||
platform->SetTitle(title);
|
||||
}
|
||||
|
||||
void Host_UpdateDisasmDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void Host_UpdateMainFrame()
|
||||
{
|
||||
updateMainFrameEvent.Set();
|
||||
}
|
||||
|
||||
void Host_RequestRenderWindowSize(int width, int height)
|
||||
{
|
||||
}
|
||||
|
||||
void Host_SetStartupDebuggingParameters()
|
||||
{
|
||||
SConfig& StartUp = SConfig::GetInstance();
|
||||
StartUp.bEnableDebugging = false;
|
||||
StartUp.bBootToPause = false;
|
||||
}
|
||||
|
||||
bool Host_UIHasFocus()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Host_RendererHasFocus()
|
||||
{
|
||||
return rendererHasFocus;
|
||||
}
|
||||
|
||||
bool Host_RendererIsFullscreen()
|
||||
{
|
||||
return rendererIsFullscreen;
|
||||
}
|
||||
|
||||
void Host_ConnectWiimote(int wm_idx, bool connect)
|
||||
{
|
||||
if (Core::IsRunning() && SConfig::GetInstance().bWii &&
|
||||
!SConfig::GetInstance().m_bt_passthrough_enabled)
|
||||
{
|
||||
Core::QueueHostJob([=] {
|
||||
bool was_unpaused = Core::PauseAndLock(true);
|
||||
const auto bt = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
|
||||
IOS::HLE::GetDeviceByName("/dev/usb/oh1/57e/305"));
|
||||
if (bt)
|
||||
bt->AccessWiiMote(wm_idx | 0x100)->Activate(connect);
|
||||
Host_UpdateMainFrame();
|
||||
Core::PauseAndLock(false, was_unpaused);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Host_SetWiiMoteConnectionState(int _State)
|
||||
{
|
||||
}
|
||||
|
||||
void Host_ShowVideoConfig(void*, const std::string&)
|
||||
{
|
||||
}
|
||||
|
||||
void Host_YieldToUI()
|
||||
{
|
||||
}
|
||||
|
||||
#if HAVE_X11
|
||||
#include <X11/keysym.h>
|
||||
#include "UICommon/X11Utils.h"
|
||||
|
||||
class PlatformX11 : public Platform
|
||||
{
|
||||
Display* dpy;
|
||||
Window win;
|
||||
Cursor blankCursor = None;
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
X11Utils::XRRConfiguration* XRRConfig;
|
||||
#endif
|
||||
|
||||
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));
|
||||
XSelectInput(dpy, win, StructureNotifyMask | KeyPressMask | FocusChangeMask);
|
||||
Atom wmProtocols[1];
|
||||
wmProtocols[0] = XInternAtom(dpy, "WM_DELETE_WINDOW", True);
|
||||
XSetWMProtocols(dpy, win, wmProtocols, 1);
|
||||
XMapRaised(dpy, win);
|
||||
XFlush(dpy);
|
||||
s_window_handle = (void*)win;
|
||||
|
||||
if (SConfig::GetInstance().bDisableScreenSaver)
|
||||
X11Utils::InhibitScreensaver(dpy, win, true);
|
||||
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
XRRConfig = new X11Utils::XRRConfiguration(dpy, win);
|
||||
#endif
|
||||
|
||||
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;
|
||||
int last_window_width = SConfig::GetInstance().iRenderWindowWidth;
|
||||
int last_window_height = SConfig::GetInstance().iRenderWindowHeight;
|
||||
|
||||
if (fullscreen)
|
||||
{
|
||||
rendererIsFullscreen = X11Utils::ToggleFullscreen(dpy, win);
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
XRRConfig->ToggleDisplayMode(True);
|
||||
#endif
|
||||
}
|
||||
|
||||
// The actual loop
|
||||
while (s_running.IsSet())
|
||||
{
|
||||
if (s_shutdown_requested.TestAndClear())
|
||||
{
|
||||
const auto stm = IOS::HLE::GetDeviceByName("/dev/stm/eventhook");
|
||||
if (!s_tried_graceful_shutdown.IsSet() && stm &&
|
||||
std::static_pointer_cast<IOS::HLE::Device::STMEventHook>(stm)->HasHookInstalled())
|
||||
{
|
||||
ProcessorInterface::PowerButton_Tap();
|
||||
s_tried_graceful_shutdown.Set();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_running.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (Core::GetState() == Core::State::Running)
|
||||
{
|
||||
if (SConfig::GetInstance().bHideCursor)
|
||||
XUndefineCursor(dpy, win);
|
||||
Core::SetState(Core::State::Paused);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SConfig::GetInstance().bHideCursor)
|
||||
XDefineCursor(dpy, win, blankCursor);
|
||||
Core::SetState(Core::State::Running);
|
||||
}
|
||||
}
|
||||
else if ((key == XK_Return) && (event.xkey.state & Mod1Mask))
|
||||
{
|
||||
fullscreen = !fullscreen;
|
||||
X11Utils::ToggleFullscreen(dpy, win);
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
XRRConfig->ToggleDisplayMode(fullscreen);
|
||||
#endif
|
||||
}
|
||||
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;
|
||||
if (SConfig::GetInstance().bHideCursor && Core::GetState() != Core::State::Paused)
|
||||
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))
|
||||
s_shutdown_requested.Set();
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
delete XRRConfig;
|
||||
#endif
|
||||
|
||||
if (SConfig::GetInstance().bHideCursor)
|
||||
XFreeCursor(dpy, blankCursor);
|
||||
|
||||
XCloseDisplay(dpy);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
static Platform* GetPlatform()
|
||||
{
|
||||
#if defined(USE_EGL) && defined(USE_HEADLESS)
|
||||
return new Platform();
|
||||
#elif HAVE_X11
|
||||
return new PlatformX11();
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
auto parser = CommandLineParse::CreateParser(CommandLineParse::ParserOptions::OmitGUIOptions);
|
||||
optparse::Values& options = CommandLineParse::ParseArguments(parser.get(), argc, argv);
|
||||
std::vector<std::string> args = parser->args();
|
||||
|
||||
std::string boot_filename;
|
||||
if (options.is_set("exec"))
|
||||
{
|
||||
boot_filename = static_cast<const char*>(options.get("exec"));
|
||||
}
|
||||
else if (args.size())
|
||||
{
|
||||
boot_filename = args.front();
|
||||
args.erase(args.begin());
|
||||
}
|
||||
else
|
||||
{
|
||||
parser->print_help();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string user_directory;
|
||||
if (options.is_set("user"))
|
||||
{
|
||||
user_directory = static_cast<const char*>(options.get("user"));
|
||||
}
|
||||
|
||||
platform = GetPlatform();
|
||||
if (!platform)
|
||||
{
|
||||
fprintf(stderr, "No platform found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
UICommon::SetUserDirectory(user_directory);
|
||||
UICommon::Init();
|
||||
|
||||
Core::SetOnStoppedCallback([]() { s_running.Clear(); });
|
||||
platform->Init();
|
||||
|
||||
// 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);
|
||||
|
||||
DolphinAnalytics::Instance()->ReportDolphinStart("nogui");
|
||||
|
||||
if (!BootManager::BootCore(boot_filename))
|
||||
{
|
||||
fprintf(stderr, "Could not boot %s\n", boot_filename.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (!Core::IsRunning() && s_running.IsSet())
|
||||
{
|
||||
Core::HostDispatchJobs();
|
||||
updateMainFrameEvent.Wait();
|
||||
}
|
||||
|
||||
if (s_running.IsSet())
|
||||
platform->MainLoop();
|
||||
Core::Stop();
|
||||
|
||||
Core::Shutdown();
|
||||
platform->Shutdown();
|
||||
UICommon::Shutdown();
|
||||
|
||||
delete platform;
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user