diff --git a/js/ui/screenshot.js b/js/ui/screenshot.js index db9634364..c50c4a363 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 a7296e566..4af51c698 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 2367d518e..18c6667e1 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,