[gnome-shell/wip/carlosg/screenshots-to-clipboard: 1/2] shell: Make screenshot API stream based



commit 163933fc33f6953e548d05ae1226c4609f9386e3
Author: Carlos Garnacho <carlosg gnome org>
Date:   Wed Nov 6 00:46:41 2019 +0100

    shell: Make screenshot API stream based
    
    Instead of dealing with filenames, make the low-level API use streams
    so the target remains generic.
    
    This so far means JS code now determines the appropriate filename to
    use for storing the screenshot, but will be used in other ways in
    future commits.
    
    https://gitlab.gnome.org/GNOME/mutter/issues/789
    https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/810

 js/ui/screenshot.js    |  77 ++++++++++++++++++++++----
 src/shell-screenshot.c | 147 +++++++------------------------------------------
 src/shell-screenshot.h |   9 +--
 3 files changed, 91 insertions(+), 142 deletions(-)
---
diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js
index db96343649..c50c4a3639 100644
--- a/js/ui/screenshot.js
+++ b/js/ui/screenshot.js
@@ -64,7 +64,52 @@ var ScreenshotService = class {
                y + height <= global.screen_height;
     }
 
-    _onScreenshotComplete(result, area, filenameUsed, flash, invocation) {
+    *_resolveRelativeFilename(filename) {
+        if (GLib.str_has_suffix(filename, '.png'))
+            filename = filename.substr(0, -4);
+
+        let path = [
+            GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_PICTURES),
+            GLib.get_home_dir(),
+        ].find(p => GLib.file_test(p, GLib.FileTest.EXISTS));
+
+        if (!path)
+            return null;
+
+        yield Gio.File.new_for_path(
+            GLib.build_filenamev([path, `${filename}.png`]));
+
+        for (let idx = 1; ; idx++) {
+            yield Gio.File.new_for_path(
+                GLib.build_filenamev([path, `${filename}-${idx}.png`]));
+        }
+    }
+
+    _createStream(filename) {
+        if (GLib.path_is_absolute(filename)) {
+            try {
+                let file = Gio.File.new_for_path(filename);
+                let stream = file.replace(null, false, Gio.FileCreateFlags.NONE, null);
+                return [stream, file];
+            } catch (e) {
+                return [null, null];
+            }
+        }
+
+        for (let file of this._resolveRelativeFilename(filename)) {
+            try {
+                let stream = file.create(Gio.FileCreateFlags.NONE, null);
+                return [stream, file];
+            } catch (e) {
+                if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
+                    return [null, null];
+            }
+        }
+
+        return [null, null];
+    }
+
+    _onScreenshotComplete(result, area, stream, file, flash, invocation) {
         if (result) {
             if (flash) {
                 let flashspot = new Flashspot(area);
@@ -76,6 +121,9 @@ var ScreenshotService = class {
             }
         }
 
+        stream.close(null);
+
+        let filenameUsed = file.get_path();
         let retval = GLib.Variant.new('(bs)', [result, filenameUsed]);
         invocation.return_value(retval);
     }
@@ -110,13 +158,16 @@ var ScreenshotService = class {
         let screenshot = this._createScreenshot(invocation);
         if (!screenshot)
             return;
-        screenshot.screenshot_area (x, y, width, height, filename,
+
+        let [stream, file] = this._createStream(filename);
+
+        screenshot.screenshot_area (x, y, width, height, stream,
             (o, res) => {
                 try {
-                    let [result, area, filenameUsed] =
+                    let [result, area] =
                         screenshot.screenshot_area_finish(res);
                     this._onScreenshotComplete(
-                        result, area, filenameUsed, flash, invocation);
+                        result, area, stream, file, flash, invocation);
                 } catch (e) {
                     invocation.return_gerror (e);
                 }
@@ -128,13 +179,16 @@ var ScreenshotService = class {
         let screenshot = this._createScreenshot(invocation);
         if (!screenshot)
             return;
-        screenshot.screenshot_window (includeFrame, includeCursor, filename,
+
+        let [stream, file] = this._createStream(filename);
+
+        screenshot.screenshot_window (includeFrame, includeCursor, stream,
             (o, res) => {
                 try {
-                    let [result, area, filenameUsed] =
+                    let [result, area] =
                         screenshot.screenshot_window_finish(res);
                     this._onScreenshotComplete(
-                        result, area, filenameUsed, flash, invocation);
+                        result, area, stream, file, flash, invocation);
                 } catch (e) {
                     invocation.return_gerror (e);
                 }
@@ -146,13 +200,16 @@ var ScreenshotService = class {
         let screenshot = this._createScreenshot(invocation);
         if (!screenshot)
             return;
-        screenshot.screenshot(includeCursor, filename,
+
+        let [stream, file] = this._createStream(filename);
+
+        screenshot.screenshot(includeCursor, stream,
             (o, res) => {
                 try {
-                    let [result, area, filenameUsed] =
+                    let [result, area] =
                         screenshot.screenshot_finish(res);
                     this._onScreenshotComplete(
-                        result, area, filenameUsed, flash, invocation);
+                        result, area, stream, file, flash, invocation);
                 } catch (e) {
                     invocation.return_gerror (e);
                 }
diff --git a/src/shell-screenshot.c b/src/shell-screenshot.c
index a7296e5664..4af51c698d 100644
--- a/src/shell-screenshot.c
+++ b/src/shell-screenshot.c
@@ -25,8 +25,7 @@ struct _ShellScreenshotPrivate
 {
   ShellGlobal *global;
 
-  char *filename;
-  char *filename_used;
+  GOutputStream *stream;
 
   GDateTime *datetime;
 
@@ -72,102 +71,12 @@ on_screenshot_written (GObject      *source,
   g_object_unref (result);
 
   g_clear_pointer (&priv->image, cairo_surface_destroy);
-  g_clear_pointer (&priv->filename, g_free);
-  g_clear_pointer (&priv->filename_used, g_free);
+  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));
 }
 
-/* called in an I/O thread */
-static GOutputStream *
-get_stream_for_unique_path (const gchar *path,
-                            const gchar *filename,
-                            gchar **filename_used)
-{
-  GOutputStream *stream;
-  GFile *file;
-  gchar *real_path, *real_filename, *name, *ptr;
-  gint idx;
-
-  ptr = g_strrstr (filename, ".png");
-
-  if (ptr != NULL)
-    real_filename = g_strndup (filename, ptr - filename);
-  else
-    real_filename = g_strdup (filename);
-
-  idx = 0;
-  real_path = NULL;
-
-  do
-    {
-      if (idx == 0)
-        name = g_strdup_printf ("%s.png", real_filename);
-      else
-        name = g_strdup_printf ("%s - %d.png", real_filename, idx);
-
-      real_path = g_build_filename (path, name, NULL);
-      g_free (name);
-
-      file = g_file_new_for_path (real_path);
-      stream = G_OUTPUT_STREAM (g_file_create (file, G_FILE_CREATE_NONE, NULL, NULL));
-      g_object_unref (file);
-
-      if (stream != NULL)
-        *filename_used = real_path;
-      else
-        g_free (real_path);
-
-      idx++;
-    }
-  while (stream == NULL);
-
-  g_free (real_filename);
-
-  return stream;
-}
-
-/* called in an I/O thread */
-static GOutputStream *
-get_stream_for_filename (const gchar *filename,
-                         gchar **filename_used)
-{
-  const gchar *path;
-
-  path = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
-  if (!g_file_test (path, G_FILE_TEST_EXISTS))
-    {
-      path = g_get_home_dir ();
-      if (!g_file_test (path, G_FILE_TEST_EXISTS))
-        return NULL;
-    }
-
-  return get_stream_for_unique_path (path, filename, filename_used);
-}
-
-static GOutputStream *
-prepare_write_stream (const gchar *filename,
-                      gchar **filename_used)
-{
-  GOutputStream *stream;
-  GFile *file;
-
-  if (g_path_is_absolute (filename))
-    {
-      file = g_file_new_for_path (filename);
-      *filename_used = g_strdup (filename);
-      stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL));
-      g_object_unref (file);
-    }
-  else
-    {
-      stream = get_stream_for_filename (filename, filename_used);
-    }
-
-  return stream;
-}
-
 static void
 write_screenshot_thread (GTask        *result,
                          gpointer      object,
@@ -184,8 +93,7 @@ write_screenshot_thread (GTask        *result,
 
   priv = screenshot->priv;
 
-  stream = prepare_write_stream (priv->filename,
-                                 &priv->filename_used);
+  stream = g_object_ref (priv->stream);
 
   if (stream == NULL)
     status = CAIRO_STATUS_FILE_NOT_FOUND;
@@ -517,7 +425,6 @@ static gboolean
 finish_screenshot (ShellScreenshot        *screenshot,
                    GAsyncResult           *result,
                    cairo_rectangle_int_t **area,
-                   const char            **filename_used,
                    GError                **error)
 {
   ShellScreenshotPrivate *priv = screenshot->priv;
@@ -528,9 +435,6 @@ finish_screenshot (ShellScreenshot        *screenshot,
   if (area)
     *area = &priv->screenshot_area;
 
-  if (filename_used)
-    *filename_used = priv->filename_used;
-
   return TRUE;
 }
 
@@ -538,19 +442,19 @@ finish_screenshot (ShellScreenshot        *screenshot,
  * shell_screenshot_screenshot:
  * @screenshot: the #ShellScreenshot
  * @include_cursor: Whether to include the cursor or not
- * @filename: The filename for the screenshot
+ * @stream: The stream for the screenshot
  * @callback: (scope async): function to call returning success or failure
  * of the async grabbing
  * @user_data: the data to pass to callback function
  *
  * Takes a screenshot of the whole screen
- * in @filename as png image.
+ * in @stream as png image.
  *
  */
 void
 shell_screenshot_screenshot (ShellScreenshot     *screenshot,
                              gboolean             include_cursor,
-                             const char          *filename,
+                             GOutputStream       *stream,
                              GAsyncReadyCallback  callback,
                              gpointer             user_data)
 {
@@ -559,7 +463,7 @@ shell_screenshot_screenshot (ShellScreenshot     *screenshot,
   const char *paint_signal;
   GTask *result;
 
-  if (priv->filename != NULL) {
+  if (priv->stream != NULL) {
     if (callback)
       g_task_report_new_error (screenshot,
                                callback,
@@ -575,7 +479,7 @@ shell_screenshot_screenshot (ShellScreenshot     *screenshot,
   result = g_task_new (screenshot, NULL, callback, user_data);
   g_task_set_source_tag (result, shell_screenshot_screenshot);
 
-  priv->filename = g_strdup (filename);
+  priv->stream = g_object_ref (stream);
   priv->include_cursor = FALSE;
 
   stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global));
@@ -601,8 +505,6 @@ shell_screenshot_screenshot (ShellScreenshot     *screenshot,
  * @screenshot: the #ShellScreenshot
  * @result: the #GAsyncResult that was provided to the callback
  * @area: (out) (transfer none): the area that was grabbed in screen coordinates
- * @filename_used: (out) (transfer none): the name of the file the screenshot
- * was written to
  * @error: #GError for error reporting
  *
  * Finish the asynchronous operation started by shell_screenshot_screenshot()
@@ -615,13 +517,12 @@ gboolean
 shell_screenshot_screenshot_finish (ShellScreenshot        *screenshot,
                                     GAsyncResult           *result,
                                     cairo_rectangle_int_t **area,
-                                    const char            **filename_used,
                                     GError                **error)
 {
   g_return_val_if_fail (g_async_result_is_tagged (result,
                                                   shell_screenshot_screenshot),
                         FALSE);
-  return finish_screenshot (screenshot, result, area, filename_used, error);
+  return finish_screenshot (screenshot, result, area, error);
 }
 
 /**
@@ -631,13 +532,13 @@ shell_screenshot_screenshot_finish (ShellScreenshot        *screenshot,
  * @y: The Y coordinate of the area
  * @width: The width of the area
  * @height: The height of the area
- * @filename: The filename for the screenshot
+ * @stream: The stream for the screenshot
  * @callback: (scope async): function to call returning success or failure
  * of the async grabbing
  * @user_data: the data to pass to callback function
  *
  * Takes a screenshot of the passed in area and saves it
- * in @filename as png image.
+ * in @stream as png image.
  *
  */
 void
@@ -646,7 +547,7 @@ shell_screenshot_screenshot_area (ShellScreenshot     *screenshot,
                                   int                  y,
                                   int                  width,
                                   int                  height,
-                                  const char          *filename,
+                                  GOutputStream       *stream,
                                   GAsyncReadyCallback  callback,
                                   gpointer             user_data)
 {
@@ -654,7 +555,7 @@ shell_screenshot_screenshot_area (ShellScreenshot     *screenshot,
   ShellScreenshotPrivate *priv = screenshot->priv;
   GTask *result;
 
-  if (priv->filename != NULL) {
+  if (priv->stream != NULL) {
     if (callback)
       g_task_report_new_error (screenshot,
                                callback,
@@ -670,7 +571,7 @@ shell_screenshot_screenshot_area (ShellScreenshot     *screenshot,
   result = g_task_new (screenshot, NULL, callback, user_data);
   g_task_set_source_tag (result, shell_screenshot_screenshot_area);
 
-  priv->filename = g_strdup (filename);
+  priv->stream = g_object_ref (stream);
   priv->screenshot_area.x = x;
   priv->screenshot_area.y = y;
   priv->screenshot_area.width = width;
@@ -690,8 +591,6 @@ shell_screenshot_screenshot_area (ShellScreenshot     *screenshot,
  * @screenshot: the #ShellScreenshot
  * @result: the #GAsyncResult that was provided to the callback
  * @area: (out) (transfer none): the area that was grabbed in screen coordinates
- * @filename_used: (out) (transfer none): the name of the file the screenshot
- * was written to
  * @error: #GError for error reporting
  *
  * Finish the asynchronous operation started by shell_screenshot_screenshot_area()
@@ -704,13 +603,12 @@ gboolean
 shell_screenshot_screenshot_area_finish (ShellScreenshot        *screenshot,
                                          GAsyncResult           *result,
                                          cairo_rectangle_int_t **area,
-                                         const char            **filename_used,
                                          GError                **error)
 {
   g_return_val_if_fail (g_async_result_is_tagged (result,
                                                   shell_screenshot_screenshot_area),
                         FALSE);
-  return finish_screenshot (screenshot, result, area, filename_used, error);
+  return finish_screenshot (screenshot, result, area, error);
 }
 
 /**
@@ -718,20 +616,20 @@ shell_screenshot_screenshot_area_finish (ShellScreenshot        *screenshot,
  * @screenshot: the #ShellScreenshot
  * @include_frame: Whether to include the frame or not
  * @include_cursor: Whether to include the cursor or not
- * @filename: The filename for the screenshot
+ * @stream: The stream for the screenshot
  * @callback: (scope async): function to call returning success or failure
  * of the async grabbing
  * @user_data: the data to pass to callback function
  *
  * Takes a screenshot of the focused window (optionally omitting the frame)
- * in @filename as png image.
+ * in @stream as png image.
  *
  */
 void
 shell_screenshot_screenshot_window (ShellScreenshot     *screenshot,
                                     gboolean             include_frame,
                                     gboolean             include_cursor,
-                                    const char          *filename,
+                                    GOutputStream       *stream,
                                     GAsyncReadyCallback  callback,
                                     gpointer             user_data)
 {
@@ -741,7 +639,7 @@ shell_screenshot_screenshot_window (ShellScreenshot     *screenshot,
   MetaWindow *window = meta_display_get_focus_window (display);
   GTask *result;
 
-  if (priv->filename != NULL || !window) {
+  if (priv->stream != NULL || !window) {
     if (callback)
       g_task_report_new_error (screenshot,
                                callback,
@@ -757,7 +655,7 @@ shell_screenshot_screenshot_window (ShellScreenshot     *screenshot,
   result = g_task_new (screenshot, NULL, callback, user_data);
   g_task_set_source_tag (result, shell_screenshot_screenshot_window);
 
-  priv->filename = g_strdup (filename);
+  priv->stream = g_object_ref (stream);
   priv->include_frame = include_frame;
   priv->include_cursor = include_cursor &&
                          should_draw_cursor_image (SHELL_SCREENSHOT_WINDOW);
@@ -776,8 +674,6 @@ shell_screenshot_screenshot_window (ShellScreenshot     *screenshot,
  * @screenshot: the #ShellScreenshot
  * @result: the #GAsyncResult that was provided to the callback
  * @area: (out) (transfer none): the area that was grabbed in screen coordinates
- * @filename_used: (out) (transfer none): the name of the file the screenshot
- * was written to
  * @error: #GError for error reporting
  *
  * Finish the asynchronous operation started by shell_screenshot_screenshot_window()
@@ -790,13 +686,12 @@ gboolean
 shell_screenshot_screenshot_window_finish (ShellScreenshot        *screenshot,
                                            GAsyncResult           *result,
                                            cairo_rectangle_int_t **area,
-                                           const char            **filename_used,
                                            GError                **error)
 {
   g_return_val_if_fail (g_async_result_is_tagged (result,
                                                   shell_screenshot_screenshot_window),
                         FALSE);
-  return finish_screenshot (screenshot, result, area, filename_used, error);
+  return finish_screenshot (screenshot, result, area, error);
 }
 
 /**
diff --git a/src/shell-screenshot.h b/src/shell-screenshot.h
index 2367d518e3..18c6667e1b 100644
--- a/src/shell-screenshot.h
+++ b/src/shell-screenshot.h
@@ -21,36 +21,33 @@ void    shell_screenshot_screenshot_area      (ShellScreenshot      *screenshot,
                                                int                   y,
                                                int                   width,
                                                int                   height,
-                                               const char           *filename,
+                                               GOutputStream        *stream,
                                                GAsyncReadyCallback   callback,
                                                gpointer              user_data);
 gboolean shell_screenshot_screenshot_area_finish (ShellScreenshot       *screenshot,
                                                   GAsyncResult          *result,
                                                   cairo_rectangle_int_t **area,
-                                                  const char            **filename_used,
                                                   GError                **error);
 
 void    shell_screenshot_screenshot_window    (ShellScreenshot     *screenshot,
                                                gboolean             include_frame,
                                                gboolean             include_cursor,
-                                               const char          *filename,
+                                               GOutputStream       *stream,
                                                GAsyncReadyCallback  callback,
                                                gpointer             user_data);
 gboolean shell_screenshot_screenshot_window_finish (ShellScreenshot        *screenshot,
                                                     GAsyncResult           *result,
                                                     cairo_rectangle_int_t **area,
-                                                    const char            **filename_used,
                                                     GError                **error);
 
 void    shell_screenshot_screenshot           (ShellScreenshot     *screenshot,
                                                gboolean             include_cursor,
-                                               const char          *filename,
+                                               GOutputStream       *stream,
                                                GAsyncReadyCallback  callback,
                                                gpointer             user_data);
 gboolean shell_screenshot_screenshot_finish   (ShellScreenshot        *screenshot,
                                                GAsyncResult           *result,
                                                cairo_rectangle_int_t **area,
-                                               const char            **filename_used,
                                                GError                **error);
 
 void     shell_screenshot_pick_color        (ShellScreenshot      *screenshot,


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