From 757e4b6731f9b430dce13aedd28efdaef55456c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 30 Jul 2020 09:51:06 +0200 Subject: [PATCH] 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 | 292 ++++++++++------------------------------- 1 file changed, 72 insertions(+), 220 deletions(-) diff --git a/src/shell-screenshot.c b/src/shell-screenshot.c index 36ca2fb24..3a1c92cce 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; + int image_width; + int image_height; + float scale; + cairo_surface_t *image; + ClutterPaintFlag paint_flags = CLUTTER_PAINT_FLAG_NONE; + g_autoptr (GError) error = NULL; - if (!clutter_stage_capture (stage, FALSE, - &screenshot_rect, - &captures, - &n_captures)) - return; + 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 (n_captures == 1) - priv->image = cairo_surface_reference (captures[0].image); + 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->image = image; + priv->datetime = g_date_time_new_now_local (); - - for (i = 0; i < n_captures; i++) - cairo_surface_destroy (captures[i].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; } 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)); + do_grab_screenshot (screenshot, + priv->screenshot_area.x, + priv->screenshot_area.y, + priv->screenshot_area.width, + priv->screenshot_area.height, + SHELL_SCREENSHOT_FLAG_NONE); - 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); - - 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)); + do_grab_screenshot (screenshot, + priv->screenshot_area.x, + priv->screenshot_area.y, + 1, + 1, + SHELL_SCREENSHOT_FLAG_NONE); - meta_disable_unredirect_for_display (display); - - g_signal_connect_after (stage, "actors-painted", G_CALLBACK (grab_pixel), result); - - clutter_actor_queue_redraw (stage); + g_task_return_boolean (result, TRUE); } #if G_BYTE_ORDER == G_LITTLE_ENDIAN