Repository: qtile/qtile

Issue:
Widgets are causing bar to glitch when text updates
It's been a while I didn't pull qtile and I noticed this constant flicking caused but widget updates on my bars. I tried to record it but the flickering doesn't get recorded, it might be too fast. 

I don't have enough time to bisect this now and I believe someone might know what has changed. This flickering is very distracting and bad for my eyes. It seems that it only happens when there is a text resize, doesn't happen when just the text changes without the need of resizing. It looks like a glitch.

Happens on:
da93518b2ab924e803552decdb364d64de46088d

Didn't back happen on:
d8c00aae90d551fa2c326b2bd07d331d66ded04e

Tested under X11, wayland not tested.



Discussion:
Can you post your config so we can see what widgets you're using. Can you also try default config and see if there's still an issue.

It's not something that I've noticed but I'm happy to look into it and see if I can recreate.
I tried recreating with a minimal config on Xephyr with widgets with gradient colors for foreground and background that change with (specifically Net, CPU and Memory), but I couldn't reproduce. My whole config where this is happening is huge ~mess~ https://gitlab.com/matheusfillipeag/dotfiles/-/blob/master/.config/qtile/config.py. 

But I took a little time to bisect it and found out that this problem happens since: 6f41e52e "Add borders to Bar" commit.

If it matters I using nvidia proprietary drivers, my widget foreground and backgrounds are all gradients and using the default "sans" font.

The glitch only happens when the text has to resize. If I change to consolas font just for the problematic widgets like this, the glitch is gone:

```python
        widget.Net(
            font="consolas",
            fontsize=18,
            interface="wlp3s0",
            update_interval=1,
            margin=5,
            format="<b>↓{down:<3}  ↑{up:<3}</b>",
        ),
        widget.CPU(
            font="consolas",
            fontsize=18,
            format="<big>🖥</big><b>{load_percent:5.2f}</b>%",
        ),
        widget.Memory(
            font="consolas",
            fontsize=18,
            prefix_text="🖬",
            measure_mem="G",
            format="<b>{MemUsed:2.0f}</b>/{MemTotal:2.0f}{mm}",
        ),
```

If I change the font to something like "sans" then the glitch is back.

Maybe you are able to reproduce just with that? Sorry for not providing an actual proper minimal config to reproduce this, but was surely on that commit, which doesn't seem small.
I did wonder if it was (my) bar border commit.

I'll see if I can take a look at some point.
I just quickly tried those widgets with commenting out the font name. I don't get any glitching of the bar when the widgets change width.
That config file is quite something!

I'll really need a minimal config that recreates this to go any further here. I'm not going to be able to use your config file.
I'm sorry, don't even look on that config :P 

I am pretty sure it is related to or at least happens mostly when there are those width changes. Maybe it has something to do with color gradients, I have those as well. Maybe try with something like this:

```python
        widget.Net(
            font="sans",
            fontsize=14,
            interface="wlp3s0",
            foreground=["#dddddd", "#ffffff"],
            background=["#8c008c", "#613553"],
            update_interval=1,
            margin=5,
            format="<b>↓{down}  ↑{up}</b>",
        ),
        widget.CPU(
            font="sans",
            fontsize=14,
            foreground=["#dddddd", "#ffffff"],
            background=["#258fe1", "#3f56c7"],
            format="<big>🖥</big><b>{load_percent}</b>%",
        ),
        widget.Memory(
            font="sans",
            fontsize=14,
            foreground=["#dddddd", "#ffffff"],
            background=["#8c008c", "#613553"],
            prefix_text="🖬",
            measure_mem="G",
            format="<b>{MemUsed}</b>/{MemTotal}{mm}",
        ),
```

If it still doesn't happen for you, maybe it is my graphics drivers or something related to my Xorg config? 

Also if you have any insight of what part of that commit I should change or comment out to make a test you can tell me.
No joy. Still no glitching.
You had a glitch issue in #2547 too, didn't you?

So maybe this is driver related.
Unfortunately, it's hard to point to a part of that commit when I have no idea what the issue is!
> You had a glitch issue in #2547 too, didn't you?
> 
> So maybe this is driver related.


Yes indeed! I always have those weird issues. That one I tested without loading the nvidia module and it would still happen though.

