From 49602cedde34860355a3ce0203e3003bb2f984b6 Mon Sep 17 00:00:00 2001 From: Hyblocker Date: Fri, 29 Aug 2025 01:03:41 +0200 Subject: [PATCH] (chore): Abstract away window creation --- src/overlay/main.cpp | 192 +++------------------------------ src/overlay/user_interface.cpp | 7 ++ src/overlay/user_interface.h | 8 ++ src/overlay/window.cpp | 180 +++++++++++++++++++++++++++++++ src/overlay/window.h | 25 +++++ 5 files changed, 232 insertions(+), 180 deletions(-) create mode 100644 src/overlay/user_interface.cpp create mode 100644 src/overlay/user_interface.h create mode 100644 src/overlay/window.cpp create mode 100644 src/overlay/window.h diff --git a/src/overlay/main.cpp b/src/overlay/main.cpp index b2b8b86..3d56654 100644 --- a/src/overlay/main.cpp +++ b/src/overlay/main.cpp @@ -2,199 +2,31 @@ #include #include "util.h" #include "log.h" - -#include -#define GLFW_EXPOSE_NATIVE_WIN32 -#include -#include - -#include -#include -#include -#include - -static GLFWwindow* glfwWindow = nullptr; -static GLuint fboHandle = 0, fboTextureHandle = 0; -static int fboTextureWidth = 0, fboTextureHeight = 0; - -const float MINIMIZED_MAX_FPS = 60.0f; +#include "window.h" extern "C" __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; extern "C" __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; -void CreateGLFWWindow() -{ - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_RESIZABLE, false); - -#ifdef DEBUG_LOGS - glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); -#endif - - fboTextureWidth = 1200; - fboTextureHeight = 800; - - glfwWindow = glfwCreateWindow(fboTextureWidth, fboTextureHeight, "Space Calibrator", nullptr, nullptr); - if (!glfwWindow) - throw std::runtime_error("Failed to create window"); - - glfwMakeContextCurrent(glfwWindow); - glfwSwapInterval(1); - gladLoadGL(); - - // Minimise the window - glfwIconifyWindow(glfwWindow); - HWND windowHwmd = glfwGetWin32Window(glfwWindow); - // EnableDarkModeTopBar(windowHwmd); - - // Load icon and set it in the window - // GLFWimage images[1] = {}; - // std::string iconPath = cwd; - // iconPath += "\\taskbar_icon.png"; - // images[0].pixels = stbi_load(iconPath.c_str(), &images[0].width, &images[0].height, 0, 4); - // glfwSetWindowIcon(glfwWindow, 1, images); - // stbi_image_free(images[0].pixels); - -#ifdef DEBUG_LOGS - glDebugMessageCallback(openGLDebugCallback, nullptr); - glEnable(GL_DEBUG_OUTPUT); -#endif - - ImGui::CreateContext(); - // ImPlot::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; - io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; - io.IniFilename = nullptr; - // io.Fonts->AddFontFromMemoryCompressedTTF(DroidSans_compressed_data, DroidSans_compressed_size, 24.0f); - - ImGui_ImplGlfw_InitForOpenGL(glfwWindow, true); - ImGui_ImplOpenGL3_Init("#version 330"); - - ImGui::StyleColorsDark(); - - glGenTextures(1, &fboTextureHandle); - glBindTexture(GL_TEXTURE_2D, fboTextureHandle); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, fboTextureWidth, fboTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - glGenFramebuffers(1, &fboHandle); - glBindFramebuffer(GL_FRAMEBUFFER, fboHandle); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, fboTextureHandle, 0); - - GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 }; - glDrawBuffers(1, drawBuffers); - - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) - { - // @TODO: Avoid exceptions - throw std::runtime_error("OpenGL framebuffer incomplete"); - } -} - -double lastFrameStartTime = glfwGetTime(); -void RunLoop() { - while (!glfwWindowShouldClose(glfwWindow)) - { - double time = glfwGetTime(); - - int width, height; - glfwGetFramebufferSize(glfwWindow, &width, &height); - const bool windowVisible = (width > 0 && height > 0); - - auto& io = ImGui::GetIO(); - - // These change state now, so we must execute these before doing our own modifications to the io state for VR - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - - io.DisplaySize = ImVec2((float)fboTextureWidth, (float)fboTextureHeight); - io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f); - - io.ConfigFlags = io.ConfigFlags & ~ImGuiConfigFlags_NoMouseCursorChange; - // if (dashboardVisible) { - // io.ConfigFlags = io.ConfigFlags | ImGuiConfigFlags_NoMouseCursorChange; - // } - - ImGui::NewFrame(); - - ImGui::ShowDemoWindow(); - - ImGui::EndFrame(); - ImGui::Render(); - - glBindFramebuffer(GL_FRAMEBUFFER, fboHandle); - glViewport(0, 0, fboTextureWidth, fboTextureHeight); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - if (width && height) - { - glBindFramebuffer(GL_READ_FRAMEBUFFER, fboHandle); - glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); - glfwSwapBuffers(glfwWindow); - } - - - const double dashboardInterval = 1.0 / 90.0; // fps - // double waitEventsTimeout = std::max(CalCtx.wantedUpdateInterval, dashboardInterval); - double waitEventsTimeout = dashboardInterval; - - glfwWaitEventsTimeout(waitEventsTimeout); - - // If we're minimized rendering won't limit our frame rate so we need to do it ourselves. - if (glfwGetWindowAttrib(glfwWindow, GLFW_ICONIFIED)) - { - double targetFrameTime = 1 / MINIMIZED_MAX_FPS; - double waitTime = targetFrameTime - (glfwGetTime() - lastFrameStartTime); - if (waitTime > 0) - { - std::this_thread::sleep_for(std::chrono::duration(waitTime)); - } - - lastFrameStartTime += targetFrameTime; - } - } -} - int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { util::init(); logging::Init(/* isOverlay */ true); - if (!glfwInit()) - { - const char* szGlfwError = nullptr; - glfwGetError(&szGlfwError); - LOG_FATAL("Failed to initialise GLFW, got {}.", szGlfwError); - MessageBoxW(nullptr, L"Failed to initialize GLFW", L"An error occured initialising Space Calibrator Nova", 0); - return 0; + spacecal::Window* theWindow = new spacecal::Window; + if (!theWindow) { + // @TODO: MessageBox + return -1; } - CreateGLFWWindow(); + if (!theWindow->CreateNativeWindow()) { + return -1; + } - LOG_INFO("made window hiiii"); + LOG_INFO("Started Space Calibrator Nova!"); - RunLoop(); + theWindow->RunLoop(); - if (fboHandle) - glDeleteFramebuffers(1, &fboHandle); - - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - // ImPlot::DestroyContext(); - ImGui::DestroyContext(); - - if (glfwWindow) - glfwDestroyWindow(glfwWindow); - - glfwTerminate(); + theWindow->Shutdown(); + delete theWindow; return 0; } \ No newline at end of file diff --git a/src/overlay/user_interface.cpp b/src/overlay/user_interface.cpp new file mode 100644 index 0000000..95ae17b --- /dev/null +++ b/src/overlay/user_interface.cpp @@ -0,0 +1,7 @@ +#include "user_interface.h" + +namespace spacecal { + void DrawInterface() { + ImGui::ShowDemoWindow(); + } +} \ No newline at end of file diff --git a/src/overlay/user_interface.h b/src/overlay/user_interface.h new file mode 100644 index 0000000..ac48d8d --- /dev/null +++ b/src/overlay/user_interface.h @@ -0,0 +1,8 @@ +#pragma once + +#include "imgui.h" + +namespace spacecal { + // Draws the user interface with ImGUI + void DrawInterface(); +} \ No newline at end of file diff --git a/src/overlay/window.cpp b/src/overlay/window.cpp new file mode 100644 index 0000000..6908055 --- /dev/null +++ b/src/overlay/window.cpp @@ -0,0 +1,180 @@ +#include "window.h" +#include "log.h" +#include "user_interface.h" + +#include +#include +#include +#include + +namespace spacecal { + bool Window::CreateNativeWindow() { + if (!glfwInit()) + { + const char* szGlfwError = nullptr; + glfwGetError(&szGlfwError); + LOG_FATAL("Failed to initialise GLFW, got {}.", szGlfwError); + MessageBoxW(nullptr, L"Failed to initialize GLFW", L"An error occured initialising Space Calibrator Nova", 0); + return false; + } + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_RESIZABLE, false); + +#ifdef _DEBUG + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE); +#endif + + m_glfwWindow = glfwCreateWindow(m_fboTextureWidth, m_fboTextureHeight, "Space Calibrator", nullptr, nullptr); + if (!m_glfwWindow) { + LOG_FATAL("Failed to create GLFW window"); + MessageBoxW(nullptr, L"Failed to create GLFW window", L"An error occured initialising Space Calibrator Nova", 0); + return false; + } + + glfwMakeContextCurrent(m_glfwWindow); + glfwSwapInterval(1); + gladLoadGL(); + + // Minimise the window + glfwIconifyWindow(m_glfwWindow); + HWND windowHwmd = glfwGetWin32Window(m_glfwWindow); + // EnableDarkModeTopBar(windowHwmd); + + // Load icon and set it in the window + // GLFWimage images[1] = {}; + // std::string iconPath = cwd; + // iconPath += "\\taskbar_icon.png"; + // images[0].pixels = stbi_load(iconPath.c_str(), &images[0].width, &images[0].height, 0, 4); + // glfwSetWindowIcon(glfwWindow, 1, images); + // stbi_image_free(images[0].pixels); + +#ifdef _DEBUG + // glDebugMessageCallback(openGLDebugCallback, nullptr); + // glEnable(GL_DEBUG_OUTPUT); +#endif + + ImGui::CreateContext(); + // ImPlot::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; + io.IniFilename = nullptr; + + // load resources + // io.Fonts->AddFontFromMemoryCompressedTTF(DroidSans_compressed_data, DroidSans_compressed_size, 24.0f); + + ImGui_ImplGlfw_InitForOpenGL(m_glfwWindow, true); + ImGui_ImplOpenGL3_Init("#version 330"); + + ImGui::StyleColorsDark(); + + glGenTextures(1, &m_fboTextureHandle); + glBindTexture(GL_TEXTURE_2D, m_fboTextureHandle); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_fboTextureWidth, m_fboTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glGenFramebuffers(1, &m_fboHandle); + glBindFramebuffer(GL_FRAMEBUFFER, m_fboHandle); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_fboTextureHandle, 0); + + GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 }; + glDrawBuffers(1, drawBuffers); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + LOG_FATAL("OpenGL framebuffer incomplete."); + MessageBoxW(nullptr, L"OpenGL framebuffer incomplete", L"An error occured initialising Space Calibrator Nova", 0); + return false; + } + + return true; + } + + void Window::Shutdown() { + if (m_fboHandle) + glDeleteFramebuffers(1, &m_fboHandle); + + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + // ImPlot::DestroyContext(); + ImGui::DestroyContext(); + + if (m_glfwWindow) + glfwDestroyWindow(m_glfwWindow); + + glfwTerminate(); + } + + void Window::RunLoop() { + double lastFrameStartTime = glfwGetTime(); + while (!glfwWindowShouldClose(m_glfwWindow)) + { + double time = glfwGetTime(); + + int width, height; + glfwGetFramebufferSize(m_glfwWindow, &width, &height); + const bool windowVisible = (width > 0 && height > 0); + + auto& io = ImGui::GetIO(); + + // These change state now, so we must execute these before doing our own modifications to the io state for VR + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + + io.DisplaySize = ImVec2((float)m_fboTextureWidth, (float)m_fboTextureHeight); + io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f); + + io.ConfigFlags = io.ConfigFlags & ~ImGuiConfigFlags_NoMouseCursorChange; + // if (dashboardVisible) { + // io.ConfigFlags = io.ConfigFlags | ImGuiConfigFlags_NoMouseCursorChange; + // } + + ImGui::NewFrame(); + + spacecal::DrawInterface(); + + ImGui::EndFrame(); + ImGui::Render(); + + glBindFramebuffer(GL_FRAMEBUFFER, m_fboHandle); + glViewport(0, 0, m_fboTextureWidth, m_fboTextureHeight); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + if (width && height) + { + glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fboHandle); + glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + glfwSwapBuffers(m_glfwWindow); + } + + + const double dashboardInterval = 1.0 / 90.0; // fps + // double waitEventsTimeout = std::max(CalCtx.wantedUpdateInterval, dashboardInterval); + double waitEventsTimeout = dashboardInterval; + + glfwWaitEventsTimeout(waitEventsTimeout); + + // If we're minimized rendering won't limit our frame rate so we need to do it ourselves. + if (glfwGetWindowAttrib(m_glfwWindow, GLFW_ICONIFIED)) + { + double targetFrameTime = 1 / k_MINIMIZED_MAX_FPS; + double waitTime = targetFrameTime - (glfwGetTime() - lastFrameStartTime); + if (waitTime > 0) + { + std::this_thread::sleep_for(std::chrono::duration(waitTime)); + } + + lastFrameStartTime += targetFrameTime; + } + } + } +} \ No newline at end of file diff --git a/src/overlay/window.h b/src/overlay/window.h new file mode 100644 index 0000000..e865b7e --- /dev/null +++ b/src/overlay/window.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#define GLFW_EXPOSE_NATIVE_WIN32 +#include +#include + +namespace spacecal { + // handles window creation, setting the icon, and initialising a renderer for UI, also handles connecting it to SteamVR as an overlay + class Window { + public: + bool CreateNativeWindow(); + void Shutdown(); + void RunLoop(); + + private: + GLFWwindow* m_glfwWindow = nullptr; + GLuint m_fboHandle = 0; + GLuint m_fboTextureHandle = 0; + int m_fboTextureWidth = 1200; + int m_fboTextureHeight = 800; + + const float k_MINIMIZED_MAX_FPS = 60.0f; + }; +} \ No newline at end of file