fix: skip full-screen clear in fullscreen mode to prevent flicker#190
fix: skip full-screen clear in fullscreen mode to prevent flicker#190djordjeglbvc wants to merge 1 commit into
Conversation
In fullscreen (alternate screen) mode, clear_canvas() used ClearFromCursorDown which wipes the entire visible area before write_canvas() rewrites it top-to-bottom. When state changes are frequent (e.g. rapid mouse-move events), the bottom rows spend a visible moment cleared before being redrawn, producing flicker. Since write_canvas() overwrites every row and each row already emits CSI K (erase to end of line), the full-screen clear is redundant. Replace it with a simple cursor reposition to (0,0). The non-fullscreen (inline) path is unchanged — it still needs the clear to handle variable-height output correctly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Thanks for the PR! This makes sense to me, but it is assuming that all fullscreen mode TUIs actually fill the entire height of the terminal. This isn't necessarily the case. For example, if you modify the fullscreen example like so... diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs
index 41d4424..88f7e06 100644
--- a/examples/fullscreen.rs
+++ b/examples/fullscreen.rs
@@ -35,7 +35,7 @@ fn Example(mut hooks: Hooks) -> impl Into<AnyElement<'static>> {
element! {
View(
width,
- height,
+ height: height - (time.get().timestamp() % 10) as u16,
background_color: Color::DarkGrey,
border_style: BorderStyle::Double,
border_color: Color::Blue,You'll see artifacts like these:
I think the fix for that is pretty simple, but you might also want to take a look at #179, which optimizes things even further. |
|
Thanks for the review! It passed over my head to test with lower height... I saw #179, it looks very good, and it would make my change unnecessary, but I assumed it is a long term change. I did a quick test, and saw some artifacts, so I gave up. I will re-check. Thanks!
|
In fullscreen mode, write_canvas() already overwrites every row and emits CSI K (erase to end of line) for each row. The full clear was redundant and caused visible flicker on rapid state changes with dense per-cell styling. This matches the approach described in PR ccbrown#190 from djordjeglbvc. Fixes: ccbrown#190
|
Ok, I think I found an issue which originally discouraged me from using feat: add row-level diff rendering, and have posted a suggestion for a fix there. |
|
Fixed by #179. Closing as obsolete. |

When using
element.fullscreen(), rapid state changes which fire TUI updates cause visible flicker at the bottom of the terminal. The flicker ranges from a few rows to more than half the screen, and always affects the bottom portion. The issue is apparent in applications with dense per-cell styling (background colors, multiple text styles per row) and complex web of UI components/elements.Observed on Konsole 25.04.2 (Wayland). The terminal supports DEC mode 2026 (synchronized output), but the flicker occurs regardless — the volume of ANSI data between clear and rewrite appears to exceed what synchronized output can atomically batch.
Since write_canvas() overwrites every row and each row already emits CSI K (erase to end of line), the full-screen clear is redundant. Replace it with a simple cursor reposition to (0,0).
The non-fullscreen (inline) path is unchanged — it still needs the clear to handle variable-height output correctly.
What It Does
In fullscreen mode, replace the clear with a
cursor::MoveTo(0, 0).This is safe because (as already mentioned) write_canvas() overwrites every row and emits CSI K for each row, so additional clear is redundant.
Related Issues
(possibly related) Flickering issue