Sounds like your config is identifying some edge cases that we don't see in testing!
I was able to record it 
https://user-images.githubusercontent.com/24435787/144155337-5493e2bf-71b4-4a91-ad7b-7d993385099c.mp4


It's a long shot but can you try changing this line:
https://github.com/qtile/qtile/blob/6f41e52e1f2a7692df99ad2127c997e97e80bae7/libqtile/bar.py#L494
to
``` python
if any(self.border_width):
```

I wonder if that extra code in `Bar._actual_draw` is causing the glitch. 

It shouldn't run if there's no border but I've got the test wrong (`border_width` will always be True even if there's no border). However, if this does solve the glitching, it does mean you won't be able to draw borders without glitching. So, I almost hope this _isn't_ the solution!

If you do want borders for your bar, you could try commenting out this line:
https://github.com/qtile/qtile/blob/da93518b2ab924e803552decdb364d64de46088d/libqtile/bar.py#L530

I think a better approach may be to draw the borders once and only draw them again if the bar is reconfigured, otherwise there's no need to keep drawing them.
> It's a long shot but can you try changing this line:
> 
> https://github.com/qtile/qtile/blob/6f41e52e1f2a7692df99ad2127c997e97e80bae7/libqtile/bar.py#L494
> to
> ```python
> if any(self.border_width):
>```

Hello! I had the same Issue. Making this change solved it. Thank you, it was very distracting :+1: 
I can confirm changing the check from `if self.border_width: ` to `if any(self.border_width):` indeed stops the glitching.


> I think a better approach may be to draw the borders once and only draw them again if the bar is reconfigured, otherwise there's no need to keep drawing them.

That sounds reasonable to me.
I suspect the borders will still need to be drawn upon `Bar.draw`, as 
that method is hooked up on the `Internal` window to be called upon 
expose events via `self.window.process_window_expose`.

On Wed, Dec 1 2021 at 10:52:04 -0800, Matheus Fillipe 
***@***.***> wrote:
> I can confirm changing the check from if self.border_width: to if 
> any(self.border_width): indeed stops the glitching.
> 
>> I think a better approach may be to draw the borders once and only 
>> draw them again if the bar is reconfigured, otherwise there's no 
>> need to keep drawing them.
>> 
> That sounds reasonable to me.
> 
> —
> You are receiving this because you are subscribed to this thread.
> Reply to this email directly, view it on GitHub, or unsubscribe.
> Triage notifications on the go with GitHub Mobile for iOS or Android.
> 



> If you do want borders for your bar, you could try commenting out this line:
> 
> https://github.com/qtile/qtile/blob/da93518b2ab924e803552decdb364d64de46088d/libqtile/bar.py#L530

I tried that and the glitch still happens with borders.
Thanks for all the feedback. Let me test a few ideas and see if we can push a fix.

Resolving patch:
diff --git a/libqtile/bar.py b/libqtile/bar.py
--- a/libqtile/bar.py
+++ b/libqtile/bar.py
@@ -189,11 +189,13 @@ def __init__(self, widgets, size, **config):
 
         self.queued_draws = 0
         self.future = None
+        self._borders_drawn = False
 
     def _configure(self, qtile, screen):
         # We only want to adjust margin sizes once unless there's a new strut
         if not self._configured or self._add_strut:
             Gap._configure(self, qtile, screen)
+            self._borders_drawn = False
 
             if sum(self._initial_margin) or sum(self.border_width) or self._add_strut:
 
@@ -498,7 +500,7 @@ def _actual_draw(self):
         self._resize(self.length, self.widgets)
 
         # We draw the border before the widgets
-        if self.border_width:
+        if any(self.border_width) and not self._borders_drawn:
 
             # The border is drawn "outside" of the bar (i.e. not in the space that the
             # widgets occupy) so we need to add the additional space
@@ -545,6 +547,9 @@ def _actual_draw(self):
 
             self.drawer.draw(0, 0)
 
+            # Prevent multiple redraws of borders
+            self._borders_drawn = True
+
         for i in self.widgets:
             i.draw()
         end = i.offset + i.length  # pylint: disable=undefined-loop-variable
@@ -573,6 +578,7 @@ def show(self, is_show=True):
         if is_show != self.is_show():
             if is_show:
                 self.size = self.size_calculated
+                self._borders_drawn = False
                 self.window.unhide()
             else:
                 self.size_calculated = self.size


Test patch: