From 22d2bb1c407d81096d42bd6655715c43dedcc76d Mon Sep 17 00:00:00 2001 From: portersky <24420859+portersky@users.noreply.github.com> Date: Tue, 5 May 2026 23:37:19 +0200 Subject: [PATCH] refactor: separate window from opengl::context Extract GLFW window management into a dedicated cbt::window class (new files in cbt/). The opengl::context now only handles GLAD setup and context activation (no more window creation or GLFW init/terminate). Updated main loop in cuber.cpp, CMakeLists.txt (to build the new source), and AGENTS.md (docs + source layout). Addresses the design note in context.cpp about mixing concerns. --- AGENTS.md | 6 ++- CMakeLists.txt | 1 + cbt/opengl/context.cpp | 78 +++----------------------------------- cbt/opengl/context.hpp | 16 ++------ cbt/window.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++ cbt/window.hpp | 29 ++++++++++++++ cuber.cpp | 17 ++++++--- 7 files changed, 140 insertions(+), 93 deletions(-) create mode 100644 cbt/window.cpp create mode 100644 cbt/window.hpp diff --git a/AGENTS.md b/AGENTS.md index 2611a33..4d3f471 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -34,7 +34,7 @@ To add a new dependency: The project is split into static libraries: -- **`cbt_opengl`** — OpenGL abstraction (context, buffer, texture, vao, shader, descriptor) +- **`cbt_opengl`** — OpenGL abstraction and window management (window, context, buffer, texture, vao, shader, descriptor) - **`cbt_scene`** — Base scene class - **`scenes_cube`** — Cube scene implementation @@ -102,8 +102,10 @@ cuber/ cbt/ # Project namespace (utilities) scene.hpp # Base scene class scene.cpp # Scene implementation + window.hpp # GLFW window RAII wrapper + window.cpp # Window implementation opengl/ # OpenGL abstraction layer - context.hpp # OpenGL context RAII wrapper + context.hpp # OpenGL context RAII wrapper (GLAD setup) context.cpp # Context implementation buffer.hpp # Buffer resource (VBO, EBO, UBO, SSBO) buffer.cpp # Buffer implementation diff --git a/CMakeLists.txt b/CMakeLists.txt index 261ed35..fa306dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ find_package(glm REQUIRED) # OpenGL abstraction library add_library(cbt_opengl STATIC + "cbt/window.cpp" "cbt/opengl/context.cpp" "cbt/opengl/buffer.cpp" "cbt/opengl/texture.cpp" diff --git a/cbt/opengl/context.cpp b/cbt/opengl/context.cpp index 770aa7c..a2047a7 100644 --- a/cbt/opengl/context.cpp +++ b/cbt/opengl/context.cpp @@ -1,18 +1,8 @@ #include -#ifdef _WIN32 -#include -#include -#endif - #define GLFW_INCLUDE_NONE #include "GLFW/glfw3.h" -#ifdef _WIN32 -#define GLFW_EXPOSE_NATIVE_WIN32 -#include "GLFW/glfw3native.h" -#endif - #include "fmt/std.h" #include "glad/glad.h" @@ -20,18 +10,6 @@ namespace cbt::opengl { -auto context::init() -> bool { - if (!glfwInit()) { - fmt::print("Failed to initialize GLFW\n"); - return false; - } - return true; -} - -auto context::terminate() -> void { - glfwTerminate(); -} - auto context::setup_gl() -> bool { if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { fmt::print("Failed to initialize GLAD\n"); @@ -50,64 +28,18 @@ auto context::print_info() -> void { fmt::print("\n"); } -context::context(std::string title, int width, int height) { - if (!init()) { - return; - } - m_initialized = true; - - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - - m_window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); - if (!m_window) { - fmt::print("Failed to create window\n"); - terminate(); - return; - } - - glfwMakeContextCurrent(m_window); - -#ifdef _WIN32 - auto hwnd = glfwGetWin32Window(static_cast(m_window)); - BOOL use_dark_mode = TRUE; - DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &use_dark_mode, sizeof(use_dark_mode)); -#endif - +context::context(window const& win) { + glfwMakeContextCurrent(win.raw()); if (!setup_gl()) { - terminate(); return; } + m_valid = true; } -context::~context() { - if (m_window) { - glfwDestroyWindow(m_window); - } - if (m_initialized) { - terminate(); - } -} - -auto context::should_close() const -> bool { - return m_window && glfwWindowShouldClose(m_window); -} +context::~context() {} auto context::valid() const -> bool { - return m_window != nullptr; -} - -auto context::swap_buffers() -> void { - glfwSwapBuffers(m_window); -} - -auto context::poll_events() -> void { - glfwPollEvents(); -} - -auto context::raw() const -> GLFWwindow* { - return m_window; + return m_valid; } } diff --git a/cbt/opengl/context.hpp b/cbt/opengl/context.hpp index d26f744..dfa11c2 100644 --- a/cbt/opengl/context.hpp +++ b/cbt/opengl/context.hpp @@ -1,29 +1,19 @@ #pragma once -#include - -#define GLFW_INCLUDE_NONE -#include "GLFW/glfw3.h" +#include "cbt/window.hpp" namespace cbt::opengl { class context { public: - explicit context(std::string title, int width, int height); + explicit context(window const& win); ~context(); - auto should_close() const -> bool; auto valid() const -> bool; - auto swap_buffers() -> void; - auto poll_events() -> void; - auto raw() const -> GLFWwindow*; private: - GLFWwindow* m_window = nullptr; - bool m_initialized = false; + bool m_valid = false; - static auto init() -> bool; - static auto terminate() -> void; static auto setup_gl() -> bool; static auto print_info() -> void; }; diff --git a/cbt/window.cpp b/cbt/window.cpp new file mode 100644 index 0000000..be3a9d6 --- /dev/null +++ b/cbt/window.cpp @@ -0,0 +1,86 @@ +#ifdef _WIN32 +#include +#include +#endif + +#define GLFW_INCLUDE_NONE +#include "GLFW/glfw3.h" + +#ifdef _WIN32 +#define GLFW_EXPOSE_NATIVE_WIN32 +#include "GLFW/glfw3native.h" +#endif + +#include "fmt/std.h" + +#include "cbt/window.hpp" + +namespace cbt { + +auto window::init_glfw() -> bool { + if (!glfwInit()) { + fmt::print("Failed to initialize GLFW\n"); + return false; + } + return true; +} + +auto window::terminate_glfw() -> void { + glfwTerminate(); +} + +window::window(std::string title, int width, int height) { + if (!init_glfw()) { + return; + } + m_glfw_initialized = true; + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + m_window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); + if (!m_window) { + fmt::print("Failed to create window\n"); + terminate_glfw(); + m_glfw_initialized = false; + return; + } + +#ifdef _WIN32 + auto hwnd = glfwGetWin32Window(m_window); + BOOL use_dark_mode = TRUE; + DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &use_dark_mode, sizeof(use_dark_mode)); +#endif +} + +window::~window() { + if (m_window) { + glfwDestroyWindow(m_window); + } + if (m_glfw_initialized) { + terminate_glfw(); + } +} + +auto window::should_close() const -> bool { + return m_window && glfwWindowShouldClose(m_window); +} + +auto window::valid() const -> bool { + return m_window != nullptr; +} + +auto window::swap_buffers() -> void { + glfwSwapBuffers(m_window); +} + +auto window::poll_events() -> void { + glfwPollEvents(); +} + +auto window::raw() const -> GLFWwindow* { + return m_window; +} + +} diff --git a/cbt/window.hpp b/cbt/window.hpp new file mode 100644 index 0000000..17168f6 --- /dev/null +++ b/cbt/window.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#define GLFW_INCLUDE_NONE +#include "GLFW/glfw3.h" + +namespace cbt { + +class window { +public: + explicit window(std::string title, int width, int height); + ~window(); + + auto should_close() const -> bool; + auto valid() const -> bool; + auto swap_buffers() -> void; + auto poll_events() -> void; + auto raw() const -> GLFWwindow*; + +private: + GLFWwindow* m_window = nullptr; + bool m_glfw_initialized = false; + + static auto init_glfw() -> bool; + static auto terminate_glfw() -> void; +}; + +} diff --git a/cuber.cpp b/cuber.cpp index 3d5ec14..706579a 100644 --- a/cuber.cpp +++ b/cuber.cpp @@ -5,11 +5,18 @@ #include "GLFW/glfw3.h" #include "asio.hpp" +#include "cbt/window.hpp" #include "cbt/opengl/context.hpp" #include "scenes/cube.hpp" auto main(int, char const*[]) -> int { - auto ctx = cbt::opengl::context("cuber", 1280, 720); + auto win = cbt::window("cuber", 1280, 720); + + if (!win.valid()) { + return 1; + } + + auto ctx = cbt::opengl::context(win); if (!ctx.valid()) { return 1; @@ -37,10 +44,10 @@ auto main(int, char const*[]) -> int { // render loop auto prev = std::chrono::steady_clock::now(); - while (!ctx.should_close()) { + while (!win.should_close()) { process_signals(); - if (quit || glfwGetKey(ctx.raw(), GLFW_KEY_Q) == GLFW_PRESS) { + if (quit || glfwGetKey(win.raw(), GLFW_KEY_Q) == GLFW_PRESS) { break; } @@ -51,8 +58,8 @@ auto main(int, char const*[]) -> int { scn.update(dt); scn.render(); - ctx.swap_buffers(); - ctx.poll_events(); + win.swap_buffers(); + win.poll_events(); } return 0;