rivo / tview

Terminal UI library with rich, interactive widgets — written in Golang
MIT License
11.13k stars 575 forks source link

Flickering screen in Windows powershell and cmd 🦆 #913

Closed FJuedesOrcl closed 11 months ago

FJuedesOrcl commented 1 year ago

Hi Friends,

i am writing some utilities for directory-servers, for example a browser. My development environment is a Linux box, but i have to cross-compile everything also for Windows. Never used it on Windows myself, but my colleagues are reporting that on Windows the screen is flickering. It looks like as if the entire screen is cleared and redrawn. It is a nuisance when the screen has a black background by default, but when using a white background it is beyond annoying. When using the OoenSUSE bash window of the wsl everything looks fine - of course that's a Linux shell, now Windows…

Do i miss something or is that just the way how screen updates are performed in Windows?

I am neither an expert in Go nor with the TView package, so any help is welcome.

Thank you very much in advance for your help.

FJuedesOrcl commented 1 year ago

Okay, i figured something out: There are two go-routines, that will update a TextView primitive in an endless loop. I had coded: Application.QueueUpdate**Draw**(func(){StatusLine.SetText(Buffer)}) To update the content of the TextView element. I have changed this to Application.QueueUpdate(func(){StatusLine.SetText(Buffer)})\ Now the flickering is gone, but the content of those TextView elements is only updated when i press a key on the keyboard. How can i redraw just the content of a TextView element? Yes there is a TextView.Draw() function, but what do i use as the Screen parameter?

rivo commented 1 year ago

Regarding flickering on Windows, I'm trying to get some answers here: https://github.com/gdamore/tcell/issues/647. I hope to be able to remove the flickering. As a temporary workaround, you can remove this line:

https://github.com/rivo/tview/blob/1b91b8131c43011d923fe59855b4de3571dac997/application.go#L604

Regarding writing to the TextView, you could try to buffer your writes and flush them to TextView at fixed intervals (or similar). It's a bit more complicated but it might be a good idea anyway to reduce the number of draw events.

Don't call TextView.Draw() directly. This is never needed. This function should actually be private (but it's not, due to backwards compatibility).

FJuedesOrcl commented 1 year ago

@rivo Hi Rivo,

Thank you very much for your answer! I will try to comment out the screen.Clear() in the tcell package and test how that effects the screen-output on windows and (more important) *ux. It will just take some time to change the package-code and then run the build-script.

i have not understood (yet) how the tcell package internally works, but aeons ago i had a somewhat similar issue on a Unix system with a cshell script that was periodically refreshing the display on a slow 9600 baud terminal. My solution at that time was not to clear the screen, but instead move the cursor home, then refresh the screen line by line, character by character from the left to wherever the line ended and then send the delete to EOL escape code. There were still some visible artifacts, but only in single lines and much less disturbing for the human eye.

I am not using any call to *Draw method, except in the function to update the status-line:

//--------------------------------------------------------------------------------
// Update the status-line every 500 milli-seconds in an endless loop
//--------------------------------------------------------------------------------
func UpdateStatusLine() {
  for {
    time.Sleep(500 * time.Millisecond)
    KeyName := LastKey.Name()
    KeyName = strings.ReplaceAll(KeyName,"Rune[","'")
    KeyName = strings.ReplaceAll(KeyName,"]","'")
    Content := fmt.Sprintf("[#F0F000]Display-Frequency: [#F0F0F0]%-4dms " + 
                           "[#F0F000]| Display-Update: [#F0F0F0]%-5t " +
                           "[#F0F000]| Compact-Mode: [#F0F0F0]%-5t " +
                           "[#F0F000]| LastKey: [#F0F0F0]%-14s " + 
                           "[#F0F000]| Page: [#F0F0F0]%-9s " +
                           "[#F0F000]| [#B01010]Press F1 for help.",
                           DataPanelDelay,DisplayUpdate,CompactDisplay,KeyName,CurrentPage)
    MonApp.QueueUpdateDraw(func() { StatusLine.SetText(Content) })
  } // END for
} // END UpdateStatusPanel

which is executed every 500ms. No call to *Draw() anywhere else in my code:

godev@ffappsdev [OUDbrowse]: grep -i draw *.go
StatusLine.go:    MonApp.QueueUpdateDraw(func() { StatusLine.SetText(Content) })
godev@ffappsdev [OUDbrowse]:

It must be something windows specific, so far i have tested Linux on I86, X64, Arm32, Arm64, Sparc and AIX and no flicker, it happens just in Windows cmd and power-shell, inside windows and in full-screen.

FJuedesOrcl commented 1 year ago

OK, update with the screen.Clear() in the tcell package commented out:

FJuedesOrcl commented 1 year ago

@rivo Update on this issue:

Changes to the code of application.go

- line 604: commented out "//[fjuedes 2023-11-17] screen.Clear()
- line 609: changed to "screen.Sync() //[fjuedes 2023-11-17] screen.Show()"
- line 623: changed to: "screen.Sync() //[fjuedes 2023-11-17] screen.Show()"

This will prevent screen-flickering in the windows terminals, cmd, MobaXTerm and MS-Terminal (cmd and powershell). Few artifacts are visible if the window is resized, especially when the width is changed.

On *nix terminals no visible artifacts, tested with MobaXTerm, PuTTY, MS-Terminal and on Linux-consoles.

To be sure that the changes above will only affect Windows, the runtime package could be used to apply the changes only on windows.

rivo commented 11 months ago

This issue was resolved in tcell and I upgraded tview to the latest version. So the flickering should be gone with the latest commit.