[gnome-shell] screenshot: Fix slow audiovisual feedback on when taking screenshot



commit c1bfdd74d8f03975994fedfb47cdcec99c854ae2
Author: Jonas Dreßler <verdre v0yd nl>
Date:   Tue Feb 9 12:51:33 2021 +0100

    screenshot: Fix slow audiovisual feedback on when taking screenshot
    
    Add a "screenshot-taken" signal from the screenshot service's internal C
    implementation, and use that to trigger the camera flash visual effect
    and the click sound, allowing them to run in parallel with the PNG
    compression instead of waiting until the file is complete to start.
    
    This significantly improves perceived latency on high res setups such as
    4K, 5K, or dual 4K screens.
    
    Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/512
    
    Co-authored-by: Brion Vibber <bvibber wikimedia org>
    Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1658>

 js/ui/screenshot.js    | 45 +++++++++++++++++++++++++++++----------------
 po/POTFILES.in         |  1 +
 src/shell-screenshot.c | 30 +++++++++++++++++++++++++++++-
 3 files changed, 59 insertions(+), 17 deletions(-)
---
diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js
index 1c724f0bab..59a6c46aff 100644
--- a/js/ui/screenshot.js
+++ b/js/ui/screenshot.js
@@ -131,16 +131,19 @@ var ScreenshotService = class {
         return [null, null];
     }
 
-    _onScreenshotComplete(area, stream, file, flash, invocation) {
-        if (flash) {
-            let flashspot = new Flashspot(area);
-            flashspot.fire(() => {
-                this._removeShooterForSender(invocation.get_sender());
+    _flashAsync(shooter) {
+        return new Promise((resolve, _reject) => {
+            shooter.connect('screenshot_taken', (s, area) => {
+                const flashspot = new Flashspot(area);
+                flashspot.fire(resolve);
+
+                global.display.get_sound_player().play_from_theme(
+                    'screen-capture', _('Screenshot taken'), null);
             });
-        } else {
-            this._removeShooterForSender(invocation.get_sender());
-        }
+        });
+    }
 
+    _onScreenshotComplete(stream, file, invocation) {
         stream.close(null);
 
         let filenameUsed = '';
@@ -192,9 +195,12 @@ var ScreenshotService = class {
             return;
 
         try {
-            let [area] =
-                await screenshot.screenshot_area(x, y, width, height, stream);
-            this._onScreenshotComplete(area, stream, file, flash, invocation);
+            await Promise.all([
+                flash ? this._flashAsync(screenshot) : null,
+                screenshot.screenshot_area(x, y, width, height, stream),
+            ]);
+            this._onScreenshotComplete(stream, file, invocation);
+            this._removeShooterForSender(invocation.get_sender());
         } catch (e) {
             this._removeShooterForSender(invocation.get_sender());
             invocation.return_value(new GLib.Variant('(bs)', [false, '']));
@@ -212,9 +218,12 @@ var ScreenshotService = class {
             return;
 
         try {
-            let [area] =
-                await screenshot.screenshot_window(includeFrame, includeCursor, stream);
-            this._onScreenshotComplete(area, stream, file, flash, invocation);
+            await Promise.all([
+                flash ? this._flashAsync(screenshot) : null,
+                screenshot.screenshot_window(includeFrame, includeCursor, stream),
+            ]);
+            this._onScreenshotComplete(stream, file, invocation);
+            this._removeShooterForSender(invocation.get_sender());
         } catch (e) {
             this._removeShooterForSender(invocation.get_sender());
             invocation.return_value(new GLib.Variant('(bs)', [false, '']));
@@ -232,8 +241,12 @@ var ScreenshotService = class {
             return;
 
         try {
-            let [area] = await screenshot.screenshot(includeCursor, stream);
-            this._onScreenshotComplete(area, stream, file, flash, invocation);
+            await Promise.all([
+                flash ? this._flashAsync(screenshot) : null,
+                screenshot.screenshot(includeCursor, stream),
+            ]);
+            this._onScreenshotComplete(stream, file, invocation);
+            this._removeShooterForSender(invocation.get_sender());
         } catch (e) {
             this._removeShooterForSender(invocation.get_sender());
             invocation.return_value(new GLib.Variant('(bs)', [false, '']));
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4f83b62cf7..37f6569033 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -47,6 +47,7 @@ js/ui/panel.js
 js/ui/popupMenu.js
 js/ui/runDialog.js
 js/ui/screenShield.js
+js/ui/screenshot.js
 js/ui/search.js
 js/ui/shellEntry.js
 js/ui/shellMountOperation.js
diff --git a/src/shell-screenshot.c b/src/shell-screenshot.c
index 321a82ec7d..100429f217 100644
--- a/src/shell-screenshot.c
+++ b/src/shell-screenshot.c
@@ -25,6 +25,15 @@ typedef enum _ShellScreenshotMode
   SHELL_SCREENSHOT_AREA,
 } ShellScreenshotMode;
 
+enum
+{
+  SCREENSHOT_TAKEN,
+
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
 typedef struct _ShellScreenshotPrivate  ShellScreenshotPrivate;
 
 struct _ShellScreenshot
@@ -55,7 +64,15 @@ G_DEFINE_TYPE_WITH_PRIVATE (ShellScreenshot, shell_screenshot, G_TYPE_OBJECT);
 static void
 shell_screenshot_class_init (ShellScreenshotClass *screenshot_class)
 {
-  (void) screenshot_class;
+  signals[SCREENSHOT_TAKEN] =
+    g_signal_new ("screenshot-taken",
+                  G_TYPE_FROM_CLASS(screenshot_class),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE,
+                  1,
+                  META_TYPE_RECTANGLE);
 }
 
 static void
@@ -321,6 +338,8 @@ grab_window_screenshot (ShellScreenshot     *screenshot,
       draw_cursor_image (priv->image, priv->screenshot_area);
     }
 
+  g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0, &rect);
+
   task = g_task_new (screenshot, NULL, on_screenshot_written, result);
   g_task_run_in_thread (task, write_screenshot_thread);
   g_object_unref (task);
@@ -372,6 +391,9 @@ on_after_paint (ClutterStage     *stage,
       grab_screenshot (screenshot, priv->flags, result);
     }
 
+  g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0,
+                 (cairo_rectangle_int_t *) &priv->screenshot_area);
+
   meta_enable_unredirect_for_display (display);
 }
 
@@ -430,6 +452,9 @@ shell_screenshot_screenshot (ShellScreenshot     *screenshot,
   if (meta_is_wayland_compositor ())
     {
       grab_screenshot (screenshot, flags, result);
+
+      g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0,
+                     (cairo_rectangle_int_t *) &priv->screenshot_area);
     }
   else
     {
@@ -540,6 +565,9 @@ shell_screenshot_screenshot_area (ShellScreenshot     *screenshot,
                           priv->screenshot_area.height,
                           SHELL_SCREENSHOT_FLAG_NONE);
 
+      g_signal_emit (screenshot, signals[SCREENSHOT_TAKEN], 0,
+                     (cairo_rectangle_int_t *) &priv->screenshot_area);
+
       task = g_task_new (screenshot, NULL, on_screenshot_written, result);
       g_task_run_in_thread (task, write_screenshot_thread);
     }


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