[gnome-shell] screenshot: Add API to screenshot stage to a ClutterContent



commit deb614a031b1e0ec3811f537bc7138dd18a02fc3
Author: Ivan Molodetskikh <yalterz gmail com>
Date:   Sat Jan 15 18:22:44 2022 +0300

    screenshot: Add API to screenshot stage to a ClutterContent
    
    As mentioned in the last commit, we'll split up taking screenshots into
    creating a GPU texture first, and later saving that to disk as a PNG.
    
    For individual windows it's already easy to get a ClutterContent with
    the texture using meta_window_actor_paint_to_content (), for the stage
    it's not that easy and involves a few extra steps including X11 specific
    ones.
    
    So introduce a new ShellScreenshot API which does all that and provides
    the caller with a ClutterContent of the stage:
    shell_screenshot_screenshot_stage_to_content ()
    
    Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1954>

 src/shell-screenshot.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/shell-screenshot.h |   8 +++
 2 files changed, 154 insertions(+)
---
diff --git a/src/shell-screenshot.c b/src/shell-screenshot.c
index 34494185a6..ac549297b5 100644
--- a/src/shell-screenshot.c
+++ b/src/shell-screenshot.c
@@ -57,6 +57,8 @@ struct _ShellScreenshotPrivate
   cairo_rectangle_int_t screenshot_area;
 
   gboolean include_frame;
+
+  float scale;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (ShellScreenshot, shell_screenshot, G_TYPE_OBJECT);
@@ -289,6 +291,51 @@ grab_screenshot (ShellScreenshot     *screenshot,
   g_object_unref (task);
 }
 
