Waybar
Waybar copied to clipboard
Warbar claims buffer is opaque when it's being hidden by depressing $mod
Waybar seems to swap out the buffer when it's trying to hide/show itself when using sway IPC mode with the $mod key being depressed. When it swaps the buffer out to something that's completely transparent (functionally hiding it) it still claims the buffer being opaque and so the wayland compositor might try to do optimizations that aren't safe (like in sway master). Waybar needs to have a solid background color configured for the bug to occur.
It's strange that we are committing a new buffer instead of just mapping/unmapping the layer shell?
Ref: https://github.com/swaywm/sway/issues/8075
This issue appears partially fixed with https://github.com/Alexays/Waybar/pull/3528 . Only tested for ~5min but the usual "$mod+focus elsewhere" doesn't leave a black silhouette anymore. That is such a relief!
On a side note, I have a (short) CSS animation to slowly transition the opacity of the bar when displayed. I see that it first displays a black background when the bar appears.
Example when increasing the animation time + low opacity + pink background: a black background appears first. This does not happen on waybar v0.11.0
https://github.com/user-attachments/assets/cfaa78af-3594-4790-9118-337d7cb5f8f3
In Sway, a black silhouette is still left on the wallpaper in workspaces with only floating windows. Reproduction steps: create a floating window and focus on it -> press and hold $mod to display Waybar -> change focus -> release $mod : black silhouette is displayed on the background.
It disappears the next time the bar is displayed
I don't believe it's possible to completely fix that one :(
The root of the problem is in the GtkWindow::update_opaque_region. GTK window has 3 sources of alpha: CSS background-color, CSS opacity and user alpha set via gtk_window_set_opacity. Whenever the style context is changed, update_opaque_region will check the background-color and the user alpha to see if the window is opaque and set the region accordingly.
If you feel like something is missing in the list - indeed, CSS opacity value is ignored and will not affect the opaque region. It seems easy to fix, just need to check GtkWidgetPrivate.alpha instead of GtkWidgetPrivate.user_alpha, but while the latter is exposed via gtk_widget_get_opacity, the former is a private implementation detail with no way to access[^1].
The documented workaround is to use gdk_window_set_opaque_region, which could look like this:
waybar::Bar::Bar(...) {
// ...
window.signal_style_updated().connect(sigc::mem_fun(*this, &Bar::onStyleUpdated));
// ...
}
void waybar::Bar::onStyleUpdated() {
if (auto gdk_window = window.get_window(); gdk_window) {
/* Always assume that the window is transparent */
static Cairo::RefPtr<Cairo::Region> region;
gdk_window->set_opaque_region(region);
}
}
The problem is, GTK explicitly commits its own update, and our change gets deferred until the next frame callback. In the best case it results in flickering, and in the worst case - you'll be seeing a black rectangle until the compositor starts sending frame callbacks again.
The full patch is here: https://github.com/alebastr/Waybar/commit/set-opaque-region. I'm fine with submitting it if it makes things better for someone, but it didn't do a lot in my testing.
tl;dr:
Don't use the opacity property. Specify alpha in the background-color instead.
window#waybar {
background-color: alpha(pink, 0.5);
}
[^1]: now, that's not completely accurate, but I don't intend to work on the necessary GTK3 changes
@alebastr , thank you for the in-depth research. The background-color suggestion works perfectly. That's fixing an issue I had since March, amazing!