23#include <GLFW/glfw3.h>
27#ifndef GL_CONTEXT_LOST
28#define GL_CONTEXT_LOST 0x0507
34 : m_title(std::move(title)), m_width(width), m_height(height) {}
46 glfwSetErrorCallback(ErrorCallback);
47 if (glfwInit() == GLFW_FALSE) {
55 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
56 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
57 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
58 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
61 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
62 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
63 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
67 m_window = glfwCreateWindow(m_width, m_height, m_title.c_str(),
nullptr,
nullptr);
68 if (m_window ==
nullptr) {
69 LOG_ERROR(
"Failed to create GLFW window");
75 glfwSetWindowUserPointer(m_window,
this);
77 glfwMakeContextCurrent(m_window);
81 const GLubyte* version = glGetString(GL_VERSION);
82 const GLubyte* vendor = glGetString(GL_VENDOR);
83 const GLubyte* renderer = glGetString(GL_RENDERER);
85 if (version !=
nullptr) {
86 LOG_INFO(
"OpenGL version: {}",
reinterpret_cast<const char*
>(version));
88 LOG_ERROR(
"Failed to get OpenGL version");
91 if (vendor !=
nullptr) {
92 LOG_INFO(
"OpenGL vendor: {}",
reinterpret_cast<const char*
>(vendor));
97 if (renderer !=
nullptr) {
98 LOG_INFO(
"OpenGL renderer: {}",
reinterpret_cast<const char*
>(renderer));
100 LOG_ERROR(
"Failed to get OpenGL renderer");
105 m_initialized =
true;
110 if (!m_initialized) {
114 if (m_window !=
nullptr) {
115 glfwDestroyWindow(m_window);
120 m_initialized =
false;
124 return m_window ==
nullptr || (glfwWindowShouldClose(m_window) == GLFW_TRUE);
132 if (m_window ==
nullptr) {
133 LOG_ERROR(
"BeginFrame called with null window");
139 LOG_ERROR(
"BeginFrame: Context validation failed - skipping frame");
145 glfwGetFramebufferSize(m_window, &width, &height);
149 glViewport(0, 0, m_width, m_height);
150 glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
151 glClear(GL_COLOR_BUFFER_BIT);
154 const GLenum error = glGetError();
156 LOG_WARNING(
"OpenGL error during clear (0x{:X}) - will attempt recovery next frame", error);
162 if (m_window ==
nullptr) {
163 LOG_ERROR(
"EndFrame called with null window");
169 LOG_ERROR(
"EndFrame: Context validation failed - skipping swap");
173 glfwSwapBuffers(m_window);
177 if (m_window !=
nullptr) {
178 glfwGetFramebufferSize(m_window, &width, &height);
186 if (m_window !=
nullptr) {
187 glfwGetWindowSize(m_window, &width, &height);
195 if (m_window !=
nullptr) {
196 glfwSetWindowShouldClose(m_window, GLFW_TRUE);
201 m_framebufferSizeCallback = callback;
202 if (m_window !=
nullptr) {
203 glfwSetFramebufferSizeCallback(m_window, FramebufferSizeCallbackInternal);
208 m_keyCallback = callback;
209 if (m_window !=
nullptr) {
210 glfwSetKeyCallback(m_window, KeyCallbackInternal);
215 m_windowCloseCallback = callback;
216 if (m_window !=
nullptr) {
217 glfwSetWindowCloseCallback(m_window, WindowCloseCallbackInternal);
222 if (m_window !=
nullptr) {
223 glfwSetWindowShouldClose(m_window, GLFW_FALSE);
228 m_contextLossCallback = callback;
232 if (m_window ==
nullptr) {
245 if (glfwGetCurrentContext() != m_window) {
246 LOG_WARNING(
"OpenGL context is no longer valid - attempting recovery");
247 return RecreateContext();
251 const GLenum error = glGetError();
253 LOG_WARNING(
"OpenGL context lost (error: 0x{:X}) - attempting recovery", error);
254 return RecreateContext();
258 m_contextRecoveryAttempts = 0;
262bool WindowManager::RecreateContext() {
263 m_contextRecoveryAttempts++;
265 if (m_contextRecoveryAttempts > MAX_RECOVERY_ATTEMPTS) {
266 LOG_ERROR(
"Failed to recover OpenGL context after {} attempts - requesting window close",
267 MAX_RECOVERY_ATTEMPTS);
268 glfwSetWindowShouldClose(m_window, GLFW_TRUE);
272 LOG_INFO(
"Attempting to recreate OpenGL context (attempt {}/{})", m_contextRecoveryAttempts, MAX_RECOVERY_ATTEMPTS);
275 glfwMakeContextCurrent(m_window);
278 if (glfwGetCurrentContext() != m_window) {
279 LOG_ERROR(
"Failed to make context current");
284 while (glGetError() != GL_NO_ERROR) {
290 if (m_contextLossCallback) {
291 LOG_INFO(
"Calling context loss callback for application-level recovery");
292 if (!m_contextLossCallback()) {
293 LOG_ERROR(
"Application-level context recovery failed");
298 LOG_INFO(
"OpenGL context successfully recovered");
299 m_contextRecoveryAttempts = 0;
305void WindowManager::ErrorCallback(
int error,
const char* description) {
306 LOG_ERROR(
"GLFW Error {}: {}", error, description);
309void WindowManager::FramebufferSizeCallbackInternal(GLFWwindow* window,
int width,
int height) {
310 auto* manager =
static_cast<WindowManager*
>(glfwGetWindowUserPointer(window));
311 if (manager !=
nullptr && manager->m_framebufferSizeCallback) {
312 manager->m_framebufferSizeCallback(width, height);
316void WindowManager::KeyCallbackInternal(GLFWwindow* window,
int key,
int scancode,
int action,
int mods) {
317 auto* manager =
static_cast<WindowManager*
>(glfwGetWindowUserPointer(window));
318 if (manager !=
nullptr && manager->m_keyCallback) {
319 manager->m_keyCallback(key, scancode, action, mods);
323void WindowManager::WindowCloseCallbackInternal(GLFWwindow* window) {
324 auto* manager =
static_cast<WindowManager*
>(glfwGetWindowUserPointer(window));
325 if (manager !=
nullptr && manager->m_windowCloseCallback) {
326 manager->m_windowCloseCallback();