From 087437624ffbf58a6c9cc7d3ad8e955568eacb10 Mon Sep 17 00:00:00 2001 From: rocknix Date: Fri, 21 Nov 2025 23:05:51 +0000 Subject: [PATCH 1/2] SDL2Text, filter out unrecognized chars --- .../packages/apps/sdl2text/sdl2text.cpp | 175 +++++++++++++++--- 1 file changed, 148 insertions(+), 27 deletions(-) diff --git a/projects/ROCKNIX/packages/apps/sdl2text/sdl2text.cpp b/projects/ROCKNIX/packages/apps/sdl2text/sdl2text.cpp index 06bd45fd1b..7d59f763fe 100644 --- a/projects/ROCKNIX/packages/apps/sdl2text/sdl2text.cpp +++ b/projects/ROCKNIX/packages/apps/sdl2text/sdl2text.cpp @@ -11,6 +11,7 @@ #include #include #include +#include // -------------------- Config Helpers -------------------- std::filesystem::path get_config_file() { @@ -19,7 +20,7 @@ std::filesystem::path get_config_file() { return cfgDir / "sdl2text.conf"; } -struct FileConfig { int scroll = 0; int fontSize = 40; }; // default font size 32 +struct FileConfig { int scroll = 0; int fontSize = 40; }; // default font size 40 std::map load_config() { std::map cfg; @@ -33,10 +34,12 @@ std::map load_config() { std::string val = line.substr(sep+1); size_t comma = val.find(','); int scroll = 0; - int font = 32; + int font = 40; if(comma != std::string::npos){ - scroll = std::stoi(val.substr(0,comma)); - font = std::stoi(val.substr(comma+1)); + try { + scroll = std::stoi(val.substr(0,comma)); + font = std::stoi(val.substr(comma+1)); + } catch(...) { /* ignore parse errors, use defaults */ } } cfg[key] = {scroll,font}; } @@ -85,7 +88,77 @@ std::string find_any_ttf_font() { return ""; } -// Split long line into chunks to fit screen width +// -------------------- UTF-8 safe filtering for unsupported glyphs -------------------- +// Returns true if font provides glyph for codepoint (only checks BMP codepoints) +static bool font_can_render_codepoint(TTF_Font* font, uint32_t cp) { + // TTF_GlyphIsProvided takes Uint16; it only checks BMP range. + if (cp <= 0xFFFF) { + return TTF_GlyphIsProvided(font, static_cast(cp)) != 0; + } + // For codepoints outside BMP, assume not supported by TTF_GlyphIsProvided. + // Safer to skip those to avoid replacement glyphs. + return false; +} + +// Decode UTF-8 and return a filtered string containing only characters the font can render. +// This is a straightforward decoder that skips invalid sequences and unsupported codepoints. +std::string filter_invalid_chars(const std::string& s, TTF_Font* font) { + std::string out; + size_t i = 0; + while (i < s.size()) { + unsigned char c = static_cast(s[i]); + if (c < 0x80) { + // ASCII + if (font_can_render_codepoint(font, c)) out.push_back(static_cast(c)); + ++i; + continue; + } + + // multi-byte sequence + uint32_t cp = 0; + size_t seqLen = 0; + + if ((c & 0xE0) == 0xC0) { // 2-byte + if (i + 1 >= s.size()) { ++i; continue; } + unsigned char c1 = static_cast(s[i+1]); + if ((c1 & 0xC0) != 0x80) { ++i; continue; } + cp = ((c & 0x1F) << 6) | (c1 & 0x3F); + seqLen = 2; + if (cp < 0x80) { i += 2; continue; } // overlong + } else if ((c & 0xF0) == 0xE0) { // 3-byte + if (i + 2 >= s.size()) { ++i; continue; } + unsigned char c1 = static_cast(s[i+1]); + unsigned char c2 = static_cast(s[i+2]); + if ((c1 & 0xC0) != 0x80 || (c2 & 0xC0) != 0x80) { ++i; continue; } + cp = ((c & 0x0F) << 12) | ((c1 & 0x3F) << 6) | (c2 & 0x3F); + seqLen = 3; + } else if ((c & 0xF8) == 0xF0) { // 4-byte + if (i + 3 >= s.size()) { ++i; continue; } + unsigned char c1 = static_cast(s[i+1]); + unsigned char c2 = static_cast(s[i+2]); + unsigned char c3 = static_cast(s[i+3]); + if ((c1 & 0xC0) != 0x80 || (c2 & 0xC0) != 0x80 || (c3 & 0xC0) != 0x80) { ++i; continue; } + cp = ((c & 0x07) << 18) | ((c1 & 0x3F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F); + seqLen = 4; + } else { + // Invalid leading byte — skip + ++i; + continue; + } + + if (seqLen == 0) { ++i; continue; } + + // Only include if font can render (we only check BMP via TTF_GlyphIsProvided) + if (font_can_render_codepoint(font, cp)) { + out.append(s.substr(i, seqLen)); + } + + i += seqLen; + } + return out; +} + +// -------------------- Split long line into chunks to fit screen width -------------------- std::vector wrap_line(const std::string& line, TTF_Font* font, int maxWidth) { std::vector result; size_t start = 0; @@ -136,15 +209,22 @@ int main(int argc, char* argv[]) { auto lines = load_file_lines(textFile); if(lines.empty()){ std::cout << "Failed to load file\n"; return 1; } - SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER); - TTF_Init(); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) != 0) { + std::cerr << "SDL_Init error: " << SDL_GetError() << "\n"; + return 1; + } + if (TTF_Init() != 0) { + std::cerr << "TTF_Init error: " << TTF_GetError() << "\n"; + SDL_Quit(); + return 1; + } std::string fontPath = find_any_ttf_font(); - if(fontPath.empty()){ std::cout << "No TTF font found\n"; return 1; } + if(fontPath.empty()){ std::cout << "No TTF font found\n"; TTF_Quit(); SDL_Quit(); return 1; } std::cout << "Using font: " << fontPath << "\n"; auto cfg = load_config(); - FileConfig fcfg = cfg[textFile]; // default scroll=0, fontSize=32 + FileConfig fcfg = cfg[textFile]; // default scroll=0, fontSize=40 int fontSize = fcfg.fontSize; auto loadFont = [&](int size) -> TTF_Font* { @@ -153,14 +233,34 @@ int main(int argc, char* argv[]) { return f; }; TTF_Font* font = loadFont(fontSize); + if (!font) { + TTF_Quit(); + SDL_Quit(); + return 1; + } SDL_Window* win = SDL_CreateWindow("Text Viewer", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 0,0, SDL_WINDOW_FULLSCREEN_DESKTOP|SDL_WINDOW_BORDERLESS); + if (!win) { + std::cerr << "SDL_CreateWindow error: " << SDL_GetError() << "\n"; + TTF_CloseFont(font); + TTF_Quit(); + SDL_Quit(); + return 1; + } SDL_Renderer* ren = SDL_CreateRenderer(win,-1,SDL_RENDERER_ACCELERATED); + if (!ren) { + std::cerr << "SDL_CreateRenderer error: " << SDL_GetError() << "\n"; + SDL_DestroyWindow(win); + TTF_CloseFont(font); + TTF_Quit(); + SDL_Quit(); + return 1; + } - int WINDOW_W, WINDOW_H; + int WINDOW_W = 0, WINDOW_H = 0; SDL_GetWindowSize(win,&WINDOW_W,&WINDOW_H); SDL_Color white = {255,255,255,255}; @@ -171,12 +271,19 @@ int main(int argc, char* argv[]) { SDL_GameController* pad = nullptr; for(int i=0;i wrapped; for(auto &line: lines){ - auto chunks = wrap_line(line,font,WINDOW_W-20); + std::string clean = filter_invalid_chars(line, font); + auto chunks = wrap_line(clean,font,WINDOW_W-20); wrapped.insert(wrapped.end(),chunks.begin(),chunks.end()); } auto textures = create_textures(ren, font, wrapped, white); @@ -187,37 +294,47 @@ int main(int argc, char* argv[]) { while(running){ while(SDL_PollEvent(&e)){ - if(e.type==SDL_QUIT) running=false; + if(e.type==SDL_QUIT) { running=false; break; } if(e.type==SDL_CONTROLLERBUTTONDOWN){ switch(e.cbutton.button){ case SDL_CONTROLLER_BUTTON_DPAD_UP: upPressed=true; break; case SDL_CONTROLLER_BUTTON_DPAD_DOWN: downPressed=true; break; case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: l1Pressed=true; break; case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: r1Pressed=true; break; - case SDL_CONTROLLER_BUTTON_Y: // North - increase font + case SDL_CONTROLLER_BUTTON_X: // North - increase font fontSize += 2; TTF_CloseFont(font); font = loadFont(fontSize); + if (!font) { + fontSize -= 2; + font = loadFont(fontSize); + } wrapped.clear(); for(auto &line: lines){ - auto chunks = wrap_line(line,font,WINDOW_W-20); + std::string clean = filter_invalid_chars(line, font); + auto chunks = wrap_line(clean,font,WINDOW_W-20); wrapped.insert(wrapped.end(),chunks.begin(),chunks.end()); } - for(auto <: textures) SDL_DestroyTexture(lt.tex); + for(auto <: textures) if (lt.tex) SDL_DestroyTexture(lt.tex); textures = create_textures(ren, font, wrapped, white); lineHeight = TTF_FontHeight(font); break; - case SDL_CONTROLLER_BUTTON_X: // West - decrease font + case SDL_CONTROLLER_BUTTON_Y: // West - decrease font fontSize -= 2; if(fontSize<8) fontSize=8; TTF_CloseFont(font); font = loadFont(fontSize); + if (!font) { + fontSize += 2; + font = loadFont(fontSize); + } wrapped.clear(); for(auto &line: lines){ - auto chunks = wrap_line(line,font,WINDOW_W-20); + std::string clean = filter_invalid_chars(line, font); + auto chunks = wrap_line(clean,font,WINDOW_W-20); wrapped.insert(wrapped.end(),chunks.begin(),chunks.end()); } - for(auto <: textures) SDL_DestroyTexture(lt.tex); + for(auto <: textures) if (lt.tex) SDL_DestroyTexture(lt.tex); textures = create_textures(ren, font, wrapped, white); lineHeight = TTF_FontHeight(font); break; @@ -243,6 +360,7 @@ int main(int argc, char* argv[]) { if(r1Pressed) scroll_y += SKIP_LINES*lineHeight; int total_height = (int)textures.size()*lineHeight; + if(total_height < WINDOW_H) total_height = WINDOW_H; // avoid negative dividing later if(scroll_y < 0) scroll_y=0; if(scroll_y > total_height-WINDOW_H) scroll_y = total_height-WINDOW_H; @@ -253,17 +371,20 @@ int main(int argc, char* argv[]) { int firstLine = scroll_y/lineHeight; int offsetY = -(scroll_y%lineHeight); for(size_t i=firstLine; i WINDOW_H) scrollPercent = (float)scroll_y / (float)(total_height - WINDOW_H); if(scrollPercent < 0) { scrollPercent = 0; } if(scrollPercent > 1) { scrollPercent = 1; } - int barHeight = (int)((float)WINDOW_H * (float)WINDOW_H / total_height); + int barHeight = (int)((float)WINDOW_H * (float)WINDOW_H / (float)total_height); if(barHeight < 10) barHeight = 10; int barY = (int)(scrollPercent*(WINDOW_H - barHeight)); SDL_Rect scrollbar = {WINDOW_W - barWidth - 2, barY, barWidth, barHeight}; @@ -278,11 +399,11 @@ int main(int argc, char* argv[]) { cfg[textFile] = {scroll_y, fontSize}; save_config(cfg); - for(auto <: textures) SDL_DestroyTexture(lt.tex); + for(auto <: textures) if (lt.tex) SDL_DestroyTexture(lt.tex); if(pad) SDL_GameControllerClose(pad); - TTF_CloseFont(font); - SDL_DestroyRenderer(ren); - SDL_DestroyWindow(win); + if(font) TTF_CloseFont(font); + if(ren) SDL_DestroyRenderer(ren); + if(win) SDL_DestroyWindow(win); TTF_Quit(); SDL_Quit(); return 0; From 5adf19c930e0cb5aba46e2d81fa7ae51a3233d13 Mon Sep 17 00:00:00 2001 From: rocknix Date: Fri, 21 Nov 2025 23:42:34 +0000 Subject: [PATCH 2/2] SDL2Text: add touch support and help window --- .../packages/apps/sdl2text/sdl2text.cpp | 151 +++++++++++------- 1 file changed, 89 insertions(+), 62 deletions(-) diff --git a/projects/ROCKNIX/packages/apps/sdl2text/sdl2text.cpp b/projects/ROCKNIX/packages/apps/sdl2text/sdl2text.cpp index 7d59f763fe..001b11fcf6 100644 --- a/projects/ROCKNIX/packages/apps/sdl2text/sdl2text.cpp +++ b/projects/ROCKNIX/packages/apps/sdl2text/sdl2text.cpp @@ -89,50 +89,42 @@ std::string find_any_ttf_font() { } // -------------------- UTF-8 safe filtering for unsupported glyphs -------------------- -// Returns true if font provides glyph for codepoint (only checks BMP codepoints) static bool font_can_render_codepoint(TTF_Font* font, uint32_t cp) { - // TTF_GlyphIsProvided takes Uint16; it only checks BMP range. if (cp <= 0xFFFF) { return TTF_GlyphIsProvided(font, static_cast(cp)) != 0; } - // For codepoints outside BMP, assume not supported by TTF_GlyphIsProvided. - // Safer to skip those to avoid replacement glyphs. return false; } -// Decode UTF-8 and return a filtered string containing only characters the font can render. -// This is a straightforward decoder that skips invalid sequences and unsupported codepoints. std::string filter_invalid_chars(const std::string& s, TTF_Font* font) { std::string out; size_t i = 0; while (i < s.size()) { unsigned char c = static_cast(s[i]); if (c < 0x80) { - // ASCII if (font_can_render_codepoint(font, c)) out.push_back(static_cast(c)); ++i; continue; } - // multi-byte sequence uint32_t cp = 0; size_t seqLen = 0; - if ((c & 0xE0) == 0xC0) { // 2-byte + if ((c & 0xE0) == 0xC0) { if (i + 1 >= s.size()) { ++i; continue; } unsigned char c1 = static_cast(s[i+1]); if ((c1 & 0xC0) != 0x80) { ++i; continue; } cp = ((c & 0x1F) << 6) | (c1 & 0x3F); seqLen = 2; - if (cp < 0x80) { i += 2; continue; } // overlong - } else if ((c & 0xF0) == 0xE0) { // 3-byte + if (cp < 0x80) { i += 2; continue; } + } else if ((c & 0xF0) == 0xE0) { if (i + 2 >= s.size()) { ++i; continue; } unsigned char c1 = static_cast(s[i+1]); unsigned char c2 = static_cast(s[i+2]); if ((c1 & 0xC0) != 0x80 || (c2 & 0xC0) != 0x80) { ++i; continue; } cp = ((c & 0x0F) << 12) | ((c1 & 0x3F) << 6) | (c2 & 0x3F); seqLen = 3; - } else if ((c & 0xF8) == 0xF0) { // 4-byte + } else if ((c & 0xF8) == 0xF0) { if (i + 3 >= s.size()) { ++i; continue; } unsigned char c1 = static_cast(s[i+1]); unsigned char c2 = static_cast(s[i+2]); @@ -140,15 +132,10 @@ std::string filter_invalid_chars(const std::string& s, TTF_Font* font) { if ((c1 & 0xC0) != 0x80 || (c2 & 0xC0) != 0x80 || (c3 & 0xC0) != 0x80) { ++i; continue; } cp = ((c & 0x07) << 18) | ((c1 & 0x3F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F); seqLen = 4; - } else { - // Invalid leading byte — skip - ++i; - continue; - } + } else { ++i; continue; } if (seqLen == 0) { ++i; continue; } - // Only include if font can render (we only check BMP via TTF_GlyphIsProvided) if (font_can_render_codepoint(font, cp)) { out.append(s.substr(i, seqLen)); } @@ -224,7 +211,7 @@ int main(int argc, char* argv[]) { std::cout << "Using font: " << fontPath << "\n"; auto cfg = load_config(); - FileConfig fcfg = cfg[textFile]; // default scroll=0, fontSize=40 + FileConfig fcfg = cfg[textFile]; int fontSize = fcfg.fontSize; auto loadFont = [&](int size) -> TTF_Font* { @@ -233,32 +220,15 @@ int main(int argc, char* argv[]) { return f; }; TTF_Font* font = loadFont(fontSize); - if (!font) { - TTF_Quit(); - SDL_Quit(); - return 1; - } + if (!font) { TTF_Quit(); SDL_Quit(); return 1; } SDL_Window* win = SDL_CreateWindow("Text Viewer", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 0,0, SDL_WINDOW_FULLSCREEN_DESKTOP|SDL_WINDOW_BORDERLESS); - if (!win) { - std::cerr << "SDL_CreateWindow error: " << SDL_GetError() << "\n"; - TTF_CloseFont(font); - TTF_Quit(); - SDL_Quit(); - return 1; - } + if (!win) { std::cerr << "SDL_CreateWindow error\n"; TTF_CloseFont(font); TTF_Quit(); SDL_Quit(); return 1; } SDL_Renderer* ren = SDL_CreateRenderer(win,-1,SDL_RENDERER_ACCELERATED); - if (!ren) { - std::cerr << "SDL_CreateRenderer error: " << SDL_GetError() << "\n"; - SDL_DestroyWindow(win); - TTF_CloseFont(font); - TTF_Quit(); - SDL_Quit(); - return 1; - } + if (!ren) { SDL_DestroyWindow(win); TTF_CloseFont(font); TTF_Quit(); SDL_Quit(); return 1; } int WINDOW_W = 0, WINDOW_H = 0; SDL_GetWindowSize(win,&WINDOW_W,&WINDOW_H); @@ -272,14 +242,12 @@ int main(int argc, char* argv[]) { SDL_GameController* pad = nullptr; for(int i=0;i wrapped; for(auto &line: lines){ std::string clean = filter_invalid_chars(line, font); @@ -292,23 +260,32 @@ int main(int argc, char* argv[]) { bool running=true; SDL_Event e; + // Touch drag-only state + bool touchActive = false; + float lastTouchY = 0.0f; + const float TOUCH_MULTIPLIER = 1.75f; + + // Help overlay + bool showHelp = false; + SDL_Color helpColor = {255,255,0,255}; + SDL_Color boxColor = {0,0,0,200}; + while(running){ while(SDL_PollEvent(&e)){ if(e.type==SDL_QUIT) { running=false; break; } + + // Controller input if(e.type==SDL_CONTROLLERBUTTONDOWN){ switch(e.cbutton.button){ case SDL_CONTROLLER_BUTTON_DPAD_UP: upPressed=true; break; case SDL_CONTROLLER_BUTTON_DPAD_DOWN: downPressed=true; break; case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: l1Pressed=true; break; case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: r1Pressed=true; break; - case SDL_CONTROLLER_BUTTON_X: // North - increase font + case SDL_CONTROLLER_BUTTON_X: fontSize += 2; TTF_CloseFont(font); font = loadFont(fontSize); - if (!font) { - fontSize -= 2; - font = loadFont(fontSize); - } + if (!font) { fontSize -= 2; font = loadFont(fontSize); } wrapped.clear(); for(auto &line: lines){ std::string clean = filter_invalid_chars(line, font); @@ -319,15 +296,12 @@ int main(int argc, char* argv[]) { textures = create_textures(ren, font, wrapped, white); lineHeight = TTF_FontHeight(font); break; - case SDL_CONTROLLER_BUTTON_Y: // West - decrease font + case SDL_CONTROLLER_BUTTON_Y: fontSize -= 2; if(fontSize<8) fontSize=8; TTF_CloseFont(font); font = loadFont(fontSize); - if (!font) { - fontSize += 2; - font = loadFont(fontSize); - } + if (!font) { fontSize += 2; font = loadFont(fontSize); } wrapped.clear(); for(auto &line: lines){ std::string clean = filter_invalid_chars(line, font); @@ -338,9 +312,12 @@ int main(int argc, char* argv[]) { textures = create_textures(ren, font, wrapped, white); lineHeight = TTF_FontHeight(font); break; - case SDL_CONTROLLER_BUTTON_B: // South - close + case SDL_CONTROLLER_BUTTON_B: running=false; break; + case SDL_CONTROLLER_BUTTON_BACK: // SELECT button + showHelp = !showHelp; + break; } } if(e.type==SDL_CONTROLLERBUTTONUP){ @@ -351,20 +328,35 @@ int main(int argc, char* argv[]) { case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: r1Pressed=false; break; } } + + // Touch events + if(e.type == SDL_FINGERDOWN) { + touchActive = true; + lastTouchY = e.tfinger.y; + } + if(e.type == SDL_FINGERMOTION && touchActive) { + float y = e.tfinger.y; + float dy_norm = y - lastTouchY; + lastTouchY = y; + int deltaPixels = (int)(-dy_norm * WINDOW_H * TOUCH_MULTIPLIER); + scroll_y += deltaPixels; + } + if(e.type == SDL_FINGERUP) touchActive = false; } - // scrolling + // Controller continuous scrolling if(upPressed) scroll_y -= SCROLL_SPEED; if(downPressed) scroll_y += SCROLL_SPEED; if(l1Pressed) scroll_y -= SKIP_LINES*lineHeight; if(r1Pressed) scroll_y += SKIP_LINES*lineHeight; + // Clamp int total_height = (int)textures.size()*lineHeight; - if(total_height < WINDOW_H) total_height = WINDOW_H; // avoid negative dividing later + if(total_height < WINDOW_H) total_height = WINDOW_H; if(scroll_y < 0) scroll_y=0; if(scroll_y > total_height-WINDOW_H) scroll_y = total_height-WINDOW_H; - // render + // Render SDL_SetRenderDrawColor(ren,0,0,0,255); SDL_RenderClear(ren); @@ -378,12 +370,12 @@ int main(int argc, char* argv[]) { offsetY += textures[i].h; } - // scroll bar + // Scroll bar int barWidth = 8; float scrollPercent = 0.0f; if (total_height > WINDOW_H) scrollPercent = (float)scroll_y / (float)(total_height - WINDOW_H); - if(scrollPercent < 0) { scrollPercent = 0; } - if(scrollPercent > 1) { scrollPercent = 1; } + if(scrollPercent < 0) scrollPercent = 0; + if(scrollPercent > 1) scrollPercent = 1; int barHeight = (int)((float)WINDOW_H * (float)WINDOW_H / (float)total_height); if(barHeight < 10) barHeight = 10; int barY = (int)(scrollPercent*(WINDOW_H - barHeight)); @@ -391,11 +383,46 @@ int main(int argc, char* argv[]) { SDL_SetRenderDrawColor(ren, 200,200,200,255); SDL_RenderFillRect(ren, &scrollbar); + // Help overlay + if(showHelp) { + int boxW = WINDOW_W / 2; + int boxH = WINDOW_H / 2; + int boxX = (WINDOW_W - boxW)/2; + int boxY = (WINDOW_H - boxH)/2; + + SDL_Rect helpBox = {boxX, boxY, boxW, boxH}; + SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND); + SDL_SetRenderDrawColor(ren, boxColor.r, boxColor.g, boxColor.b, boxColor.a); + SDL_RenderFillRect(ren, &helpBox); + + std::vector helpLines = { + "CONTROLS:", + "UP/DOWN DPAD - Scroll", + "L1/R1 - Skip lines", + "X/Y - Increase/Decrease font", + "B - Exit", + "SELECT - Toggle this help", + "Touch Drag - Scroll" + }; + + int ty = boxY + 20; + for(auto &line : helpLines){ + SDL_Surface* surf = TTF_RenderUTF8_Blended(font, line.c_str(), helpColor); + if(!surf) continue; + SDL_Texture* tex = SDL_CreateTextureFromSurface(ren, surf); + SDL_Rect dst = {boxX + 20, ty, surf->w, surf->h}; + SDL_RenderCopy(ren, tex, nullptr, &dst); + ty += surf->h + 8; + SDL_DestroyTexture(tex); + SDL_FreeSurface(surf); + } + } + SDL_RenderPresent(ren); SDL_Delay(16); } - // save scroll and fontSize using full path + // Save config cfg[textFile] = {scroll_y, fontSize}; save_config(cfg);