From b87c4f1cbb4cecd4a68114a422884c82f9a65417 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 18 Aug 2024 00:03:06 -0700 Subject: [PATCH] Allow commiting surface directly instead of waiting for a frame callback. Check GTK internal state to avoid reintroducing the crashes. Fixes #185 --- include/gtk-layer-shell.h | 13 +++++++++++++ src/api.c | 9 +++++++++ src/custom-shell-surface.c | 19 +++++++++++++++++++ src/custom-shell-surface.h | 6 +++++- src/gtk-priv-access.c | 9 +++++++++ src/gtk-priv-access.h | 3 +++ 6 files changed, 58 insertions(+), 1 deletion(-) diff --git a/include/gtk-layer-shell.h b/include/gtk-layer-shell.h index 93557fb..38f2633 100644 --- a/include/gtk-layer-shell.h +++ b/include/gtk-layer-shell.h @@ -387,6 +387,19 @@ void gtk_layer_set_keyboard_interactivity (GtkWindow *window, gboolean interacti */ gboolean gtk_layer_get_keyboard_interactivity (GtkWindow *window); +/** + * gtk_layer_try_force_commit: + * @window: A layer surface. + * + * Commits a surface state if there's no pending commit scheduled by the GTK. + * You almost never need to call this; the only known case is when the surface is in a state + * where it does not receive frame callbacks and the regular deferred commit mechanism + * is unavailable. + * + * Since: 0.9 + */ +void gtk_layer_try_force_commit (GtkWindow *window); + G_END_DECLS #endif // GTK_LAYER_SHELL_H diff --git a/src/api.c b/src/api.c index 7b51dce..383260e 100644 --- a/src/api.c +++ b/src/api.c @@ -265,3 +265,12 @@ gtk_layer_get_keyboard_mode (GtkWindow *window) if (!layer_surface) return GTK_LAYER_SHELL_KEYBOARD_MODE_NONE; // Error message already shown in gtk_window_get_layer_surface return layer_surface->keyboard_mode; } + +void +gtk_layer_try_force_commit (GtkWindow *window) +{ + CustomShellSurface *shell_surface = gtk_window_get_custom_shell_surface (window); + if (!shell_surface) + return; + custom_shell_surface_force_commit (shell_surface); +} diff --git a/src/custom-shell-surface.c b/src/custom-shell-surface.c index 93befed..3c8cb90 100644 --- a/src/custom-shell-surface.c +++ b/src/custom-shell-surface.c @@ -124,6 +124,25 @@ custom_shell_surface_needs_commit (CustomShellSurface *self) gdk_window_invalidate_rect (gdk_window, NULL, FALSE); } +void +custom_shell_surface_force_commit (CustomShellSurface *self) +{ + if (!self->private->gtk_window) + return; + + GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (self->private->gtk_window)); + + if (!gdk_window || gdk_window_get_priv_pending_commit (gdk_window)) + return; + + struct wl_surface *wl_surface = gdk_wayland_window_get_wl_surface (gdk_window); + + if (!wl_surface) + return; + + wl_surface_commit (wl_surface); +} + void custom_shell_surface_remap (CustomShellSurface *self) { diff --git a/src/custom-shell-surface.h b/src/custom-shell-surface.h index 7acb28e..b1e7c28 100644 --- a/src/custom-shell-surface.h +++ b/src/custom-shell-surface.h @@ -59,10 +59,14 @@ CustomShellSurface *gtk_window_get_custom_shell_surface (GtkWindow *gtk_window); GtkWindow *custom_shell_surface_get_gtk_window (CustomShellSurface *self); -// In theory this could commit once on next event loop, but for now it will just commit every time it is called +// Schedules commit on the next frame callback // Does nothing is the shell surface does not currently have a GdkWindow with a wl_surface void custom_shell_surface_needs_commit (CustomShellSurface *self); +// Attempts to commit the surface immediately +// Does nothing is the shell surface does not currently have a GdkWindow with a wl_surface +void custom_shell_surface_force_commit (CustomShellSurface *self); + // Unmap and remap a currently mapped shell surface void custom_shell_surface_remap (CustomShellSurface *self); diff --git a/src/gtk-priv-access.c b/src/gtk-priv-access.c index a0d18c7..ac7cd72 100644 --- a/src/gtk-priv-access.c +++ b/src/gtk-priv-access.c @@ -208,3 +208,12 @@ gtk_window_get_priv_logical_geom (GtkWindow *gtk_window) gdk_window_impl_wayland_priv_get_margin_bottom (window_impl); return result; } + +gboolean +gdk_window_get_priv_pending_commit (GdkWindow *gdk_window) +{ + GdkWindowImplWayland *window_impl = (GdkWindowImplWayland *)gdk_window_priv_get_impl (gdk_window); + + return (gdk_window_impl_wayland_priv_get_pending_commit (window_impl) || + gdk_window_impl_wayland_priv_get_pending_buffer_attached (window_impl)); +} diff --git a/src/gtk-priv-access.h b/src/gtk-priv-access.h index e54c6ce..628454b 100644 --- a/src/gtk-priv-access.h +++ b/src/gtk-priv-access.h @@ -34,4 +34,7 @@ void gdk_window_set_priv_mapped (GdkWindow *gdk_window); // Gets the window geometry (area inside the window that excludes the shadow) GdkRectangle gtk_window_get_priv_logical_geom (GtkWindow *widget); +// Checks if it is safe to commit wl_surface for the window directly +gboolean gdk_window_get_priv_pending_commit (GdkWindow *gdk_window); + #endif // GDK_WINDOW_HACK_H