gdk: Move begin/end_frame() functions
authorBenjamin Otte <otte@redhat.com>
Mon, 23 Apr 2018 15:28:55 +0000 (17:28 +0200)
committerBenjamin Otte <otte@redhat.com>
Tue, 24 Apr 2018 21:16:58 +0000 (23:16 +0200)
As they require a draw context and the draw context is already bound to
the surface, it makes much more sense and reduces abiguity by moving
these APIs to the draw context.

As a side effect, we simplify GdkSurface APIs to a point where
GdkSurface now does not concern itself with drawing anymore at all,
apart from being the object that creates draw contexts.

13 files changed:
docs/reference/gdk/gdk4-sections.txt
gdk/gdkcairocontext.c
gdk/gdkdrawcontext.c
gdk/gdkdrawcontext.h
gdk/gdkdrawcontextprivate.h
gdk/gdkglcontext.c
gdk/gdksurface.c
gdk/gdksurface.h
gdk/gdkvulkancontext.c
gsk/gl/gskglrenderer.c
gsk/gskbroadwayrenderer.c
gsk/gskcairorenderer.c
gsk/vulkan/gskvulkanrenderer.c

index cc56a592b91945a8699ff3f4c4417d30bcfbbb2b..18ba178c7785a1170bdbab93e9ffaa640a93ff26 100644 (file)
@@ -233,10 +233,6 @@ gdk_surface_set_opaque_region
 gdk_surface_create_gl_context
 gdk_surface_create_vulkan_context
 
-<SUBSECTION>
-gdk_surface_begin_draw_frame
-gdk_surface_end_draw_frame
-
 <SUBSECTION>
 gdk_surface_invalidate_rect
 gdk_surface_invalidate_region
@@ -1106,6 +1102,8 @@ gdk_frame_timings_get_type
 GdkDrawContext
 gdk_draw_context_get_display
 gdk_draw_context_get_surface
+gdk_draw_context_begin_frame
+gdk_draw_context_end_frame
 gdk_draw_context_get_frame_region
 
 <SUBSECTION Standard>
index 9308af8a67528a158ef2021891b7dddeee897f65..34d3c7fa1765d5931214db998cac4fa7c9bb2a7a 100644 (file)
@@ -196,7 +196,7 @@ gdk_cairo_context_end_frame (GdkDrawContext *draw_context,
   surface = gdk_draw_context_get_surface (draw_context);
   if (surface->current_paint.surface == NULL)
     {
-      g_warning (G_STRLOC": no preceding call to gdk_surface_begin_draw_frame(), see documentation");
+      g_warning (G_STRLOC": no preceding call to gdk_draw_context_end_frame(), see documentation");
       return;
     }
 
@@ -255,11 +255,11 @@ gdk_cairo_context_init (GdkCairoContext *self)
  * @context: a #GdkCairoContext that is currently drawing
  *
  * Retrieves a Cairo context to be used to draw on the #GdkSurface
- * of @context. A call to gdk_surface_begin_draw_frame() with this
+ * of @context. A call to gdk_draw_context_begin_frame() with this
  * @context must have been done or this function will return %NULL.
  *
  * The returned context is guaranteed to be valid until
- * gdk_surface_end_draw_frame() is called.
+ * gdk_draw_context_end_frame() is called.
  *
  * Returns: (transfer full) (nullable): a Cairo context to be used
  *   to draw the contents of the #GdkSurface. %NULL is returned
index 45a353b08699c79c32c1f390385306192db1aada..dcedc540ca118a0c6a33ca8f351139d63ef95ee5 100644 (file)
@@ -197,63 +197,6 @@ gdk_draw_context_is_drawing (GdkDrawContext *context)
   return priv->is_drawing;
 }
 