+static void
+grab_screenshot_content (ShellScreenshot *screenshot,
+                         GTask           *result)
+{
+  ShellScreenshotPrivate *priv = screenshot->priv;
+  MetaDisplay *display;
+  int width, height;
+  cairo_rectangle_int_t screenshot_rect;
+  ClutterStage *stage;
+  int image_width;
+  int image_height;
+  float scale;
+  g_autoptr (GError) error = NULL;
+  g_autoptr (ClutterContent) content = NULL;
+
+  display = shell_global_get_display (priv->global);
+  meta_display_get_size (display, &width, &height);
+  screenshot_rect = (cairo_rectangle_int_t) {
+      .x = 0,
+      .y = 0,
+      .width = width,
+      .height = height,
+  };
+
+  stage = shell_global_get_stage (priv->global);
+
+  clutter_stage_get_capture_final_size (stage, &screenshot_rect,
+                                        &image_width,
+                                        &image_height,
+                                        &scale);
+
+  priv->scale = scale;
+
+  content = clutter_stage_paint_to_content (stage, &screenshot_rect, scale,
+                                            CLUTTER_PAINT_FLAG_NO_CURSORS,
+                                            &error);
+  if (!content)
+    {
+      g_task_return_error (result, g_steal_pointer (&error));
+      return;
+    }
+
+  g_task_return_pointer (result, g_steal_pointer (&content), g_object_unref);
+}
+
 static void
 grab_window_screenshot (ShellScreenshot     *screenshot,
                         ShellScreenshotFlag  flags,
@@ -497,6 +544,105 @@ shell_screenshot_screenshot_finish (ShellScreenshot        *screenshot,
   return finish_screenshot (screenshot, result, area, error);
 }
 
+static void
+screenshot_stage_to_content_on_after_paint (ClutterStage     *stage,
+                                            ClutterStageView *view,
+                                            GTask            *result)
+{
+  ShellScreenshot *screenshot = g_task_get_task_data (result);
+  ShellScreenshotPrivate *priv = screenshot->priv;
+  MetaDisplay *display = shell_global_get_display (priv->global);
+
+  g_signal_handlers_disconnect_by_func (stage,
+                                        screenshot_stage_to_content_on_after_paint,
+                                        result);
+
+  meta_enable_unredirect_for_display (display);
+
+  grab_screenshot_content (screenshot, result);
+}
+
+/**
+ * shell_screenshot_screenshot_stage_to_content:
+ * @screenshot: the #ShellScreenshot
+ * @callback: (scope async): function to call returning success or failure
+ * of the async grabbing
+ * @user_data: the data to pass to callback function
+ *
+ * Takes a screenshot of the whole screen as #ClutterContent.
+ *
+ */
+void
+shell_screenshot_screenshot_stage_to_content (ShellScreenshot     *screenshot,
+                                              GAsyncReadyCallback  callback,
+                                              gpointer             user_data)
+{
+  ShellScreenshotPrivate *priv;
+  GTask *result;
+
+  g_return_if_fail (SHELL_IS_SCREENSHOT (screenshot));
+
+  result = g_task_new (screenshot, NULL, callback, user_data);
+  g_task_set_source_tag (result, shell_screenshot_screenshot_stage_to_content);
+  g_task_set_task_data (result, screenshot, NULL);
+
+  if (meta_is_wayland_compositor ())
+    {
+      grab_screenshot_content (screenshot, result);
+    }
+  else
+    {
+      priv = screenshot->priv;
+
+      MetaDisplay *display = shell_global_get_display (priv->global);
+      ClutterStage *stage = shell_global_get_stage (priv->global);
+
+      meta_disable_unredirect_for_display (display);
+      clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+      g_signal_connect (stage, "after-paint",
+                        G_CALLBACK (screenshot_stage_to_content_on_after_paint),
+                        result);
+    }
+}
+
+/**
+ * shell_screenshot_screenshot_stage_to_content_finish:
+ * @screenshot: the #ShellScreenshot
+ * @result: the #GAsyncResult that was provided to the callback
+ * @scale: (out) (optional): location to store the content scale
+ * @error: #GError for error reporting
+ *
+ * Finish the asynchronous operation started by
+ * shell_screenshot_screenshot_stage_to_content() and obtain its result.
+ *
+ * Returns: (transfer full): the #ClutterContent, or NULL
+ *
+ */
+ClutterContent *
+shell_screenshot_screenshot_stage_to_content_finish (ShellScreenshot  *screenshot,
+                                                     GAsyncResult     *result,
+                                                     float            *scale,
+                                                     GError          **error)
+{
+  ShellScreenshotPrivate *priv = screenshot->priv;
+  ClutterContent *content;
+
+  g_return_val_if_fail (SHELL_IS_SCREENSHOT (screenshot), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+  g_return_val_if_fail (g_async_result_is_tagged (result,
+                                                  shell_screenshot_screenshot_stage_to_content),
+                        FALSE);
+
+  content = g_task_propagate_pointer (G_TASK (result), error);
+  if (!content)
+    return NULL;
+
+  if (scale)
+    *scale = priv->scale;
+
+  return content;
+}
+
 /**
  * shell_screenshot_screenshot_area:
  * @screenshot: the #ShellScreenshot
diff --git a/src/shell-screenshot.h b/src/shell-screenshot.h
index 838e910a7e..a5dc96de89 100644
--- a/src/shell-screenshot.h
+++ b/src/shell-screenshot.h
@@ -50,6 +50,14 @@ gboolean shell_screenshot_screenshot_finish   (ShellScreenshot        *screensho
                                                cairo_rectangle_int_t **area,
                                                GError                **error);
 
+void     shell_screenshot_screenshot_stage_to_content (ShellScreenshot     *screenshot,
+                                                       GAsyncReadyCallback  callback,
+                                                       gpointer             user_data);
+ClutterContent *shell_screenshot_screenshot_stage_to_content_finish (ShellScreenshot  *screenshot,
+                                                                     GAsyncResult     *result,
+                                                                     float            *scale,
+                                                                     GError          **error);
+
 void     shell_screenshot_pick_color        (ShellScreenshot      *screenshot,
                                              int                   x,
                                              int                   y,


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