From 60a99138c747c8decc1d3247aa9cba19aebc2e0e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 03:27:48 +0000 Subject: [PATCH 1/2] fix(boot): keep QR scannable on landscape 800x600+ boot screens The middle-zone layout search took the largest text scale for which any QR module size fit, so on landscape panels >= 800x600 it kept oversized text and shrank the QR to 2-3px modules in the corner. Most visible on the reTerminal E1004 (1200x1600 dual-controller) rotated to 1600x1200; also hit 1304x984, 960x640 and 800x600. The search now first requires a module floor of half the panel's module cap (panels with cap >= 8 only) and shrinks the text to fit, falling back to the old any-size search if nothing fits. Panels below 800x600, portrait layouts and 1872x1404 render identically to before (checked with a host render of boot_screen.cpp across 14 panel sizes). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BquLGp9hmm8sgAYixz8you --- src/boot_screen.cpp | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/boot_screen.cpp b/src/boot_screen.cpp index c8fe941..416da8b 100644 --- a/src/boot_screen.cpp +++ b/src/boot_screen.cpp @@ -341,6 +341,16 @@ static int bootQrModuleMax(uint16_t w, uint16_t h_full) { return 6; } +// Smallest QR module the layout search accepts before it gives up on text size. +// Without this floor the search keeps the largest text scale and shrinks the QR +// to whatever is left, so landscape 800x600+ panels (e.g. 1600x1200) got 2-3px +// modules squeezed beside oversized text. Panels below 800x600 (cap < 8) keep +// the old behaviour: there, readable text matters more than QR size. +static int bootQrModuleMin(uint16_t w, uint16_t h_full) { + const int moduleMax = bootQrModuleMax(w, h_full); + return (moduleMax >= 8) ? moduleMax / 2 : 1; +} + // Max text scale for the middle zone (header/footer use their own logic). static int bootMiddleScaleHi(uint16_t w_log, uint16_t h_log, bool useHighResLayout) { if (w_log >= 1600 && h_log >= 1200) return 10; @@ -451,7 +461,7 @@ static void bootPickHeaderScales(int headerH, int headerMaxX, int pad, *manufScaleOut = manufS; } -static bool bootLayoutFit(uint16_t w, uint16_t h, uint16_t h_full, int blockH, int pad, int qrModules, int* modulePxOut, +static bool bootLayoutFit(uint16_t w, uint16_t h, uint16_t h_full, int blockH, int pad, int qrModules, int moduleMin, int* modulePxOut, int* qrPxOut, bool* qrRightOut, int* qrXOut, int* qrYOut, int* availWOut, int* textYOut, uint16_t maxTextW) { int textGap = pad; @@ -464,7 +474,8 @@ static bool bootLayoutFit(uint16_t w, uint16_t h, uint16_t h_full, int blockH, i const int moduleMax = bootQrModuleMax(w, h_full); if (moduleIdeal > moduleMax) moduleIdeal = moduleMax; - for (modulePx = moduleIdeal; modulePx >= 1; modulePx--) { + if (moduleMin < 1) moduleMin = 1; + for (modulePx = moduleIdeal; modulePx >= moduleMin; modulePx--) { qrPx = modulePx * qrModules; if (qrPx > (int)w - pad * 2) continue; // Landscape: text left, QR right — both vertically centered in the taller of the two @@ -764,16 +775,22 @@ bool writeBootScreenWithQr() { fwKey1Gap = 0; const int scaleHi = bootMiddleScaleHi(w_log, h_log, useHighResLayout); - for (tryScale = useZoneLayout ? scaleHi : 1; tryScale >= 1 && !layoutOk; tryScale--) { - middleScaleText = ultraHiResPanel ? (tryScale > 2 ? tryScale - 2 : 1) : tryScale; - pad = bootMiddlePad(middleScaleText, w_log, h_log, useZoneLayout); - fwKey1Gap = useZoneLayout ? middleScaleText * 6 : 0; - maxTextW = bootMaxTextWidth(bootLines, numBootLines, middleScaleText); - int contentH = useZoneLayout - ? (4 * bootLineStep(middleScaleText) + fwKey1Gap + 7 * middleScaleText) - : (((int)numBootLines - 1) * bootLineStep(middleScaleText) + 7 * middleScaleText); - layoutOk = bootLayoutFit(w_log, (uint16_t)middleH, h_log, contentH, pad, (int)qrModules, &modulePx, &qrPx, &qrRight, &qrX, - &qrY, &availW, &textY, maxTextW); + // Round 0 keeps the QR at a scannable size and shrinks the text to fit; + // round 1 (any module size) is the fallback for panels too small for that. + const int qrModuleMin = useZoneLayout ? bootQrModuleMin(w_log, h_log) : 1; + for (int round = (qrModuleMin > 1) ? 0 : 1; round < 2 && !layoutOk; round++) { + for (tryScale = useZoneLayout ? scaleHi : 1; tryScale >= 1 && !layoutOk; tryScale--) { + middleScaleText = ultraHiResPanel ? (tryScale > 2 ? tryScale - 2 : 1) : tryScale; + pad = bootMiddlePad(middleScaleText, w_log, h_log, useZoneLayout); + fwKey1Gap = useZoneLayout ? middleScaleText * 6 : 0; + maxTextW = bootMaxTextWidth(bootLines, numBootLines, middleScaleText); + int contentH = useZoneLayout + ? (4 * bootLineStep(middleScaleText) + fwKey1Gap + 7 * middleScaleText) + : (((int)numBootLines - 1) * bootLineStep(middleScaleText) + 7 * middleScaleText); + layoutOk = bootLayoutFit(w_log, (uint16_t)middleH, h_log, contentH, pad, (int)qrModules, + round == 0 ? qrModuleMin : 1, &modulePx, &qrPx, &qrRight, &qrX, + &qrY, &availW, &textY, maxTextW); + } } if (!layoutOk) { middleScaleText = 1; From 54cf579a2fcbafbfd8274533f293613d1dc2efe7 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 03:50:53 +0000 Subject: [PATCH 2/2] refactor(boot): clarify QR-floor search passes, avoid shadowing round() Rename the layout-search loop variable from `round` to `fitPass` so a later round() call inside it cannot bind to the int, and correct the comment: panels below 800x600 skip the floored pass entirely rather than falling back to the unfloored one. No behaviour change (host renders of 14 panel sizes are byte-identical). Note on the previous commit: besides the 800x600+ landscape panels, the floor also moves some near-square landscape sizes (e.g. 1700x1600, 1232x1028, 816x716) from the stacked layout to text-left/QR-right with a larger QR. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BquLGp9hmm8sgAYixz8you --- src/boot_screen.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/boot_screen.cpp b/src/boot_screen.cpp index 416da8b..b05e65d 100644 --- a/src/boot_screen.cpp +++ b/src/boot_screen.cpp @@ -775,10 +775,11 @@ bool writeBootScreenWithQr() { fwKey1Gap = 0; const int scaleHi = bootMiddleScaleHi(w_log, h_log, useHighResLayout); - // Round 0 keeps the QR at a scannable size and shrinks the text to fit; - // round 1 (any module size) is the fallback for panels too small for that. + // Pass 0 keeps the QR at a scannable size and shrinks the text to fit. + // Pass 1 (any module size) is the original search: panels below 800x600 + // (no floor) start there, and it stays as a safety net should pass 0 fail. const int qrModuleMin = useZoneLayout ? bootQrModuleMin(w_log, h_log) : 1; - for (int round = (qrModuleMin > 1) ? 0 : 1; round < 2 && !layoutOk; round++) { + for (int fitPass = (qrModuleMin > 1) ? 0 : 1; fitPass < 2 && !layoutOk; fitPass++) { for (tryScale = useZoneLayout ? scaleHi : 1; tryScale >= 1 && !layoutOk; tryScale--) { middleScaleText = ultraHiResPanel ? (tryScale > 2 ? tryScale - 2 : 1) : tryScale; pad = bootMiddlePad(middleScaleText, w_log, h_log, useZoneLayout); @@ -788,7 +789,7 @@ bool writeBootScreenWithQr() { ? (4 * bootLineStep(middleScaleText) + fwKey1Gap + 7 * middleScaleText) : (((int)numBootLines - 1) * bootLineStep(middleScaleText) + 7 * middleScaleText); layoutOk = bootLayoutFit(w_log, (uint16_t)middleH, h_log, contentH, pad, (int)qrModules, - round == 0 ? qrModuleMin : 1, &modulePx, &qrPx, &qrRight, &qrX, + fitPass == 0 ? qrModuleMin : 1, &modulePx, &qrPx, &qrRight, &qrX, &qrY, &availW, &textY, maxTextW); } }