Suppress block cursor during post-input repaint catch-up#3
Open
imakris wants to merge 3 commits into
Open
Conversation
The block cursor could momentarily draw at, or behind, the column the user last saw - inverting the character they just typed - for a single frame while a TUI (e.g. codex, claude) repaints under a concurrent animation, then snap to the correct position a frame later. Root cause: rendering is pure remote echo (no local prediction) and one render snapshot is published per backend output segment. A snapshot can therefore capture the child's cursor at an intermediate point of a repaint - the just-typed glyph has landed but the cursor-settling sequence has not yet been emitted. The existing input-freshness machinery gates frames on whether the typed content is present; content freshness does not imply cursor settledness, so it cannot rule out this frame. The next segment's snapshot carries the settled cursor and the glitch clears, which is why it is one frame and worse the more the application repaints. Fix: add cursor-risk suppression in the post-input catch-up window. When forward-advancing printable input is in flight and a content-fresh but not-yet-strictly-settled snapshot shows a block cursor on the pre-input row at or before the pre-input column with the typed glyph already landed there, suppress the block cursor for that one frame and drain toward the settled snapshot. The window is bounded by the strict freshness token, so the cursor reappears as soon as the caret advances or the frame settles. Bar and underline cursors are left untouched; only the block shape inverts the glyph. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012xHtQ5MMfYzuXZzcjCzMWn
The "Install package" step ran `cmake --install` on the main build, which links vnm_msdf_text from source via FetchContent. That configuration is intentionally non-installable: the MSDF install guard in CMakeLists.txt fails closed (FATAL_ERROR) so a source-built dependency is never packaged into a consumer config whose `find_dependency(vnm_msdf_text)` would be unsatisfiable. The guard has been tripping the install step on every run since it landed, independent of any product change. Install and consume a dedicated MSDF-disabled library build instead. MSDF-off is a first-class, supported package configuration: the renderer guards the MSDF path behind VNM_TERMINAL_MSDF_TEXT_RENDERER_ENABLED, the generated config records the flag, and tests/package_smoke already branches on it (only requiring the vnm_msdf_text dependency when MSDF is enabled). This validates the real install/export and package-consume mechanics without the by-design guard. An MSDF-enabled package remains a distribution build that supplies a packaged vnm_msdf_text via -DVNM_TERMINAL_MSDF_TEXT_RENDERER_USE_SYSTEM_LIBS=ON. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012xHtQ5MMfYzuXZzcjCzMWn
The text-renderer policy, LCD-order, and render-summary helpers (qsg_atlas_text_renderer_policy_requires_msdf, qsg_atlas_text_renderer_policy_allows_fallback, qsg_atlas_effective_text_renderer, qsg_atlas_lcd_subpixel_order_uses_lcd_sampling, and qsg_atlas_update_text_renderer_summary) were defined inside the VNM_TERMINAL_MSDF_TEXT_RENDERER_ENABLED guard but called from unguarded code and from the non-MSDF (#else) branches. With the MSDF renderer disabled they were undeclared, so the glyph-only configuration of the library did not compile. CI never exercised that configuration, so the breakage stayed dormant. These helpers only classify policy/subpixel-order enums and fold per-frame draw counters into the render summary; none reference the MSDF text path. Close the MSDF guard after the two genuinely MSDF-dependent run-candidate predicates and leave the MSDF-independent helpers always compiled. No behavior change for the MSDF-enabled build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012xHtQ5MMfYzuXZzcjCzMWn
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a visual artifact where the block cursor momentarily inverts a freshly typed character during the intermediate repaint frame after text input acceptance, before the application's cursor movement settles the caret position.
Problem
With remote echo and per-segment snapshot publication, a render snapshot can arrive carrying the just-typed glyph while the application has not yet emitted the cursor movement command that advances the caret past that glyph. When rendering this intermediate snapshot with a block cursor, the cursor sits at or behind the pre-input column and inverts the freshly typed character, creating a brief visual glitch before the next segment corrects it.
Content freshness tokens alone cannot distinguish this intermediate frame from a settled state, since the snapshot is content-fresh but not yet strictly cursor-settled.
Changes
Added cursor position tracking: Store the pre-input cursor position (from the pre-echo snapshot) when text input is accepted, along with a flag indicating whether this position is available.
Implemented cursor suppression logic: Added detection in
should_suppress_cursor_for_render_snapshot()to identify the exact intermediate frame:Selective suppression: Only block-shaped cursors are suppressed; bar and underline cursors are unaffected since they do not invert glyphs.
Implementation Details
The suppression is narrowly scoped to a single frame—the settled snapshot one segment later will render the cursor at the advanced position. This avoids cursor blink artifacts while maintaining correct cursor visibility in all other states.
https://claude.ai/code/session_012xHtQ5MMfYzuXZzcjCzMWn