Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 192 additions & 43 deletions mate-screenshot/src/screenshot-save.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@

#include "screenshot-save.h"

typedef struct
{
GdkPixbuf *pixbuf;
GFileOutputStream *stream;
SaveFunction callback;
gpointer user_data;
}
SaveAsyncData;

static char *parent_dir = NULL;
static char *tmp_filename = NULL;

Expand All @@ -55,38 +64,199 @@ clean_up_temporary_dir (void)
parent_dir = NULL;
}

static char *
make_temp_directory (void)
static void
cleanup_handler (void)
{
gchar *dir_name;
clean_up_temporary_dir ();
}

static void
save_async_finish_and_free (SaveAsyncData *data, GError *err)
{
if (err)
{
GtkWidget *dialog;

// mkdtemp uses XXXXXX as a template to create a unique name for the temp dir
dir_name = g_build_filename (g_get_tmp_dir (),
"mate-screenshot.XXXXXX",
NULL);
dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
"Unable to save the screenshot to disk:\n\n%s",
err->message);
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);

clean_up_temporary_dir ();
}

if (data->callback)
data->callback (data->user_data);

if (data->stream)
g_clear_object (&data->stream);

g_clear_object (&data->pixbuf);
g_free (data);
}

static void
stream_close_async_ready (GObject* object, GAsyncResult* res, gpointer user_data)
{
SaveAsyncData *data = user_data;
GError *err = NULL;

if (mkdtemp (dir_name) == NULL)
if (! g_output_stream_close_finish (G_OUTPUT_STREAM(object), res, &err))
{
g_warning ("Failed to create temporary directory: %s", g_strerror (errno));
g_free (dir_name);
return NULL;
save_async_finish_and_free (data, err);
g_error_free (err);
}
else
{
/* all done! */
save_async_finish_and_free (data, NULL);
}
}

static void
save_to_stream_async_ready (GObject* object, GAsyncResult* res, gpointer user_data)
{
SaveAsyncData *data = user_data;
GError *err = NULL;

return dir_name;
if (! gdk_pixbuf_save_to_stream_finish (res, &err))
{
save_async_finish_and_free (data, err);
g_error_free (err);
}
else
{
g_output_stream_close_async (G_OUTPUT_STREAM (data->stream), G_PRIORITY_DEFAULT,
NULL, stream_close_async_ready, data);
}
}

static void
cleanup_handler (void)
replace_async_ready (GObject* object, GAsyncResult* res, gpointer user_data)
{
clean_up_temporary_dir ();
SaveAsyncData *data = user_data;
GError *err = NULL;

data->stream = g_file_replace_finish (G_FILE(object), res, &err);
if (! data->stream)
{
save_async_finish_and_free (data, err);
g_error_free (err);
}
else
{
gdk_pixbuf_save_to_stream_async (data->pixbuf, G_OUTPUT_STREAM (data->stream),
"png", NULL, save_to_stream_async_ready, data,
"tEXt::Software", "mate-screenshot",
NULL);
}
}

#if ! GLIB_CHECK_VERSION (2, 74, 0)
/* poor man's emulation of the temp dir async API -- it's not actually async */
static void
file_new_tmp_dir_async (const char *tmpl,
int io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GError *error = NULL;
gchar *path;
gpointer task_data[2];

path = g_dir_make_tmp (tmpl, &error);
task_data[0] = path;
task_data[1] = error;
callback (NULL, (GAsyncResult *) task_data, user_data);
if (path)
g_free (path);
/* error is freed in finish() */
}

static GFile *
file_new_tmp_dir_finish (GAsyncResult *result,
GError **error)
{
gpointer *task_data = (gpointer *) result;

if (task_data[0])
return g_file_new_for_path (task_data[0]);

g_propagate_error (error, task_data[1]);

return NULL;
}

#define g_file_new_tmp_dir_async file_new_tmp_dir_async
#define g_file_new_tmp_dir_finish file_new_tmp_dir_finish
#endif

/* gets a #GFile path, setting @err if there is none */
static gchar *
get_path (GFile *file, GError **err)
{
gchar *path;

path = g_file_get_path (file);
if (! path)
{
gchar *uri = g_file_get_uri (file);

g_set_error (err, G_FILE_ERROR, G_FILE_ERROR_NOENT,
_("Temporary file \"%s\" does not have a path"), uri);
g_free (uri);
}

return path;
}

static void
new_tmp_dir_async_ready (GObject* object, GAsyncResult* res, gpointer user_data)
{
SaveAsyncData *data = user_data;
GError *err = NULL;

GFile *dir = g_file_new_tmp_dir_finish (res, &err);
if (dir)
{
g_free (parent_dir);
parent_dir = get_path (dir, &err);
if (parent_dir)
{
GFile *file = g_file_get_child (dir, _("Screenshot.png"));

g_free (tmp_filename);
tmp_filename = get_path (file, &err);
if (tmp_filename)
{
g_file_replace_async (file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION,
G_PRIORITY_DEFAULT, NULL, replace_async_ready, data);
}

g_object_unref (file);
}

g_object_unref (dir);
}

/* error will be set if anything failed, so we didn't schedule the next
* asynchronous call */
if (err)
{
save_async_finish_and_free (data, err);
g_error_free (err);
}
}

void
screenshot_save_start (GdkPixbuf *pixbuf,
SaveFunction callback,
gpointer user_data)
{
GError *error = NULL;
SaveAsyncData *data;

static gboolean cleanup_registered = FALSE;
if (!cleanup_registered)
Expand All @@ -97,35 +267,14 @@ screenshot_save_start (GdkPixbuf *pixbuf,

clean_up_temporary_dir ();

parent_dir = make_temp_directory ();
if (parent_dir == NULL)
{
if (callback)
callback (user_data);
return;
}

tmp_filename = g_build_filename (parent_dir,
_("Screenshot.png"),
NULL);

if (! gdk_pixbuf_save (pixbuf, tmp_filename,
"png", &error,
"tEXt::Software", "mate-screenshot",
NULL))
{
if (error && error->message)
g_warning ("Failed to save screenshot: %s", error->message);
else
g_warning ("Failed to save screenshot: Unknown error");

g_error_free (error);

clean_up_temporary_dir ();
}
data = g_malloc (sizeof *data);
data->pixbuf = g_object_ref (pixbuf);
data->stream = NULL;
data->callback = callback;
data->user_data = user_data;

if (callback)
callback (user_data);
g_file_new_tmp_dir_async ("mate-screenshot.XXXXXX", G_PRIORITY_DEFAULT, NULL,
new_tmp_dir_async_ready, data);
}

const char *
Expand Down