Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

custom_shell_surface_needs_commit is unreliable #185

Closed
alebastr opened this issue Aug 18, 2024 · 9 comments · Fixed by #186
Closed

custom_shell_surface_needs_commit is unreliable #185

alebastr opened this issue Aug 18, 2024 · 9 comments · Fixed by #186

Comments

@alebastr
Copy link
Contributor

I've been debugging an issue where Waybar fails to switch from the GTK_LAYER_SHELL_LAYER_BOTTOM to OVERLAY or TOP with Sway 1.10.
The following fragment of WAYLAND_DEBUG log has sent me to the scary depths of GTK

[2653097.622] {Default Queue}  -> zwlr_layer_surface_v1#46.set_layer(3)
[2653321.642] {Default Queue}  -> zwlr_layer_surface_v1#46.set_layer(1)
[2653673.080] {Default Queue}  -> zwlr_layer_surface_v1#46.set_layer(3)
[2653824.898] {Default Queue}  -> zwlr_layer_surface_v1#46.set_layer(1)
[2653961.042] {Default Queue}  -> zwlr_layer_surface_v1#46.set_layer(3)
[2654040.982] {Default Queue}  -> zwlr_layer_surface_v1#46.set_layer(1)
[2654153.370] {Default Queue}  -> zwlr_layer_surface_v1#46.set_layer(3)
[2654249.116] {Default Queue}  -> zwlr_layer_surface_v1#46.set_layer(1)

and helped me realize two facts

  • gdk_window_invalidate_rect schedules a redraw and commit on the next frame callback
  • wlr_scene may stop delivering frame callbacks to a fully occluded surface

So once the layer surface is fully hidden behind an opaque surface, it won't be able to get out without external help.

It appears that we have to use a direct wl_surface_commit, but without reintroducing #47 or #51. Looking at these issues, it occurs to me that it would be safe to commit if GdkWindowImplWayland.pending_commit and GdkWindowImplWayland.pending_buffer_attached are unset, which we can easily check via private struct accessors.

In the meantime, I'm going to work around that in waybar by adding a conditional wl_surface_commit that is only allowed between GdkFrameClock::after-paint and GdkFrameClock::before-paint. That's the closest approximation of the check above possible with public GTK APIs.

alebastr added a commit to alebastr/Waybar that referenced this issue Aug 18, 2024
All the mode or visibility changes require `wl_surface_commit` to be
applied. gtk-layer-shell will attempt to force GTK to commit, but may
fail if the surface has stopped receiving frame callbacks[^1].

Thus, we could get stuck in a state where the bar is hidden and unable
to regain visibility.

Until that's addressed in `gtk-layer-shell`, we should take care of the
commits ourselves.

[^1]: wmww/gtk-layer-shell#185
alebastr added a commit to alebastr/gtk-layer-shell that referenced this issue Aug 18, 2024
Check GTK internal state to avoid reintroducing the crashes.

Fixes wmww#185
@wmww
Copy link
Owner

wmww commented Sep 1, 2024

Hmm, I believe you that there are problems, but I'm scared to change anything because

  • as you mentioned, there have been multiple subtle, hard to debug bugs and crashes in this area
  • what we have now doesn't crash, and at least works in the standard case where surface properties don't change
  • gtk-priv is bad and every additional usage increases maintenance burden and chance of GTK breaking us by changing it's internal logic
  • this whole project is somewhat legacy (replaced by the gtk4 version), so I'm biased against risking instability for the sake of new things working (even if it is technically a bug fix)

