diff --git a/assets/neocat.png b/assets/neocat.png new file mode 100644 index 0000000..5aa7617 Binary files /dev/null and b/assets/neocat.png differ diff --git a/src/main.cpp b/src/main.cpp index 283ffd6..cdfe69e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,27 @@ -#include +import sdl_app; -int main() { - std::cout << "Meow" << std::endl; +#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/src/sdl_app.cppm b/src/sdl_app.cppm new file mode 100644 index 0000000..7e14111 --- /dev/null +++ b/src/sdl_app.cppm @@ -0,0 +1,95 @@ +module; + +#include +#include +#include + +export module sdl_app; + +import sprite; + +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() << '\n'; + + 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/src/sprite.cppm b/src/sprite.cppm new file mode 100644 index 0000000..3318180 --- /dev/null +++ b/src/sprite.cppm @@ -0,0 +1,81 @@ +module; + +#include +#include +#include +#include + +export module sprite; + +export class Sprite +{ + 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); + } + + // Don't allow copy operations + Sprite(const Sprite&) = delete; + Sprite& operator=(const Sprite&) = delete; + + // Move constructor + Sprite(Sprite&& other) noexcept + : sdl_texture(other.sdl_texture), + dest_rect(other.dest_rect) + { + other.sdl_texture = nullptr; + } + + // Move assignment + Sprite& operator=(Sprite&& other) noexcept + { + // Move inner resources from other + sdl_texture = other.sdl_texture; + dest_rect = other.dest_rect; + + // Reset other to make it safe for deletion + other.sdl_texture = nullptr; + + return *this; + } + + ~Sprite() + { + if (sdl_texture != nullptr) { + 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); + } +};