b3b5c285 |
/// Guards
#ifdef GLBACKEND_GLFW
/// Includes
#include <glbackend_glfw.hpp>
#include <array>
#include <functional>
#include <limits>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <glbase.hpp>
#include <glbackend.hpp>
#include <GLFW/glfw3.h>
// NOLINTNEXTLINE
#define STR_EXCEPTION GLBase::Exception
#include <str.hpp>
/// Special member functions
GLBackendGLFW::GLBackendGLFW(
std::string const & title,
std::array<int, 2> size,
std::array<int, 2> version,
int samples,
bool fullscreen,
bool transparent
)
:
GLBackend(),
window_{nullptr}
{
//// Backend errors
glfwSetErrorCallback(debug_glfw_error_callback_);
auto const glfw_get_error_description = []()
{
char const * description{};
glfwGetError(&description);
return description;
};
//// Backend init
if (!glfwInit())
STR_THROW(
"Failed to initialize GLFW" << ":\n" <<
glfw_get_error_description()
);
++init_count_;
//// Window options
glfwDefaultWindowHints();
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
GLFWmonitor * monitor = nullptr;
if (fullscreen)
monitor = glfwGetPrimaryMonitor();
if (transparent)
{
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
}
if (size[0] == 0 || size[1] == 0)
{
auto const * mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
size[0] = mode->width;
size[1] = mode->height;
}
size_ = size;
//// Context options
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
glfwWindowHint(GLFW_RED_BITS, 8); // NOLINT
glfwWindowHint(GLFW_GREEN_BITS, 8); // NOLINT
glfwWindowHint(GLFW_BLUE_BITS, 8); // NOLINT
glfwWindowHint(GLFW_ALPHA_BITS, 8); // NOLINT
glfwWindowHint(GLFW_DEPTH_BITS, 24); // NOLINT
glfwWindowHint(GLFW_STENCIL_BITS, 8); // NOLINT
if (samples)
glfwWindowHint(GLFW_SAMPLES, samples);
if (version[0])
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, version[0]);
if (version[1])
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, version[1]);
if ((version[0] == 3 && version[1] >= 2) || version[0] >= 4) // NOLINT
{
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
}
if (debug() >= 1)
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
else if (version[0] >= 2) // NOLINT
glfwWindowHint(GLFW_CONTEXT_NO_ERROR, GLFW_TRUE);
try
{
//// Window
window_ = glfwCreateWindow(
size[0],
size[1],
title.c_str(),
monitor,
nullptr
);
if (!window_)
STR_THROW(
"Failed to create GLFW window" << ":\n" <<
glfw_get_error_description()
);
//// Context
glfwMakeContextCurrent(window_);
if (debug() == 0)
glfwSwapInterval(1);
init_();
//// Lock
if (fullscreen)
lock(true);
}
catch (...)
{
destroy_();
throw;
}
//// Callbacks
// NOLINTNEXTLINE
#define GLBACKEND_GLFW_SELF_(WINDOW) \
(*static_cast<GLBackendGLFW *>(glfwGetWindowUserPointer(WINDOW)))
glfwSetWindowUserPointer(window_, this);
glfwSetKeyCallback(
window_,
[](
GLFWwindow * window,
int key,
int scancode,
int action,
int mods
)
{
(void)scancode;
(void)mods;
if (action != GLFW_PRESS)
return;
auto & self = GLBACKEND_GLFW_SELF_(window);
if (self.callback_key_)
{
if(key == GLFW_KEY_LEFT)
self.callback_key_("Left");
else if (key == GLFW_KEY_RIGHT)
self.callback_key_("Right");
else if (key == GLFW_KEY_UP)
self.callback_key_("Up");
else if (key == GLFW_KEY_DOWN)
self.callback_key_("Down");
else if (key == GLFW_KEY_ENTER)
self.callback_key_("Enter");
else if (key == GLFW_KEY_ESCAPE)
self.callback_key_("Escape");
else if (key == GLFW_KEY_TAB)
self.callback_key_("Tab");
else if (key == GLFW_KEY_BACKSPACE)
self.callback_key_("Backspace");
else if (
key == GLFW_KEY_RIGHT_CONTROL ||
key == GLFW_KEY_LEFT_CONTROL
)
self.callback_key_("Control");
else if (
key == GLFW_KEY_RIGHT_SHIFT ||
key == GLFW_KEY_LEFT_SHIFT
)
self.callback_key_("Shift");
else if (
key == GLFW_KEY_RIGHT_ALT ||
key == GLFW_KEY_LEFT_ALT
)
self.callback_key_("Alt");
else if (
0 <= key && key <= std::numeric_limits<char>::max()
)
self.callback_key_(std::string(1, (char)key));
}
}
);
glfwSetMouseButtonCallback(
window_,
[](
GLFWwindow * window,
int button,
int action,
int mods
)
{
(void)mods;
if (action != GLFW_PRESS)
return;
auto & self = GLBACKEND_GLFW_SELF_(window);
if (self.callback_button_)
self.callback_button_(button);
}
);
glfwSetScrollCallback(
window_,
[](
GLFWwindow * window,
double scroll_x,
double scroll_y
)
{
auto & self = GLBACKEND_GLFW_SELF_(window);
self.scroll_ = {
(float)scroll_x,
(float)scroll_y,
};
if (self.callback_scroll_)
self.callback_scroll_(self.scroll_);
}
);
glfwSetCursorPosCallback(
window_,
[](
GLFWwindow * window,
double x,
double y
)
{
auto & self = GLBACKEND_GLFW_SELF_(window);
self.move_ = {
(float)x - self.position_[0],
(float)y - self.position_[1],
};
self.position_ = {
(float)x,
(float)y,
};
if (self.callback_position_)
self.callback_position_(self.position_);
if (self.callback_move_)
self.callback_move_(self.move_);
}
);
glfwSetFramebufferSizeCallback(
window_,
[](
GLFWwindow * window,
int width,
int height
)
{
auto & self = GLBACKEND_GLFW_SELF_(window);
self.size_ = {
width,
height,
};
if (self.callback_size_)
self.callback_size_(self.size_);
}
);
}
GLBackendGLFW::GLBackendGLFW(GLBackendGLFW && other) noexcept
:
GLBackend(std::move(other)),
window_{other.window_}
{
other.window_ = nullptr;
}
GLBackendGLFW::~GLBackendGLFW()
{
destroy_();
}
void GLBackendGLFW::destroy_()
{
if (window_)
{
lock(false);
glfwDestroyWindow(window_);
}
if (--init_count_ == 0)
glfwTerminate();
}
GLBASE_GLOBAL(GLBackendGLFW::init_count_, {0})
/// Debug
std::string GLBackendGLFW::debug_info() const
{
auto ostream = std::ostringstream{};
auto version_major = int{};
auto version_minor = int{};
auto version_rev = int{};
glfwGetVersion(
&version_major,
&version_minor,
&version_rev
);
debug_info_(ostream, "GLFW", {
{
"VERSION_COMPILED",
glfwGetVersionString()
},
{
"VERSION_LINKED",
std::to_string(version_major) + "." +
std::to_string(version_minor) + "." +
std::to_string(version_rev),
},
});
ostream << GLBackend::debug_info();
return ostream.str();
}
void GLBackendGLFW::debug_glfw_error_callback_(
int error,
char const * message
)
{
auto ostream = std::ostringstream{};
ostream << std::hex << std::showbase;
// https://www.glfw.org/docs/3.3/group__errors.html
ostream << "GLFW error ";
// NOLINTNEXTLINE
#define GLBACKEND_GLFW_CALLBACK_CASE_(VALUE) \
case GLFW_ ## VALUE: \
ostream << #VALUE; \
break;
switch(error)
{
GLBACKEND_GLFW_CALLBACK_CASE_(NOT_INITIALIZED)
GLBACKEND_GLFW_CALLBACK_CASE_(NO_CURRENT_CONTEXT)
GLBACKEND_GLFW_CALLBACK_CASE_(INVALID_ENUM)
GLBACKEND_GLFW_CALLBACK_CASE_(INVALID_VALUE)
GLBACKEND_GLFW_CALLBACK_CASE_(OUT_OF_MEMORY)
GLBACKEND_GLFW_CALLBACK_CASE_(API_UNAVAILABLE)
GLBACKEND_GLFW_CALLBACK_CASE_(VERSION_UNAVAILABLE)
GLBACKEND_GLFW_CALLBACK_CASE_(PLATFORM_ERROR)
GLBACKEND_GLFW_CALLBACK_CASE_(FORMAT_UNAVAILABLE)
GLBACKEND_GLFW_CALLBACK_CASE_(NO_WINDOW_CONTEXT)
default:
ostream << error;
}
ostream << ":\n";
ostream << message;
debug_callback()(ostream.str());
}
/// Guards
#endif
|