-/*< private >
- * gdk_draw_context_begin_frame:
- * @context: a #GdkDrawContext
- * @region: (inout): The clip region that needs to be repainted
- *
- * Sets up @context and @drawing for a new drawing.
- *
- * The @context is free to update @region to the size that actually needs to
- * be repainted. Contexts that do not support partial blits for example may
- * want to invalidate the whole surface instead.
- *
- * The function does not clear the background. Clearing the backgroud is the
- * job of the renderer. The contents of the backbuffer are undefined after this
- * function call.
- */
-void
-gdk_draw_context_begin_frame (GdkDrawContext *context,
-                              cairo_region_t *region)
-{
-  GdkDrawContextPrivate *priv;
-
-  g_return_if_fail (GDK_IS_DRAW_CONTEXT (context));
-  g_return_if_fail (region != NULL);
-
-  priv = gdk_draw_context_get_instance_private (context);
-  priv->is_drawing = TRUE;
-
-  GDK_DRAW_CONTEXT_GET_CLASS (context)->begin_frame (context, region);
-}
-
-/*< private >
- * gdk_draw_context_end_frame:
- * @context: a #GdkDrawContext
- * @painted: The area that has been redrawn this frame
- * @damage: The area that we know is actually different from the last frame
- *
- * Copies the back buffer to the front buffer.
- *
- * This function may call `glFlush()` implicitly before returning; it
- * is not recommended to call `glFlush()` explicitly before calling
- * this function.
- */
-void
-gdk_draw_context_end_frame (GdkDrawContext *context,
-                            cairo_region_t *painted,
-                            cairo_region_t *damage)
-{
-  GdkDrawContextPrivate *priv;
-
-  g_return_if_fail (GDK_IS_DRAW_CONTEXT (context));
-
-  GDK_DRAW_CONTEXT_GET_CLASS (context)->end_frame (context, painted, damage);
-
-  priv = gdk_draw_context_get_instance_private (context);
-  priv->is_drawing = FALSE;
-}
-
 /*< private >
  * gdk_draw_context_surface_resized:
  * @context: a #GdkDrawContext
@@ -303,6 +246,119 @@ gdk_draw_context_get_surface (GdkDrawContext *context)
   return priv->surface;
 }
 
+/**
+ * gdk_draw_context_begin_frame:
+ * @context: the context used to draw the frame
+ * @region: minimum region that should be drawn
+ *
+ * Indicates that you are beginning the process of redrawing @region
+ * on the @context's surface.
+ *
+ * Calling this function begins a drawing operation using @context on the
+ * surface that @context was created from. The actual requirements and
+ * guarantees for the drawing operation vary for different implementations
+ * of drawing, so a #GdkCairoContext and a #GdkGLContext need to be treated
+ * differently.
+ *
+ * A call to this function is a requirement for drawing and must be followed
+ * by a call to gdk_draw_context_end_frame(), which will complete the
+ * drawing operation and ensure the contents become visible on screen.
+ *
+ * Note that the @region passed to this function is the minimum region that
+ * needs to be drawn and depending on implementation, windowing system and
+ * hardware in use, it might be necessary to draw a larger region. Drawing
+ * implementation must use gdk_draw_context_get_frame_region() to query the
+ * region that must be drawn.
+ *
+ * When using GTK+, the widget system automatically places calls to
+ * gdk_draw_context_begin_frame() and gdk_draw_context_end_frame() via the
+ * use of #GskRenderers, so application code does not need to call these
+ * functions explicitly.
+ */
+void
+gdk_draw_context_begin_frame (GdkDrawContext       *context,
+                              const cairo_region_t *region)
+{
+  GdkDrawContextPrivate *priv = gdk_draw_context_get_instance_private (context);
+
+  g_return_if_fail (GDK_IS_DRAW_CONTEXT (context));
+  g_return_if_fail (region != NULL);
+
+  if (GDK_SURFACE_DESTROYED (priv->surface))
+    return;
+
+  if (priv->surface->paint_context != NULL)
+    {
+      if (priv->surface->paint_context == context)
+        {
+          g_critical ("The surface %p is already drawing. You must finish the "
+                      "previous drawing operation with gdk_draw_context_end_frame() first.",
+                      priv->surface);
+        }
+      else
+        {
+          g_critical ("The surface %p is already being drawn by %s %p. "
+                      "You cannot draw s surface wih multiple contexts at the same time.",
+                      priv->surface,
+                      G_OBJECT_TYPE_NAME (priv->surface->paint_context), priv->surface->paint_context);
+        }
+      return;
+    }
+
+  context->frame_region = cairo_region_copy (region);
+  priv->is_drawing = TRUE;
+  priv->surface->paint_context = g_object_ref (context);
+
+  GDK_DRAW_CONTEXT_GET_CLASS (context)->begin_frame (context, context->frame_region);
+}
+
+/**
+ * gdk_draw_context_end_frame:
+ * @context: a #GdkDrawContext
+ *
+ * Ends a drawing operation started with gdk_draw_context_begin_frame()
+ * and makes the drawing available on screen. See that function for more
+ * details about drawing.
+ *
+ * When using a #GdkGLContext, this function may call `glFlush()`
+ * implicitly before returning; it is not recommended to call `glFlush()`
+ * explicitly before calling this function.
+ */
+void
+gdk_draw_context_end_frame (GdkDrawContext *context)
+{
+  GdkDrawContextPrivate *priv = gdk_draw_context_get_instance_private (context);
+
+  g_return_if_fail (GDK_IS_DRAW_CONTEXT (context));
+
+  if (GDK_SURFACE_DESTROYED (priv->surface))
+    return;
+
+  if (priv->surface->paint_context == NULL)
+    {
+      g_critical ("The surface %p has no drawing context. You must call "
+                  "gdk_draw_context_begin_frame() before calling "
+                  "gdk_draw_context_end_frame().", priv->surface);
+      return;
+    }
+  else if (priv->surface->paint_context != context)
+    {
+      g_critical ("The surface %p is not drawn by this context but by %s %p.",
+                  priv->surface, 
+                  G_OBJECT_TYPE_NAME (priv->surface->paint_context), priv->surface->paint_context);
+      return;
+    }
+
+  GDK_DRAW_CONTEXT_GET_CLASS (context)->end_frame (context,
+                                                   context->frame_region,
+                                                   priv->surface->active_update_area);
+
+
+  priv->is_drawing = FALSE;
+  g_clear_pointer (&context->frame_region, cairo_region_destroy);
+  g_clear_object (&priv->surface->paint_context);
+}
+
 /**
  * gdk_draw_context_get_frame_region:
  * @context: a #GdkDrawContext
index 5b542667cdb570b2a57843be34dcc1f8d31a596c..4fdc2898d7fbef930fb8ffa1cff76c0c31c06052 100644 (file)
@@ -38,12 +38,17 @@ GDK_AVAILABLE_IN_ALL
 GType gdk_draw_context_get_type (void) G_GNUC_CONST;
 
 GDK_AVAILABLE_IN_ALL
-GdkDisplay *            gdk_draw_context_get_display              (GdkDrawContext  *context);
+GdkDisplay *            gdk_draw_context_get_display            (GdkDrawContext         *context);
 GDK_AVAILABLE_IN_ALL
-GdkSurface *            gdk_draw_context_get_surface              (GdkDrawContext  *context);
+GdkSurface *            gdk_draw_context_get_surface            (GdkDrawContext         *context);
 
 GDK_AVAILABLE_IN_ALL
-const cairo_region_t *  gdk_draw_context_get_frame_region         (GdkDrawContext  *context);
+void                    gdk_draw_context_begin_frame            (GdkDrawContext         *context,
+                                                                 const cairo_region_t   *region);
+GDK_AVAILABLE_IN_ALL
+void                    gdk_draw_context_end_frame              (GdkDrawContext         *context);
+GDK_AVAILABLE_IN_ALL
+const cairo_region_t *  gdk_draw_context_get_frame_region       (GdkDrawContext         *context);
 
 G_END_DECLS
 
index 242919698f272ccee12a1704728df6f16991e4f9..39f3b9663129154332f99775828d5dca34df747a 100644 (file)
@@ -51,11 +51,6 @@ struct _GdkDrawContextClass
 };
 
 gboolean                gdk_draw_context_is_drawing             (GdkDrawContext         *context);
-void                    gdk_draw_context_begin_frame            (GdkDrawContext         *context,
-                                                                 cairo_region_t         *region);
-void                    gdk_draw_context_end_frame              (GdkDrawContext         *context,
-                                                                 cairo_region_t         *painted,
-                                                                 cairo_region_t         *damage);
 
 void                    gdk_draw_context_surface_resized        (GdkDrawContext         *context);
 
index d60f8e2b4c087a5ca592ec02a1a9e1c38eeff6bb..0b9f93d79546ac730917811eac4342ec56aa30b3 100644 (file)
@@ -301,7 +301,7 @@ gdk_gl_context_real_begin_frame (GdkDrawContext *draw_context,
   shared = gdk_gl_context_get_shared_context (context);
   if (shared)
     {
-      gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (shared), region);
+      GDK_DRAW_CONTEXT_GET_CLASS (GDK_DRAW_CONTEXT (shared))->begin_frame (GDK_DRAW_CONTEXT (shared), region);
       return;
     }
 
@@ -335,7 +335,7 @@ gdk_gl_context_real_end_frame (GdkDrawContext *draw_context,
   shared = gdk_gl_context_get_shared_context (context);
   if (shared)
     {
-      gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (shared), painted, damage);
+      GDK_DRAW_CONTEXT_GET_CLASS (GDK_DRAW_CONTEXT (shared))->end_frame (GDK_DRAW_CONTEXT (shared), painted, damage);
       return;
     }
 
index b4e0d264939a283728e47041cc96daf5fdfcfd4d..f5234bcc33e35d5c63dc8d8a587f16b0322736f8 100644 (file)
@@ -1541,107 +1541,6 @@ gdk_surface_create_vulkan_context (GdkSurface  *surface,
                          NULL);
 }
 
-/**
- * gdk_surface_begin_draw_frame:
- * @surface: a #GdkSurface
- * @context: (allow-none): the context used to draw the frame
- * @region: a Cairo region
- *
- * Indicates that you are beginning the process of redrawing @region
- * on @surface.
- *
- * If @surface is a top level #GdkSurface, backed by a native surface
- * implementation, a backing store (offscreen buffer) large enough to
- * contain @region will be created. The backing store will be initialized
- * with the background color or background surface for @surface. Then, all
- * drawing operations performed on @surface will be diverted to the
- * backing store. When you call gdk_surface_end_frame(), the contents of
- * the backing store will be copied to @surface, making it visible
- * on screen. Only the part of @surface contained in @region will be
- * modified; that is, drawing operations are clipped to @region.
- *
- * The net result of all this is to remove flicker, because the user
- * sees the finished product appear all at once when you call
- * gdk_surface_end_draw_frame(). If you draw to @surface directly without
- * calling gdk_surface_begin_draw_frame(), the user may see flicker
- * as individual drawing operations are performed in sequence.
- *
- * When using GTK+, the widget system automatically places calls to
- * gdk_surface_begin_draw_frame() and gdk_surface_end_draw_frame() around
- * emissions of the `GtkWidget::draw` signal. That is, if you’re
- * drawing the contents of the widget yourself, you can assume that the
- * widget has a cleared background, is already set as the clip region,
- * and already has a backing store. Therefore in most cases, application
- * code in GTK does not need to call gdk_surface_begin_draw_frame()
- * explicitly.
- */
-void
-gdk_surface_begin_draw_frame (GdkSurface           *surface,
-                              GdkDrawContext       *draw_context,
-                              const cairo_region_t *region)
-{
-  g_return_if_fail (GDK_IS_SURFACE (surface));
-  g_return_if_fail (gdk_surface_has_native (surface));
-  g_return_if_fail (gdk_surface_is_toplevel (surface));
-  g_return_if_fail (GDK_IS_DRAW_CONTEXT (draw_context));
-  g_return_if_fail (gdk_draw_context_get_surface (draw_context) == surface);
-  g_return_if_fail (region != NULL);
-
-  if (GDK_SURFACE_DESTROYED (surface))
-    return;
-
-  if (surface->paint_context != NULL)
-    {
-      g_critical ("The surface %p already has a drawing context. You cannot "
-                  "call gdk_surface_begin_draw_frame() without calling "
-                  "gdk_surface_end_draw_frame() first.", surface);
-      return;
-    }
-
-  draw_context->frame_region = cairo_region_copy (region);
-
-  gdk_draw_context_begin_frame (draw_context, draw_context->frame_region);
-
-  surface->paint_context = g_object_ref (draw_context);
-}
-
-/**
- * gdk_surface_end_draw_frame:
- * @surface: a #GdkSurface
- *
- * Indicates that the drawing of the contents of @surface started with
- * gdk_surface_begin_frame() has been completed.
- *
- * It is an error to call this function without a matching
- * gdk_surface_begin_frame() first.
- */
-void
-gdk_surface_end_draw_frame (GdkSurface *surface)
-{
-  GdkDrawContext *paint_context;
-
-  g_return_if_fail (GDK_IS_SURFACE (surface));
-
-  if (GDK_SURFACE_DESTROYED (surface))
-    return;
-
-  if (surface->paint_context == NULL)
-    {
-      g_critical ("The surface %p has no drawing context. You must call "
-                  "gdk_surface_begin_draw_frame() before calling "
-                  "gdk_surface_end_draw_frame().", surface);
-      return;
-    }
-
-  paint_context = g_steal_pointer (&surface->paint_context);
-  gdk_draw_context_end_frame (paint_context,
-                              paint_context->frame_region,
-                              surface->active_update_area);
-
-  g_clear_pointer (&paint_context->frame_region, cairo_region_destroy);
-  g_object_unref (paint_context);
-}
-
 /*< private >
  * gdk_surface_get_current_paint_region:
  * @surface: a #GdkSurface
index c0709d1dda88b9f0dd2a82e7d9d82f415cb24077..ddae4f21289fcac5d33e8621fafb36d278915c23 100644 (file)
@@ -575,13 +575,6 @@ void          gdk_surface_set_geometry_hints (GdkSurface          *surface,
                                               const GdkGeometry  *geometry,
                                               GdkSurfaceHints      geom_mask);
 
-GDK_AVAILABLE_IN_ALL
-void          gdk_surface_begin_draw_frame       (GdkSurface            *surface,
-                                                  GdkDrawContext       *context,
-                                                  const cairo_region_t *region);
-GDK_AVAILABLE_IN_ALL
-void          gdk_surface_end_draw_frame    (GdkSurface            *surface);
-
 GDK_AVAILABLE_IN_ALL
 void          gdk_surface_set_title         (GdkSurface   *surface,
                                              const gchar  *title);
index ef5f0d808f5397e1d978912db5961b64b8a0bf12..dae1de9632e847bb7a4ed4703752c03db207293d 100644 (file)
@@ -707,9 +707,8 @@ gdk_vulkan_context_get_image (GdkVulkanContext *context,
  *
  * Gets the index of the image that is currently being drawn.
  *
- * This function can only be used between gdk_surface_begin_draw_frame() and
- * gdk_surface_end_draw_frame() calls for the toplevel surface that the
- * @context is associated with.
+ * This function can only be used between gdk_cairo_context_begin_frame() and
+ * gdk_draw_context_end_frame() calls.
  *
  * Returns: the index of the images that is being drawn
  */
@@ -731,9 +730,8 @@ gdk_vulkan_context_get_draw_index (GdkVulkanContext *context)
  * Gets the Vulkan semaphore that protects access to the image that is
  * currently being drawn.
  *
- * This function can only be used between gdk_surface_begin_draw_frame() and
- * gdk_surface_end_draw_frame() calls for the toplevel surface that the
- * @context is associated with.
+ * This function can only be used between gdk_cairo_context_begin_frame() and
+ * gdk_draw_context_end_frame() calls.
  *
  * Returns: (transfer none): the VkSemaphore
  */
index e76c1f8eedf046d405c17e9fca25cd256a605fdc..2a11cb53a5beec91864777bb980d88affd34ddbb 100644 (file)
@@ -2577,8 +2577,7 @@ gsk_gl_renderer_render (GskRenderer          *renderer,
                       gdk_surface_get_height (surface) * self->scale_factor
                   };
 
-  gdk_surface_begin_draw_frame (surface,
-                                GDK_DRAW_CONTEXT (self->gl_context),
+  gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (self->gl_context),
                                 update_area);
 
   damage = gdk_draw_context_get_frame_region (GDK_DRAW_CONTEXT (self->gl_context));
@@ -2612,7 +2611,7 @@ gsk_gl_renderer_render (GskRenderer          *renderer,
   gdk_gl_context_make_current (self->gl_context);
   gsk_gl_renderer_clear_tree (self);
 
-  gdk_surface_end_draw_frame (surface);
+  gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (self->gl_context));
 
   g_clear_pointer (&self->render_region, cairo_region_destroy);
 }
