#include namespace nb { std::map Window::WindowContexts = {}; std::map Window::GLFWHints = {}; bool Window::StrictInitialization = true; bool Window::_glfw_init = false; Window* Window::_current = nullptr; static std::map defailt_window_hints = { {GLFW_CONTEXT_VERSION_MAJOR, 3}, {GLFW_CONTEXT_VERSION_MINOR, 1}, {GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE} #ifdef _NB_GL_DEBUG_ON , {GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE} #endif }; 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"} }; using WindowErrorCodes = WindowError::Codes; const std::string WindowError::type = "nb::WindowError"; const ErrorCodeMap WindowError::ErrorMessages = { {WindowErrorCodes::UNDEFINED, "Error"}, {WindowErrorCodes::INITIALIZED_WINDOW, "Window already initialized"}, {WindowErrorCodes::NO_GLFW, "GLFW has not been initialized"}, {WindowErrorCodes::INIT_FAILED, "Could not intialized window"} }; // class Window int Window::getGLFWHint(int hint_key) { if (GLFWHints.find(hint_key) == GLFWHints.end()) { return 0; } else { return GLFWHints.at(hint_key); } } int Window::setGLFWHint(int hint_key, int hint_val) { if (Window::_glfw_init) { THROW(GLFWError(GLFWCodes::GLFW_INTIALIZED)); } else { GLFWHints[hint_key] = hint_val; } return GLFWHints[hint_key]; } Window* Window::getCurrent() { return _current; } bool Window::checkKillGLFW() { if(Window::WindowContexts.size() == 0 && _glfw_init) { glfwTerminate(); Window::_glfw_init = false; return true; } return false; } Window::Window(const uint16_t x, const uint16_t y, const char* initName, GLFWmonitor* initMonitor, GLFWwindow* initWindow) { if (!Window::_glfw_init) { for (const auto& hint : Window::GLFWHints) { glfwInitHint(hint.first, hint.second); } int glfwResponse = glfwInit(); if (glfwResponse) { Window::_glfw_init = true; } else { if (Window::StrictInitialization) { THROW(GLFWError(GLFWCodes::INIT_FAILED)); } } } windowSize = {x, y}; _aspect_ratio = float(x)/float(y); windowName = std::string(initName); monitor = initMonitor; shareWindow = initWindow; for (const auto& hint: defailt_window_hints) { windowHints[hint.first] = hint.second; } } Window::Window(const std::array initSize, const char* initName, GLFWmonitor* initMonitor, GLFWwindow* initWindow) : Window(initSize[0], initSize[1], initName, initMonitor, initWindow){} Window::~Window() { glfwSetWindowShouldClose(window, true); glfwDestroyWindow(window); Window::WindowContexts.erase(window); if (Window::WindowContexts.size()==0) { glfwTerminate(); } } void Window::setCurrent() { _current = this; glfwMakeContextCurrent(window); } int Window::getWindowHint(int hint_key) const { if (windowHints.find(hint_key) == windowHints.end()) { return 0; } else { return windowHints.at(hint_key); } } int Window::setWindowHint(int hint_key, int hint_val) { if (_init) { THROW(WindowError(WindowErrorCodes::INITIALIZED_WINDOW)); } else { windowHints[hint_key] = hint_val; } return windowHints[hint_key]; } int Window::init() { if (!_glfw_init) { THROW(WindowError(WindowErrorCodes::NO_GLFW)); } for (const auto& hint : windowHints) { glfwWindowHint(hint.first, hint.second); } window = glfwCreateWindow(windowSize[0], windowSize[1], windowName.c_str(), monitor, shareWindow); if (window == NULL) { Window::WindowContexts.erase(window); if (Window::WindowContexts.size()==0 && Window::_glfw_init) { glfwTerminate(); } if (Window::StrictInitialization) { THROW(WindowError(WindowErrorCodes::INIT_FAILED)); } } glfwMakeContextCurrent(window); Window::WindowContexts[window] = this; gladResponse = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress); if (!gladResponse) { Window::checkKillGLFW(); if (Window::StrictInitialization) { THROW(GLFWError(GLFWCodes::GLAD_FAILED)); } } _init = true; OPENGL_CALL( glViewport(0, 0, windowSize[0], windowSize[1]) ); return gladResponse; } GLFWwindow* Window::getWindow() const { return window; } float Window::getAspectRatio() const { return _aspect_ratio; } std::array Window::getSize() const { return windowSize; } std::string Window::getName() const { return windowName; } void Window::resize(const std::array newSize) { windowSize = newSize; _aspect_ratio = float(newSize[0]) / float(newSize[1]); OPENGL_CALL( glViewport(0, 0, windowSize[0], windowSize[1]) ); glfwSetWindowSize(window, windowSize[0], windowSize[1]); } void Window::resize(const uint16_t x, const uint16_t y) { resize({x, y}); } // class OGLWindow std::atomic OGLWindow::_glfwInitialized = false; thread_local std::shared_ptr OGLWindow::_currWindow = nullptr; thread_local 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(); } void OGLWindow::SetHintInteger(const std::array& hint) { CheckForMainThread(); glfwWindowHint(hint[0], hint[1]); } void OGLWindow::SetHintString(const std::tuple& hint) { CheckForMainThread(); glfwWindowHintString(std::get<0>(hint), std::get<1>(hint).c_str()); } std::shared_ptr OGLWindow::MakeCurrent(const std::shared_ptr& window) { auto tmp = _currWindow; _currWindow = window; if (window) { glfwMakeContextCurrent(_currWindow->_window_ptr); _currArena = window->_arena; } else { glfwMakeContextCurrent(nullptr); _currArena = nullptr; } return tmp; } OGLWindow::OGLWindow( unsigned int x, unsigned int y, const std::string& title, const std::vector>& integerHints, const std::vector>& stringHints ) : _title(title) { CheckForMainThread(); InitGLFW(); for (const auto& hint : integerHints) { SetHintInteger(hint); } for (const auto& hint : stringHints) { SetHintString(hint); } _window_ptr = glfwCreateWindow( x, y, _title.c_str(), NULL, NULL ); if (_window_ptr == NULL) { THROW(GLFWError(GLFWError::Codes::WINDOW_FAILED)); } _arena = _window_ptr; if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { THROW(GLFWError(GLFWError::Codes::GLAD_FAILED)); } OGLWindow::_count.fetch_add(1); } std::shared_ptr OGLWindow::CreateWindow( unsigned int x, unsigned int y, const std::string& title, const std::vector>& integerHints, const std::vector>& stringHints ) { return std::shared_ptr( new OGLWindow( x, y, title, integerHints, stringHints ) ); } OGLWindow::~OGLWindow() { CheckForMainThread(); if (_window_ptr) { if (_window_ptr == _currWindow->_window_ptr) { _currWindow = nullptr; _currArena = nullptr; } glfwDestroyWindow(_window_ptr); _count.fetch_add(-1); if (!_count.load()) { TerminateGLFW(); } } } 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}; } 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()); } };