[gnome-shell] screenshot-ui: Split out captureScreenshot()
- From: Marge Bot <marge-bot src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell] screenshot-ui: Split out captureScreenshot()
- Date: Sat, 12 Feb 2022 00:26:10 +0000 (UTC)
commit 907377ec400f56cfe236f2a95ec969e563f83f42
Author: Ivan Molodetskikh <yalterz gmail com>
Date: Fri Aug 27 19:17:13 2021 +0300
screenshot-ui: Split out captureScreenshot()
It will be used for the window right-click menu and for handling keys
that are moving here from g-s-d.
Lockdown settings are also moving into the split _storeScreenshot() as
that is the only place where they are used.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2105>
js/ui/screenshot.js | 355 ++++++++++++++++++++++++++++------------------------
1 file changed, 192 insertions(+), 163 deletions(-)
---
diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js
index c846721aa1..1b27900e52 100644
--- a/js/ui/screenshot.js
+++ b/js/ui/screenshot.js
@@ -1,5 +1,5 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-/* exported ScreenshotService, ScreenshotUI, showScreenshotUI */
+/* exported ScreenshotService, ScreenshotUI, showScreenshotUI, captureScreenshot */
const { Clutter, Cogl, Gio, GObject, GLib, Graphene, Gtk, Meta, Shell, St } = imports.gi;
@@ -1681,166 +1681,28 @@ var ScreenshotUI = GObject.registerClass({
}
}
- _storeScreenshot(bytes, pixbuf) {
- // Store to the clipboard first in case storing to file fails.
- const clipboard = St.Clipboard.get_default();
- clipboard.set_content(St.ClipboardType.CLIPBOARD, 'image/png', bytes);
-
- const time = GLib.DateTime.new_now_local();
-
- // This will be set in the first save to disk branch and then accessed
- // in the second save to disk branch, so we need to declare it outside.
- let file;
-
- // The function is declared here rather than inside the condition to
- // satisfy eslint.
-
- /**
- * Returns a filename suffix with an increasingly large index.
- *
- * @returns {Generator<string|*, void, *>} suffix string
- */
- function* suffixes() {
- yield '';
-
- for (let i = 1; ; i++)
- yield '-%s'.format(i);
- }
-
- const disableSaveToDisk =
- this._lockdownSettings.get_boolean('disable-save-to-disk');
-
- if (!disableSaveToDisk) {
- const dir = Gio.File.new_for_path(GLib.build_filenamev([
- GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES),
- // Translators: name of the folder under ~/Pictures for screenshots.
- _('Screenshots'),
- ]));
-
- try {
- dir.make_directory_with_parents(null);
- } catch (e) {
- if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
- throw e;
- }
-
- const timestamp = time.format('%Y-%m-%d %H-%M-%S');
- // Translators: this is the name of the file that the screenshot is
- // saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
- const name = _('Screenshot from %s').format(timestamp);
-
- // If the target file already exists, try appending a suffix with an
- // increasing number to it.
- for (const suffix of suffixes()) {
- file = Gio.File.new_for_path(GLib.build_filenamev([
- dir.get_path(), '%s%s.png'.format(name, suffix),
- ]));
-
- try {
- const stream = file.create(Gio.FileCreateFlags.NONE, null);
- stream.write_bytes(bytes, null);
- break;
- } catch (e) {
- if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
- throw e;
- }
- }
-
- // Add it to recent files.
- Gtk.RecentManager.get_default().add_item(file.get_uri());
- }
-
- // Create a St.ImageContent icon for the notification. We want
- // St.ImageContent specifically because it preserves the aspect ratio when
- // shown in a notification.
- const pixels = pixbuf.read_pixel_bytes();
- const content =
- St.ImageContent.new_with_preferred_size(pixbuf.width, pixbuf.height);
- content.set_bytes(
- pixels,
- Cogl.PixelFormat.RGBA_8888,
- pixbuf.width,
- pixbuf.height,
- pixbuf.rowstride
- );
-
- // Show a notification.
- const source = new MessageTray.Source(
- // Translators: notification source name.
- _('Screenshot'),
- 'applets-screenshooter'
- );
- const notification = new MessageTray.Notification(
- source,
- // Translators: notification title.
- _('Screenshot captured'),
- // Translators: notification body when a screenshot was captured.
- _('You can paste the image from the clipboard.'),
- { datetime: time, gicon: content }
- );
-
- if (!disableSaveToDisk) {
- // Translators: button on the screenshot notification.
- notification.addAction(_('Show in Files'), () => {
- const app =
- Gio.app_info_get_default_for_type('inode/directory', false);
-
- if (app === null) {
- // It may be null e.g. in a toolbox without nautilus.
- log('Error showing in files: no default app set for inode/directory');
- return;
- }
-
- app.launch([file], global.create_app_launch_context(0, -1));
- });
- notification.connect('activated', () => {
- try {
- Gio.app_info_launch_default_for_uri(
- file.get_uri(), global.create_app_launch_context(0, -1));
- } catch (err) {
- logError(err, 'Error opening screenshot');
- }
- });
- }
-
- notification.setTransient(true);
- Main.messageTray.add(source);
- source.showNotification(notification);
- }
-
_saveScreenshot() {
- global.display.get_sound_player().play_from_theme(
- 'screen-capture', _('Screenshot taken'), null);
-
if (this._selectionButton.checked || this._screenButton.checked) {
const content = this._stageScreenshot.get_content();
if (!content)
return; // Failed to capture the screenshot for some reason.
const texture = content.get_texture();
- const stream = Gio.MemoryOutputStream.new_resizable();
-
- const [x, y, w, h] = this._getSelectedGeometry(true);
+ const geometry = this._getSelectedGeometry(true);
let cursorTexture = this._cursor.content?.get_texture();
if (!this._cursor.visible)
cursorTexture = null;
- Shell.Screenshot.composite_to_stream(
- texture,
- x, y, w, h,
- this._scale,
- cursorTexture ?? null,
- this._cursor.x * this._scale,
- this._cursor.y * this._scale,
- this._cursorScale,
- stream
- ).then(pixbuf => {
- stream.close(null);
- this._storeScreenshot(stream.steal_as_bytes(), pixbuf);
- }).catch(err => {
- logError(err, 'Error capturing screenshot');
- });
+ captureScreenshot(
+ texture, geometry, this._scale,
+ {
+ texture: cursorTexture ?? null,
+ x: this._cursor.x * this._scale,
+ y: this._cursor.y * this._scale,
+ scale: this._cursorScale,
+ }
+ );
} else if (this._windowButton.checked) {
const window =
this._windowSelectors.flatMap(selector => selector.windows())
@@ -1853,27 +1715,22 @@ var ScreenshotUI = GObject.registerClass({
return;
const texture = content.get_texture();
- const stream = Gio.MemoryOutputStream.new_resizable();
let cursorTexture = this._cursor.content?.get_texture();
if (!this._cursor.visible)
cursorTexture = null;
- Shell.Screenshot.composite_to_stream(
+ captureScreenshot(
texture,
- 0, 0, -1, -1,
+ null,
window.bufferScale,
- cursorTexture ?? null,
- window.cursorPoint.x * window.bufferScale,
- window.cursorPoint.y * window.bufferScale,
- this._cursorScale,
- stream
- ).then(pixbuf => {
- stream.close(null);
- this._storeScreenshot(stream.steal_as_bytes(), pixbuf);
- }).catch(err => {
- logError(err, 'Error capturing screenshot');
- });
+ {
+ texture: cursorTexture ?? null,
+ x: window.cursorPoint.x * window.bufferScale,
+ y: window.cursorPoint.y * window.bufferScale,
+ scale: this._cursorScale,
+ }
+ );
}
}
@@ -2109,6 +1966,178 @@ var ScreenshotUI = GObject.registerClass({
}
});
+/**
+ * Stores a PNG-encoded screenshot into the clipboard and a file, and shows a
+ * notification.
+ *
+ * @param {GLib.Bytes} bytes - The PNG-encoded screenshot.
+ * @param {GdkPixbuf.Pixbuf} pixbuf - The Pixbuf with the screenshot.
+ */
+function _storeScreenshot(bytes, pixbuf) {
+ // Store to the clipboard first in case storing to file fails.
+ const clipboard = St.Clipboard.get_default();
+ clipboard.set_content(St.ClipboardType.CLIPBOARD, 'image/png', bytes);
+
+ const time = GLib.DateTime.new_now_local();
+
+ // This will be set in the first save to disk branch and then accessed
+ // in the second save to disk branch, so we need to declare it outside.
+ let file;
+
+ // The function is declared here rather than inside the condition to
+ // satisfy eslint.
+
+ /**
+ * Returns a filename suffix with an increasingly large index.
+ *
+ * @returns {Generator<string|*, void, *>} suffix string
+ */
+ function* suffixes() {
+ yield '';
+
+ for (let i = 1; ; i++)
+ yield '-%s'.format(i);
+ }
+
+ const lockdownSettings =
+ new Gio.Settings({ schema_id: 'org.gnome.desktop.lockdown' });
+ const disableSaveToDisk =
+ lockdownSettings.get_boolean('disable-save-to-disk');
+
+ if (!disableSaveToDisk) {
+ const dir = Gio.File.new_for_path(GLib.build_filenamev([
+ GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES),
+ // Translators: name of the folder under ~/Pictures for screenshots.
+ _('Screenshots'),
+ ]));
+
+ try {
+ dir.make_directory_with_parents(null);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
+ throw e;
+ }
+
+ const timestamp = time.format('%Y-%m-%d %H-%M-%S');
+ // Translators: this is the name of the file that the screenshot is
+ // saved to. The placeholder is a timestamp, e.g. "2017-05-21 12-24-03".
+ const name = _('Screenshot from %s').format(timestamp);
+
+ // If the target file already exists, try appending a suffix with an
+ // increasing number to it.
+ for (const suffix of suffixes()) {
+ file = Gio.File.new_for_path(GLib.build_filenamev([
+ dir.get_path(), '%s%s.png'.format(name, suffix),
+ ]));
+
+ try {
+ const stream = file.create(Gio.FileCreateFlags.NONE, null);
+ stream.write_bytes(bytes, null);
+ break;
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
+ throw e;
+ }
+ }
+
+ // Add it to recent files.
+ Gtk.RecentManager.get_default().add_item(file.get_uri());
+ }
+
+ // Create a St.ImageContent icon for the notification. We want
+ // St.ImageContent specifically because it preserves the aspect ratio when
+ // shown in a notification.
+ const pixels = pixbuf.read_pixel_bytes();
+ const content =
+ St.ImageContent.new_with_preferred_size(pixbuf.width, pixbuf.height);
+ content.set_bytes(
+ pixels,
+ Cogl.PixelFormat.RGBA_8888,
+ pixbuf.width,
+ pixbuf.height,
+ pixbuf.rowstride
+ );
+
+ // Show a notification.
+ const source = new MessageTray.Source(
+ // Translators: notification source name.
+ _('Screenshot'),
+ 'applets-screenshooter'
+ );
+ const notification = new MessageTray.Notification(
+ source,
+ // Translators: notification title.
+ _('Screenshot captured'),
+ // Translators: notification body when a screenshot was captured.
+ _('You can paste the image from the clipboard.'),
+ { datetime: time, gicon: content }
+ );
+
+ if (!disableSaveToDisk) {
+ // Translators: button on the screenshot notification.
+ notification.addAction(_('Show in Files'), () => {
+ const app =
+ Gio.app_info_get_default_for_type('inode/directory', false);
+
+ if (app === null) {
+ // It may be null e.g. in a toolbox without nautilus.
+ log('Error showing in files: no default app set for inode/directory');
+ return;
+ }
+
+ app.launch([file], global.create_app_launch_context(0, -1));
+ });
+ notification.connect('activated', () => {
+ try {
+ Gio.app_info_launch_default_for_uri(
+ file.get_uri(), global.create_app_launch_context(0, -1));
+ } catch (err) {
+ logError(err, 'Error opening screenshot');
+ }
+ });
+ }
+
+ notification.setTransient(true);
+ Main.messageTray.add(source);
+ source.showNotification(notification);
+}
+
+/**
+ * Captures a screenshot from a texture, given a region, scale and optional
+ * cursor data.
+ *
+ * @param {Cogl.Texture} texture - The texture to take the screenshot from.
+ * @param {number[4]} [geometry] - The region to use: x, y, width and height.
+ * @param {number} scale - The texture scale.
+ * @param {Object} [cursor] - Cursor data to include in the screenshot.
+ * @param {Cogl.Texture} cursor.texture - The cursor texture.
+ * @param {number} cursor.x - The cursor x coordinate.
+ * @param {number} cursor.y - The cursor y coordinate.
+ * @param {number} cursor.scale - The cursor texture scale.
+ */
+function captureScreenshot(texture, geometry, scale, cursor) {
+ const stream = Gio.MemoryOutputStream.new_resizable();
+ const [x, y, w, h] = geometry ?? [0, 0, -1, -1];
+ if (cursor === null)
+ cursor = { texture: null, x: 0, y: 0, scale: 1 };
+
+ global.display.get_sound_player().play_from_theme(
+ 'screen-capture', _('Screenshot taken'), null);
+
+ Shell.Screenshot.composite_to_stream(
+ texture,
+ x, y, w, h,
+ scale,
+ cursor.texture, cursor.x, cursor.y, cursor.scale,
+ stream
+ ).then(pixbuf => {
+ stream.close(null);
+ _storeScreenshot(stream.steal_as_bytes(), pixbuf);
+ }).catch(err => {
+ logError(err, 'Error capturing screenshot');
+ });
+}
+
/**
* Shows the screenshot UI.
*/
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]