my preference would be either for app devs to work around the issue in their code if possible, or failing that to somehow make the fixed but potentially less stable code path opt-in (so we don't break existing apps that work fine as-is)

@alebastr
Copy link
Contributor Author

alebastr commented Sep 1, 2024

my preference would be either for app devs to work around the issue in their code if possible,

I really don't like the workaround code: alebastr/Waybar@6416f7d. And it isn't something I want to see being copied from app to app.

or failing that to somehow make the fixed but potentially less stable code path opt-in (so we don't break existing apps that work fine as-is)

How about exposing this code as gtk_layer_force_commit(GtkWindow *window)?

@wmww
Copy link
Owner

wmww commented Sep 1, 2024

How about exposing this code as gtk_layer_force_commit(GtkWindow *window)?

That would be acceptable, as long as the library continues to work the same when apps don't call it. I realize this isn't a great solution, but I think it's the best compromise if more private struct usage is indeed needed.

Taking a closer look at the issue, it looks like the call to custom_shell_surface_needs_commit() is already on an optional code path:

if (version >= ZWLR_LAYER_SURFACE_V1_SET_LAYER_SINCE_VERSION) {
. The alternative is to remap the window. Might a solution to your problem be to remap the window whenever the layer (or other property?) changes? This could be done either in your app or the library. Might cause a flicker, but that shouldn't be that big a deal, right?

@alebastr
Copy link
Contributor Author

alebastr commented Sep 2, 2024

Updated the PR with explicit commit method.

Remapping seems a bit too heavy when you only need to send two Wayland protocol messages. And yes, there is an obnoxious flickering that would accompany any layer surface state updates, as you can't determine when you really require a remap. Layer change is not the only reason the surface can become occluded or visible.

@wmww
Copy link
Owner

wmww commented Sep 2, 2024

How often are you updating the layer surface state in Waybar? Does this happen in cases other than when user configuration changes?

@alebastr
Copy link
Contributor Author

alebastr commented Sep 2, 2024

Waybar partially implements sway-bar(5) IPC protocol, and I've been planning to add support for more properties.

At the moment, user can change layer/exclusivity/visibility via IPC and bind bar visibility to a press/release of the modifier (Super) key. Which is really often when you work with a tiling WM.

@wmww
Copy link
Owner

wmww commented Sep 2, 2024

I don't understand why flickering on property change would be an issue for those usecases?

@alebastr
Copy link
Contributor Author

alebastr commented Sep 2, 2024

This is how it works with custom_shell_surface_remap (web player seems to skip a few frames and make it look better than it actually is). Not sure of your definition of issue, but for me it looks very annoying.

The bar is configured to toggle between the bottom and the overlay layers depending on the modifier key state.

bar.webm

@wmww
Copy link
Owner

wmww commented Sep 2, 2024

All right fine, I'm convinced

@wmww wmww closed this as completed in #186 Sep 2, 2024
@wmww wmww closed this as completed in b87c4f1 Sep 2, 2024
alebastr added a commit to alebastr/Waybar that referenced this issue Sep 10, 2024
All the mode or visibility changes require `wl_surface_commit` to be
applied. gtk-layer-shell will attempt to force GTK to commit, but may
fail if the surface has stopped receiving frame callbacks[^1].
Thus, we could get stuck in a state where the bar is hidden and unable
to regain visibility.

To address this, a new API has been added to gtk-layer-shell,
`gtk_layer_try_force_commit`, which does `wl_surface_commit` with the
necessary safety checks to avoid corrupting GTK internal state.

Note: this change bumps gtk-layer-shell requirement to 0.9.0.

[^1]: wmww/gtk-layer-shell#185
alebastr added a commit to alebastr/Waybar that referenced this issue Sep 14, 2024
All the mode or visibility changes require `wl_surface_commit` to be
applied. gtk-layer-shell will attempt to force GTK to commit, but may
fail if the surface has stopped receiving frame callbacks[^1].
Thus, we could get stuck in a state where the bar is hidden and unable
to regain visibility.

To address this, a new API has been added to gtk-layer-shell,
`gtk_layer_try_force_commit`, which does `wl_surface_commit` with the
necessary safety checks to avoid corrupting GTK internal state.

Note: this change bumps gtk-layer-shell requirement to 0.9.0.

[^1]: wmww/gtk-layer-shell#185
alebastr added a commit to alebastr/Waybar that referenced this issue Sep 15, 2024
All the mode or visibility changes require `wl_surface_commit` to be
applied. gtk-layer-shell will attempt to force GTK to commit, but may
fail if the surface has stopped receiving frame callbacks[^1].
Thus, we could get stuck in a state where the bar is hidden and unable
to regain visibility.

To address this, a new API has been added to gtk-layer-shell,
`gtk_layer_try_force_commit`, which does `wl_surface_commit` with the
necessary safety checks to avoid corrupting GTK internal state.

Note: this change bumps gtk-layer-shell requirement to 0.9.0.

[^1]: wmww/gtk-layer-shell#185
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants