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