335 lines
9.2 KiB
C++
335 lines
9.2 KiB
C++
#include <NBGraphics/Window.hpp>
|
|
|
|
namespace nb {
|
|
|
|
std::map<GLFWwindow*, Window*> Window::WindowContexts = {};
|
|
std::map<int, int> Window::GLFWHints = {};
|
|
bool Window::StrictInitialization = true;
|
|
bool Window::_glfw_init = false;
|
|
Window* Window::_current = nullptr;
|
|
static std::map<int, int> 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<uint16_t, 2> 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<uint16_t, 2> Window::getSize() const {
|
|
return windowSize;
|
|
}
|
|
|
|
std::string Window::getName() const {
|
|
return windowName;
|
|
}
|
|
|
|
void Window::resize(const std::array<uint16_t, 2> 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<bool> OGLWindow::_glfwInitialized = false;
|
|
thread_local std::shared_ptr<OGLWindow> OGLWindow::_currWindow = nullptr;
|
|
thread_local GLFWwindow* OGLWindow::_currArena = nullptr;
|
|
std::atomic<unsigned int> OGLWindow::_count = 0;
|
|
|
|
bool OGLWindow::InitGLFW(const std::vector<GLFWHint>& 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<int, 2>& hint) {
|
|
CheckForMainThread();
|
|
glfwWindowHint(hint[0], hint[1]);
|
|
}
|
|
|
|
void OGLWindow::SetHintString(const std::tuple<int, std::string>& hint) {
|
|
CheckForMainThread();
|
|
glfwWindowHintString(std::get<0>(hint), std::get<1>(hint).c_str());
|
|
}
|
|
|
|
std::shared_ptr<OGLWindow> OGLWindow::MakeCurrent(const std::shared_ptr<OGLWindow>& 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<std::array<int, 2>>& integerHints,
|
|
const std::vector<std::tuple<int, std::string>>& 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> OGLWindow::CreateWindow(
|
|
unsigned int x,
|
|
unsigned int y,
|
|
const std::string& title,
|
|
const std::vector<std::array<int, 2>>& integerHints,
|
|
const std::vector<std::tuple<int, std::string>>& stringHints
|
|
) {
|
|
return std::shared_ptr<OGLWindow>( 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<int, 2> 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());
|
|
}
|
|
|
|
}; |