screenshot: Adopt GIO's async pattern

A custom callback type is more convenient, but only as long as no
other callback type is required. We are about to add functionality
that does not return the filename to a screenshot saved on disk, so
prepare for that by moving to GIO's generic async callback pattern.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/286
This commit is contained in:
Florian Müllner 2018-07-19 15:54:33 +02:00 committed by Florian Müllner
parent 304c667bca
commit da537cda43
3 changed files with 253 additions and 82 deletions

View File

@ -110,7 +110,7 @@ var ScreenshotService = new Lang.Class({
y + height <= global.screen_height; y + height <= global.screen_height;
}, },
_onScreenshotComplete(obj, result, area, filenameUsed, flash, invocation) { _onScreenshotComplete(result, area, filenameUsed, flash, invocation) {
if (result) { if (result) {
if (flash) { if (flash) {
let flashspot = new Flashspot(area); let flashspot = new Flashspot(area);
@ -157,9 +157,15 @@ var ScreenshotService = new Lang.Class({
if (!screenshot) if (!screenshot)
return; return;
screenshot.screenshot_area (x, y, width, height, filename, screenshot.screenshot_area (x, y, width, height, filename,
(obj, result, area, filenameUsed) => { (o, res) => {
this._onScreenshotComplete(obj, result, area, filenameUsed, try {
let [result, area, filenameUsed] =
screenshot.screenshot_area_finish(res);
this._onScreenshotComplete(result, area, filenameUsed,
flash, invocation); flash, invocation);
} catch (e) {
invocation.return_gerror (e);
}
}); });
}, },
@ -169,9 +175,15 @@ var ScreenshotService = new Lang.Class({
if (!screenshot) if (!screenshot)
return; return;
screenshot.screenshot_window (include_frame, include_cursor, filename, screenshot.screenshot_window (include_frame, include_cursor, filename,
(obj, result, area, filenameUsed) => { (o, res) => {
this._onScreenshotComplete(obj, result, area, filenameUsed, try {
let [result, area, filenameUsed] =
screenshot.screenshot_window_finish(res);
this._onScreenshotComplete(result, area, filenameUsed,
flash, invocation); flash, invocation);
} catch (e) {
invocation.return_gerror (e);
}
}); });
}, },
@ -181,9 +193,15 @@ var ScreenshotService = new Lang.Class({
if (!screenshot) if (!screenshot)
return; return;
screenshot.screenshot(include_cursor, filename, screenshot.screenshot(include_cursor, filename,
(obj, result, area, filenameUsed) => { (o, res) => {
this._onScreenshotComplete(obj, result, area, filenameUsed, try {
let [result, area, filenameUsed] =
screenshot.screenshot_finish(res);
this._onScreenshotComplete(result, area, filenameUsed,
flash, invocation); flash, invocation);
} catch (e) {
invocation.return_gerror (e);
}
}); });
}, },

View File

@ -38,8 +38,6 @@ struct _ShellScreenshotPrivate
gboolean include_cursor; gboolean include_cursor;
gboolean include_frame; gboolean include_frame;
ShellScreenshotCallback callback;
}; };
G_DEFINE_TYPE_WITH_PRIVATE (ShellScreenshot, shell_screenshot, G_TYPE_OBJECT); G_DEFINE_TYPE_WITH_PRIVATE (ShellScreenshot, shell_screenshot, G_TYPE_OBJECT);
@ -59,17 +57,15 @@ shell_screenshot_init (ShellScreenshot *screenshot)
static void static void
on_screenshot_written (GObject *source, on_screenshot_written (GObject *source,
GAsyncResult *result, GAsyncResult *task,
gpointer user_data) gpointer user_data)
{ {
ShellScreenshot *screenshot = SHELL_SCREENSHOT (source); ShellScreenshot *screenshot = SHELL_SCREENSHOT (source);
ShellScreenshotPrivate *priv = screenshot->priv; ShellScreenshotPrivate *priv = screenshot->priv;
GTask *result = user_data;
if (priv->callback) g_task_return_boolean (result, g_task_propagate_boolean (G_TASK (task), NULL));
priv->callback (screenshot, g_object_unref (result);
g_task_propagate_boolean (G_TASK (result), NULL),
&priv->screenshot_area,
priv->filename_used);
g_clear_pointer (&priv->image, cairo_surface_destroy); g_clear_pointer (&priv->image, cairo_surface_destroy);
g_clear_pointer (&priv->filename, g_free); g_clear_pointer (&priv->filename, g_free);
@ -313,14 +309,15 @@ _draw_cursor_image (MetaCursorTracker *tracker,
static void static void
grab_screenshot (ClutterActor *stage, grab_screenshot (ClutterActor *stage,
ShellScreenshot *screenshot) GTask *result)
{ {
MetaDisplay *display; MetaDisplay *display;
MetaCursorTracker *tracker; MetaCursorTracker *tracker;
int width, height; int width, height;
GTask *result;
GSettings *settings; GSettings *settings;
ShellScreenshot *screenshot = g_task_get_source_object (result);
ShellScreenshotPrivate *priv = screenshot->priv; ShellScreenshotPrivate *priv = screenshot->priv;
GTask *task;
display = shell_global_get_display (priv->global); display = shell_global_get_display (priv->global);
meta_display_get_size (display, &width, &height); meta_display_get_size (display, &width, &height);
@ -380,19 +377,20 @@ grab_screenshot (ClutterActor *stage,
} }
g_object_unref (settings); g_object_unref (settings);
g_signal_handlers_disconnect_by_func (stage, (void *)grab_screenshot, (gpointer)screenshot); g_signal_handlers_disconnect_by_func (stage, grab_screenshot, result);
result = g_task_new (screenshot, NULL, on_screenshot_written, NULL); task = g_task_new (screenshot, NULL, on_screenshot_written, result);
g_task_run_in_thread (result, write_screenshot_thread); g_task_run_in_thread (task, write_screenshot_thread);
g_object_unref (result); g_object_unref (task);
} }
static void static void
grab_area_screenshot (ClutterActor *stage, grab_area_screenshot (ClutterActor *stage,
ShellScreenshot *screenshot) GTask *result)
{ {
GTask *result; ShellScreenshot *screenshot = g_task_get_source_object (result);
ShellScreenshotPrivate *priv = screenshot->priv; ShellScreenshotPrivate *priv = screenshot->priv;
GTask *task;
do_grab_screenshot (screenshot, do_grab_screenshot (screenshot,
CLUTTER_STAGE (stage), CLUTTER_STAGE (stage),
@ -401,18 +399,19 @@ grab_area_screenshot (ClutterActor *stage,
priv->screenshot_area.width, priv->screenshot_area.width,
priv->screenshot_area.height); priv->screenshot_area.height);
g_signal_handlers_disconnect_by_func (stage, (void *)grab_area_screenshot, (gpointer)screenshot); g_signal_handlers_disconnect_by_func (stage, grab_area_screenshot, result);
result = g_task_new (screenshot, NULL, on_screenshot_written, NULL); task = g_task_new (screenshot, NULL, on_screenshot_written, result);
g_task_run_in_thread (result, write_screenshot_thread); g_task_run_in_thread (task, write_screenshot_thread);
g_object_unref (result); g_object_unref (task);
} }
static void static void
grab_window_screenshot (ClutterActor *stage, grab_window_screenshot (ClutterActor *stage,
ShellScreenshot *screenshot) GTask *result)
{ {
ShellScreenshot *screenshot = g_task_get_source_object (result);
ShellScreenshotPrivate *priv = screenshot->priv; ShellScreenshotPrivate *priv = screenshot->priv;
GTask *result; GTask *task;
GSettings *settings; GSettings *settings;
MetaDisplay *display = shell_global_get_display (priv->global); MetaDisplay *display = shell_global_get_display (priv->global);
MetaCursorTracker *tracker; MetaCursorTracker *tracker;
@ -451,10 +450,31 @@ grab_window_screenshot (ClutterActor *stage,
} }
g_object_unref (settings); g_object_unref (settings);
g_signal_handlers_disconnect_by_func (stage, (void *)grab_window_screenshot, (gpointer)screenshot); g_signal_handlers_disconnect_by_func (stage, grab_window_screenshot, result);
result = g_task_new (screenshot, NULL, on_screenshot_written, NULL); task = g_task_new (screenshot, NULL, on_screenshot_written, result);
g_task_run_in_thread (result, write_screenshot_thread); g_task_run_in_thread (task, write_screenshot_thread);
g_object_unref (result); g_object_unref (task);
}
static gboolean
finish_screenshot (ShellScreenshot *screenshot,
GAsyncResult *result,
cairo_rectangle_int_t **area,
const char **filename_used,
GError **error)
{
ShellScreenshotPrivate *priv = screenshot->priv;
if (!g_task_propagate_boolean (G_TASK (result), error))
return FALSE;
if (area)
*area = &priv->screenshot_area;
if (filename_used)
*filename_used = priv->filename_used;
return TRUE;
} }
/** /**
@ -464,6 +484,7 @@ grab_window_screenshot (ClutterActor *stage,
* @filename: The filename for the screenshot * @filename: The filename for the screenshot
* @callback: (scope async): function to call returning success or failure * @callback: (scope async): function to call returning success or failure
* of the async grabbing * of the async grabbing
* @user_data: the data to pass to callback function
* *
* Takes a screenshot of the whole screen * Takes a screenshot of the whole screen
* in @filename as png image. * in @filename as png image.
@ -473,30 +494,69 @@ void
shell_screenshot_screenshot (ShellScreenshot *screenshot, shell_screenshot_screenshot (ShellScreenshot *screenshot,
gboolean include_cursor, gboolean include_cursor,
const char *filename, const char *filename,
ShellScreenshotCallback callback) GAsyncReadyCallback callback,
gpointer user_data)
{ {
ClutterActor *stage; ClutterActor *stage;
ShellScreenshotPrivate *priv = screenshot->priv; ShellScreenshotPrivate *priv = screenshot->priv;
GTask *result;
if (priv->filename != NULL) { if (priv->filename != NULL) {
if (callback) if (callback)
callback (screenshot, FALSE, NULL, ""); g_task_report_new_error (screenshot,
callback,
user_data,
shell_screenshot_screenshot,
G_IO_ERROR,
G_IO_ERROR_PENDING,
"Only one screenshot operation at a time "
"is permitted");
return; return;
} }
result = g_task_new (screenshot, NULL, callback, user_data);
g_task_set_source_tag (result, shell_screenshot_screenshot);
priv->filename = g_strdup (filename); priv->filename = g_strdup (filename);
priv->callback = callback;
priv->include_cursor = include_cursor; priv->include_cursor = include_cursor;
stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global)); stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global));
meta_disable_unredirect_for_display (shell_global_get_display (priv->global)); meta_disable_unredirect_for_display (shell_global_get_display (priv->global));
g_signal_connect_after (stage, "paint", G_CALLBACK (grab_screenshot), (gpointer)screenshot); g_signal_connect_after (stage, "paint", G_CALLBACK (grab_screenshot), result);
clutter_actor_queue_redraw (stage); clutter_actor_queue_redraw (stage);
} }
/**
* shell_screenshot_screenshot_finish:
* @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()
* and obtain its result.
*
* Returns: whether the operation was successful
*
*/
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);
}
/** /**
* shell_screenshot_screenshot_area: * shell_screenshot_screenshot_area:
* @screenshot: the #ShellScreenshot * @screenshot: the #ShellScreenshot
@ -507,6 +567,7 @@ shell_screenshot_screenshot (ShellScreenshot *screenshot,
* @filename: The filename for the screenshot * @filename: The filename for the screenshot
* @callback: (scope async): function to call returning success or failure * @callback: (scope async): function to call returning success or failure
* of the async grabbing * of the async grabbing
* @user_data: the data to pass to callback function
* *
* Takes a screenshot of the passed in area and saves it * Takes a screenshot of the passed in area and saves it
* in @filename as png image. * in @filename as png image.
@ -519,33 +580,72 @@ shell_screenshot_screenshot_area (ShellScreenshot *screenshot,
int width, int width,
int height, int height,
const char *filename, const char *filename,
ShellScreenshotCallback callback) GAsyncReadyCallback callback,
gpointer user_data)
{ {
ClutterActor *stage; ClutterActor *stage;
ShellScreenshotPrivate *priv = screenshot->priv; ShellScreenshotPrivate *priv = screenshot->priv;
GTask *result;
if (priv->filename != NULL) { if (priv->filename != NULL) {
if (callback) if (callback)
callback (screenshot, FALSE, NULL, ""); g_task_report_new_error (screenshot,
callback,
NULL,
shell_screenshot_screenshot_area,
G_IO_ERROR,
G_IO_ERROR_PENDING,
"Only one screenshot operation at a time "
"is permitted");
return; return;
} }
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->filename = g_strdup (filename);
priv->screenshot_area.x = x; priv->screenshot_area.x = x;
priv->screenshot_area.y = y; priv->screenshot_area.y = y;
priv->screenshot_area.width = width; priv->screenshot_area.width = width;
priv->screenshot_area.height = height; priv->screenshot_area.height = height;
priv->callback = callback;
stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global)); stage = CLUTTER_ACTOR (shell_global_get_stage (priv->global));
meta_disable_unredirect_for_display (shell_global_get_display (shell_global_get ())); meta_disable_unredirect_for_display (shell_global_get_display (shell_global_get ()));
g_signal_connect_after (stage, "paint", G_CALLBACK (grab_area_screenshot), (gpointer)screenshot); g_signal_connect_after (stage, "paint", G_CALLBACK (grab_area_screenshot), result);
clutter_actor_queue_redraw (stage); clutter_actor_queue_redraw (stage);
} }
/**
* shell_screenshot_screenshot_area_finish:
* @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()
* and obtain its result.
*
* Returns: whether the operation was successful
*
*/
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);
}
/** /**
* shell_screenshot_screenshot_window: * shell_screenshot_screenshot_window:
* @screenshot: the #ShellScreenshot * @screenshot: the #ShellScreenshot
@ -554,6 +654,7 @@ shell_screenshot_screenshot_area (ShellScreenshot *screenshot,
* @filename: The filename for the screenshot * @filename: The filename for the screenshot
* @callback: (scope async): function to call returning success or failure * @callback: (scope async): function to call returning success or failure
* of the async grabbing * of the async grabbing
* @user_data: the data to pass to callback function
* *
* Takes a screenshot of the focused window (optionally omitting the frame) * Takes a screenshot of the focused window (optionally omitting the frame)
* in @filename as png image. * in @filename as png image.
@ -564,21 +665,32 @@ shell_screenshot_screenshot_window (ShellScreenshot *screenshot,
gboolean include_frame, gboolean include_frame,
gboolean include_cursor, gboolean include_cursor,
const char *filename, const char *filename,
ShellScreenshotCallback callback) GAsyncReadyCallback callback,
gpointer user_data)
{ {
ShellScreenshotPrivate *priv = screenshot->priv; ShellScreenshotPrivate *priv = screenshot->priv;
MetaDisplay *display = shell_global_get_display (priv->global); MetaDisplay *display = shell_global_get_display (priv->global);
ClutterActor *stage; ClutterActor *stage;
MetaWindow *window = meta_display_get_focus_window (display); MetaWindow *window = meta_display_get_focus_window (display);
GTask *result;
if (priv->filename != NULL || !window) { if (priv->filename != NULL || !window) {
if (callback) if (callback)
callback (screenshot, FALSE, NULL, ""); g_task_report_new_error (screenshot,
callback,
NULL,
shell_screenshot_screenshot_window,
G_IO_ERROR,
G_IO_ERROR_PENDING,
"Only one screenshot operation at a time "
"is permitted");
return; return;
} }
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->filename = g_strdup (filename);
priv->callback = callback;
priv->include_frame = include_frame; priv->include_frame = include_frame;
priv->include_cursor = include_cursor; priv->include_cursor = include_cursor;
@ -586,11 +698,39 @@ shell_screenshot_screenshot_window (ShellScreenshot *screenshot,
meta_disable_unredirect_for_display (shell_global_get_display (shell_global_get ())); meta_disable_unredirect_for_display (shell_global_get_display (shell_global_get ()));
g_signal_connect_after (stage, "paint", G_CALLBACK (grab_window_screenshot), (gpointer)screenshot); g_signal_connect_after (stage, "paint", G_CALLBACK (grab_window_screenshot), result);
clutter_actor_queue_redraw (stage); clutter_actor_queue_redraw (stage);
} }
/**
* shell_screenshot_screenshot_window_finish:
* @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()
* and obtain its result.
*
* Returns: whether the operation was successful
*
*/
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);
}
ShellScreenshot * ShellScreenshot *
shell_screenshot_new (void) shell_screenshot_new (void)
{ {

View File

@ -16,28 +16,41 @@ G_DECLARE_FINAL_TYPE (ShellScreenshot, shell_screenshot,
ShellScreenshot *shell_screenshot_new (void); ShellScreenshot *shell_screenshot_new (void);
typedef void (*ShellScreenshotCallback) (ShellScreenshot *screenshot,
gboolean success,
cairo_rectangle_int_t *screenshot_area,
const gchar *filename_used);
void shell_screenshot_screenshot_area (ShellScreenshot *screenshot, void shell_screenshot_screenshot_area (ShellScreenshot *screenshot,
int x, int x,
int y, int y,
int width, int width,
int height, int height,
const char *filename, const char *filename,
ShellScreenshotCallback callback); 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, void shell_screenshot_screenshot_window (ShellScreenshot *screenshot,
gboolean include_frame, gboolean include_frame,
gboolean include_cursor, gboolean include_cursor,
const char *filename, const char *filename,
ShellScreenshotCallback callback); 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, void shell_screenshot_screenshot (ShellScreenshot *screenshot,
gboolean include_cursor, gboolean include_cursor,
const char *filename, const char *filename,
ShellScreenshotCallback callback); 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);
#endif /* ___SHELL_SCREENSHOT_H__ */ #endif /* ___SHELL_SCREENSHOT_H__ */