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
This commit is contained in:
parent
a58bdbfbd4
commit
be5f5ec9d4
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user