diff --git a/examples/sdl_app/.gitignore b/examples/sdl_app/.gitignore new file mode 100644 index 0000000..75c3626 --- /dev/null +++ b/examples/sdl_app/.gitignore @@ -0,0 +1,4 @@ +# Build files +/build +/build.ninja +/shuriken.override.yaml diff --git a/examples/sdl_app/assets/neocat.png b/examples/sdl_app/assets/neocat.png new file mode 100644 index 0000000..5aa7617 Binary files /dev/null and b/examples/sdl_app/assets/neocat.png differ diff --git a/examples/sdl_app/shuriken.yaml b/examples/sdl_app/shuriken.yaml new file mode 100644 index 0000000..830efb4 --- /dev/null +++ b/examples/sdl_app/shuriken.yaml @@ -0,0 +1,10 @@ +defaults: + cpp_standard: c++20 + cpp_flags: -Wall -Wextra -Werror -pedantic + linker_args: -lSDL3 -lSDL3_image + +default_target: linux + +targets: + linux: + output_file: sdl_example diff --git a/examples/sdl_app/src/main.cpp b/examples/sdl_app/src/main.cpp new file mode 100644 index 0000000..ec47182 --- /dev/null +++ b/examples/sdl_app/src/main.cpp @@ -0,0 +1,23 @@ +import sdl_app; + +#define SDL_MAIN_USE_CALLBACKS +#include + +SDL_AppResult SDL_AppInit(void** appstate, int /*argc*/, char** /*argv*/) { + *appstate = new AppState; + return sdl_app_init(static_cast(*appstate)); +} + +SDL_AppResult SDL_AppIterate(void* appstate) { + return sdl_app_iterate(static_cast(appstate)); +} + +SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) { + return sdl_app_event(static_cast(appstate), event); +} + +void SDL_AppQuit(void* appstate, SDL_AppResult /*result*/) { + const auto game_app_state = static_cast(appstate); + sdl_app_shutdown(game_app_state); + delete game_app_state; +} diff --git a/examples/sdl_app/src/sdl_app.cppm b/examples/sdl_app/src/sdl_app.cppm new file mode 100644 index 0000000..6d21b7a --- /dev/null +++ b/examples/sdl_app/src/sdl_app.cppm @@ -0,0 +1,89 @@ +module; + +#include +#include +#include + +import sprite; + +export module sdl_app; + +export { + struct AppState { + SDL_Window* window = nullptr; + SDL_Renderer* renderer = nullptr; + Sprite* sprite = nullptr; + }; + + SDL_AppResult sdl_panic( + const std::string& error_prefix, + SDL_Window* window = nullptr, + SDL_Renderer* renderer = nullptr + ) { + std::cerr << error_prefix << ": " << SDL_GetError() << std::endl; + + if (renderer != nullptr) { + SDL_DestroyRenderer(renderer); + } + if (window != nullptr) { + SDL_DestroyWindow(window); + } + + SDL_Quit(); + return SDL_APP_FAILURE; + } + + SDL_AppResult sdl_app_init(AppState* app_state) { + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) { + return sdl_panic("SDL_Init error"); + } + + app_state->window = SDL_CreateWindow("BuildSystemTest", 640, 480, 0); + if (app_state->window == nullptr) { + return sdl_panic("SDL_CreateWindow error"); + } + + app_state->renderer = SDL_CreateRenderer(app_state->window, nullptr); + if (app_state->renderer == nullptr) { + return sdl_panic("SDL_CreateRenderer error", app_state->window); + } + + try { + app_state->sprite = new Sprite(app_state->renderer, "assets/neocat.png", 100, 100); + } + catch (const std::runtime_error& e) { + return sdl_panic(e.what(), app_state->window, app_state->renderer); + } + + return SDL_APP_CONTINUE; + } + + SDL_AppResult sdl_app_event(const AppState* app_state, const SDL_Event* event) { + if (event->type == SDL_EVENT_QUIT) { + return SDL_APP_SUCCESS; + } + + if (event->type == SDL_EVENT_MOUSE_MOTION) { + app_state->sprite->move( + event->motion.x - 50, + event->motion.y - 50 + ); + } + + return SDL_APP_CONTINUE; + } + + SDL_AppResult sdl_app_iterate(const AppState* app_state) { + SDL_RenderClear(app_state->renderer); + app_state->sprite->render(app_state->renderer); + SDL_RenderPresent(app_state->renderer); + + return SDL_APP_CONTINUE; + } + + void sdl_app_shutdown(const AppState* app_state) { + delete app_state->sprite; + SDL_DestroyRenderer(app_state->renderer); + SDL_DestroyWindow(app_state->window); + } +} diff --git a/examples/sdl_app/src/sprite.cppm b/examples/sdl_app/src/sprite.cppm new file mode 100644 index 0000000..6664597 --- /dev/null +++ b/examples/sdl_app/src/sprite.cppm @@ -0,0 +1,52 @@ +module; + +#include +#include +#include +#include + +export module sprite; + +export class Sprite { + private: + SDL_Texture* sdl_texture; + SDL_FRect dest_rect{0, 0, 0, 0}; + + public: + explicit Sprite( + SDL_Renderer* renderer, + const std::string& filename, + const int width, + const int height + ) { + SDL_Surface* texture_surface = IMG_Load(filename.c_str()); + if (texture_surface == nullptr) { + throw std::runtime_error("IMG_Load error"); + } + + sdl_texture = SDL_CreateTextureFromSurface(renderer, texture_surface); + SDL_DestroySurface(texture_surface); + + if (sdl_texture == nullptr) { + throw std::runtime_error("SDL_CreateTextureFromSurface error"); + } + + dest_rect.w = static_cast(width); + dest_rect.h = static_cast(height); + } + + ~Sprite() { + if (sdl_texture) { + SDL_DestroyTexture(sdl_texture); + } + } + + void move(const float x, const float y) { + dest_rect.x = x; + dest_rect.y = y; + } + + void render(SDL_Renderer* renderer) const { + SDL_RenderTexture(renderer, sdl_texture, nullptr, &dest_rect); + } +}; diff --git a/examples/sdl_app/tools/shuriken.py b/examples/sdl_app/tools/shuriken.py new file mode 120000 index 0000000..dc142ea --- /dev/null +++ b/examples/sdl_app/tools/shuriken.py @@ -0,0 +1 @@ +../../../src/shuriken.py \ No newline at end of file