index ee2a45d6dd2861245cd3417b77b7d224e561634e..0c118859cc3770f0b08d922108e5ba947262c1ba 100644 (file)
@@ -681,7 +681,7 @@ gsk_broadway_renderer_render (GskRenderer          *self,
                                            gdk_surface_get_width (surface),
                                            gdk_surface_get_height (surface)
                                          });
-  gdk_surface_begin_draw_frame (surface, NULL, whole);
+  gdk_draw_context_begin_frame (NULL, whole);
   cairo_region_destroy (whole);
 
   nodes = g_array_new (FALSE, FALSE, sizeof(guint32));
@@ -691,7 +691,7 @@ gsk_broadway_renderer_render (GskRenderer          *self,
   g_array_unref (nodes);
   g_ptr_array_unref (node_textures);
 
-  gdk_surface_end_draw_frame (surface);
+  gdk_draw_context_end_frame (NULL);
 }
 
 static void
index 22e723fdb92f523b4b40ee1eaadcb3c11cfa60cc..3d37e838cb83fa73f0aee42ec0debc2e565c9612 100644 (file)
@@ -108,11 +108,9 @@ gsk_cairo_renderer_render (GskRenderer          *renderer,
                            const cairo_region_t *region)
 {
   GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer);
-  GdkSurface *surface = gsk_renderer_get_surface (renderer);
   cairo_t *cr;
 
-  gdk_surface_begin_draw_frame (surface,
-                                GDK_DRAW_CONTEXT (self->cairo_context),
+  gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (self->cairo_context),
                                 region);
   cr = gdk_cairo_context_cairo_create (self->cairo_context);
 
@@ -121,6 +119,8 @@ gsk_cairo_renderer_render (GskRenderer          *renderer,
 #ifdef G_ENABLE_DEBUG
   if (GSK_RENDERER_DEBUG_CHECK (renderer, GEOMETRY))
     {
+      GdkSurface *surface = gsk_renderer_get_surface (renderer);
+
       cairo_save (cr);
       cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
       cairo_rectangle (cr,
@@ -136,7 +136,7 @@ gsk_cairo_renderer_render (GskRenderer          *renderer,
 
   cairo_destroy (cr);
 
-  gdk_surface_end_draw_frame (surface);
+  gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (self->cairo_context));
 }
 
 static void
index c80c4abb13848c7bc9ce846adc2a2353c6aadb08..f948027ec3ba01696e37c950598ef02702fbd40c 100644 (file)
@@ -217,15 +217,12 @@ gsk_vulkan_renderer_render (GskRenderer          *renderer,
 {
   GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer);
   GskVulkanRender *render;
-  GdkSurface *surface;
   const cairo_region_t *clip;
 #ifdef G_ENABLE_DEBUG
   GskProfiler *profiler;
   gint64 cpu_time;
 #endif
 
-  surface = gsk_renderer_get_surface (renderer);
-
 #ifdef G_ENABLE_DEBUG
   profiler = gsk_renderer_get_profiler (renderer);
   gsk_profiler_counter_set (profiler, self->profile_counters.fallback_pixels, 0);
@@ -234,9 +231,7 @@ gsk_vulkan_renderer_render (GskRenderer          *renderer,
   gsk_profiler_timer_begin (profiler, self->profile_timers.cpu_time);
 #endif
 
-  gdk_surface_begin_draw_frame (surface,
-                                GDK_DRAW_CONTEXT (self->vulkan),
-                                region);
+  gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (self->vulkan), region);
   render = self->render;
 
   clip = gdk_draw_context_get_frame_region (GDK_DRAW_CONTEXT (self->vulkan));
@@ -257,7 +252,7 @@ gsk_vulkan_renderer_render (GskRenderer          *renderer,
   gsk_profiler_push_samples (profiler);
 #endif
 
-  gdk_surface_end_draw_frame (surface);
+  gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (self->vulkan));
 }
 
 static void