aa70ce7e |
/// Guards
#ifdef GLBACKEND_SDL
/// Includes
#include <glbackend_sdl.hpp>
#include <functional>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <glbase.hpp>
#include <glbackend.hpp>
#include <SDL2/SDL.h>
#include <SDL2/SDL_error.h>
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_keycode.h>
#include <SDL2/SDL_version.h>
// NOLINTNEXTLINE
#define STR_EXCEPTION GLBase::Exception
#include <str.hpp>
/// Constants
auto static constexpr sdl_subsystems_ =
SDL_INIT_VIDEO |
SDL_INIT_EVENTS |
SDL_INIT_TIMER;
/// Special member functions
GLBackendSDL::GLBackendSDL(
std::string const & title,
std::array<int, 2> size,
std::array<int, 2> version,
int samples,
bool fullscreen,
bool transparent
)
:
GLBackend(),
window_ {nullptr},
context_{nullptr},
running_{true},
time_ {0.0F}
{
// Backend init
if (SDL_InitSubSystem(sdl_subsystems_) != 0)
STR_THROW(
"Failed to initialize SDL" << ":\n" <<
SDL_GetError()
);
// Window options
SDL_GL_ResetAttributes();
auto window_flags = Uint32{0};
window_flags |= SDL_WINDOW_OPENGL;
if (fullscreen)
window_flags |= SDL_WINDOW_FULLSCREEN;
if (transparent)
{
window_flags |= SDL_WINDOW_BORDERLESS;
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
}
if (size[0] == 0 || size[1] == 0)
{
auto mode = SDL_DisplayMode{};
SDL_GetCurrentDisplayMode(0, &mode);
size[0] = mode.w;
size[1] = mode.h;
}
size_ = size;
// Context options
auto context_flags = int{0};
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, SDL_TRUE);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); // NOLINT
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); // NOLINT
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); // NOLINT
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); // NOLINT
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); // NOLINT
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); // NOLINT
if (samples)
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, samples);
}
if (version[0])
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, version[0]);
if (version[1])
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, version[1]);
if ((version[0] == 3 && version[1] >= 2) || version[0] >= 4) // NOLINT
{
SDL_GL_SetAttribute(
SDL_GL_CONTEXT_PROFILE_MASK,
SDL_GL_CONTEXT_PROFILE_CORE
);
context_flags |= SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG; // NOLINT
}
if (debug() >= 1)
context_flags |= SDL_GL_CONTEXT_DEBUG_FLAG; // NOLINT
else if ((version[0] == 4 && version[1] >= 6) || version[0] >= 5) // NOLINT
SDL_GL_SetAttribute(SDL_GL_CONTEXT_NO_ERROR, SDL_TRUE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, context_flags);
try
{
// Window
window_ = SDL_CreateWindow(
title.c_str(),
SDL_WINDOWPOS_UNDEFINED, // NOLINT
SDL_WINDOWPOS_UNDEFINED, // NOLINT
size[0],
size[1],
window_flags
);
if (!window_)
STR_THROW(
"Failed to create SDL window" << ":\n" <<
SDL_GetError()
);
// Context
context_ = SDL_GL_CreateContext(window_);
if (!context_)
STR_THROW(
"Failed to create SDL context" << ":\n" <<
SDL_GetError()
);
SDL_GL_MakeCurrent(window_, context_);
if (debug() == 0)
SDL_GL_SetSwapInterval(1);
init_();
// Lock
if (fullscreen)
GLBackendSDL::lock(true);
}
catch (...)
{
destroy_();
throw;
}
}
GLBackendSDL::~GLBackendSDL()
{
destroy_();
}
GLBackendSDL::GLBackendSDL(GLBackendSDL && other) noexcept
:
GLBackend(std::move(other)),
window_ {other.window_},
context_{other.context_},
running_{other.running_},
time_ {other.time_}
{
other.window_ = nullptr;
other.context_ = nullptr;
}
void GLBackendSDL::destroy_()
{
GLBackendSDL::lock(false);
if (context_)
SDL_GL_DeleteContext(context_);
if (window_)
SDL_DestroyWindow(window_);
SDL_QuitSubSystem(sdl_subsystems_);
}
/// Render loop
inline void GLBackendSDL::events()
{
auto event = SDL_Event{};
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_KEYDOWN:
if (event.key.repeat)
break;
if (callback_key_)
{
auto key = event.key.keysym.sym;
if (key == SDLK_RETURN)
callback_key_("Enter");
else if (key == SDLK_LCTRL || key == SDLK_RCTRL)
callback_key_("Control");
else if (key == SDLK_LSHIFT || key == SDLK_RSHIFT)
callback_key_("Shift");
else if (key == SDLK_LALT || key == SDLK_RALT)
callback_key_("Alt");
else
callback_key_(SDL_GetKeyName(key));
}
break;
case SDL_MOUSEBUTTONDOWN:
if (callback_button_)
callback_button_(event.button.button);
break;
case SDL_MOUSEWHEEL:
scroll_ = {
(float)event.wheel.x,
(float)event.wheel.y,
};
if (callback_scroll_)
callback_scroll_(scroll_);
break;
case SDL_MOUSEMOTION:
position_ = {
(float)event.motion.x,
(float)event.motion.y,
};
move_ = {
(float)event.motion.xrel,
(float)event.motion.yrel,
};
if (callback_position_)
callback_position_(position_);
if (callback_move_)
callback_move_(move_);
break;
case SDL_WINDOWEVENT:
switch (event.window.event)
{
case SDL_WINDOWEVENT_SIZE_CHANGED:
// size_ = {
// event.window.data1,
// event.window.data2,
// };
SDL_GL_GetDrawableSize(
window_,
&size_[0],
&size_[1]
);
if (callback_size_)
callback_size_(size_);
break;
case SDL_WINDOWEVENT_CLOSE:
running_ = false;
break;
}
break;
}
}
}
/// Debug
std::string GLBackendSDL::debug_info() const
{
auto ostream = std::ostringstream{};
auto version_compiled = SDL_version{};
auto version_linked = SDL_version{};
SDL_VERSION(&version_compiled);
SDL_GetVersion(&version_linked);
debug_info_(ostream, "SDL", {
{
"VERSION_COMPILED",
std::to_string(version_compiled.major) + "." +
std::to_string(version_compiled.minor) + "." +
std::to_string(version_compiled.patch),
},
{
"VERSION_LINKED",
std::to_string(version_linked.major) + "." +
std::to_string(version_linked.minor) + "." +
std::to_string(version_linked.patch),
},
});
ostream << GLBackend::debug_info();
return ostream.str();
}
/// Guards
#endif
|