From deb614a031b1e0ec3811f537bc7138dd18a02fc3 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Sat, 15 Jan 2022 18:22:44 +0300 Subject: [PATCH] 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: --- 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 34494185a..ac549297b 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 838e910a7..a5dc96de8 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,