From f9869cb2163b0e3644dae4c7b592cdc5b516608b Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 24 Oct 2018 14:47:48 +1000 Subject: [PATCH 1/3] Vulkan: Support runtime selection of WSI --- .../Core/VideoBackends/Vulkan/SwapChain.cpp | 196 ++++++++---------- Source/Core/VideoBackends/Vulkan/SwapChain.h | 14 +- .../VideoBackends/Vulkan/VulkanContext.cpp | 43 ++-- .../Core/VideoBackends/Vulkan/VulkanContext.h | 5 +- .../Vulkan/VulkanEntryPoints.inl | 19 +- .../VideoBackends/Vulkan/VulkanLoader.cpp | 26 +-- .../Core/VideoBackends/Vulkan/VulkanLoader.h | 17 +- Source/Core/VideoBackends/Vulkan/main.cpp | 14 +- 8 files changed, 155 insertions(+), 179 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/SwapChain.cpp b/Source/Core/VideoBackends/Vulkan/SwapChain.cpp index 2759a59283..94cd0ade67 100644 --- a/Source/Core/VideoBackends/Vulkan/SwapChain.cpp +++ b/Source/Core/VideoBackends/Vulkan/SwapChain.cpp @@ -18,16 +18,12 @@ #if defined(VK_USE_PLATFORM_XLIB_KHR) #include -#elif defined(VK_USE_PLATFORM_XCB_KHR) -#include -#include #endif namespace Vulkan { -SwapChain::SwapChain(void* display_handle, void* native_handle, VkSurfaceKHR surface, bool vsync) - : m_display_handle(display_handle), m_native_handle(native_handle), m_surface(surface), - m_vsync_enabled(vsync) +SwapChain::SwapChain(const WindowSystemInfo& wsi, VkSurfaceKHR surface, bool vsync) + : m_wsi(wsi), m_surface(surface), m_vsync_enabled(vsync) { } @@ -39,110 +35,101 @@ SwapChain::~SwapChain() DestroySemaphores(); } -VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, void* display_handle, void* hwnd) +VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, const WindowSystemInfo& wsi) { #if defined(VK_USE_PLATFORM_WIN32_KHR) - VkWin32SurfaceCreateInfoKHR surface_create_info = { - VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, // VkStructureType sType - nullptr, // const void* pNext - 0, // VkWin32SurfaceCreateFlagsKHR flags - nullptr, // HINSTANCE hinstance - reinterpret_cast(hwnd) // HWND hwnd - }; - - VkSurfaceKHR surface; - VkResult res = vkCreateWin32SurfaceKHR(instance, &surface_create_info, nullptr, &surface); - if (res != VK_SUCCESS) + if (wsi.type == WindowSystemType::Windows) { - LOG_VULKAN_ERROR(res, "vkCreateWin32SurfaceKHR failed: "); - return VK_NULL_HANDLE; + VkWin32SurfaceCreateInfoKHR surface_create_info = { + VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, // VkStructureType sType + nullptr, // const void* pNext + 0, // VkWin32SurfaceCreateFlagsKHR flags + nullptr, // HINSTANCE hinstance + reinterpret_cast(wsi.render_surface) // HWND hwnd + }; + + VkSurfaceKHR surface; + VkResult res = vkCreateWin32SurfaceKHR(instance, &surface_create_info, nullptr, &surface); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateWin32SurfaceKHR failed: "); + return VK_NULL_HANDLE; + } + + return surface; } - - return surface; - -#elif defined(VK_USE_PLATFORM_XLIB_KHR) - VkXlibSurfaceCreateInfoKHR surface_create_info = { - VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, // VkStructureType sType - nullptr, // const void* pNext - 0, // VkXlibSurfaceCreateFlagsKHR flags - static_cast(display_handle), // Display* dpy - reinterpret_cast(hwnd) // Window window - }; - - VkSurfaceKHR surface; - VkResult res = vkCreateXlibSurfaceKHR(instance, &surface_create_info, nullptr, &surface); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateXlibSurfaceKHR failed: "); - return VK_NULL_HANDLE; - } - - return surface; - -#elif defined(VK_USE_PLATFORM_XCB_KHR) - // If we ever switch to using xcb, we should pass the display handle as well. - xcb_connection_t* connection = XGetXCBConnection(display_handle); - - VkXcbSurfaceCreateInfoKHR surface_create_info = { - VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR, // VkStructureType sType - nullptr, // const void* pNext - 0, // VkXcbSurfaceCreateFlagsKHR flags - connection, // xcb_connection_t* connection - static_cast(reinterpret_cast(hwnd)) // xcb_window_t window - }; - - VkSurfaceKHR surface; - VkResult res = vkCreateXcbSurfaceKHR(instance, &surface_create_info, nullptr, &surface); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateXcbSurfaceKHR failed: "); - return VK_NULL_HANDLE; - } - - return surface; - -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) - VkAndroidSurfaceCreateInfoKHR surface_create_info = { - VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, // VkStructureType sType - nullptr, // const void* pNext - 0, // VkAndroidSurfaceCreateFlagsKHR flags - reinterpret_cast(hwnd) // ANativeWindow* window - }; - - VkSurfaceKHR surface; - VkResult res = vkCreateAndroidSurfaceKHR(instance, &surface_create_info, nullptr, &surface); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateAndroidSurfaceKHR failed: "); - return VK_NULL_HANDLE; - } - - return surface; - -#elif defined(VK_USE_PLATFORM_MACOS_MVK) - VkMacOSSurfaceCreateInfoMVK surface_create_info = { - VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, nullptr, 0, hwnd}; - - VkSurfaceKHR surface; - VkResult res = vkCreateMacOSSurfaceMVK(instance, &surface_create_info, nullptr, &surface); - if (res != VK_SUCCESS) - { - LOG_VULKAN_ERROR(res, "vkCreateMacOSSurfaceMVK failed: "); - return VK_NULL_HANDLE; - } - - return surface; -#else - return VK_NULL_HANDLE; #endif + +#if defined(VK_USE_PLATFORM_XLIB_KHR) + if (wsi.type == WindowSystemType::X11) + { + VkXlibSurfaceCreateInfoKHR surface_create_info = { + VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, // VkStructureType sType + nullptr, // const void* pNext + 0, // VkXlibSurfaceCreateFlagsKHR flags + static_cast(wsi.display_connection), // Display* dpy + reinterpret_cast(wsi.render_surface) // Window window + }; + + VkSurfaceKHR surface; + VkResult res = vkCreateXlibSurfaceKHR(instance, &surface_create_info, nullptr, &surface); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateXlibSurfaceKHR failed: "); + return VK_NULL_HANDLE; + } + + return surface; + } +#endif + +#if defined(VK_USE_PLATFORM_ANDROID_KHR) + if (wsi.type == WindowSystemType::Android) + { + VkAndroidSurfaceCreateInfoKHR surface_create_info = { + VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, // VkStructureType sType + nullptr, // const void* pNext + 0, // VkAndroidSurfaceCreateFlagsKHR flags + reinterpret_cast(wsi.render_surface) // ANativeWindow* window + }; + + VkSurfaceKHR surface; + VkResult res = vkCreateAndroidSurfaceKHR(instance, &surface_create_info, nullptr, &surface); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateAndroidSurfaceKHR failed: "); + return VK_NULL_HANDLE; + } + + return surface; + } +#endif + +#if defined(VK_USE_PLATFORM_MACOS_MVK) + if (wsi.type == WindowSystemType::MacOS) + { + VkMacOSSurfaceCreateInfoMVK surface_create_info = { + VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, nullptr, 0, wsi.render_surface}; + + VkSurfaceKHR surface; + VkResult res = vkCreateMacOSSurfaceMVK(instance, &surface_create_info, nullptr, &surface); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateMacOSSurfaceMVK failed: "); + return VK_NULL_HANDLE; + } + + return surface; + } +#endif + + return VK_NULL_HANDLE; } -std::unique_ptr SwapChain::Create(void* display_handle, void* native_handle, - VkSurfaceKHR surface, bool vsync) +std::unique_ptr SwapChain::Create(const WindowSystemInfo& wsi, VkSurfaceKHR surface, + bool vsync) { - std::unique_ptr swap_chain = - std::make_unique(display_handle, native_handle, surface, vsync); - + std::unique_ptr swap_chain = std::make_unique(wsi, surface, vsync); if (!swap_chain->CreateSemaphores() || !swap_chain->CreateSwapChain() || !swap_chain->SetupSwapChainImages()) { @@ -531,9 +518,8 @@ bool SwapChain::RecreateSurface(void* native_handle) DestroySurface(); // Re-create the surface with the new native handle - m_native_handle = native_handle; - m_surface = - CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), m_display_handle, native_handle); + m_wsi.render_surface = native_handle; + m_surface = CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), m_wsi); if (m_surface == VK_NULL_HANDLE) return false; diff --git a/Source/Core/VideoBackends/Vulkan/SwapChain.h b/Source/Core/VideoBackends/Vulkan/SwapChain.h index 11b32c7951..0687249acd 100644 --- a/Source/Core/VideoBackends/Vulkan/SwapChain.h +++ b/Source/Core/VideoBackends/Vulkan/SwapChain.h @@ -8,6 +8,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/WindowSystemInfo.h" #include "VideoBackends/Vulkan/Constants.h" #include "VideoBackends/Vulkan/Texture2D.h" #include "VideoCommon/TextureConfig.h" @@ -20,18 +21,16 @@ class ObjectCache; class SwapChain { public: - SwapChain(void* display_handle, void* native_handle, VkSurfaceKHR surface, bool vsync); + SwapChain(const WindowSystemInfo& wsi, VkSurfaceKHR surface, bool vsync); ~SwapChain(); // Creates a vulkan-renderable surface for the specified window handle. - static VkSurfaceKHR CreateVulkanSurface(VkInstance instance, void* display_handle, void* hwnd); + static VkSurfaceKHR CreateVulkanSurface(VkInstance instance, const WindowSystemInfo& wsi); // Create a new swap chain from a pre-existing surface. - static std::unique_ptr Create(void* display_handle, void* native_handle, - VkSurfaceKHR surface, bool vsync); + static std::unique_ptr Create(const WindowSystemInfo& wsi, VkSurfaceKHR surface, + bool vsync); - void* GetDisplayHandle() const { return m_display_handle; } - void* GetNativeHandle() const { return m_native_handle; } VkSurfaceKHR GetSurface() const { return m_surface; } VkSurfaceFormatKHR GetSurfaceFormat() const { return m_surface_format; } AbstractTextureFormat GetTextureFormat() const { return m_texture_format; } @@ -89,8 +88,7 @@ private: VkFramebuffer framebuffer; }; - void* m_display_handle; - void* m_native_handle; + WindowSystemInfo m_wsi; VkSurfaceKHR m_surface = VK_NULL_HANDLE; VkSurfaceFormatKHR m_surface_format = {}; VkPresentModeKHR m_present_mode = VK_PRESENT_MODE_RANGE_SIZE_KHR; diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index e38c47eefc..4eaea78865 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -84,11 +84,11 @@ bool VulkanContext::CheckValidationLayerAvailablility() }) != layer_list.end()); } -VkInstance VulkanContext::CreateVulkanInstance(bool enable_surface, bool enable_debug_report, +VkInstance VulkanContext::CreateVulkanInstance(WindowSystemType wstype, bool enable_debug_report, bool enable_validation_layer) { ExtensionList enabled_extensions; - if (!SelectInstanceExtensions(&enabled_extensions, enable_surface, enable_debug_report)) + if (!SelectInstanceExtensions(&enabled_extensions, wstype, enable_debug_report)) return VK_NULL_HANDLE; VkApplicationInfo app_info = {}; @@ -129,7 +129,7 @@ VkInstance VulkanContext::CreateVulkanInstance(bool enable_surface, bool enable_ return instance; } -bool VulkanContext::SelectInstanceExtensions(ExtensionList* extension_list, bool enable_surface, +bool VulkanContext::SelectInstanceExtensions(ExtensionList* extension_list, WindowSystemType wstype, bool enable_debug_report) { u32 extension_count = 0; @@ -172,24 +172,39 @@ bool VulkanContext::SelectInstanceExtensions(ExtensionList* extension_list, bool }; // Common extensions - if (enable_surface && !SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true)) + if (wstype != WindowSystemType::Headless && + !SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true)) + { return false; + } #if defined(VK_USE_PLATFORM_WIN32_KHR) - if (enable_surface && !SupportsExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, true)) + if (wstype == WindowSystemType::Windows && + !SupportsExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, true)) + { return false; -#elif defined(VK_USE_PLATFORM_XLIB_KHR) - if (enable_surface && !SupportsExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, true)) + } +#endif +#if defined(VK_USE_PLATFORM_XLIB_KHR) + if (wstype == WindowSystemType::X11 && + !SupportsExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, true)) + { return false; -#elif defined(VK_USE_PLATFORM_XCB_KHR) - if (enable_surface && !SupportsExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME, true)) + } +#endif +#if defined(VK_USE_PLATFORM_ANDROID_KHR) + if (wstype == WindowSystemType::Android && + !SupportsExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true)) + { return false; -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) - if (enable_surface && !SupportsExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true)) - return false; -#elif defined(VK_USE_PLATFORM_MACOS_MVK) - if (enable_surface && !SupportsExtension(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, true)) + } +#endif +#if defined(VK_USE_PLATFORM_MACOS_MVK) + if (wstype == WindowSystemType::MacOS && + !SupportsExtension(VK_MVK_MACOS_SURFACE_EXTENSION_NAME, true)) + { return false; + } #endif // VK_EXT_debug_report diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.h b/Source/Core/VideoBackends/Vulkan/VulkanContext.h index ec20ded1c8..6b254e24c0 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.h +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.h @@ -8,6 +8,7 @@ #include #include "Common/CommonTypes.h" +#include "Common/WindowSystemInfo.h" #include "VideoBackends/Vulkan/Constants.h" #include "VideoCommon/VideoConfig.h" @@ -23,7 +24,7 @@ public: static bool CheckValidationLayerAvailablility(); // Helper method to create a Vulkan instance. - static VkInstance CreateVulkanInstance(bool enable_surface, bool enable_debug_report, + static VkInstance CreateVulkanInstance(WindowSystemType wstype, bool enable_debug_report, bool enable_validation_layer); // Returns a list of Vulkan-compatible GPUs. @@ -109,7 +110,7 @@ public: private: using ExtensionList = std::vector; - static bool SelectInstanceExtensions(ExtensionList* extension_list, bool enable_surface, + static bool SelectInstanceExtensions(ExtensionList* extension_list, WindowSystemType wstype, bool enable_debug_report); bool SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface); bool SelectDeviceFeatures(); diff --git a/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl b/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl index 54a7f43e24..c40483e10e 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl +++ b/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl @@ -39,28 +39,21 @@ VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceSurfaceFormatsKHR, false) VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceSurfacePresentModesKHR, false) #if defined(VK_USE_PLATFORM_WIN32_KHR) - VULKAN_INSTANCE_ENTRY_POINT(vkCreateWin32SurfaceKHR, false) VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceWin32PresentationSupportKHR, false) +#endif -#elif defined(VK_USE_PLATFORM_XLIB_KHR) - +#if defined(VK_USE_PLATFORM_XLIB_KHR) VULKAN_INSTANCE_ENTRY_POINT(vkCreateXlibSurfaceKHR, false) VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceXlibPresentationSupportKHR, false) +#endif -#elif defined(VK_USE_PLATFORM_XCB_KHR) - -VULKAN_INSTANCE_ENTRY_POINT(vkCreateXcbSurfaceKHR, false) -VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceXcbPresentationSupportKHR, false) - -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) - +#if defined(VK_USE_PLATFORM_ANDROID_KHR) VULKAN_INSTANCE_ENTRY_POINT(vkCreateAndroidSurfaceKHR, false) +#endif -#elif defined(VK_USE_PLATFORM_MACOS_MVK) - +#if defined(VK_USE_PLATFORM_MACOS_MVK) VULKAN_INSTANCE_ENTRY_POINT(vkCreateMacOSSurfaceMVK, false) - #endif VULKAN_INSTANCE_ENTRY_POINT(vkCreateDebugReportCallbackEXT, false) diff --git a/Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp b/Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp index 96f955ce2c..d8d1e99d0c 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanLoader.cpp @@ -13,11 +13,9 @@ #include "VideoBackends/Vulkan/VulkanLoader.h" -#if defined(VK_USE_PLATFORM_WIN32_KHR) +#if defined(_WIN32) #include -#elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) || \ - defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(VK_USE_PLATFORM_MACOS_MVK) || \ - defined(USE_HEADLESS) +#else #include #endif @@ -42,7 +40,7 @@ static void ResetVulkanLibraryFunctionPointers() #undef VULKAN_MODULE_ENTRY_POINT } -#if defined(VK_USE_PLATFORM_WIN32_KHR) +#if defined(_WIN32) static HMODULE vulkan_module; static std::atomic_int vulkan_module_ref_count = {0}; @@ -100,9 +98,7 @@ void UnloadVulkanLibrary() vulkan_module = nullptr; } -#elif defined(VK_USE_PLATFORM_XLIB_KHR) || defined(VK_USE_PLATFORM_XCB_KHR) || \ - defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(VK_USE_PLATFORM_MACOS_MVK) || \ - defined(USE_HEADLESS) +#else static void* vulkan_module; static std::atomic_int vulkan_module_ref_count = {0}; @@ -181,20 +177,6 @@ void UnloadVulkanLibrary() vulkan_module = nullptr; } -#else - -//#warning Unknown platform, not compiling loader. - -bool LoadVulkanLibrary() -{ - return false; -} - -void UnloadVulkanLibrary() -{ - ResetVulkanLibraryFunctionPointers(); -} - #endif bool LoadVulkanInstanceFunctions(VkInstance instance) diff --git a/Source/Core/VideoBackends/Vulkan/VulkanLoader.h b/Source/Core/VideoBackends/Vulkan/VulkanLoader.h index 3ae2584f8b..1bb0cae867 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanLoader.h +++ b/Source/Core/VideoBackends/Vulkan/VulkanLoader.h @@ -8,17 +8,18 @@ #if defined(WIN32) #define VK_USE_PLATFORM_WIN32_KHR -#elif defined(HAVE_X11) -// Currently we're getting xlib handles passed to the backend. -// If this ever changes to xcb, it's a simple change here. +#endif + +#if defined(HAVE_X11) #define VK_USE_PLATFORM_XLIB_KHR -//#define VK_USE_PLATFORM_XCB_KHR -#elif defined(ANDROID) +#endif + +#if defined(ANDROID) #define VK_USE_PLATFORM_ANDROID_KHR -#elif defined(__APPLE__) +#endif + +#if defined(__APPLE__) #define VK_USE_PLATFORM_MACOS_MVK -#else -//#warning Unknown platform #endif #include "vulkan/vulkan.h" diff --git a/Source/Core/VideoBackends/Vulkan/main.cpp b/Source/Core/VideoBackends/Vulkan/main.cpp index b5eeb51da6..8055aab178 100644 --- a/Source/Core/VideoBackends/Vulkan/main.cpp +++ b/Source/Core/VideoBackends/Vulkan/main.cpp @@ -36,7 +36,8 @@ void VideoBackend::InitBackendInfo() if (LoadVulkanLibrary()) { - VkInstance temp_instance = VulkanContext::CreateVulkanInstance(false, false, false); + VkInstance temp_instance = + VulkanContext::CreateVulkanInstance(WindowSystemType::Headless, false, false); if (temp_instance) { if (LoadVulkanInstanceFunctions(temp_instance)) @@ -111,10 +112,10 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) // Create Vulkan instance, needed before we can create a surface, or enumerate devices. // We use this instance to fill in backend info, then re-use it for the actual device. - bool enable_surface = wsi.render_surface != nullptr; + bool enable_surface = wsi.type != WindowSystemType::Headless; bool enable_debug_reports = ShouldEnableDebugReports(enable_validation_layer); - VkInstance instance = VulkanContext::CreateVulkanInstance(enable_surface, enable_debug_reports, - enable_validation_layer); + VkInstance instance = + VulkanContext::CreateVulkanInstance(wsi.type, enable_debug_reports, enable_validation_layer); if (instance == VK_NULL_HANDLE) { PanicAlert("Failed to create Vulkan instance."); @@ -150,7 +151,7 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) VkSurfaceKHR surface = VK_NULL_HANDLE; if (enable_surface) { - surface = SwapChain::CreateVulkanSurface(instance, wsi.display_connection, wsi.render_surface); + surface = SwapChain::CreateVulkanSurface(instance, wsi); if (surface == VK_NULL_HANDLE) { PanicAlert("Failed to create Vulkan surface."); @@ -213,8 +214,7 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) std::unique_ptr swap_chain; if (surface != VK_NULL_HANDLE) { - swap_chain = SwapChain::Create(wsi.display_connection, wsi.render_surface, surface, - g_ActiveConfig.bVSyncActive); + swap_chain = SwapChain::Create(wsi, surface, g_ActiveConfig.bVSyncActive); if (!swap_chain) { PanicAlert("Failed to create Vulkan swap chain."); From 836f76da898ee514022d7b2c84a88f6dcc1a4c0b Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 24 Oct 2018 15:50:54 +1000 Subject: [PATCH 2/3] DolphinNoGUI: Runtime selection of platform --- Source/Core/DolphinNoGUI/CMakeLists.txt | 6 + Source/Core/DolphinNoGUI/MainNoGUI.cpp | 342 +++--------------- Source/Core/DolphinNoGUI/Platform.cpp | 53 +++ Source/Core/DolphinNoGUI/Platform.h | 48 +++ Source/Core/DolphinNoGUI/PlatformHeadless.cpp | 50 +++ Source/Core/DolphinNoGUI/PlatformX11.cpp | 270 ++++++++++++++ 6 files changed, 474 insertions(+), 295 deletions(-) create mode 100644 Source/Core/DolphinNoGUI/Platform.cpp create mode 100644 Source/Core/DolphinNoGUI/Platform.h create mode 100644 Source/Core/DolphinNoGUI/PlatformHeadless.cpp create mode 100644 Source/Core/DolphinNoGUI/PlatformX11.cpp diff --git a/Source/Core/DolphinNoGUI/CMakeLists.txt b/Source/Core/DolphinNoGUI/CMakeLists.txt index 34890d294e..b14152d194 100644 --- a/Source/Core/DolphinNoGUI/CMakeLists.txt +++ b/Source/Core/DolphinNoGUI/CMakeLists.txt @@ -3,9 +3,15 @@ if(NOT((ENABLE_X11 AND X11_FOUND) OR ENABLE_HEADLESS)) endif() add_executable(dolphin-nogui + Platform.cpp + PlatformHeadless.cpp MainNoGUI.cpp ) +if(ENABLE_X11 AND X11_FOUND) + target_sources(dolphin-nogui PRIVATE PlatformX11.cpp) +endif() + set_target_properties(dolphin-nogui PROPERTIES OUTPUT_NAME dolphin-emu-nogui) target_link_libraries(dolphin-nogui diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp index 697e4cbe6d..c5ec03430f 100644 --- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp +++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp @@ -2,30 +2,23 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "DolphinNoGUI/Platform.h" + #include #include #include #include #include #include -#include +#ifndef _WIN32 #include - -#include "Common/CommonTypes.h" -#include "Common/Event.h" -#include "Common/Flag.h" -#include "Common/Logging/LogManager.h" -#include "Common/MsgHandler.h" +#endif #include "Core/Analytics.h" #include "Core/Boot/Boot.h" #include "Core/BootManager.h" -#include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/Host.h" -#include "Core/IOS/IOS.h" -#include "Core/IOS/STM/STM.h" -#include "Core/State.h" #include "UICommon/CommandLineParse.h" #ifdef USE_DISCORD_PRESENCE @@ -36,49 +29,22 @@ #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 std::unique_ptr s_platform; static void signal_handler(int) { const char message[] = "A signal was received. A second signal will force Dolphin to stop.\n"; +#ifdef _WIN32 + puts(message); +#else if (write(STDERR_FILENO, message, sizeof(message)) < 0) { } - s_shutdown_requested.Set(); +#endif + + s_platform->RequestShutdown(); } -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() {} - - virtual WindowSystemType GetWindowSystem() const { return WindowSystemType::Headless; } - virtual void* GetDisplayHandle() const { return nullptr; } - virtual void* GetWindowHandle() const { return nullptr; } -}; - -static Platform* platform; - void Host_NotifyMapLoaded() { } @@ -86,18 +52,16 @@ void Host_RefreshDSPDebuggerWindow() { } -static Common::Event updateMainFrameEvent; +static Common::Event s_update_main_frame_event; void Host_Message(HostMessageID id) { if (id == HostMessageID::WMUserStop) - s_running.Clear(); - if (id == HostMessageID::WMUserJobDispatch || id == HostMessageID::WMUserStop) - updateMainFrameEvent.Set(); + s_platform->Stop(); } void Host_UpdateTitle(const std::string& title) { - platform->SetTitle(title); + s_platform->SetTitle(title); } void Host_UpdateDisasmDialog() @@ -106,7 +70,7 @@ void Host_UpdateDisasmDialog() void Host_UpdateMainFrame() { - updateMainFrameEvent.Set(); + s_update_main_frame_event.Set(); } void Host_RequestRenderWindowSize(int width, int height) @@ -120,12 +84,12 @@ bool Host_UINeedsControllerState() bool Host_RendererHasFocus() { - return rendererHasFocus; + return s_platform->IsWindowFocused(); } bool Host_RendererIsFullscreen() { - return rendererIsFullscreen; + return s_platform->IsWindowFullscreen(); } void Host_YieldToUI() @@ -136,232 +100,35 @@ void Host_UpdateProgressDialog(const char* caption, int position, int total) { } +static std::unique_ptr GetPlatform(const optparse::Values& options) +{ + std::string platform_name = static_cast(options.get("platform")); + #if HAVE_X11 -#include -#include -#include -#include "UICommon/X11Utils.h" -#ifndef HOST_NAME_MAX -#include -#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX + if (platform_name == "x11" || platform_name.empty()) + return Platform::CreateX11Platform(); #endif -class PlatformX11 : public Platform -{ - Display* dpy; - Window win; - Cursor blankCursor = None; -#if defined(HAVE_XRANDR) && HAVE_XRANDR - X11Utils::XRRConfiguration* XRRConfig; -#endif + if (platform_name == "headless" || platform_name.empty()) + return Platform::CreateHeadlessPlatform(); - 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); - pid_t pid = getpid(); - XChangeProperty(dpy, win, XInternAtom(dpy, "_NET_WM_PID", False), XA_CARDINAL, 32, - PropModeReplace, reinterpret_cast(&pid), 1); - char host_name[HOST_NAME_MAX] = ""; - if (!gethostname(host_name, sizeof(host_name))) - { - XTextProperty wmClientMachine = {reinterpret_cast(host_name), XA_STRING, 8, - strlen(host_name)}; - XSetWMClientMachine(dpy, win, &wmClientMachine); - } - XMapRaised(dpy, win); - XFlush(dpy); - - if (SConfig::GetInstance().bDisableScreenSaver) - X11Utils::InhibitScreensaver(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; - 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 ios = IOS::HLE::GetIOS(); - const auto stm = ios ? ios->GetDeviceByName("/dev/stm/eventhook") : nullptr; - if (!s_tried_graceful_shutdown.IsSet() && stm && - std::static_pointer_cast(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) - { - s_shutdown_requested.Set(); - } - else if (key == XK_F10) - { - 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 (g_renderer) - g_renderer->ResizeSurface(); - } - 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); - } - - WindowSystemType GetWindowSystem() const override { return WindowSystemType::X11; } - void* GetDisplayHandle() const override { return static_cast(dpy); } - void* GetWindowHandle() const override { return reinterpret_cast(win); } -}; -#endif - -static Platform* GetPlatform() -{ -#if 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); + parser->add_option("-p", "--platform") + .action("store") + .help("Window platform to use [%choices]") + .choices({ + "headless" +#if HAVE_X11 + , + "x11" +#endif + }); + optparse::Values& options = CommandLineParse::ParseArguments(parser.get(), argc, argv); std::vector args = parser->args(); @@ -398,25 +165,22 @@ int main(int argc, char* argv[]) std::string user_directory; if (options.is_set("user")) - { user_directory = static_cast(options.get("user")); - } - - platform = GetPlatform(); - if (!platform) - { - fprintf(stderr, "No platform found\n"); - return 1; - } UICommon::SetUserDirectory(user_directory); UICommon::Init(); + s_platform = GetPlatform(options); + if (!s_platform || !s_platform->Init()) + { + fprintf(stderr, "No platform found, or failed to initialize.\n"); + return 1; + } + Core::SetOnStateChangedCallback([](Core::State state) { if (state == Core::State::Uninitialized) - s_running.Clear(); + s_platform->Stop(); }); - platform->Init(); // Shut down cleanly on SIGINT and SIGTERM struct sigaction sa; @@ -428,10 +192,7 @@ int main(int argc, char* argv[]) DolphinAnalytics::Instance()->ReportDolphinStart("nogui"); - WindowSystemInfo wsi(platform->GetWindowSystem(), platform->GetDisplayHandle(), - platform->GetWindowHandle()); - - if (!BootManager::BootCore(std::move(boot), wsi)) + if (!BootManager::BootCore(std::move(boot), s_platform->GetWindowSystemInfo())) { fprintf(stderr, "Could not boot the specified file\n"); return 1; @@ -441,21 +202,12 @@ int main(int argc, char* argv[]) Discord::UpdateDiscordPresence(); #endif - while (!Core::IsRunning() && s_running.IsSet()) - { - Core::HostDispatchJobs(); - updateMainFrameEvent.Wait(); - } - - if (s_running.IsSet()) - platform->MainLoop(); + s_platform->MainLoop(); Core::Stop(); Core::Shutdown(); - platform->Shutdown(); + s_platform.reset(); UICommon::Shutdown(); - delete platform; - return 0; } diff --git a/Source/Core/DolphinNoGUI/Platform.cpp b/Source/Core/DolphinNoGUI/Platform.cpp new file mode 100644 index 0000000000..69b184bfe9 --- /dev/null +++ b/Source/Core/DolphinNoGUI/Platform.cpp @@ -0,0 +1,53 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinNoGUI/Platform.h" +#include "Core/IOS/IOS.h" +#include "Core/IOS/STM/STM.h" +#include "Core/State.h" + +namespace ProcessorInterface +{ +void PowerButton_Tap(); +} + +Platform::~Platform() = default; + +bool Platform::Init() +{ + return true; +} + +void Platform::SetTitle(const std::string& title) +{ +} + +void Platform::UpdateRunningFlag() +{ + if (m_shutdown_requested.TestAndClear()) + { + const auto ios = IOS::HLE::GetIOS(); + const auto stm = ios ? ios->GetDeviceByName("/dev/stm/eventhook") : nullptr; + if (!m_tried_graceful_shutdown.IsSet() && stm && + std::static_pointer_cast(stm)->HasHookInstalled()) + { + ProcessorInterface::PowerButton_Tap(); + m_tried_graceful_shutdown.Set(); + } + else + { + m_running.Clear(); + } + } +} + +void Platform::Stop() +{ + m_running.Clear(); +} + +void Platform::RequestShutdown() +{ + m_shutdown_requested.Set(); +} diff --git a/Source/Core/DolphinNoGUI/Platform.h b/Source/Core/DolphinNoGUI/Platform.h new file mode 100644 index 0000000000..19edbb1168 --- /dev/null +++ b/Source/Core/DolphinNoGUI/Platform.h @@ -0,0 +1,48 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/Flag.h" +#include "Common/WindowSystemInfo.h" + +class Platform +{ +public: + virtual ~Platform(); + + bool IsRunning() const { return m_running.IsSet(); } + bool IsWindowFocused() const { return m_window_focus; } + bool IsWindowFullscreen() const { return m_window_fullscreen; } + + virtual bool Init(); + virtual void SetTitle(const std::string& title); + virtual void MainLoop() = 0; + + virtual WindowSystemInfo GetWindowSystemInfo() const = 0; + + // Requests a graceful shutdown, from SIGINT/SIGTERM. + void RequestShutdown(); + + // Request an immediate shutdown. + void Stop(); + + static std::unique_ptr CreateHeadlessPlatform(); +#ifdef HAVE_X11 + static std::unique_ptr CreateX11Platform(); +#endif + +protected: + void UpdateRunningFlag(); + + Common::Flag m_running{true}; + Common::Flag m_shutdown_requested{false}; + Common::Flag m_tried_graceful_shutdown{false}; + + bool m_window_focus = true; + bool m_window_fullscreen = false; +}; diff --git a/Source/Core/DolphinNoGUI/PlatformHeadless.cpp b/Source/Core/DolphinNoGUI/PlatformHeadless.cpp new file mode 100644 index 0000000000..b848492152 --- /dev/null +++ b/Source/Core/DolphinNoGUI/PlatformHeadless.cpp @@ -0,0 +1,50 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include "Core/Core.h" +#include "DolphinNoGUI/Platform.h" + +namespace +{ +class PlatformHeadless : public Platform +{ +public: + void SetTitle(const std::string& title) override; + void MainLoop() override; + + WindowSystemInfo GetWindowSystemInfo() const override; +}; + +void PlatformHeadless::SetTitle(const std::string& title) +{ + std::fprintf(stdout, "%s\n", title.c_str()); +} + +void PlatformHeadless::MainLoop() +{ + while (m_running.IsSet()) + { + UpdateRunningFlag(); + Core::HostDispatchJobs(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +} + +WindowSystemInfo PlatformHeadless::GetWindowSystemInfo() const +{ + WindowSystemInfo wsi; + wsi.type = WindowSystemType::Headless; + wsi.display_connection = nullptr; + wsi.render_surface = nullptr; + return wsi; +} + +} // namespace + +std::unique_ptr Platform::CreateHeadlessPlatform() +{ + return std::make_unique(); +} diff --git a/Source/Core/DolphinNoGUI/PlatformX11.cpp b/Source/Core/DolphinNoGUI/PlatformX11.cpp new file mode 100644 index 0000000000..31a8a2e28d --- /dev/null +++ b/Source/Core/DolphinNoGUI/PlatformX11.cpp @@ -0,0 +1,270 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include "DolphinNoGUI/Platform.h" + +#include "Common/MsgHandler.h" +#include "Core/ConfigManager.h" +#include "Core/Core.h" +#include "Core/State.h" + +#include +#include + +#include +#include +#include +#include "UICommon/X11Utils.h" +#include "VideoCommon/RenderBase.h" + +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +#endif + +namespace +{ +class PlatformX11 : public Platform +{ +public: + ~PlatformX11() override; + + bool Init() override; + void SetTitle(const std::string& string) override; + void MainLoop() override; + + WindowSystemInfo GetWindowSystemInfo() const; + +private: + void CloseDisplay(); + void UpdateWindowPosition(); + void ProcessEvents(); + + Display* m_display = nullptr; + Window m_window = {}; + Cursor m_blank_cursor = None; +#if defined(HAVE_XRANDR) && HAVE_XRANDR + X11Utils::XRRConfiguration* m_xrr_config = nullptr; +#endif +}; + +PlatformX11::~PlatformX11() +{ +#if defined(HAVE_XRANDR) && HAVE_XRANDR + delete m_xrr_config; +#endif + + if (m_display) + { + if (SConfig::GetInstance().bHideCursor) + XFreeCursor(m_display, m_blank_cursor); + + XCloseDisplay(m_display); + } +} + +bool PlatformX11::Init() +{ + XInitThreads(); + m_display = XOpenDisplay(nullptr); + if (!m_display) + { + PanicAlert("No X11 display found"); + return false; + } + + m_window = XCreateSimpleWindow( + m_display, DefaultRootWindow(m_display), SConfig::GetInstance().iRenderWindowXPos, + SConfig::GetInstance().iRenderWindowYPos, SConfig::GetInstance().iRenderWindowWidth, + SConfig::GetInstance().iRenderWindowHeight, 0, 0, BlackPixel(m_display, 0)); + XSelectInput(m_display, m_window, StructureNotifyMask | KeyPressMask | FocusChangeMask); + Atom wmProtocols[1]; + wmProtocols[0] = XInternAtom(m_display, "WM_DELETE_WINDOW", True); + XSetWMProtocols(m_display, m_window, wmProtocols, 1); + pid_t pid = getpid(); + XChangeProperty(m_display, m_window, XInternAtom(m_display, "_NET_WM_PID", False), XA_CARDINAL, + 32, PropModeReplace, reinterpret_cast(&pid), 1); + char host_name[HOST_NAME_MAX] = ""; + if (!gethostname(host_name, sizeof(host_name))) + { + XTextProperty wmClientMachine = {reinterpret_cast(host_name), XA_STRING, 8, + strlen(host_name)}; + XSetWMClientMachine(m_display, m_window, &wmClientMachine); + } + XMapRaised(m_display, m_window); + XFlush(m_display); + XSync(m_display, True); + ProcessEvents(); + + if (SConfig::GetInstance().bDisableScreenSaver) + X11Utils::InhibitScreensaver(m_window, true); + +#if defined(HAVE_XRANDR) && HAVE_XRANDR + m_xrr_config = new X11Utils::XRRConfiguration(m_display, m_window); +#endif + + if (SConfig::GetInstance().bHideCursor) + { + // make a blank cursor + Pixmap Blank; + XColor DummyColor; + char ZeroData[1] = {0}; + Blank = XCreateBitmapFromData(m_display, m_window, ZeroData, 1, 1); + m_blank_cursor = XCreatePixmapCursor(m_display, Blank, Blank, &DummyColor, &DummyColor, 0, 0); + XFreePixmap(m_display, Blank); + XDefineCursor(m_display, m_window, m_blank_cursor); + } + + // Enter fullscreen if enabled. + if (SConfig::GetInstance().bFullscreen) + { + m_window_fullscreen = X11Utils::ToggleFullscreen(m_display, m_window); +#if defined(HAVE_XRANDR) && HAVE_XRANDR + m_xrr_config->ToggleDisplayMode(True); +#endif + ProcessEvents(); + } + + UpdateWindowPosition(); + return true; +} + +void PlatformX11::SetTitle(const std::string& string) +{ + XStoreName(m_display, m_window, string.c_str()); +} + +void PlatformX11::MainLoop() +{ + while (IsRunning()) + { + UpdateRunningFlag(); + Core::HostDispatchJobs(); + ProcessEvents(); + UpdateWindowPosition(); + + // TODO: Is this sleep appropriate? + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +WindowSystemInfo PlatformX11::GetWindowSystemInfo() const +{ + WindowSystemInfo wsi; + wsi.type = WindowSystemType::X11; + wsi.display_connection = static_cast(m_display); + wsi.render_surface = reinterpret_cast(m_window); + return wsi; +} + +void PlatformX11::UpdateWindowPosition() +{ + if (m_window_fullscreen) + return; + + Window winDummy; + unsigned int borderDummy, depthDummy; + XGetGeometry(m_display, m_window, &winDummy, &SConfig::GetInstance().iRenderWindowXPos, + &SConfig::GetInstance().iRenderWindowYPos, + reinterpret_cast(&SConfig::GetInstance().iRenderWindowWidth), + reinterpret_cast(&SConfig::GetInstance().iRenderWindowHeight), + &borderDummy, &depthDummy); +} + +void PlatformX11::ProcessEvents() +{ + XEvent event; + KeySym key; + for (int num_events = XPending(m_display); num_events > 0; num_events--) + { + XNextEvent(m_display, &event); + switch (event.type) + { + case KeyPress: + key = XLookupKeysym((XKeyEvent*)&event, 0); + if (key == XK_Escape) + { + RequestShutdown(); + } + else if (key == XK_F10) + { + if (Core::GetState() == Core::State::Running) + { + if (SConfig::GetInstance().bHideCursor) + XUndefineCursor(m_display, m_window); + Core::SetState(Core::State::Paused); + } + else + { + if (SConfig::GetInstance().bHideCursor) + XDefineCursor(m_display, m_window, m_blank_cursor); + Core::SetState(Core::State::Running); + } + } + else if ((key == XK_Return) && (event.xkey.state & Mod1Mask)) + { + m_window_fullscreen = !m_window_fullscreen; + X11Utils::ToggleFullscreen(m_display, m_window); +#if defined(HAVE_XRANDR) && HAVE_XRANDR + m_xrr_config->ToggleDisplayMode(m_window_fullscreen); +#endif + UpdateWindowPosition(); + } + 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: + { + m_window_focus = true; + if (SConfig::GetInstance().bHideCursor && Core::GetState() != Core::State::Paused) + XDefineCursor(m_display, m_window, m_blank_cursor); + } + break; + case FocusOut: + { + m_window_focus = false; + if (SConfig::GetInstance().bHideCursor) + XUndefineCursor(m_display, m_window); + } + break; + case ClientMessage: + { + if ((unsigned long)event.xclient.data.l[0] == + XInternAtom(m_display, "WM_DELETE_WINDOW", False)) + Stop(); + } + break; + case ConfigureNotify: + { + if (g_renderer) + g_renderer->ResizeSurface(); + } + break; + } + } +} +} // namespace + +std::unique_ptr Platform::CreateX11Platform() +{ + return std::make_unique(); +} From eabde77892faf4408481f685f2fbf1b348232d5f Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 24 Oct 2018 18:14:50 +1000 Subject: [PATCH 3/3] CMake: Add an ENABLE_NOGUI option --- CMakeLists.txt | 6 +++++- Source/Core/CMakeLists.txt | 5 ++++- Source/Core/DolphinNoGUI/CMakeLists.txt | 4 ---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 99b3e58d89..315f75591c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ endif() option(USE_SHARED_ENET "Use shared libenet if found rather than Dolphin's soon-to-compatibly-diverge version" OFF) option(USE_UPNP "Enables UPnP port mapping support" ON) +option(ENABLE_NOGUI "Enable NoGUI frontend" ON) option(ENABLE_QT "Enable Qt (Default)" ON) option(ENABLE_LTO "Enables Link Time Optimization" OFF) option(ENABLE_GENERIC "Enables generic build that should run on any little-endian host" OFF) @@ -364,6 +365,10 @@ if(ANDROID) message(STATUS "Building for Android") if(NOT ENABLE_HEADLESS) add_definitions(-DANDROID) + if(ENABLE_NOGUI) + message(STATUS "Building Android app, disabling NoGUI frontend.") + set(ENABLE_NOGUI 0) + endif() else() # Lie to cmake a bit. We are cross compiling to Android # but not as a shared library. We want an executable. @@ -384,7 +389,6 @@ if(ENABLE_HEADLESS) message(STATUS "Enabling Headless! Disabling GUI.") set(ENABLE_QT 0) set(USE_DISCORD_PRESENCE 0) - add_definitions(-DUSE_HEADLESS) endif() # Set file offset size to 64 bits. diff --git a/Source/Core/CMakeLists.txt b/Source/Core/CMakeLists.txt index 1eb5f30a4b..522f3e3e37 100644 --- a/Source/Core/CMakeLists.txt +++ b/Source/Core/CMakeLists.txt @@ -2,12 +2,15 @@ add_subdirectory(AudioCommon) add_subdirectory(Common) add_subdirectory(Core) add_subdirectory(DiscIO) -add_subdirectory(DolphinNoGUI) add_subdirectory(InputCommon) add_subdirectory(UICommon) add_subdirectory(VideoCommon) add_subdirectory(VideoBackends) +if(ENABLE_NOGUI) + add_subdirectory(DolphinNoGUI) +endif() + if(ENABLE_QT) add_subdirectory(DolphinQt) endif() diff --git a/Source/Core/DolphinNoGUI/CMakeLists.txt b/Source/Core/DolphinNoGUI/CMakeLists.txt index b14152d194..b2bf174d8b 100644 --- a/Source/Core/DolphinNoGUI/CMakeLists.txt +++ b/Source/Core/DolphinNoGUI/CMakeLists.txt @@ -1,7 +1,3 @@ -if(NOT((ENABLE_X11 AND X11_FOUND) OR ENABLE_HEADLESS)) - return() -endif() - add_executable(dolphin-nogui Platform.cpp PlatformHeadless.cpp