diff --git a/CMakeLists.txt b/CMakeLists.txt
index bc0176ba82..19fa9014ee 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -369,11 +369,6 @@ if(ENABLE_VTUNE)
)
endif()
-if(WIN32)
- message(STATUS "Building for Windows, disabling NoGUI frontend.")
- set(ENABLE_NOGUI OFF)
-endif()
-
if(ANDROID)
message(STATUS "Building for Android")
if(NOT ENABLE_HEADLESS)
diff --git a/Source/Core/DolphinNoGUI/CMakeLists.txt b/Source/Core/DolphinNoGUI/CMakeLists.txt
index 6247dac2d0..3943582ad2 100644
--- a/Source/Core/DolphinNoGUI/CMakeLists.txt
+++ b/Source/Core/DolphinNoGUI/CMakeLists.txt
@@ -9,6 +9,10 @@ if(ENABLE_X11 AND X11_FOUND)
target_sources(dolphin-nogui PRIVATE PlatformX11.cpp)
endif()
+if(WIN32)
+ target_sources(dolphin-nogui PRIVATE PlatformWin32.cpp)
+endif()
+
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
target_sources(dolphin-nogui PRIVATE PlatformFBDev.cpp)
endif()
diff --git a/Source/Core/DolphinNoGUI/DolphinNoGUI.exe.manifest b/Source/Core/DolphinNoGUI/DolphinNoGUI.exe.manifest
new file mode 100644
index 0000000000..69f9e43d44
--- /dev/null
+++ b/Source/Core/DolphinNoGUI/DolphinNoGUI.exe.manifest
@@ -0,0 +1,30 @@
+
+
+
+Dolphin NoGUI Frontend
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/Core/DolphinNoGUI/DolphinNoGUI.rc b/Source/Core/DolphinNoGUI/DolphinNoGUI.rc
new file mode 100644
index 0000000000..b1471b1760
--- /dev/null
+++ b/Source/Core/DolphinNoGUI/DolphinNoGUI.rc
@@ -0,0 +1,6 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+IDI_ICON1 ICON "..\\..\\..\\Installer\\Dolphin.ico"
+"dolphin" ICON "..\\..\\..\\Installer\\Dolphin.ico"
+
diff --git a/Source/Core/DolphinNoGUI/DolphinNoGUI.vcxproj b/Source/Core/DolphinNoGUI/DolphinNoGUI.vcxproj
new file mode 100644
index 0000000000..2efd1ea4ce
--- /dev/null
+++ b/Source/Core/DolphinNoGUI/DolphinNoGUI.vcxproj
@@ -0,0 +1,107 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {974E563D-23F8-4E8F-9083-F62876B04E08}
+ 10.0
+
+
+
+ Application
+ v142
+ Unicode
+
+
+ true
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+ avrt.lib;iphlpapi.lib;winmm.lib;setupapi.lib;rpcrt4.lib;comctl32.lib;Shlwapi.lib;discord-rpc.lib;opengl32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;%(AdditionalDependencies)
+ $(ExternalsDir)ffmpeg\lib;$(IntDir)..\discord-rpc\bin;%(AdditionalLibraryDirectories)
+ Console
+
+
+
+
+ {c636d9d1-82fe-42b5-9987-63b7d4836341}
+
+
+ {e54cf649-140e-4255-81a5-30a673c1fb36}
+
+
+ {604c8368-f34a-4d55-82c8-cc92a0c13254}
+
+
+ {96020103-4ba5-4fd2-b4aa-5b6d24492d4e}
+
+
+ {53a5391b-737e-49a8-bc8f-312ada00736f}
+
+
+ {ec1a314c-5588-4506-9c1e-2e58e5817f75}
+
+
+ {a4c423aa-f57c-46c7-a172-d1a777017d29}
+
+
+ {29f29a19-f141-45ad-9679-5a2923b49da3}
+
+
+ {3de9ee35-3e91-4f27-a014-2866ad8c3fe3}
+
+
+ {570215b7-e32f-4438-95ae-c8d955f9fca3}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/Core/DolphinNoGUI/DolphinNoGUI.vcxproj.filters b/Source/Core/DolphinNoGUI/DolphinNoGUI.vcxproj.filters
new file mode 100644
index 0000000000..1a368e0465
--- /dev/null
+++ b/Source/Core/DolphinNoGUI/DolphinNoGUI.vcxproj.filters
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp
index 8c27992201..14fac966c9 100644
--- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp
+++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp
@@ -12,8 +12,11 @@
#include
#ifndef _WIN32
#include
+#else
+#include
#endif
+#include "Common/StringUtil.h"
#include "Core/Analytics.h"
#include "Core/Boot/Boot.h"
#include "Core/BootManager.h"
@@ -121,6 +124,11 @@ static std::unique_ptr GetPlatform(const optparse::Values& options)
return Platform::CreateFBDevPlatform();
#endif
+#ifdef _WIN32
+ if (platform_name == "win32" || platform_name.empty())
+ return Platform::CreateWin32Platform();
+#endif
+
if (platform_name == "headless" || platform_name.empty())
return Platform::CreateHeadlessPlatform();
@@ -142,6 +150,10 @@ int main(int argc, char* argv[])
#if HAVE_X11
,
"x11"
+#endif
+#ifdef _WIN32
+ ,
+ "win32"
#endif
});
@@ -198,6 +210,10 @@ int main(int argc, char* argv[])
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;
@@ -205,6 +221,7 @@ int main(int argc, char* argv[])
sa.sa_flags = SA_RESETHAND;
sigaction(SIGINT, &sa, nullptr);
sigaction(SIGTERM, &sa, nullptr);
+#endif
DolphinAnalytics::Instance().ReportDolphinStart("nogui");
diff --git a/Source/Core/DolphinNoGUI/Platform.h b/Source/Core/DolphinNoGUI/Platform.h
index af39492447..b0d98e58be 100644
--- a/Source/Core/DolphinNoGUI/Platform.h
+++ b/Source/Core/DolphinNoGUI/Platform.h
@@ -35,10 +35,15 @@ public:
#ifdef HAVE_X11
static std::unique_ptr CreateX11Platform();
#endif
+
#ifdef __linux__
static std::unique_ptr CreateFBDevPlatform();
#endif
+#ifdef _WIN32
+ static std::unique_ptr CreateWin32Platform();
+#endif
+
protected:
void UpdateRunningFlag();
diff --git a/Source/Core/DolphinNoGUI/PlatformWin32.cpp b/Source/Core/DolphinNoGUI/PlatformWin32.cpp
new file mode 100644
index 0000000000..add981b361
--- /dev/null
+++ b/Source/Core/DolphinNoGUI/PlatformWin32.cpp
@@ -0,0 +1,201 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "DolphinNoGUI/Platform.h"
+
+#include "Common/MsgHandler.h"
+#include "Core/Config/MainSettings.h"
+#include "Core/ConfigManager.h"
+#include "Core/Core.h"
+#include "Core/State.h"
+
+#include
+#include
+#include
+
+#include "VideoCommon/RenderBase.h"
+#include "resource.h"
+
+namespace
+{
+class PlatformWin32 : public Platform
+{
+public:
+ ~PlatformWin32() override;
+
+ bool Init() override;
+ void SetTitle(const std::string& string) override;
+ void MainLoop() override;
+
+ WindowSystemInfo GetWindowSystemInfo() const;
+
+private:
+ static constexpr TCHAR WINDOW_CLASS_NAME[] = _T("DolphinNoGUI");
+
+ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+ bool RegisterRenderWindowClass();
+ bool CreateRenderWindow();
+ void UpdateWindowPosition();
+ void ProcessEvents();
+
+ HWND m_hwnd{};
+
+ int m_window_x = Config::Get(Config::MAIN_RENDER_WINDOW_XPOS);
+ int m_window_y = Config::Get(Config::MAIN_RENDER_WINDOW_YPOS);
+ int m_window_width = Config::Get(Config::MAIN_RENDER_WINDOW_WIDTH);
+ int m_window_height = Config::Get(Config::MAIN_RENDER_WINDOW_HEIGHT);
+};
+
+PlatformWin32::~PlatformWin32()
+{
+ if (m_hwnd)
+ DestroyWindow(m_hwnd);
+}
+
+bool PlatformWin32::RegisterRenderWindowClass()
+{
+ WNDCLASSEX wc = {};
+ wc.cbSize = sizeof(WNDCLASSEX);
+ wc.style = 0;
+ wc.lpfnWndProc = WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = GetModuleHandle(nullptr);
+ wc.hIcon = LoadIcon(NULL, IDI_ICON1);
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = WINDOW_CLASS_NAME;
+ wc.hIconSm = LoadIcon(NULL, IDI_ICON1);
+
+ if (!RegisterClassEx(&wc))
+ {
+ MessageBox(nullptr, _T("Window registration failed."), _T("Error"), MB_ICONERROR | MB_OK);
+ return false;
+ }
+
+ return true;
+}
+
+bool PlatformWin32::CreateRenderWindow()
+{
+ m_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, WINDOW_CLASS_NAME, _T("Dolphin"), WS_OVERLAPPEDWINDOW,
+ m_window_x < 0 ? CW_USEDEFAULT : m_window_x,
+ m_window_y < 0 ? CW_USEDEFAULT : m_window_y, m_window_width,
+ m_window_height, nullptr, nullptr, GetModuleHandle(nullptr), this);
+ if (!m_hwnd)
+ {
+ MessageBox(nullptr, _T("CreateWindowEx failed."), _T("Error"), MB_ICONERROR | MB_OK);
+ return false;
+ }
+
+ ShowWindow(m_hwnd, SW_SHOW);
+ UpdateWindow(m_hwnd);
+ return true;
+}
+
+bool PlatformWin32::Init()
+{
+ if (!RegisterRenderWindowClass() || !CreateRenderWindow())
+ return false;
+
+ // TODO: Enter fullscreen if enabled.
+ if (Config::Get(Config::MAIN_FULLSCREEN))
+ {
+ ProcessEvents();
+ }
+
+ UpdateWindowPosition();
+ return true;
+}
+
+void PlatformWin32::SetTitle(const std::string& string)
+{
+ SetWindowTextW(m_hwnd, UTF8ToUTF16(string).c_str());
+}
+
+void PlatformWin32::MainLoop()
+{
+ while (IsRunning())
+ {
+ UpdateRunningFlag();
+ Core::HostDispatchJobs();
+ ProcessEvents();
+ UpdateWindowPosition();
+
+ // TODO: Is this sleep appropriate?
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+}
+
+WindowSystemInfo PlatformWin32::GetWindowSystemInfo() const
+{
+ WindowSystemInfo wsi;
+ wsi.type = WindowSystemType::Windows;
+ wsi.render_surface = reinterpret_cast(m_hwnd);
+ return wsi;
+}
+
+void PlatformWin32::UpdateWindowPosition()
+{
+ if (m_window_fullscreen)
+ return;
+
+ RECT rc = {};
+ if (!GetWindowRect(m_hwnd, &rc))
+ return;
+
+ m_window_x = rc.left;
+ m_window_y = rc.top;
+ m_window_width = rc.right - rc.left;
+ m_window_height = rc.bottom - rc.top;
+}
+
+void PlatformWin32::ProcessEvents()
+{
+ MSG msg;
+ while (PeekMessage(&msg, m_hwnd, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+
+LRESULT PlatformWin32::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ PlatformWin32* platform = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+ switch (msg)
+ {
+ case WM_NCCREATE:
+ {
+ platform =
+ reinterpret_cast(reinterpret_cast(lParam)->lpCreateParams);
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(platform));
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+
+ case WM_SIZE:
+ {
+ if (g_renderer)
+ g_renderer->ResizeSurface();
+ }
+ break;
+
+ case WM_CLOSE:
+ platform->RequestShutdown();
+ break;
+
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+
+ return 0;
+}
+} // namespace
+
+std::unique_ptr Platform::CreateWin32Platform()
+{
+ return std::make_unique();
+}
diff --git a/Source/Core/DolphinNoGUI/resource.h b/Source/Core/DolphinNoGUI/resource.h
new file mode 100644
index 0000000000..18fcdb7f0b
--- /dev/null
+++ b/Source/Core/DolphinNoGUI/resource.h
@@ -0,0 +1,24 @@
+// Copyright 2017 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by DolphinNoGui.rc
+//
+#ifdef RC_INVOKED
+#define IDI_ICON1 101
+#else
+#define IDI_ICON1 MAKEINTRESOURCE(101)
+#endif
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/Source/dolphin-emu.sln b/Source/dolphin-emu.sln
index 6d90865dbe..dc3cfa8f2d 100644
--- a/Source/dolphin-emu.sln
+++ b/Source/dolphin-emu.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.26430.14
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29509.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dolphin", "Core\DolphinQt\DolphinQt.vcxproj", "{FA3FA62B-6F58-4B86-9453-4D149940A066}"
EndProject
@@ -97,6 +97,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "D3D12", "Core\VideoBackends
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "D3DCommon", "Core\VideoBackends\D3DCommon\D3DCommon.vcxproj", "{DEA96CF2-F237-4A1A-B32F-C916769EFB50}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DolphinNoGUI", "Core\DolphinNoGUI\DolphinNoGUI.vcxproj", "{974E563D-23F8-4E8F-9083-F62876B04E08}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -279,6 +281,8 @@ Global
{DEA96CF2-F237-4A1A-B32F-C916769EFB50}.Debug|x64.Build.0 = Debug|x64
{DEA96CF2-F237-4A1A-B32F-C916769EFB50}.Release|x64.ActiveCfg = Release|x64
{DEA96CF2-F237-4A1A-B32F-C916769EFB50}.Release|x64.Build.0 = Release|x64
+ {974E563D-23F8-4E8F-9083-F62876B04E08}.Debug|x64.ActiveCfg = Debug|x64
+ {974E563D-23F8-4E8F-9083-F62876B04E08}.Release|x64.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE