[gnome-shell] shell-screenshot: Change to use clutter_stage_paint_to*() API



commit 757e4b6731f9b430dce13aedd28efdaef55456c9
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Thu Jul 30 09:51:06 2020 +0200

    shell-screenshot: Change to use clutter_stage_paint_to*() API
    
    This eliminates the need to wait for redraws, drawing cursors, and
    stiching together cairo images in case the screenshot covers multiple
    monitors.
    
    All of that is now handled by mutter itself.
    
    https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1383

 src/shell-screenshot.c | 296 +++++++++++++------------------------------------
 1 file changed, 74 insertions(+), 222 deletions(-)
---
diff --git a/src/shell-screenshot.c b/src/shell-screenshot.c
index 36ca2fb24b..3a1c92ccec 100644
--- a/src/shell-screenshot.c
+++ b/src/shell-screenshot.c
@@ -12,6 +12,12 @@
 #include "shell-screenshot.h"
 #include "shell-util.h"
 
+typedef enum _ShellScreenshotFlag
+{
+  SHELL_SCREENSHOT_FLAG_NONE,
+  SHELL_SCREENSHOT_FLAG_INCLUDE_CURSOR,
+} ShellScreenshotFlag;
+
 typedef struct _ShellScreenshotPrivate  ShellScreenshotPrivate;
 
 struct _ShellScreenshot
@@ -32,7 +38,6 @@ struct _ShellScreenshotPrivate
   cairo_surface_t *image;
   cairo_rectangle_int_t screenshot_area;
 
-  gboolean include_cursor;
   gboolean include_frame;
 };
 
@@ -45,10 +50,6 @@ typedef enum
 
 G_DEFINE_TYPE_WITH_PRIVATE (ShellScreenshot, shell_screenshot, G_TYPE_OBJECT);
 
-static void
-grab_screenshot (ClutterActor *stage,
-                 GTask        *result);
-
 static void
 shell_screenshot_class_init (ShellScreenshotClass *screenshot_class)
 {
@@ -77,8 +78,6 @@ on_screenshot_written (GObject      *source,
   g_clear_pointer (&priv->image, cairo_surface_destroy);
   g_clear_object (&priv->stream);
   g_clear_pointer (&priv->datetime, g_date_time_unref);
-
-  meta_enable_unredirect_for_display (shell_global_get_display (priv->global));
 }
 
 static void
@@ -121,63 +120,49 @@ write_screenshot_thread (GTask        *result,
 }
 
 static void
-do_grab_screenshot (ShellScreenshot *screenshot,
-                    ClutterStage    *stage,
-                    int               x,
-                    int               y,
-                    int               width,
-                    int               height)
+do_grab_screenshot (ShellScreenshot     *screenshot,
+                    int                  x,
+                    int                  y,
+                    int                  width,
+                    int                  height,
+                    ShellScreenshotFlag  flags)
 {
   ShellScreenshotPrivate *priv = screenshot->priv;
+  ClutterStage *stage = shell_global_get_stage (priv->global);
   cairo_rectangle_int_t screenshot_rect = { x, y, width, height };
-  ClutterCapture *captures;
-  int n_captures;
-  int i;
-
-  if (!clutter_stage_capture (stage, FALSE,
-                              &screenshot_rect,
-                              &captures,
-                              &n_captures))
-    return;
-
-  if (n_captures == 1)
-    priv->image = cairo_surface_reference (captures[0].image);
+  int image_width;
+  int image_height;
+  float scale;
+  cairo_surface_t *image;
+  ClutterPaintFlag paint_flags = CLUTTER_PAINT_FLAG_NONE;
+  g_autoptr (GError) error = NULL;
+
+  clutter_stage_get_capture_final_size (stage, &screenshot_rect,
+                                        &image_width,
+                                        &image_height,
+                                        &scale);
+  image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                      image_width, image_height);
+
+  if (flags & SHELL_SCREENSHOT_FLAG_INCLUDE_CURSOR)
+    paint_flags |= CLUTTER_PAINT_FLAG_FORCE_CURSORS;
   else
+    paint_flags |= CLUTTER_PAINT_FLAG_NO_CURSORS;
+  if (!clutter_stage_paint_to_buffer (stage, &screenshot_rect, scale,
+                                      cairo_image_surface_get_data (image),
+                                      cairo_image_surface_get_stride (image),
+                                      CLUTTER_CAIRO_FORMAT_ARGB32,
+                                      paint_flags,
+                                      &error))
     {
-      float target_scale;
-
-      clutter_stage_get_capture_final_size (stage, &screenshot_rect,
-                                            &width, &height, &target_scale);
-      priv->image = shell_util_composite_capture_images (captures,
-                                                         n_captures,
-                                                         x, y,
-                                                         width, height,
-                                                         target_scale);
+      cairo_surface_destroy (image);
+      g_warning ("Failed to take screenshot: %s", error->message);
+      return;
     }
 
-  priv->datetime = g_date_time_new_now_local ();
-
-  for (i = 0; i < n_captures; i++)
-    cairo_surface_destroy (captures[i].image);
+  priv->image = image;
 
-  g_free (captures);
-}
-
-static gboolean
-should_draw_cursor_image (ShellScreenshotMode mode)
-{
-  if (mode == SHELL_SCREENSHOT_WINDOW || !meta_is_wayland_compositor ())
-    {
-      StSettings *settings = st_settings_get ();
-      gboolean magnifier_active = FALSE;
-
-      g_object_get (settings, "magnifier-active", &magnifier_active, NULL);
-
-      if (!magnifier_active)
-        return TRUE;
-    }
-
-  return FALSE;
+  priv->datetime = g_date_time_new_now_local ();
 }
 
 static void
@@ -256,116 +241,37 @@ draw_cursor_image (cairo_surface_t       *surface,
 }
 
 static void
-on_paint (ClutterActor        *actor,
-          ClutterPaintContext *paint_context,
-          GTask               *result)
-{
-  grab_screenshot (actor, result);
-}
-
-static void
-on_actors_painted (ClutterActor *actor,
-                   GTask        *result)
-{
-  grab_screenshot (actor, result);
-}
-
-static void
-grab_screenshot (ClutterActor *stage,
-                 GTask        *result)
+grab_screenshot (ShellScreenshot     *screenshot,
+                 ShellScreenshotFlag  flags,
+                 GTask               *result)
 {
+  ShellScreenshotPrivate *priv = screenshot->priv;
   MetaDisplay *display;
   int width, height;
-  ShellScreenshot *screenshot = g_task_get_source_object (result);
-  ShellScreenshotPrivate *priv = screenshot->priv;
   GTask *task;
 
   display = shell_global_get_display (priv->global);
   meta_display_get_size (display, &width, &height);
 
-  do_grab_screenshot (screenshot, CLUTTER_STAGE (stage), 0, 0, width, height);
-
-  if (meta_display_get_n_monitors (display) > 1)
-    {
-      cairo_region_t *screen_region = cairo_region_create ();
-      cairo_region_t *stage_region;
-      MetaRectangle monitor_rect;
-      cairo_rectangle_int_t stage_rect;
-      int i;
-      cairo_t *cr;
-
-      for (i = meta_display_get_n_monitors (display) - 1; i >= 0; i--)
-        {
-          meta_display_get_monitor_geometry (display, i, &monitor_rect);
-          cairo_region_union_rectangle (screen_region,
-                                        (const cairo_rectangle_int_t *) &monitor_rect);
-        }
-
-      stage_rect.x = 0;
-      stage_rect.y = 0;
-      stage_rect.width = width;
-      stage_rect.height = height;
-
-      stage_region = cairo_region_create_rectangle ((const cairo_rectangle_int_t *) &stage_rect);
-      cairo_region_xor (stage_region, screen_region);
-      cairo_region_destroy (screen_region);
-
-      cr = cairo_create (priv->image);
-
-      for (i = 0; i < cairo_region_num_rectangles (stage_region); i++)
-        {
-          cairo_rectangle_int_t rect;
-          cairo_region_get_rectangle (stage_region, i, &rect);
-          cairo_rectangle (cr, (double) rect.x, (double) rect.y, (double) rect.width, (double) rect.height);
-          cairo_fill (cr);
-        }
-
-      cairo_destroy (cr);
-      cairo_region_destroy (stage_region);
-    }
+  do_grab_screenshot (screenshot,
+                      0, 0, width, height,
+                      flags);
 
   priv->screenshot_area.x = 0;
   priv->screenshot_area.y = 0;
   priv->screenshot_area.width = width;
   priv->screenshot_area.height = height;
 
-  if (priv->include_cursor)
-    draw_cursor_image (priv->image, priv->screenshot_area);
-
-  g_signal_handlers_disconnect_by_func (stage, on_paint, result);
-  g_signal_handlers_disconnect_by_func (stage, on_actors_painted, result);
-
   task = g_task_new (screenshot, NULL, on_screenshot_written, result);
   g_task_run_in_thread (task, write_screenshot_thread);
   g_object_unref (task);
 }
 
 static void
-grab_area_screenshot (ClutterActor *stage,
-                      GTask        *result)
+grab_window_screenshot (ShellScreenshot     *screenshot,
+                        ShellScreenshotFlag  flags,
+                        GTask               *result)
 {
-  ShellScreenshot *screenshot = g_task_get_source_object (result);
-  ShellScreenshotPrivate *priv = screenshot->priv;
-  GTask *task;
-
-  do_grab_screenshot (screenshot,
-                      CLUTTER_STAGE (stage),
-                      priv->screenshot_area.x,
-                      priv->screenshot_area.y,
-                      priv->screenshot_area.width,
-                      priv->screenshot_area.height);
-
-  g_signal_handlers_disconnect_by_func (stage, grab_area_screenshot, result);
-  task = g_task_new (screenshot, NULL, on_screenshot_written, result);
-  g_task_run_in_thread (task, write_screenshot_thread);
-  g_object_unref (task);
-}
-
-static void
-grab_window_screenshot (ClutterActor *stage,
-                        GTask        *result)
-{
-  ShellScreenshot *screenshot = g_task_get_source_object (result);
   ShellScreenshotPrivate *priv = screenshot->priv;
   GTask *task;
   MetaDisplay *display = shell_global_get_display (priv->global);
@@ -388,7 +294,7 @@ grab_window_screenshot (ClutterActor *stage,
                                              NULL);
   priv->datetime = g_date_time_new_now_local ();
 
-  if (priv->include_cursor)
+  if (flags & SHELL_SCREENSHOT_FLAG_INCLUDE_CURSOR)
     {
       if (meta_window_get_client_type (window) == META_WINDOW_CLIENT_TYPE_WAYLAND)
         {
@@ -401,33 +307,11 @@ grab_window_screenshot (ClutterActor *stage,
       draw_cursor_image (priv->image, priv->screenshot_area);
     }
 
-  g_signal_handlers_disconnect_by_func (stage, grab_window_screenshot, result);
   task = g_task_new (screenshot, NULL, on_screenshot_written, result);
   g_task_run_in_thread (task, write_screenshot_thread);
   g_object_unref (task);
 }
 
-static void
-grab_pixel (ClutterActor *stage,
-            GTask        *result)
-{
-  ShellScreenshot *screenshot = g_task_get_source_object (result);
-  ShellScreenshotPrivate *priv = screenshot->priv;
-
-  do_grab_screenshot (screenshot,
-                      CLUTTER_STAGE (stage),
-                      priv->screenshot_area.x,
-                      priv->screenshot_area.y,
-                      1,
-                      1);
-
-  meta_enable_unredirect_for_display (shell_global_get_display (priv->global));
-
-  g_signal_handlers_disconnect_by_func (stage, grab_pixel, result);
-  g_task_return_boolean (result, TRUE);
-  g_object_unref (result);
-}
-
 static gboolean
 finish_screenshot (ShellScreenshot        *screenshot,
                    GAsyncResult           *result,
@@ -465,10 +349,9 @@ shell_screenshot_screenshot (ShellScreenshot     *screenshot,
                              GAsyncReadyCallback  callback,
                              gpointer             user_data)
 {
-  ClutterActor *stage;
   ShellScreenshotPrivate *priv;
-  gboolean use_paint_signal = FALSE;
   GTask *result;
+  ShellScreenshotFlag flags;
 
   g_return_if_fail (SHELL_IS_SCREENSHOT (screenshot));
   g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
@@ -492,34 +375,12 @@ shell_screenshot_screenshot (ShellScreenshot     *screenshot,
   g_task_set_source_tag (result, shell_screenshot_screenshot);
 
   priv->stream = g_object_ref (stream);
-  priv->include_cursor = FALSE;
-
-  stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global));
-
-  meta_disable_unredirect_for_display (shell_global_get_display (priv->global));
 
+  flags = SHELL_SCREENSHOT_FLAG_NONE;
   if (include_cursor)
-    {
-      if (should_draw_cursor_image (SHELL_SCREENSHOT_SCREEN))
-        priv->include_cursor = TRUE;
-      else
-        use_paint_signal = TRUE;
-    }
+    flags |= SHELL_SCREENSHOT_FLAG_INCLUDE_CURSOR;
 
-  if (use_paint_signal)
-    {
-      g_signal_connect_after (stage, "paint",
-                              G_CALLBACK (on_paint),
-                              result);
-    }
-  else
-    {
-      g_signal_connect_after (stage, "actors-painted",
-                              G_CALLBACK (on_actors_painted),
-                              result);
-    }
-
-  clutter_actor_queue_redraw (stage);
+  grab_screenshot (screenshot, flags, result);
 }
 
 /**
@@ -575,9 +436,9 @@ shell_screenshot_screenshot_area (ShellScreenshot     *screenshot,
                                   GAsyncReadyCallback  callback,
                                   gpointer             user_data)
 {
-  ClutterActor *stage;
   ShellScreenshotPrivate *priv;
   GTask *result;
+  g_autoptr (GTask) task = NULL;
 
   g_return_if_fail (SHELL_IS_SCREENSHOT (screenshot));
   g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
@@ -606,13 +467,15 @@ shell_screenshot_screenshot_area (ShellScreenshot     *screenshot,
   priv->screenshot_area.width = width;
   priv->screenshot_area.height = height;
 
-  stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global));
-
-  meta_disable_unredirect_for_display (shell_global_get_display (shell_global_get ()));
-
-  g_signal_connect_after (stage, "actors-painted", G_CALLBACK (grab_area_screenshot), result);
+  do_grab_screenshot (screenshot,
+                      priv->screenshot_area.x,
+                      priv->screenshot_area.y,
+                      priv->screenshot_area.width,
+                      priv->screenshot_area.height,
+                      SHELL_SCREENSHOT_FLAG_NONE);
 
-  clutter_actor_queue_redraw (stage);
+  task = g_task_new (screenshot, NULL, on_screenshot_written, result);
+  g_task_run_in_thread (task, write_screenshot_thread);
 }
 
 /**
@@ -666,7 +529,6 @@ shell_screenshot_screenshot_window (ShellScreenshot     *screenshot,
 {
   ShellScreenshotPrivate *priv;
   MetaDisplay *display;
-  ClutterActor *stage;
   MetaWindow *window;
   GTask *result;
 
@@ -695,16 +557,8 @@ shell_screenshot_screenshot_window (ShellScreenshot     *screenshot,
 
   priv->stream = g_object_ref (stream);
   priv->include_frame = include_frame;
-  priv->include_cursor = include_cursor &&
-                         should_draw_cursor_image (SHELL_SCREENSHOT_WINDOW);
-
-  stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global));
 
-  meta_disable_unredirect_for_display (shell_global_get_display (shell_global_get ()));
-
-  g_signal_connect_after (stage, "actors-painted", G_CALLBACK (grab_window_screenshot), result);
-
-  clutter_actor_queue_redraw (stage);
+  grab_window_screenshot (screenshot, include_cursor, result);
 }
 
 /**
@@ -753,9 +607,7 @@ shell_screenshot_pick_color (ShellScreenshot     *screenshot,
                              gpointer             user_data)
 {
   ShellScreenshotPrivate *priv;
-  MetaDisplay *display;
-  ClutterActor *stage;
-  GTask *result;
+  g_autoptr (GTask) result = NULL;
 
   g_return_if_fail (SHELL_IS_SCREENSHOT (screenshot));
 
@@ -769,14 +621,14 @@ shell_screenshot_pick_color (ShellScreenshot     *screenshot,
   priv->screenshot_area.width = 1;
   priv->screenshot_area.height = 1;
 
-  display = shell_global_get_display (priv->global);
-  stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global));
-
-  meta_disable_unredirect_for_display (display);
-
-  g_signal_connect_after (stage, "actors-painted", G_CALLBACK (grab_pixel), result);
+  do_grab_screenshot (screenshot,
+                      priv->screenshot_area.x,
+                      priv->screenshot_area.y,
+                      1,
+                      1,
+                      SHELL_SCREENSHOT_FLAG_NONE);
 
-  clutter_actor_queue_redraw (stage);
+  g_task_return_boolean (result, TRUE);
 }
 
 #if G_BYTE_ORDER == G_LITTLE_ENDIAN


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