/// 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