summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mader <robert.mader@posteo.de>2021-01-22 20:01:07 +0100
committerRobert Mader <robert.mader@posteo.de>2021-02-12 13:18:29 +0100
commitd3316a37edb0ff7b8faf7115ea11fdce76578d95 (patch)
tree25654aaf35a716b44f8dde6b2578ab48e25c2383
parent6fd951e53d9d28a6702a0413638157cc21cb66e9 (diff)
downloadgtk+-d3316a37edb0ff7b8faf7115ea11fdce76578d95.tar.gz
wayland/window: Add API to add extra surfaces for frame callbacks
This is useful when clients use subsurfaces independently of GDK. For example if a client creates a subsurface that covers a GdkWindow entirely. If this subsurface is opaque, Wayland compositors may not emit callbacks for the surface of the GdkWindow any more. Adding the covering subsurface via this new API ensures the GdkWindow will continue to update in this case.
-rw-r--r--docs/reference/gdk/gdk3-sections.txt2
-rw-r--r--gdk/wayland/gdkwaylandwindow.h8
-rw-r--r--gdk/wayland/gdkwindow-wayland.c120
3 files changed, 126 insertions, 4 deletions
diff --git a/docs/reference/gdk/gdk3-sections.txt b/docs/reference/gdk/gdk3-sections.txt
index 7b5649c7bd..1833777500 100644
--- a/docs/reference/gdk/gdk3-sections.txt
+++ b/docs/reference/gdk/gdk3-sections.txt
@@ -1279,6 +1279,8 @@ GdkWaylandWindowExported
gdk_wayland_window_export_handle
gdk_wayland_window_unexport_handle
gdk_wayland_window_set_transient_for_exported
+gdk_wayland_window_add_frame_callback_surface
+gdk_wayland_window_remove_frame_callback_surface
<SUBSECTION Standard>
GDK_TYPE_WAYLAND_DEVICE
diff --git a/gdk/wayland/gdkwaylandwindow.h b/gdk/wayland/gdkwaylandwindow.h
index 3b76822962..b8a8a02243 100644
--- a/gdk/wayland/gdkwaylandwindow.h
+++ b/gdk/wayland/gdkwaylandwindow.h
@@ -87,6 +87,14 @@ void gdk_wayland_window_announce_csd (GdkWindow *window);
GDK_AVAILABLE_IN_3_24
void gdk_wayland_window_announce_ssd (GdkWindow *window);
+GDK_AVAILABLE_IN_3_24
+void gdk_wayland_window_add_frame_callback_surface (GdkWindow *window,
+ struct wl_surface *surface);
+
+GDK_AVAILABLE_IN_3_24
+void gdk_wayland_window_remove_frame_callback_surface (GdkWindow *window,
+ struct wl_surface *surface);
+
G_END_DECLS
#endif /* __GDK_WAYLAND_WINDOW_H__ */
diff --git a/gdk/wayland/gdkwindow-wayland.c b/gdk/wayland/gdkwindow-wayland.c
index 8d14db56f0..2d7c42bd7a 100644
--- a/gdk/wayland/gdkwindow-wayland.c
+++ b/gdk/wayland/gdkwindow-wayland.c
@@ -236,6 +236,9 @@ struct _GdkWindowImplWayland
struct zxdg_imported_v1 *imported_transient_for;
GHashTable *shortcuts_inhibitors;
+
+ struct wl_callback *surface_callback;
+ GHashTable *frame_callback_surfaces;
};
struct _GdkWindowImplWaylandClass
@@ -572,9 +575,25 @@ frame_callback (void *data,
GdkFrameClock *clock = gdk_window_get_frame_clock (window);
GdkFrameTimings *timings;
- GDK_NOTE (EVENTS,
- g_message ("frame %p", window));
+ if (callback == impl->surface_callback)
+ {
+ impl->surface_callback = NULL;
+ }
+ else
+ {
+ GHashTableIter iter;
+ gpointer surface_callback;
+ g_hash_table_iter_init (&iter, impl->frame_callback_surfaces);
+ while (g_hash_table_iter_next (&iter, NULL, &surface_callback))
+ {
+ if (callback == surface_callback)
+ {
+ g_hash_table_iter_replace (&iter, NULL);
+ break;
+ }
+ }
+ }
wl_callback_destroy (callback);
if (GDK_WINDOW_DESTROYED (window))
@@ -583,6 +602,9 @@ frame_callback (void *data,
if (!impl->awaiting_frame)
return;
+ GDK_NOTE (EVENTS,
+ g_message ("frame %p", window));
+
impl->awaiting_frame = FALSE;
_gdk_frame_clock_thaw (clock);
@@ -660,6 +682,8 @@ on_frame_clock_after_paint (GdkFrameClock *clock,
{
GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
struct wl_callback *callback;
+ GHashTableIter iter;
+ gpointer surface, surface_callback;
if (!impl->pending_commit)
return;
@@ -667,8 +691,6 @@ on_frame_clock_after_paint (GdkFrameClock *clock,
if (window->update_freeze_count > 0)
return;
- callback = wl_surface_frame (impl->display_server.wl_surface);
- wl_callback_add_listener (callback, &frame_listener, window);
_gdk_frame_clock_freeze (clock);
/* Before we commit a new buffer, make sure we've backfilled
@@ -677,6 +699,24 @@ on_frame_clock_after_paint (GdkFrameClock *clock,
if (impl->pending_buffer_attached)
read_back_cairo_surface (window);
+ if (impl->surface_callback == NULL)
+ {
+ callback = wl_surface_frame (impl->display_server.wl_surface);
+ wl_callback_add_listener (callback, &frame_listener, window);
+ impl->surface_callback = callback;
+ }
+
+ g_hash_table_iter_init (&iter, impl->frame_callback_surfaces);
+ while (g_hash_table_iter_next (&iter, &surface, &surface_callback))
+ {
+ if (surface_callback)
+ continue;
+
+ callback = wl_surface_frame (surface);
+ wl_callback_add_listener (callback, &frame_listener, window);
+ g_hash_table_iter_replace (&iter, callback);
+ }
+
/* From this commit forward, we can't write to the buffer,
* it's "live". In the future, if we need to stage more changes
* we have to allocate a new staging buffer and draw to it instead.
@@ -756,6 +796,8 @@ _gdk_wayland_display_create_window_impl (GdkDisplay *display,
impl->wrapper = GDK_WINDOW (window);
impl->shortcuts_inhibitors = g_hash_table_new (NULL, NULL);
impl->using_csd = TRUE;
+ impl->surface_callback = NULL;
+ impl->frame_callback_surfaces = g_hash_table_new (NULL, NULL);
if (window->width > 65535)
{
@@ -1067,6 +1109,7 @@ gdk_window_impl_wayland_finalize (GObject *object)
g_clear_pointer (&impl->staged_updates_region, cairo_region_destroy);
g_clear_pointer (&impl->shortcuts_inhibitors, g_hash_table_unref);
+ g_clear_pointer (&impl->frame_callback_surfaces, g_hash_table_unref);
G_OBJECT_CLASS (_gdk_window_impl_wayland_parent_class)->finalize (object);
}
@@ -3352,6 +3395,7 @@ gdk_wayland_window_hide_surface (GdkWindow *window)
wl_surface_destroy (impl->display_server.wl_surface);
impl->display_server.wl_surface = NULL;
+ impl->surface_callback = NULL;
g_slist_free (impl->display_server.outputs);
impl->display_server.outputs = NULL;
@@ -5397,3 +5441,71 @@ gdk_wayland_window_restore_shortcuts (GdkWindow *window,
g_hash_table_remove (impl->shortcuts_inhibitors, seat);
}
+/**
+ * gdk_wayland_window_add_frame_callback_surface:
+ * @window: the #GdkWindow requesting callbacks
+ * @surface: the wl_surface to add
+ *
+ * Add @surface to a list of surfaces for which frame callback
+ * listeners will get set up, additionally to the one of @window.
+ *
+ * This is useful when clients use subsurfaces independently of GDK.
+ * For example if a client creates a subsurface that covers @window
+ * entirely. If this subsurface is opaque, Wayland compositors may not
+ * emit callbacks for the surface of @window any more.
+ * Adding the covering subsurface via this function ensures the
+ * @window will continue to update.
+ *
+ * A single callback is sufficient to trigger the next update. If more
+ * than one are triggered, the later ones will get ignored.
+ *
+ * Before @surface gets destroyed it must get removed again using
+ * gdk_wayland_window_remove_frame_callback_surface().
+ *
+ * Note that the client is responsible to commit the @surface.
+ * One way to archive this is to always commit after the
+ * #GdkSurface::after-paint signal was triggered.
+ *
+ * Since: 3.24.25
+ */
+void
+gdk_wayland_window_add_frame_callback_surface (GdkWindow *window,
+ struct wl_surface *surface)
+{
+ GdkWindowImplWayland *impl;
+
+ g_return_if_fail (GDK_IS_WAYLAND_WINDOW (window));
+ g_return_if_fail (GDK_IS_WINDOW_IMPL_WAYLAND (window->impl));
+ g_return_if_fail (surface != NULL);
+
+ impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
+
+ g_hash_table_insert (impl->frame_callback_surfaces, surface, NULL);
+}
+
+/**
+ * gdk_wayland_window_remove_frame_callback_surface:
+ * @window: the #GdkWindow requesting callbacks
+ * @surface: the surface to remove
+ *
+ * Remove @surface from the list of surfaces again that got added via
+ * gdk_wayland_window_add_frame_callback_surface().
+ *
+ * This function must be called before @surface gets destroyed.
+ *
+ * Since: 3.24.25
+ */
+void
+gdk_wayland_window_remove_frame_callback_surface (GdkWindow *window,
+ struct wl_surface *surface)
+{
+ GdkWindowImplWayland *impl;
+
+ g_return_if_fail (GDK_IS_WAYLAND_WINDOW (window));
+ g_return_if_fail (GDK_IS_WINDOW_IMPL_WAYLAND (window->impl));
+ g_return_if_fail (surface != NULL);
+
+ impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
+
+ g_hash_table_remove (impl->frame_callback_surfaces, surface);
+}