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>
This commit is contained in:
Jonas Dreßler 2021-02-09 12:51:33 +01:00 committed by Marge Bot
parent 461c65c93b
commit c1bfdd74d8
3 changed files with 60 additions and 18 deletions

View File

@ -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());
});
} else {
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);
});
});
}
_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, '']));

View File

@ -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

View File

@ -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);
}