#include namespace nb { using GLFWCodes = GLFWError::Codes; const std::string GLFWError::type = "nb::GLFWError"; const ErrorCodeMap GLFWError::ErrorMessages = { {GLFWCodes::UNDEFINED, "Error"}, {GLFWCodes::INIT_FAILED, "GLFW initialization failed"}, {GLFWCodes::GLFW_INTIALIZED, "GLFW has already been initialized"}, {GLFWCodes::GLAD_FAILED, "GLAD initialization failed"}, {GLFWCodes::NOT_MAIN, "Action can only be done on main thread"}, {GLFWCodes::WINDOW_FAILED, "Failed to initialize window"} }; // class OGLWindow std::atomic OGLWindow::_glfwInitialized = false; thread_local GLFWwindow* OGLWindow::_currWindow = nullptr; thread_local const GLFWwindow* OGLWindow::_currArena = nullptr; std::atomic OGLWindow::_count = 0; bool OGLWindow::InitGLFW(const std::vector& hints) { if (nb::MainTID != GetTID()) { THROW(GLFWError(GLFWError::NOT_MAIN)); } if (!IsGLFWInitialized()) { for (const auto& hint : hints) { glfwInitHint(hint[0], hint[1]); } if (glfwInit()) { OGLWindow::_glfwInitialized.store(true, std::memory_order_release); } else { THROW(GLFWError(GLFWError::INIT_FAILED)); } } return IsGLFWInitialized(); } bool OGLWindow::TerminateGLFW() { if (nb::MainTID != GetTID()) { THROW(GLFWError(GLFWError::NOT_MAIN)); } if (IsGLFWInitialized()) { glfwTerminate(); OGLWindow::_glfwInitialized.store(false, std::memory_order_release); } return !IsGLFWInitialized(); } GLFWwindow* OGLWindow::MakeCurrent(const std::shared_ptr& window) { auto tmp = _currWindow; if (window) { _currWindow = window->getWindowPtr(); _currArena = window->_arena; } else { _currWindow = nullptr; _currArena = nullptr; } glfwMakeContextCurrent(_currWindow); return tmp; } OGLWindow::OGLWindow( unsigned int x, unsigned int y, const std::string&, GLFWwindow* sharingContext ) { CheckForMainThread(); InitGLFW(); _window_ptr = glfwCreateWindow( x, y, _title.c_str(), NULL, sharingContext ); if (_window_ptr == NULL) { THROW(GLFWError(GLFWError::Codes::WINDOW_FAILED)); } _arena = _window_ptr; auto tmp = makeCurrent(); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { THROW(GLFWError(GLFWError::Codes::GLAD_FAILED)); } glfwMakeContextCurrent(tmp); OGLWindow::_count.fetch_add(1); } OGLWindow::~OGLWindow() { CheckForMainThread(); if (_window_ptr) { if (_window_ptr == _currWindow) { _currWindow = nullptr; _currArena = nullptr; } glfwDestroyWindow(_window_ptr); _count.fetch_add(-1); if (!_count.load()) { TerminateGLFW(); } } } void OGLWindow::detachWindow() { _title = ""; _window_ptr = nullptr; _arena = nullptr; } int OGLWindow::getWindowAttribute(int attr) const { CheckForMainThread(); return glfwGetWindowAttrib(_window_ptr, attr); } std::array OGLWindow::getSize() const { CheckForMainThread(); int x, y; glfwGetWindowSize(_window_ptr, &x, &y); return {x, y}; } bool OGLWindow::shouldClose() const { CheckForMainThread(); return glfwWindowShouldClose(_window_ptr); } void OGLWindow::shouldClose(bool val) { CheckForMainThread(); glfwSetWindowShouldClose(_window_ptr, val); } GLFWwindow* OGLWindow::makeCurrent() const { auto tmp = glfwGetCurrentContext(); glfwMakeContextCurrent(_window_ptr); return tmp; } void OGLWindow::setSize(unsigned int x, unsigned int y) { CheckForMainThread(); glfwSetWindowSize(_window_ptr, x, y); } void OGLWindow::setTitle(const std::string& name) { CheckForMainThread(); _title = name; glfwSetWindowTitle(_window_ptr, _title.c_str()); } };