Skip to content

windows: flickering on powershell due to redundant \r in clearProgressBar #219

@mengshengwu

Description

@mengshengwu

Description:
On Windows, the progress bar flickers significantly during updates. This does not happen on Unix-like systems, and appears to be a platform-specific issue related to how carriage return (\r) is handled in the Windows console environment.


Reproduction Steps:

  1. Run any basic example (e.g., progressbar.Default(...)) under Windows Terminal or PowerShell (pwsh)
  2. Observe the progress bar while it's rendering
  3. Flickering or line flashing occurs throughout the update

Expected Behavior:
Smooth, frame-consistent progress updates (similar to behavior under Unix/Linux terminals).


Actual Behavior:
The progress bar flickers, with visible artifacts and inconsistent frame stability during each render pass.


Root Cause:
The issue lies in the clearProgressBar function (progressbar.go, lines ~1365–1380):

str := fmt.Sprintf("\r%s\r", strings.Repeat(" ", s.maxLineWidth))

This approach emits multiple \r characters and overwrites the entire line with whitespace, then repositions again using \r. While this works on ANSI-based Unix terminals, Windows consoles do not always handle \r symmetrically, especially under the ConPTY layer (used by Windows Terminal and pwsh), which leads to flickering.


Technical Explanation:

Category Unix/Linux Windows
Terminal model VT100-style ConPTY / Win32 Console
\r behavior Resets to line start May cause partial line redraw
Buffering Line-buffered + direct ANSI rendering Double-buffered with scroll regions
Consequence Clean redraw Visual artifact due to redraw mismatch

Proposed Fix:
Use ANSI clear-line sequence (\033[2K\r) instead of redundant space overwrite when ANSI mode is enabled, and fallback to minimal \r if on Windows:

func clearProgressBar(c config, s state) error {
	if s.maxLineWidth == 0 {
		return nil
	}
	if c.useANSICodes {
		// Use ANSI clear line + carriage return
		return writeString(c, "\033[2K\r")
	}
	if runtime.GOOS == "windows" {
		// Avoid full-line overwrite; minimal cursor reset
		return writeString(c, "\r")
	}
	// Legacy fallback
	str := fmt.Sprintf("\r%s\r", strings.Repeat(" ", s.maxLineWidth))
	return writeString(c, str)
}

This improves compatibility without changing behavior on Unix-like systems, and significantly reduces flicker under Windows Terminal environments.


Comparison Video:

progressbar_flick_10mb.mp4

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions