CMake/project reorg

This commit is contained in:
NaifBanana
2025-12-27 03:45:07 -06:00
parent 6134a66c71
commit de686e7daa
25 changed files with 44 additions and 27 deletions
+3
View File
@@ -0,0 +1,3 @@
add_subdirectory(./NBCoreUtils)
add_subdirectory(./NBEvents)
add_subdirectory(./NBWindow)
+7
View File
@@ -0,0 +1,7 @@
include_directories(./.)
add_library(NBCore
./src/Errors.cpp
)
add_subdirectory(./tests)
+75
View File
@@ -0,0 +1,75 @@
#pragma once
#ifndef _NB_DATASINK
#define _NB_DATASINK
namespace nb {
template <typename T>
class SinkBase {
using InputType = T;
public:
virtual bool in(const T&) = 0;
protected:
SinkBase();
};
/* template <typename T>
class Sink : public SinkBase {
public:
Sink& operator=(const Sink&) = 0;
template<typename U>
virtual bool in(const U&) = 0;
protected:
Sink();
}; */
template <typename T>
class SourceBase {
using OutputType = T;
public:
SourceBase& operator=(const SourceBase&) = 0;
virtual bool out(T*) = 0;
protected:
SourceBase();
SinkBase<T>* _sink;
};
template <typename T>
class Pipe : public SinkBase<T>, public SourceBase<T>{
using Input = SourceBase<T>;
using Output = SinkBase<T>;
public:
Pipe(Sink<T>& sink) : Input::_sink{&sink} {}
bool in(const T&) = 0;
protected:
using Input::_sink;
};
/* template <typename T>
class Source : SourceBase {
public:
Source& operator=(const Source&) = 0;
template<typename U>
virtual bool out(const U&) = 0;
protected:
Source();
}; */
} // namespace nb
#endif // _NB_DATASINK
+139
View File
@@ -0,0 +1,139 @@
#pragma once
#ifndef _NB_ERROR
#define _NB_ERROR
#include <exception>
#include <memory>
#include <string>
#include <type_traits>
#include <unordered_map>
#ifndef THROW_WITH_INFO
#ifdef CODE_ERROR_LOCATIONS
#define THROW_WITH_INFO(type, ...) throw type(__VA_ARGS__, __LINE__, __FILE__)
#else
#define THROW_WITH_INFO(type, ...) throw type(__VA_ARGS__)
#endif // CODE_ERROR_LOCATIONS
#endif // THROW_WITH_INFO
#ifndef THROW
#ifdef LOGGING
#define THROW_WTIH_INFO(type, ...) throw type(__VA_ARGS__, __LINE__, __FILE__)
#else
#define THROW(...) THROW_WITH_INFO(__VA_ARGS__)
#endif // LOGGING
#endif // THROW
namespace nb {
typedef std::unordered_map<unsigned int, const char*> ErrorCodeMap;
template<class ErrorType>
class ErrorBase : public std::exception {
public:
ErrorBase(
const unsigned int code,
unsigned int line=0,
std::string filename=""
) noexcept : ErrorBase(
code,
ErrorBase<ErrorType>::lookup(code),
line,
filename
) {}
ErrorBase(
const unsigned int code,
const std::exception& trace,
unsigned int line=0,
std::string filename=""
) noexcept : ErrorBase(
code,
ErrorBase<ErrorType>::lookup(code),
trace,
line,
filename
) {}
static std::string lookup(unsigned int);
unsigned int code() const noexcept {
return static_cast<const ErrorType*>(this)->_code;
};
virtual const char* what() const noexcept override final { return _msg.c_str(); };
protected:
ErrorBase() = default;
ErrorBase(
const unsigned int code,
std::string msg,
unsigned int line=0,
std::string filename=""
) noexcept : _code{code}, _msg{""} {
static_assert(std::is_same<const std::unordered_map<unsigned int, const char*>, decltype(ErrorType::ErrorMessages)>::value,
"const std::unordered_map<unsigned int, const char*> ErrorMessages must be "
"a class member."
);
static_assert(std::is_enum_v<typename ErrorType::ErrorCodes>, "enum ErrorCodes must be a class member.");
static_assert(std::is_same<std::underlying_type_t<typename ErrorType::ErrorCodes>, unsigned int>::value,
"enum ErrorCodes must be of underlying type unsigned int."
);
static_assert(std::is_same<const std::string, decltype(ErrorType::type)>::value,
"const std::string type must be a class member."
);
_msg += std::string(ErrorType::type);
_msg += "[" + std::to_string(_code) + "]";
if (line && filename.size()) {
_msg += " in \'" + filename + "\':" + std::to_string(line);
}
_msg += "\n " + msg;
}
ErrorBase(
const unsigned int code,
std::string msg,
const std::exception& trace,
unsigned int line=0,
std::string filename=""
) noexcept : ErrorBase(code, msg, line, filename) {
std::string what_msg(trace.what());
std::string::size_type newline_pos=-1;
while((newline_pos=what_msg.find("\n", newline_pos+1))!= std::string::npos) {
what_msg.replace(newline_pos, 1, "\n ");
}
_msg += "\n Trace - " + what_msg;
}
unsigned int _code;
std::string _msg;
};
class Error : public ErrorBase<Error> {
public:
enum ErrorCodes : unsigned int {
GENERAL, UNDEFINED, BADERRORCODE
};
using ErrorBase<Error>::ErrorBase;
friend ErrorBase<Error>;
protected:
static const std::string type;
static const ErrorCodeMap ErrorMessages;
};
template<class ErrorType>
std::string ErrorBase<ErrorType>::lookup(unsigned int code) {
for (auto kv : ErrorType::ErrorMessages) {
if (kv.first==code) {
return std::string(kv.second);
}
}
throw Error(Error::ErrorCodes::BADERRORCODE);
}
} // namespace nb
#endif // _NB_ERROR
+133
View File
@@ -0,0 +1,133 @@
#pragma once
#ifndef _NB_LOGGER
#define _NB_LOGGER
#include <atomic>
#include <chrono>
#include <ostream>
#include <thread>
#include <unordered_map>
#include "ThreadSafeQueue.hpp"
namespace nb {
typedef std::chrono::time_point<
std::chrono::system_clock,
std::chrono::nanoseconds
> LoggerTimePoint;
struct LogEvent{
const LoggerTimePoint time;
const unsigned char lvl;
const std::string msg;
};
typedef std::string (*LogProcessFunction)(const LoggerTimePoint&, const std::string&);
typedef std::unordered_map<uint8_t, LogProcessFunction> LogProcessFunctionMap;
template<typename StreamType>
class Logger {
public:
Logger(StreamType& stream) : _ostream(&stream) {}
bool isRunning() const;
unsigned int count() const;
bool run();
bool stop();
LogEvent pop();
void flush() {
while(_queue.size()) {
pop();
}
}
void deleteAll() {
_queue.empty();
}
void msg(const std::string& _msg, uint8_t lvl=0) {
_queue.push(LogEvent{std::chrono::system_clock::now(), lvl, _msg});
}
void warn(const std::string& _msg, uint8_t lvl=1) {
msg(_msg, lvl);
}
void warn(const std::exception& _err, uint8_t lvl=1) {
msg(_err.what(), lvl);
}
void error(const std::string& _msg, uint8_t lvl=0xFF) {
msg(_msg, lvl);
}
void error(const std::exception& _err, uint8_t lvl=0xFF) {
msg(_err.what(), lvl);
}
protected:
virtual void process(const LogEvent& event) {
uint8_t lvl = event.lvl;
try {
*_ostream << _function_map.at(lvl)(event.time, event.msg).c_str();
} catch (std::out_of_range e) {
*_ostream << _function_map.at(0)(event.time, event.msg).c_str();
//*_ostream << event.msg;
}
*_ostream << "\n";
}
static const LogProcessFunctionMap _function_map;
ThreadsafeQueue<LogEvent> _queue;
StreamType* const _ostream;
std::atomic<bool> _running;
std::shared_ptr<std::thread> _runningThread = nullptr;
};
template <typename ST>
const LogProcessFunctionMap Logger<ST>::_function_map = {
{0, [](const LoggerTimePoint& tp, const std::string& msg){
return msg;
}}
};
template <typename ST>
bool Logger<ST>::isRunning() const {
return _running && bool(_runningThread);
}
template <typename ST>
unsigned int Logger<ST>::count() const {
return _queue.size();
}
template <typename ST>
bool Logger<ST>::run() {
if (!isRunning()) {
_runningThread = std::make_shared<std::thread>([&]{
while(this->isRunning()) {
this->flush();
}
});
}
return isRunning();
}
template <typename ST>
bool Logger<ST>::stop() {
if (isRunning()) {
_running = false;
_runningThread->join();
_runningThread = nullptr;
flush();
}
return isRunning();
}
template <typename ST>
LogEvent Logger<ST>::pop() {
std::shared_ptr<LogEvent> event;
_queue.pop(event);
process(*event);
return *event;
}
} // namespace nb
#endif // _NB_LOGGER
+92
View File
@@ -0,0 +1,92 @@
#pragma once
#ifndef _NB_SMARTSTREAM
#define _NB_SMARTSTREAM
#include <array>
#include <iostream>
#include <string>
#include <type_traits>
#include <unordered_map>
namespace nb {
using RGB = std::array<unsigned char, 3>;
std::string toANSI(const RGB& color) {
return "\x1b[38;2;"+std::to_string(color[0])+";"+std::to_string(color[1])+
";"+std::to_string(color[2])+"m";
}
std::string toANSI(const unsigned int& color) {
return "\x1b[38;5;"+std::to_string(color)+"m";
}
template <typename U, typename Derived=void>
struct SmartText {
SmartText(const U& val) : msg(val) {}
const U msg;
template<typename C, typename T>
void process(std::basic_ostream<C,T>& stream) const;
};
template <typename Derived>
struct SmartText<std::string, Derived> {
template <std::size_t N>
SmartText(char const(&val) [N]) : msg(val) {}
SmartText(const std::string& val) : msg(val) {}
SmartText(const char* val) : msg(val) {}
const std::string msg;
template<typename C, typename T>
void process(std::basic_ostream<C,T>& stream) const;
};
template <typename U, typename Derived>
template <typename C, typename T>
void SmartText<U,Derived>::process(std::basic_ostream<C,T>& stream) const {
if (std::is_void<Derived>::value) {
stream << msg;
} else {
static_cast<Derived*>(this)->process(stream);
}
}
template<typename C, typename T, typename U, typename D>
std::basic_ostream<C, T>& operator<<(std::basic_ostream<C, T>& stream, const SmartText<U,D>& smt) {
if (std::is_void<D>::value) {
stream << smt.msg;
} else {
static_cast<const D*>(&smt)->process(stream);
}
return stream;
}
template <typename U>
struct TerminalColor : public SmartText<U, TerminalColor<U>> {
typedef SmartText<U, TerminalColor<U>> Base;
using Base::msg;
explicit TerminalColor(unsigned int color, const U& val)
: Base(val), colorSequence(toANSI(color)) { }
explicit TerminalColor(const RGB& color, const U& val)
: Base(val), colorSequence(toANSI(color)) { }
const std::string colorSequence;
template <typename C, typename T>
void process(std::basic_ostream<C, T>& stream) const {
if (&stream == &std::cout) {
stream << colorSequence << msg << "\x1b[0m";
} else {
stream << msg;
}
}
};
}
#endif // _NB_SMARTSTREAM
+123
View File
@@ -0,0 +1,123 @@
#pragma once
#ifndef _NB_UTIL_THREADSAFE_QUEUE
#define _NB_UTIL_THREADSAFE_QUEUE
#include <condition_variable>
#include <memory>
#include <mutex>
#include <queue>
#include <type_traits>
namespace nb {
template<typename T, bool isPoly>
class ThreadsafeQueue_impl;
// Non-polymorphic
template<typename T>
class ThreadsafeQueue_impl<T, false> {
public:
bool empty() const {
std::lock_guard<std::mutex> lock(_mutex);
return _queue.empty();
}
unsigned int size() const {
std::lock_guard<std::mutex> lock(_mutex);
return _queue.size();
}
template<typename U>
void push(U&& val) {
std::lock_guard<std::mutex> lock(_mutex);
_queue.push(std::forward<U>(val));
_cond.notify_one();
}
bool pop(std::shared_ptr<T>& dest) {
std::lock_guard<std::mutex> lock(_mutex);
if (_queue.empty()) { return false; }
dest = std::make_shared<T>(std::move(_queue.front()));
_queue.pop();
return true;
}
std::shared_ptr<T> popBlock() {
std::unique_lock<std::mutex> lock(_mutex);
_cond.wait(lock, [&]{ return !_queue.empty(); });
auto ret = std::make_shared<T>(std::move(_queue.front()));
_queue.pop();
return ret;
}
protected:
ThreadsafeQueue_impl() {}
std::queue<T> _queue;
std::condition_variable _cond;
mutable std::mutex _mutex;
};
// Polymorphic
template<typename T>
class ThreadsafeQueue_impl<T, true> {
public:
bool empty() const {
std::lock_guard<std::mutex> lock(_mutex);
return _queue.empty();
}
unsigned int size() const {
std::lock_guard<std::mutex> lock(_mutex);
return _queue.size();
}
template<typename U>
void push(U&& val) {
std::lock_guard<std::mutex> lock(_mutex);
std::shared_ptr<T> newval = std::make_shared<T>(std::forward<U>(val));
_queue.push(newval);
_cond.notify_one();
}
bool pop(std::shared_ptr<T>& dest) {
std::lock_guard<std::mutex> lock(_mutex);
if (_queue.empty()) { return false; }
dest = std::move(_queue.front());
_queue.pop();
return true;
}
std::shared_ptr<T> popBlock() {
std::unique_lock<std::mutex> lock(_mutex);
_cond.wait(lock, [&]{ return !_queue.empty(); });
auto ret = std::move(_queue.front());
_queue.pop();
return ret;
}
protected:
ThreadsafeQueue_impl() {}
std::queue<std::shared_ptr<T>> _queue;
std::condition_variable _cond;
mutable std::mutex _mutex;
};
template<typename T>
class ThreadsafeQueue : public ThreadsafeQueue_impl<T, std::is_polymorphic<T>::value> {
public:
using Implementation = ThreadsafeQueue_impl<T, std::is_polymorphic<T>::value>;
ThreadsafeQueue() = default;
~ThreadsafeQueue() = default;
ThreadsafeQueue(ThreadsafeQueue& rhs) = delete;
ThreadsafeQueue& operator=(ThreadsafeQueue& rhs) = delete;
ThreadsafeQueue(ThreadsafeQueue&& rhs) = delete;
ThreadsafeQueue& operator=(ThreadsafeQueue&& rhs) = delete;
operator bool() const { return !Implementation::empty(); }
};
} // namespace nb
#endif // _NB_UTIL_THREADSAFE_QUEUE
+12
View File
@@ -0,0 +1,12 @@
#include "Errors.hpp"
namespace nb {
const std::string Error::type = "Error";
const ErrorCodeMap Error::ErrorMessages = {
{ErrorCodes::GENERAL, "General std::exception."},
{ErrorCodes::UNDEFINED, "Undefined / general error."},
{ErrorCodes::BADERRORCODE, "Unrecognized error code."}
};
}
+5
View File
@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.26.0)
project(gtest_Graphics VERSION 0.1.0 LANGUAGES C CXX)
add_subdirectory(./ErrorTest)
add_subdirectory(./SmartStreamTest)
@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.26.0)
project(gtest_Graphics VERSION 0.1.0 LANGUAGES C CXX)
add_executable(ErrorTest main.cpp)
target_link_libraries(ErrorTest NBCore)
@@ -0,0 +1,54 @@
#define CODE_ERROR_LOCATIONS
#include "Errors.hpp"
#include <iostream>
#include "Logger.hpp"
using namespace nb;
class TestError : public ErrorBase<TestError> {
public:
using ErrorBase<TestError>::ErrorBase;
enum ErrorCodes : unsigned int {
A, B, C, D
};
static const std::string type;
static const ErrorCodeMap ErrorMessages;
};
const std::string TestError::type="TestError";
const ErrorCodeMap TestError::ErrorMessages{
{TestError::ErrorCodes::A, "Hey!"},
{TestError::ErrorCodes::B, "How"},
{TestError::ErrorCodes::C, "You"},
{TestError::ErrorCodes::D, "Doin"}
};
int main() {
try {
try {
try {
try {
THROW(TestError, TestError::ErrorCodes::D);
} catch (const std::exception& e){
THROW(TestError, TestError::ErrorCodes::C, e);
}
} catch(const std::exception& e) {
THROW(TestError, TestError::ErrorCodes::B, e);
}
} catch(const std::exception& e) {
THROW(TestError, TestError::ErrorCodes::A, e);
}
} catch(const std::exception& e) {
// THROW(Error, Error::ErrorCodes::UNDEFINED, e);
Logger log(std::cout);
std::cout << "Logger is starting: " << log.run() << std::endl;
std::cout << "Logger is running: " << log.isRunning() << std::endl;
log.error(e);
log.stop();
std::cout << "Logger is running: " << log.isRunning() << std::endl;
}
return 0;
}
@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.26.0)
project(gtest_Graphics VERSION 0.1.0 LANGUAGES C CXX)
add_executable(SmartStreamTest main.cpp)
target_link_libraries(SmartStreamTest NBCore)
@@ -0,0 +1,25 @@
#include <exception>
#include <iostream>
#include <fstream>
#include "SmartStreams.hpp"
int main() {
auto test = nb::TerminalColor(9, 2030);
std::cout << test;
std::ofstream file;
file.open("test.txt");
if (file.is_open()) {
file << test;
}
file.close();
return 0;
}
+5
View File
@@ -0,0 +1,5 @@
include_directories(./.)
add_library(NBEvents
./src/Events.cpp
)
+103
View File
@@ -0,0 +1,103 @@
#pragma once
#ifndef _NB_EVENTS
#define _NB_EVENTS
#include <atomic>
#include <stdexcept>
#include <queue>
#include <array>
#include <memory>
#include <mutex>
#include <string>
using state_register = std::atomic<uint64_t>;
namespace NB {
extern std::invalid_argument null_mask_error;
void NULL_FUNC();
class Events {
friend class NBEventListener;
public:
Events(const uint64_t, void (*initFunc)() = NULL_FUNC, const char* initName="");
Events(const uint64_t, const char* initName="");
Events(const Events&);
Events& operator=(const Events&);
~Events();
const std::string getName() const;
const uint64_t getMask() const;
void setMask(const uint64_t);
void setName(const char*);
void setFunc(void (*newFunc)());
virtual const uint64_t check(const uint64_t) const = 0;
virtual Events* clone() const = 0;
protected:
uint64_t mask = 0x0;
void (*func)() = nullptr;
std::string name = "";
};
class NBEventBasic : public Events {
friend class NBEventListener;
public:
using Events::Events;
NBEventBasic* clone() const override;
const uint64_t check(const uint64_t) const override;
};
class NBEventState : public Events {
friend class NBEventListener;
public:
using Events::Events;
NBEventState* clone() const override;
const uint64_t check(const uint64_t) const override;
};
enum NBStateChangeType : uint8_t {
STATE_RAISE, STATE_DROP, STATE_SET
};
struct NBStateChange {
uint8_t type;
uint64_t mask;
};
class NBEventListener {
public:
NBEventListener(Events**, uint16_t, const uint64_t initState=(uint64_t)0x0, state_register* initStatePtr=nullptr);
template<size_t SIZE>
NBEventListener(std::array<Events*, SIZE> eventArray, const uint64_t initState=(uint64_t)0x0, state_register* initStatePtr=nullptr)
: NBEventListener(eventArray.data(), eventArray.size(), initState, initStatePtr) {}
state_register* getStatePtr() const;
const uint64_t getState() const;
void raiseFlags(const uint64_t);
void raiseFlags(const Events&);
void dropFlags(const uint64_t);
void dropFlags(const Events&);
const bool snoop(const uint64_t) const;
const bool snoop(const Events&) const;
void listen();
void listen(const Events&);
protected:
void _setState(const uint64_t);
Events** eventList;
uint16_t numEvents;
state_register* state;
std::mutex bufferLock;
std::queue<NBStateChange> stateBuffer;
};
};
#endif
+161
View File
@@ -0,0 +1,161 @@
#include "Events.h"
namespace NB {
void NULL_FUNC() {}
std::invalid_argument null_mask_error("NULL MASK NOT ALLOWED");
Events::Events(const uint64_t initMask, void (*initFunc)(), const char* initName):
mask(initMask), func(initFunc), name(initName) {
if (mask == 0x0) {
throw null_mask_error;
}
}
Events::Events(const uint64_t initMask, const char* initName):
Events(initMask, NULL_FUNC, initName) {}
Events::Events(const Events& cpy)
:Events(cpy.mask, cpy.func, cpy.name.c_str()) {}
Events& Events::operator=(const Events& cpy) {
func = cpy.func;
name = cpy.name;
mask = cpy.mask;
if (mask==0x0) {
throw null_mask_error;
}
return *this;
}
Events::~Events() { func = nullptr; }
const std::string Events::getName() const { return name; }
const uint64_t Events::getMask() const { return mask; }
void Events::setMask(const uint64_t newMask) {
mask = newMask;
}
void Events::setName(const char* newName) {
name = newName;
}
void Events::setFunc(void (*newFunc)()) {
func = newFunc;
}
NBEventBasic* NBEventBasic::clone() const { return new NBEventBasic(*this); }
NBEventState* NBEventState::clone() const { return new NBEventState(*this); }
const uint64_t NBEventBasic::check(const uint64_t refState) const {
if ((mask!=0) && ((refState&mask)==mask)) {
func();
return refState&(~mask);
}
return refState;
}
const uint64_t NBEventState::check(const uint64_t refState) const {
if ((mask!=0) && ((refState&mask)==mask)) {
func();
}
return refState;
}
NBEventListener::NBEventListener(Events** initEventList, uint16_t initNum, const uint64_t initState, state_register* initStatePtr) {
if (initStatePtr == nullptr) {
state = new state_register;
} else {
state = initStatePtr;
}
state->store(initState);
numEvents = initNum;
eventList = new Events*[numEvents];
for (uint16_t i = 0; i < initNum; ++i) {
eventList[i] = initEventList[i]->clone();
}
}
state_register* NBEventListener::getStatePtr() const {
return state;
}
const uint64_t NBEventListener::getState() const {
return state->load();
}
void NBEventListener::raiseFlags(const uint64_t newState) {
bufferLock.lock();
stateBuffer.push(NBStateChange{STATE_RAISE, newState});
bufferLock.unlock();
}
void NBEventListener::raiseFlags(const Events& newEvent) {
raiseFlags(newEvent.mask);
}
void NBEventListener::dropFlags(const uint64_t dropState) {
bufferLock.lock();
stateBuffer.push(NBStateChange{STATE_DROP, dropState});
bufferLock.unlock();
}
void NBEventListener::dropFlags(const Events& dropEvent) {
dropFlags(dropEvent.mask);
}
const bool NBEventListener::snoop(const uint64_t refState) const {
return ((getState()&refState)==refState);
}
const bool NBEventListener::snoop(const Events& refEvent) const {
return snoop(refEvent.mask);
}
void NBEventListener::_setState(const uint64_t newState) {
bufferLock.lock();
std::queue<NBStateChange>().swap(stateBuffer);
bufferLock.unlock();
state->store(newState);
}
void NBEventListener::listen() {
uint64_t oldState = getState();
for (uint16_t i = 0; i < numEvents; ++i) {
oldState = eventList[i]->check(oldState);
}
NBStateChange curr;
bufferLock.lock();
for (; !stateBuffer.empty(); stateBuffer.pop()) {
curr = stateBuffer.front();
switch (curr.type) {
case STATE_RAISE:
oldState |= curr.mask;
break;
case STATE_DROP:
oldState &= ~curr.mask;
break;
case STATE_SET:
oldState = curr.mask;
break;
default:
break;
}
}
bufferLock.unlock();
_setState(oldState);
}
void NBEventListener::listen(const Events& refEvent) {
uint64_t oldState = getState();
oldState = refEvent.check(oldState);
_setState(oldState);
}
};
+8
View File
@@ -0,0 +1,8 @@
include_directories(./.)
add_library(NBWindow
./src/Window.cpp
./../../../glad/src/glad.c
)
target_link_libraries(NBWindow glfw)
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#ifndef _NB_OPENGL_LOADER
#define _NB_OPENGL_LOADER
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <string>
namespace NB {
static unsigned int __NB_GL_DEBUG_ERROR_CODE__;
static std::string formatDebugString(const std::string& msg, const std::string& file="", int line=-1) {
std::string ret = "";
if (file != "") {
ret += "In file " + file;
if (line >= 0) {
ret += " at line " + std::to_string(line);
}
ret += ":\n\t";
}
return ret + msg;
}
}
#define NB_GL_DEBUG_THROW(cmd, when) while(NB::__NB_GL_DEBUG_ERROR_CODE__=glGetError()){\
std::cout << "[GL ERROR]: " << NB::__NB_GL_DEBUG_ERROR_CODE__;\
std::cout << " " << when << " " << cmd << "\n";\
}
#define NB_GL_DEBUG(cmd) NB_GL_DEBUG_THROW(#cmd, "before"); cmd; NB_GL_DEBUG_THROW(#cmd, "after");
#endif
+69
View File
@@ -0,0 +1,69 @@
#pragma once
#ifndef _NB_WINDOW
#define _NB_WINDOW
#include "GLLoad.h"
#include <atomic>
#include <array>
#include <iostream>
#include <map>
#include <stdexcept>
#include <string>
namespace NB {
class GLError : public std::runtime_error {
public:
GLError(const std::string&);
};
class WindowError : public std::runtime_error {
public:
WindowError(const std::string&);
};
class Window {
public:
static bool StrictInitialization;
static int getGLFWHint(int);
static int setGLFWHint(int, int);
static Window* getCurrent();
Window(const std::array<uint16_t, 2>, const char*, GLFWmonitor* initMonitor=NULL, GLFWwindow* initWindow=NULL);
Window(const uint16_t, const uint16_t, const char*, GLFWmonitor* initMonitor=NULL, GLFWwindow* initWindow=NULL);
~Window();
int getWindowHint(int) const;
GLFWwindow* getWindow() const;
std::array<uint16_t, 2> getSize() const;
float getAspectRatio() const;
std::string getName() const;
int init();
int setWindowHint(int, int);
void setCurrent();
void resize(const std::array<uint16_t, 2>);
void resize(const uint16_t x, const uint16_t y);
protected:
static std::map<GLFWwindow*, Window*> WindowContexts;
static std::map<int, int> GLFWHints;
static Window* _current;
static bool _glfw_init;
static bool checkKillGLFW();
std::map<int, int> windowHints;
std::array<uint16_t, 2> windowSize;
float _aspect_ratio;
std::string windowName;
GLFWwindow* window;
GLFWmonitor* monitor;
GLFWwindow* shareWindow;
bool _init = false;
int gladResponse = -1;
};
};
#endif
+170
View File
@@ -0,0 +1,170 @@
#include "Window.h"
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
};
GLError::GLError(const std::string& msg) : std::runtime_error(msg) {}
WindowError::WindowError(const std::string &msg) : std::runtime_error(msg) {}
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 GLError("Cannot set GLFW hint after window is initialized.");
} 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 GLError("Failed to load GLFW with glfwInit()="+std::to_string(glfwResponse)+".");
}
}
}
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("Cannot set window hint after window is initialized.");
} else {
windowHints[hint_key] = hint_val;
}
return windowHints[hint_key];
}
int Window::init() {
if (!_glfw_init) {
throw GLError("GLFW has not been initialized.");
}
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("Could not create window in glfwCreateWindow().");
}
}
glfwMakeContextCurrent(window);
Window::WindowContexts[window] = this;
gladResponse = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
if (!gladResponse) {
Window::checkKillGLFW();
if (Window::StrictInitialization) {
throw GLError("Failed to load GLAD with \
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)="+\
std::to_string(gladResponse)+"."
);
}
}
_init = true;
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]);
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}); }
};