[gnome-shell] screenshot-ui: Split out captureScreenshot()



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]