src/glbackend_sdl.cpp
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