#ifdef _WIN32 #include #include #endif #include #define GLFW_INCLUDE_NONE #include "GLFW/glfw3.h" #ifdef _WIN32 #define GLFW_EXPOSE_NATIVE_WIN32 #include "GLFW/glfw3native.h" #endif #include "fmt/std.h" #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" #include "glad/glad.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; } m_width = width; m_height = height; glfwSetWindowUserPointer(m_window, this); glfwSetWindowSizeCallback(m_window, resize_callback_impl); #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; } auto window::stop() -> void { if (m_window) { glfwSetWindowShouldClose(m_window, GLFW_TRUE); } } auto window::width() const -> int { return m_width; } auto window::height() const -> int { return m_height; } auto window::on_resize(resize_callback cb) -> void { m_resize_cb = cb; } auto window::resize_callback_impl(GLFWwindow* glfw_window, int width, int height) -> void { auto* self = static_cast(glfwGetWindowUserPointer(glfw_window)); if (self) { self->m_width = width; self->m_height = height; if (self->m_resize_cb) { self->m_resize_cb(width, height); } } } auto window::screenshot(std::string filepath) const -> bool { if (!m_window || !valid()) { return false; } int width = 0; int height = 0; glfwGetFramebufferSize(m_window, &width, &height); if (width <= 0 || height <= 0) { return false; } std::vector pixels(static_cast(width * height * 4)); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); // Flip vertically (OpenGL reads bottom-up; PNG is top-down) for (int y = 0; y < height / 2; ++y) { int y2 = height - 1 - y; for (int x = 0; x < width * 4; ++x) { std::swap(pixels[static_cast(y * width * 4 + x)], pixels[static_cast(y2 * width * 4 + x)]); } } if (stbi_write_png(filepath.c_str(), width, height, 4, pixels.data(), width * 4) != 0) { fmt::print("Screenshot saved to {}\n", filepath); return true; } fmt::print("Failed to write screenshot to {}\n", filepath); return false; } }