/*
Copyright 2014 Michele "King_DuckZ" Santullo
This file is part of CloonelJump.
CloonelJump is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CloonelJump is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CloonelJump. If not, see .
*/
#include "sdlmain.hpp"
#include "observersmanager.hpp"
#include "sizenotifiable.hpp"
#include "sizeratio.hpp"
#include
#include
#include
#include
#include
#include
#if defined(RASPBERRY_PI)
#include
#endif
namespace cloonel {
namespace {
///----------------------------------------------------------------------
///----------------------------------------------------------------------
std::pair GetRenderingDriver() {
typedef std::pair RetPairType;
const int count = SDL_GetNumRenderDrivers();
int opengles = -1;
int opengles2 = -1;
int opengl = -1;
SDL_RendererInfo info;
for (int z = 0; z < count; ++z) {
const int ret = SDL_GetRenderDriverInfo(z, &info);
if (0 == ret) {
if (std::strcmp("opengles", info.name) == 0)
opengles = z;
else if (std::strcmp("opengles2", info.name) == 0)
opengles2 = z;
else if (std::strcmp("opengl", info.name) == 0)
opengl = z;
}
}
#if !defined(FORCE_OPENGLES)
if (opengl > -1)
return RetPairType(opengl, "opengl");
#endif
if (opengles2 > -1)
return RetPairType(opengles2, "opengles2");
if (opengles > -1)
return RetPairType(opengles, "opengles");
#if defined(FORCE_OPENGLES)
if (opengl > -1)
return RetPairType(opengl, "opengl");
#endif
return RetPairType(-1, "default");
}
} //unnamed namespace
struct SDLMain::LocalData {
SDL_Window* window;
SDL_Renderer* renderer;
SizeRatio sizeratio;
ObserversManager resChangeNotifList;
bool initialized;
#if defined(RASPBERRY_PI)
bool bcmInitialized;
#endif
};
///------------------------------------------------------------------------
///------------------------------------------------------------------------
SDLMain::SDLMain (const char* parGameName, ushort2 parRes, ushort2 parReferenceRes) :
m_gameName(parGameName),
m_localData(new LocalData)
{
m_localData->sizeratio.SetOriginal(static_cast(parReferenceRes), static_cast(parRes));
m_localData->initialized = false;
#if defined(RASPBERRY_PI)
m_localData->bcmInitialized = false;
#endif
}
///------------------------------------------------------------------------
///------------------------------------------------------------------------
SDLMain::~SDLMain() noexcept {
ClearIFN(*m_localData);
#if defined(RASPBERRY_PI)
assert(not m_localData->bcmInitialized);
#endif
}
///------------------------------------------------------------------------
///------------------------------------------------------------------------
void SDLMain::Init() {
if (not m_localData->initialized)
InitSDL(*m_localData);
}
///------------------------------------------------------------------------
///------------------------------------------------------------------------
void SDLMain::InitSDL (LocalData& parInitSDL) {
#if defined(RASPBERRY_PI)
assert(not parInitSDL.bcmInitialized);
bcm_host_init();
parInitSDL.bcmInitialized = true;
#endif
parInitSDL.window = nullptr;
parInitSDL.renderer = nullptr;
parInitSDL.initialized = false;
if (SDL_Init(SDL_INIT_EVERYTHING) == -1)
throw std::runtime_error(SDL_GetError());
parInitSDL.initialized = true;
#if defined(FORCE_OPENGLES)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
#endif
const float2 wh(m_localData->sizeratio.Resolution());
SDL_Window* const win = SDL_CreateWindow(m_gameName.c_str(), 100, 100, static_cast(wh.x()), static_cast(wh.y()), SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if (!win)
throw std::runtime_error(SDL_GetError());
parInitSDL.window = win;
const auto rendererDriver = GetRenderingDriver();
m_rendererName = rendererDriver.second;
SDL_Renderer* const renderer = SDL_CreateRenderer(win, rendererDriver.first, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer)
throw std::runtime_error(SDL_GetError());
parInitSDL.renderer = renderer;
}
///------------------------------------------------------------------------
///------------------------------------------------------------------------
void SDLMain::ClearIFN (LocalData& parInitSDL) noexcept {
if (parInitSDL.renderer)
SDL_DestroyRenderer(parInitSDL.renderer);
if (parInitSDL.window)
SDL_DestroyWindow(parInitSDL.window);
if (parInitSDL.initialized) {
parInitSDL.initialized = false;
SDL_Quit();
}
#if defined(RASPBERRY_PI)
if (parInitSDL.bcmInitialized) {
parInitSDL.bcmInitialized = false;
bcm_host_deinit();
}
#endif
}
///------------------------------------------------------------------------
///------------------------------------------------------------------------
SDL_Renderer* SDLMain::GetRenderer() {
if (m_localData->initialized)
return m_localData->renderer;
else
return nullptr;
}
///------------------------------------------------------------------------
///------------------------------------------------------------------------
void SDLMain::SetResolution (ushort2 parRes) {
m_localData->sizeratio.UpdateResolution(static_cast(parRes));
{
SDL_Renderer* const renderer = GetRenderer();
assert(renderer);
const int retVal = SDL_RenderSetLogicalSize(renderer, parRes.x(), parRes.y());
if (retVal) {
std::ostringstream oss;
oss << "Error setting logical size to renderer to " << parRes.x() << "x" << parRes.y() << ": " << SDL_GetError();
throw std::runtime_error(oss.str());
}
const SDL_Rect area = { 0, 0, parRes.x(), parRes.y() };
const int retValViewport = SDL_RenderSetViewport(renderer, &area);
if (retValViewport) {
std::ostringstream oss;
oss << "Error setting viewport to renderer to " << parRes.x() << "x" << parRes.y() << ": " << SDL_GetError();
throw std::runtime_error(oss.str());
}
}
for (auto currNotifiable : m_localData->resChangeNotifList) {
currNotifiable->NotifyResChanged(m_localData->sizeratio);
}
}
///------------------------------------------------------------------------
///------------------------------------------------------------------------
size_t SDLMain::RegisterForResChange (SizeNotifiableBase* parNotif) {
parNotif->NotifyResChanged(m_localData->sizeratio);
return m_localData->resChangeNotifList.Add(parNotif);
}
///------------------------------------------------------------------------
///------------------------------------------------------------------------
void SDLMain::UnregisterForResChange (size_t parID) noexcept {
m_localData->resChangeNotifList.Remove(parID);
}
///------------------------------------------------------------------------
///------------------------------------------------------------------------
void SDLMain::SwapRegisteredForResChange (size_t parID, SizeNotifiableBase* parNotif) {
m_localData->resChangeNotifList.Update(parID, parNotif);
}
///------------------------------------------------------------------------
///------------------------------------------------------------------------
ushort2 SDLMain::WidthHeight() const noexcept {
return static_cast(m_localData->sizeratio.Resolution());
}
///------------------------------------------------------------------------
///------------------------------------------------------------------------
std::string SDLMain::GetVideoDriverName() const {
return std::string(SDL_GetCurrentVideoDriver());
}
} //namespace cloonel