From c5ef9e42ed938ce1824f8f0365d04068756f03f3 Mon Sep 17 00:00:00 2001 From: DanS Date: Wed, 1 Jul 2026 23:25:09 -0500 Subject: [PATCH] fix(console): correct CRT scanline alternation (was skipping rows) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The per-line scanline shade alternated on rowIndex = floor((cumulativeY + yOffset) / lineHeight + 0.5), but cumulativeY includes the 2px inter-line gap while the divisor is lineHeight alone. That drifts ~0.14 row per line and, with the rounding, jumps the index by 2 every ~7 rows — so two adjacent rows get the same shade and the alternation visibly breaks in patches. Replace it with a true per-visual-row counter (one increment per drawn sub-row), seeded with the first on-screen line's sub-row ordinal so the shading stays stable while scrolling. Parity now alternates exactly every row. Full-node + lite build clean; source hygiene clean. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/ui/windows/console_tab.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ui/windows/console_tab.cpp b/src/ui/windows/console_tab.cpp index d594354..118fdad 100644 --- a/src/ui/windows/console_tab.cpp +++ b/src/ui/windows/console_tab.cpp @@ -836,6 +836,14 @@ void ConsoleTab::drawVisibleLines(float padX, float lineHeight, bool hasTextFilt // mid-draw. -1 = none. int pendingFoldToggle = -1; + // CRT scanline parity is a true per-visual-row counter (each drawn sub-row = one row). + // Deriving it from cumulativeY/lineHeight drifted (cumulativeY includes the inter-line + // gap) and skipped rows, breaking the alternation. Seed it with the sub-row ordinal of + // the first on-screen line so the shading stays stable across scrolling. + int scanRowOrdinal = 0; + for (int k = 0; k < first_visible && k < static_cast(layout_.segments.size()); k++) + scanRowOrdinal += static_cast(layout_.segments[k].size()); + int last_rendered_vi = first_visible - 1; for (int vi = first_visible; vi < visible_count; vi++) { if (vi < static_cast(layout_.cumulativeY.size()) && @@ -917,9 +925,9 @@ void ConsoleTab::drawVisibleLines(float padX, float lineHeight, bool hasTextFilt const char* segEnd = line.text.c_str() + seg.byteEnd; if (s_scanline_enabled && lineHeight > 0.0f) { - int rowIndex = static_cast(std::floor((layout_.cumulativeY[vi] + seg.yOffset) / lineHeight + 0.5f)); - scanline_rows_.push_back({rowY, rowY + seg.height, rowIndex}); + scanline_rows_.push_back({rowY, rowY + seg.height, scanRowOrdinal}); } + ++scanRowOrdinal; // one per visual sub-row, so parity always alternates // Selection highlight for this sub-row if (lineSelected && selByteStart < seg.byteEnd && selByteEnd > seg.byteStart) {