From 989ffa818d3a664e74d35849ccd184492f3790d8 Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Sat, 13 Apr 2024 23:18:52 +1000 Subject: [PATCH] organise application --- Endoomed/src/main.cpp | 456 +++++++++++++++++++++++++----------------- 1 file changed, 275 insertions(+), 181 deletions(-) diff --git a/Endoomed/src/main.cpp b/Endoomed/src/main.cpp index 52b5d32..27baeb2 100644 --- a/Endoomed/src/main.cpp +++ b/Endoomed/src/main.cpp @@ -48,7 +48,44 @@ template constexpr T clamp(T x, T min, T max) return x < min ? min : (x > max ? max : x); } -static Uint32 timerCallback(Uint32 interval, void* param) +class Application final +{ + int argc; + const char** argv; + + SDL_Window* window; + SDL_Renderer* renderer; + SDL_Texture* codepage; + SDL_TimerID blinkTimer; + + bool needRepaint; + + bool mouseLeft, mouseRight; + int mousePrevX, mousePrevY, mouseX, mouseY; + bool mouseInWindow; + + bool drawing, blinkState; + + std::array display; + + int create(); + int handleEvent(const SDL_Event& event) noexcept; + int update(); + int repaint(); + + static Uint32 timerCallback(Uint32 interval, void* param); + + inline void damage() noexcept { needRepaint = true; } + SDL_Texture* loadTexture(const std::string& path); + +public: + Application(int argc, const char* argv[]); + ~Application(); + + int run(); +}; + +Uint32 Application::timerCallback(Uint32 interval, void* param) { SDL_Event event = { @@ -66,7 +103,54 @@ static Uint32 timerCallback(Uint32 interval, void* param) return interval; } -int main(int argc, char** argv) + +Application::Application(int aArgc, const char* aArgv[]) : + argc(aArgc), argv(aArgv), + window(nullptr), renderer(nullptr), codepage(nullptr), blinkTimer(0), + needRepaint(true), + mouseLeft(false), mouseRight(false), + mousePrevX(0), mousePrevY(0), mouseX(0), mouseY(0), + mouseInWindow(false), + drawing(false), blinkState(true) {} + +Application::~Application() +{ + SDL_RemoveTimer(blinkTimer); + SDL_DestroyTexture(codepage); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); +} + +SDL_Texture* Application::loadTexture(const std::string& path) +{ + unsigned pixelWidth, pixelHeight; + std::vector pixels; + if (lodepng::decode(pixels, pixelWidth, pixelHeight, path, LCT_RGBA, 8) != 0) + { + return nullptr; + } + + constexpr Uint32 format = SDL_PIXELFORMAT_RGBA8888; + constexpr int access = SDL_TEXTUREACCESS_STATIC; + const int textureWidth = static_cast(pixelWidth), textureHeight = static_cast(pixelHeight); + SDL_Texture* texture = SDL_CreateTexture(renderer, format, access, textureWidth, textureHeight); + if (texture == nullptr) + { + return nullptr; + } + + if (SDL_UpdateTexture(texture, nullptr, pixels.data(), 4 * static_cast(pixelWidth)) != 0 || + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND) != 0) + { + SDL_DestroyTexture(texture); + } + + return texture; +} + + +int Application::create() { if (SDL_Init(SDL_INIT_VIDEO) < 0) { @@ -76,47 +160,25 @@ int main(int argc, char** argv) constexpr int screenWidth = cellWidth * 80; constexpr int screenHeight = cellHeight * 25; - SDL_Window* window = nullptr; - SDL_Renderer* renderer = nullptr; constexpr Uint32 winFlags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE; if (SDL_CreateWindowAndRenderer(screenWidth * 2, screenHeight * 2, winFlags, &window, &renderer) < 0) { - SDL_Quit(); return -1; } SDL_SetWindowTitle(window, "EnDOOMed"); SDL_RenderSetLogicalSize(renderer, screenWidth, screenHeight); - SDL_Texture* codepage = nullptr; + if ((codepage = Application::loadTexture("codepage.png")) == nullptr) { - unsigned codepageWidth, codepageHeight; - std::vector codepagePixels; - if (lodepng::decode(codepagePixels, codepageWidth, codepageHeight, "codepage.png", LCT_RGBA, 8) != 0 || - (codepage = SDL_CreateTexture(renderer, - SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, - static_cast(codepageWidth), static_cast(codepageHeight))) == nullptr || - SDL_UpdateTexture(codepage, nullptr, codepagePixels.data(), 4 * static_cast(codepageWidth)) != 0 || - SDL_SetTextureBlendMode(codepage, SDL_BLENDMODE_BLEND) != 0) - { - SDL_DestroyTexture(codepage); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); - return -1; - } + return -1; } - std::array display; std::memset(display.data(), 0, sizeof(Cell) * display.size()); FILE* file = fopen("ENDOOM.bin", "rb"); if (file == nullptr) { - SDL_DestroyTexture(codepage); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); return -1; } @@ -134,8 +196,71 @@ int main(int argc, char** argv) }; } - SDL_AddTimer(400, timerCallback, nullptr); + fclose(file); + blinkTimer = SDL_AddTimer(400, Application::timerCallback, nullptr); + if (blinkTimer == 0) + { + return -1; + } + + return 0; +} + +int Application::handleEvent(const SDL_Event& event) noexcept +{ + switch (event.type) + { + case SDL_QUIT: + return 1; + case SDL_USEREVENT: + blinkState = !blinkState; + damage(); + return 0; + case SDL_MOUSEBUTTONDOWN: + switch (event.button.button) + { + case SDL_BUTTON_LEFT: + mouseLeft = true; + break; + case SDL_BUTTON_RIGHT: + mouseRight = true; + break; + } + return 0; + case SDL_MOUSEBUTTONUP: + switch (event.button.button) + { + case SDL_BUTTON_LEFT: + mouseLeft = false; + break; + case SDL_BUTTON_RIGHT: + mouseRight = false; + break; + } + return 0; + case SDL_MOUSEMOTION: + mouseX = event.motion.x; + mouseY = event.motion.y; + return 0; + case SDL_WINDOWEVENT: + switch (event.window.event) + { + case SDL_WINDOWEVENT_LEAVE: + mouseInWindow = false; + break; + case SDL_WINDOWEVENT_ENTER: + mouseInWindow = true; + break; + }; + return 0; + default: + return 0; + } +} + +int Application::update() +{ Cell leftBrush = { .character = 219, @@ -151,174 +276,143 @@ int main(int argc, char** argv) .blinking = false }; - bool mouseLeft = false, mouseRight = false; - int mousePrevX = 0, mousePrevY = 0; - int mouseX = 0, mouseY = 0; - bool mouseInWindow = false; - - bool running = true, damage = true; - bool blinkState = true; - - do + if (mouseInWindow && (mouseLeft || mouseRight)) { + if (!drawing) + { + mousePrevX = mouseX; + mousePrevY = mouseY; + drawing = true; + } + int fromX = clamp(mouseX / cellWidth, 0, 79); + int fromY = clamp(mouseY / cellHeight, 0, 24); + int toX = clamp(mousePrevX / cellWidth, 0, 79); + int toY = clamp(mousePrevY / cellHeight, 0, 24); + + auto srcChar = mouseLeft ? leftBrush : rightBrush; + if (toX == fromX && toY == fromY) + { + display[toX + toY * 80] = srcChar; + } + else + { + bool steep = std::abs(toY - fromY) > std::abs(toX - fromX); + if (steep) + { + std::swap(fromY, fromX); + std::swap(toY, toX); + } + if (fromX > toX) + { + std::swap(toX, fromX); + std::swap(toY, fromY); + } + + int deltaX = toX - fromX; + int deltaY = std::abs(toY - fromY); + int error = deltaX / 2; + int y = fromY; + int yStep = (fromY < toY) ? 1 : -1; + for (int x = fromX; x <= toX; ++x) + { + int idx = steep + ? y + x * 80 + : x + y * 80; + display[idx] = srcChar; + + error -= deltaY; + if (error < 0) + { + y += yStep; + error += deltaX; + } + } + } + mousePrevX = mouseX; mousePrevY = mouseY; + damage(); + } + else if (drawing && !mouseLeft && !mouseRight) + { + drawing = false; + } + return 0; +} + +int Application::repaint() +{ + SDL_SetRenderDrawColor(renderer, 48, 48, 48, 255); + SDL_RenderClear(renderer); + + SDL_Rect src, dst; + src.w = cellWidth; + src.h = cellHeight; + dst.w = cellWidth; + dst.h = cellHeight; + + int x = 0, y = 0; + for (const auto& currentCharacter : display) + { + auto currentBgColour = colourTable[static_cast(currentCharacter.bgColour)]; + auto currentFgColour = colourTable[static_cast(currentCharacter.fgColour)]; + + src.x = (currentCharacter.character % 32) * cellWidth; + src.y = (currentCharacter.character / 32) * cellHeight; + dst.x = x * cellWidth; + dst.y = y * cellHeight; + + SDL_SetRenderDrawColor(renderer, currentBgColour.r, currentBgColour.g, currentBgColour.b, currentBgColour.a); + SDL_RenderFillRect(renderer, &dst); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, currentFgColour.a); + + if (blinkState || !currentCharacter.blinking) + { + SDL_SetTextureColorMod(codepage, currentFgColour.r, currentFgColour.g, currentFgColour.b); + SDL_RenderCopy(renderer, codepage, &src, &dst); + } + + if (++x >= 80) + { + x = 0; + ++y; + } + } + + SDL_RenderPresent(renderer); + return 0; +} + +int Application::run() +{ + int res = create(); + while (res == 0) + { SDL_Event event; if (SDL_WaitEvent(&event)) { do { - switch (event.type) - { - case SDL_QUIT: - running = false; - break; - case SDL_USEREVENT: - blinkState = !blinkState; - damage = true; - break; - case SDL_MOUSEBUTTONDOWN: - switch (event.button.button) - { - case SDL_BUTTON_LEFT: - mouseLeft = true; - break; - case SDL_BUTTON_RIGHT: - mouseRight = true; - break; - } - break; - case SDL_MOUSEBUTTONUP: - switch (event.button.button) - { - case SDL_BUTTON_LEFT: - mouseLeft = false; - break; - case SDL_BUTTON_RIGHT: - mouseRight = false; - break; - } - break; - case SDL_MOUSEMOTION: - mouseX = event.motion.x; - mouseY = event.motion.y; - break; - case SDL_WINDOWEVENT: - switch (event.window.event) - { - case SDL_WINDOWEVENT_LEAVE: - mouseInWindow = false; - break; - case SDL_WINDOWEVENT_ENTER: - mouseInWindow = true; - break; - }; - break; - } - } - while (SDL_PollEvent(&event) > 0); + res = handleEvent(event); + } while (res == 0 && SDL_PollEvent(&event) > 0); } - - if (mouseInWindow && (mouseLeft || mouseRight)) + if (res == 0) { - int fromX = clamp(mouseX / cellWidth, 0, 79); - int fromY = clamp(mouseY / cellHeight, 0, 24); - int toX = clamp(mousePrevX / cellWidth, 0, 79); - int toY = clamp(mousePrevY / cellHeight, 0, 24); - - auto srcChar = mouseLeft ? leftBrush : rightBrush; - if (toX == fromX && toY == fromY) - { - display[toX + toY * 80] = srcChar; - } - else - { - bool steep = std::abs(toY - fromY) > std::abs(toX - fromX); - if (steep) - { - std::swap(fromY, fromX); - std::swap(toY, toX); - } - if (fromX > toX) - { - std::swap(toX, fromX); - std::swap(toY, fromY); - } - - int deltaX = toX - fromX; - int deltaY = std::abs(toY - fromY); - int error = deltaX / 2; - int y = fromY; - int yStep = (fromY < toY) ? 1 : -1; - for (int x = fromX; x <= toX; ++x) - { - int idx = steep - ? y + x * 80 - : x + y * 80; - display[idx] = srcChar; - - error -= deltaY; - if (error < 0) - { - y += yStep; - error += deltaX; - } - } - } - - damage = true; + res = update(); } - - if (damage) + if (res == 0 && needRepaint) { - SDL_SetRenderDrawColor(renderer, 48, 48, 48, 255); - SDL_RenderClear(renderer); - - SDL_Rect src, dst; - src.w = cellWidth; - src.h = cellHeight; - dst.w = cellWidth; - dst.h = cellHeight; - - int x = 0, y = 0; - for (const auto& currentCharacter : display) - { - auto currentBgColour = colourTable[static_cast(currentCharacter.bgColour)]; - auto currentFgColour = colourTable[static_cast(currentCharacter.fgColour)]; - - src.x = (currentCharacter.character % 32) * cellWidth; - src.y = (currentCharacter.character / 32) * cellHeight; - dst.x = x * cellWidth; - dst.y = y * cellHeight; - - SDL_SetRenderDrawColor(renderer, currentBgColour.r, currentBgColour.g, currentBgColour.b, currentBgColour.a); - SDL_RenderFillRect(renderer, &dst); - SDL_SetRenderDrawColor(renderer, 0, 0, 0, currentFgColour.a); - - if (blinkState || !currentCharacter.blinking) - { - SDL_SetTextureColorMod(codepage, currentFgColour.r, currentFgColour.g, currentFgColour.b); - SDL_RenderCopy(renderer, codepage, &src, &dst); - } - - if (++x >= 80) - { - x = 0; - ++y; - } - } - - SDL_RenderPresent(renderer); - damage = false; + res = repaint(); + needRepaint = false; } } - while (running); + return res < 0 ? 1 : 0; +}; - fclose(file); - SDL_DestroyTexture(codepage); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); - return 0; + +int main(int argc, char* argv[]) +{ + Application app(argc, const_cast(argv)); + return app.run(); }