[gtk/gtk-3-24: 1/2] wayland/window: Add API to add extra surfaces for frame callbacks




commit d3316a37edb0ff7b8faf7115ea11fdce76578d95
Author: Robert Mader <robert mader posteo de>
Date:   Fri Jan 22 20:01:07 2021 +0100

    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.

 docs/reference/gdk/gdk3-sections.txt |   2 +
 gdk/wayland/gdkwaylandwindow.h       |   8 +++
 gdk/wayland/gdkwindow-wayland.c      | 120 +++++++++++++++++++++++++++++++++--
 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);
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]