|
/* |
|
* program.cc - Copyright (c) 2024 - Olivier Poncet |
|
* |
|
* This program 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 2 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
#include <cstdio> |
|
#include <cstdlib> |
|
#include <cstring> |
|
#include <cstdint> |
|
#include <string> |
|
#include <vector> |
|
#include <memory> |
|
#include <iostream> |
|
#include <stdexcept> |
|
#ifdef __EMSCRIPTEN__ |
|
#include <emscripten.h> |
|
#endif |
|
#include "program.h" |
|
|
|
// --------------------------------------------------------------------------- |
|
// <anonymous>::ApplicationRunner |
|
// --------------------------------------------------------------------------- |
|
|
|
namespace { |
|
|
|
struct ApplicationRunner |
|
{ |
|
#ifdef __EMSCRIPTEN__ |
|
static void main_loop(Application& application) |
|
{ |
|
if(application.running()) { |
|
application.loop(); |
|
} |
|
else { |
|
::emscripten_cancel_main_loop(); |
|
} |
|
} |
|
|
|
static void run(Application& application) |
|
{ |
|
::emscripten_set_main_loop_arg(reinterpret_cast<em_arg_callback_func>(&main_loop), &application, 0, 1); |
|
} |
|
#else |
|
static void run(Application& application) |
|
{ |
|
while(application.running()) { |
|
application.loop(); |
|
} |
|
} |
|
#endif |
|
}; |
|
|
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// Console |
|
// --------------------------------------------------------------------------- |
|
|
|
void Console::println(const std::string& string) |
|
{ |
|
std::cout << string << std::endl; |
|
} |
|
|
|
void Console::errorln(const std::string& string) |
|
{ |
|
std::cerr << string << std::endl; |
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// Program |
|
// --------------------------------------------------------------------------- |
|
|
|
void Program::run(const std::string& title, const int width, const int height) |
|
{ |
|
const auto application(std::make_unique<HelloWorldApp>(title, width, height)); |
|
|
|
ApplicationRunner::run(*application); |
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// Application |
|
// --------------------------------------------------------------------------- |
|
|
|
Application::Application() |
|
: _running(true) |
|
{ |
|
sdl_init(); |
|
} |
|
|
|
Application::~Application() |
|
{ |
|
sdl_quit(); |
|
} |
|
|
|
void Application::sdl_init() |
|
{ |
|
const int rc = ::SDL_Init(SDL_INIT_VIDEO); |
|
|
|
if(rc != 0) { |
|
throw std::runtime_error("SDL_Init() has failed"); |
|
} |
|
} |
|
|
|
void Application::sdl_quit() |
|
{ |
|
::SDL_Quit(); |
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// HelloWorldApp |
|
// --------------------------------------------------------------------------- |
|
|
|
HelloWorldApp::HelloWorldApp(const std::string& title, const int width, const int height) |
|
: Application() |
|
, _window() |
|
, _renderer() |
|
, _surface() |
|
, _texture() |
|
{ |
|
sdl_create(title, width, height); |
|
} |
|
|
|
HelloWorldApp::~HelloWorldApp() |
|
{ |
|
sdl_destroy(); |
|
} |
|
|
|
void HelloWorldApp::loop() |
|
{ |
|
auto on_quit = [&](const SDL_QuitEvent& event) -> void |
|
{ |
|
_running = false; |
|
}; |
|
|
|
auto on_keypress = [&](const SDL_KeyboardEvent& event) -> void |
|
{ |
|
if(event.keysym.sym == SDLK_ESCAPE) { |
|
_running = false; |
|
} |
|
}; |
|
|
|
auto on_keyrelease = [&](const SDL_KeyboardEvent& event) -> void |
|
{ |
|
if(event.keysym.sym == SDLK_ESCAPE) { |
|
_running = false; |
|
} |
|
}; |
|
|
|
auto poll_events = [&]() -> void |
|
{ |
|
SDL_Event event; |
|
while(::SDL_PollEvent(&event) != 0) { |
|
switch(event.type) { |
|
case SDL_QUIT: |
|
on_quit(event.quit); |
|
break; |
|
case SDL_KEYDOWN: |
|
on_keypress(event.key); |
|
break; |
|
case SDL_KEYUP: |
|
on_keyrelease(event.key); |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
}; |
|
|
|
auto render_frame = [&](SDL_Renderer* renderer, SDL_Texture* texture) -> void |
|
{ |
|
::SDL_SetRenderDrawColor(renderer, 192, 192, 192, 255); |
|
::SDL_RenderClear(renderer); |
|
::SDL_RenderCopy(renderer, texture, nullptr, nullptr); |
|
::SDL_RenderPresent(renderer); |
|
}; |
|
|
|
auto process = [&]() -> void |
|
{ |
|
poll_events(); |
|
render_frame(_renderer.get(), _texture.get()); |
|
}; |
|
|
|
return process(); |
|
} |
|
|
|
void HelloWorldApp::sdl_create(const std::string& title, const int width, const int height) |
|
{ |
|
auto create_window = [&]() -> void |
|
{ |
|
const int win_x = SDL_WINDOWPOS_UNDEFINED; |
|
const int win_y = SDL_WINDOWPOS_UNDEFINED; |
|
const int win_w = (width < 0 ? 800 : width ); |
|
const int win_h = (height < 0 ? 600 : height); |
|
const Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE; |
|
|
|
if(!_window) { |
|
_window.reset(::SDL_CreateWindow(title.c_str(), win_x, win_y, win_w, win_h, flags)); |
|
} |
|
if(!_window) { |
|
throw std::runtime_error("SDL_CreateWindow() has failed"); |
|
} |
|
}; |
|
|
|
auto create_renderer = [&]() -> void |
|
{ |
|
const int index = -1; |
|
const Uint32 flags = 0; |
|
|
|
if(!_renderer) { |
|
_renderer.reset(::SDL_CreateRenderer(_window.get(), index, flags)); |
|
} |
|
if(!_renderer) { |
|
throw std::runtime_error("SDL_CreateRenderer() has failed"); |
|
} |
|
}; |
|
|
|
auto create_surface = [&]() -> void |
|
{ |
|
if(!_surface) { |
|
_surface.reset(::IMG_Load("sdl.png")); |
|
} |
|
if(!_surface) { |
|
throw std::runtime_error("IMG_Load() has failed"); |
|
} |
|
}; |
|
|
|
auto create_texture = [&]() -> void |
|
{ |
|
if(!_texture) { |
|
_texture.reset(::SDL_CreateTextureFromSurface(_renderer.get(), _surface.get())); |
|
} |
|
if(!_texture) { |
|
throw std::runtime_error("SDL_CreateTextureFromSurface() has failed"); |
|
} |
|
}; |
|
|
|
auto create = [&]() -> void |
|
{ |
|
create_window(); |
|
create_renderer(); |
|
create_surface(); |
|
create_texture(); |
|
}; |
|
|
|
return create(); |
|
} |
|
|
|
void HelloWorldApp::sdl_destroy() |
|
{ |
|
auto destroy_texture = [&]() -> void |
|
{ |
|
_texture.reset(); |
|
}; |
|
|
|
auto destroy_surface = [&]() -> void |
|
{ |
|
_surface.reset(); |
|
}; |
|
|
|
auto destroy_renderer = [&]() -> void |
|
{ |
|
_renderer.reset(); |
|
}; |
|
|
|
auto destroy_window = [&]() -> void |
|
{ |
|
_window.reset(); |
|
}; |
|
|
|
auto destroy = [&]() -> void |
|
{ |
|
destroy_texture(); |
|
destroy_surface(); |
|
destroy_renderer(); |
|
destroy_window(); |
|
}; |
|
|
|
return destroy(); |
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// main |
|
// --------------------------------------------------------------------------- |
|
|
|
int main(int argc, char* argv[]) |
|
{ |
|
try { |
|
Program::run("Hello World!", 800, 600); |
|
} |
|
catch(const std::exception& e) { |
|
Console::errorln(e.what()); |
|
} |
|
catch(...) { |
|
Console::errorln("fatal error!"); |
|
} |
|
return EXIT_SUCCESS; |
|
} |
|
|
|
// --------------------------------------------------------------------------- |
|
// End-Of-File |
|
// --------------------------------------------------------------------------- |