diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js index 96c75f1d4..523338312 100644 --- a/js/ui/shellDBus.js +++ b/js/ui/shellDBus.js @@ -146,8 +146,9 @@ const GnomeShell = new Lang.Class({ */ ScreenshotAreaAsync : function (params, invocation) { let [x, y, width, height, flash, filename, callback] = params; - global.screenshot_area (x, y, width, height, filename, - Lang.bind(this, this._onScreenshotComplete, + let screenshot = new Shell.Screenshot(); + screenshot.screenshot_area (x, y, width, height, filename, + Lang.bind(this, this._onScreenshotComplete, flash, invocation)); }, @@ -165,8 +166,9 @@ const GnomeShell = new Lang.Class({ */ ScreenshotWindowAsync : function (params, invocation) { let [include_frame, include_cursor, flash, filename] = params; - global.screenshot_window (include_frame, include_cursor, filename, - Lang.bind(this, this._onScreenshotComplete, + let screenshot = new Shell.Screenshot(); + screenshot.screenshot_window (include_frame, include_cursor, filename, + Lang.bind(this, this._onScreenshotComplete, flash, invocation)); }, @@ -183,8 +185,9 @@ const GnomeShell = new Lang.Class({ */ ScreenshotAsync : function (params, invocation) { let [include_cursor, flash, filename] = params; - global.screenshot(include_cursor, filename, - Lang.bind(this, this._onScreenshotComplete, + let screenshot = new Shell.Screenshot(); + screenshot.screenshot(include_cursor, filename, + Lang.bind(this, this._onScreenshotComplete, flash, invocation)); }, diff --git a/src/Makefile.am b/src/Makefile.am index 835ce5f7f..b512b41bd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -114,6 +114,7 @@ shell_public_headers_h = \ shell-mount-operation.h \ shell-network-agent.h \ shell-perf-log.h \ + shell-screenshot.h \ shell-screen-grabber.h \ shell-slicer.h \ shell-stack.h \ @@ -162,6 +163,7 @@ libgnome_shell_la_SOURCES = \ shell-perf-log.c \ shell-polkit-authentication-agent.h \ shell-polkit-authentication-agent.c \ + shell-screenshot.c \ shell-screen-grabber.c \ shell-slicer.c \ shell-stack.c \ diff --git a/src/shell-global-private.h b/src/shell-global-private.h index 27e782921..62f7c2603 100644 --- a/src/shell-global-private.h +++ b/src/shell-global-private.h @@ -19,18 +19,4 @@ gboolean _shell_global_check_xdnd_event (ShellGlobal *global, void _shell_global_set_session_type (ShellGlobal *global, ShellSessionType session_type); -/* Used for async screenshot grabbing */ -typedef struct _screenshot_data { - ShellGlobal *global; - - char *filename; - - cairo_surface_t *image; - cairo_rectangle_int_t screenshot_area; - - gboolean include_cursor; - - ShellGlobalScreenshotCallback callback; -} _screenshot_data; - #endif /* __SHELL_GLOBAL_PRIVATE_H__ */ diff --git a/src/shell-global.c b/src/shell-global.c index 8588f3726..ed73f2943 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -36,7 +36,6 @@ #include "shell-global-private.h" #include "shell-jsapi-compat-private.h" #include "shell-perf-log.h" -#include "shell-screen-grabber.h" #include "shell-window-tracker.h" #include "shell-wm.h" #include "st.h" @@ -705,6 +704,17 @@ shell_global_set_stage_input_region (ShellGlobal *global, shell_global_set_stage_input_mode (global, global->input_mode); } +/** + * shell_global_get_stage: + * + * Return value: (transfer none): The default #ClutterStage + */ +ClutterStage * +shell_global_get_stage (ShellGlobal *global) +{ + return global->stage; +} + /** * shell_global_get_screen: * @@ -1898,345 +1908,6 @@ shell_global_launch_calendar_server (ShellGlobal *global) g_free (calendar_server_exe); } -static void -on_screenshot_written (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - _screenshot_data *screenshot_data = (_screenshot_data*) user_data; - if (screenshot_data->callback) - screenshot_data->callback (screenshot_data->global, - g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result)), - &screenshot_data->screenshot_area); - - cairo_surface_destroy (screenshot_data->image); - g_free (screenshot_data->filename); - g_free (screenshot_data); -} - -static void -write_screenshot_thread (GSimpleAsyncResult *result, - GObject *object, - GCancellable *cancellable) -{ - cairo_status_t status; - _screenshot_data *screenshot_data = g_async_result_get_user_data (G_ASYNC_RESULT (result)); - g_assert (screenshot_data != NULL); - - status = cairo_surface_write_to_png (screenshot_data->image, screenshot_data->filename); - g_simple_async_result_set_op_res_gboolean (result, status == CAIRO_STATUS_SUCCESS); -} - -static void -do_grab_screenshot (_screenshot_data *screenshot_data, - int x, - int y, - int width, - int height) -{ - ShellScreenGrabber *grabber; - static const cairo_user_data_key_t key; - guchar *data; - - grabber = shell_screen_grabber_new (); - data = shell_screen_grabber_grab (grabber, x, y, width, height); - g_object_unref (grabber); - - screenshot_data->image = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, - width, height, width * 4); - cairo_surface_set_user_data (screenshot_data->image, &key, - data, (cairo_destroy_func_t)g_free); -} - -static void -_draw_cursor_image (cairo_surface_t *surface, - cairo_rectangle_int_t area) -{ - XFixesCursorImage *cursor_image; - - cairo_surface_t *cursor_surface; - cairo_region_t *screenshot_region; - cairo_t *cr; - - guchar *data; - int stride; - int i, j; - - cursor_image = XFixesGetCursorImage (clutter_x11_get_default_display ()); - - if (!cursor_image) - return; - - screenshot_region = cairo_region_create_rectangle (&area); - - if (!cairo_region_contains_point (screenshot_region, cursor_image->x, cursor_image->y)) - { - XFree (cursor_image); - cairo_region_destroy (screenshot_region); - return; - } - - cursor_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, cursor_image->width, cursor_image->height); - - /* The pixel data (in typical Xlib breakage) is longs even on - * 64-bit platforms, so we have to data-convert there. For simplicity, - * just do it always - */ - data = cairo_image_surface_get_data (cursor_surface); - stride = cairo_image_surface_get_stride (cursor_surface); - for (i = 0; i < cursor_image->height; i++) - for (j = 0; j < cursor_image->width; j++) - *(guint32 *)(data + i * stride + 4 * j) = cursor_image->pixels[i * cursor_image->width + j]; - - cairo_surface_mark_dirty (cursor_surface); - - cr = cairo_create (surface); - cairo_set_source_surface (cr, - cursor_surface, - cursor_image->x - cursor_image->xhot - area.x, - cursor_image->y - cursor_image->yhot - area.y); - cairo_paint (cr); - - cairo_destroy (cr); - cairo_surface_destroy (cursor_surface); - cairo_region_destroy (screenshot_region); - XFree (cursor_image); -} - -static void -grab_screenshot (ClutterActor *stage, - _screenshot_data *screenshot_data) -{ - MetaScreen *screen = shell_global_get_screen (screenshot_data->global); - int width, height; - GSimpleAsyncResult *result; - - meta_plugin_query_screen_size (screenshot_data->global->plugin, &width, &height); - - do_grab_screenshot (screenshot_data, 0, 0, width, height); - - if (meta_screen_get_n_monitors (screen) > 1) - { - cairo_region_t *screen_region = cairo_region_create (); - cairo_region_t *stage_region; - MetaRectangle monitor_rect; - cairo_rectangle_int_t stage_rect; - int i; - cairo_t *cr; - - for (i = meta_screen_get_n_monitors (screen) - 1; i >= 0; i--) - { - meta_screen_get_monitor_geometry (screen, i, &monitor_rect); - cairo_region_union_rectangle (screen_region, (const cairo_rectangle_int_t *) &monitor_rect); - } - - stage_rect.x = 0; - stage_rect.y = 0; - stage_rect.width = width; - stage_rect.height = height; - - stage_region = cairo_region_create_rectangle ((const cairo_rectangle_int_t *) &stage_rect); - cairo_region_xor (stage_region, screen_region); - cairo_region_destroy (screen_region); - - cr = cairo_create (screenshot_data->image); - - for (i = 0; i < cairo_region_num_rectangles (stage_region); i++) - { - cairo_rectangle_int_t rect; - cairo_region_get_rectangle (stage_region, i, &rect); - cairo_rectangle (cr, (double) rect.x, (double) rect.y, (double) rect.width, (double) rect.height); - cairo_fill (cr); - } - - cairo_destroy (cr); - cairo_region_destroy (stage_region); - } - - screenshot_data->screenshot_area.x = 0; - screenshot_data->screenshot_area.y = 0; - screenshot_data->screenshot_area.width = width; - screenshot_data->screenshot_area.height = height; - - if (screenshot_data->include_cursor) - _draw_cursor_image (screenshot_data->image, screenshot_data->screenshot_area); - - g_signal_handlers_disconnect_by_func (stage, (void *)grab_screenshot, (gpointer)screenshot_data); - - result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, grab_screenshot); - g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL); - g_object_unref (result); -} - -static void -grab_area_screenshot (ClutterActor *stage, - _screenshot_data *screenshot_data) -{ - GSimpleAsyncResult *result; - - do_grab_screenshot (screenshot_data, - screenshot_data->screenshot_area.x, - screenshot_data->screenshot_area.y, - screenshot_data->screenshot_area.width, - screenshot_data->screenshot_area.height); - - g_signal_handlers_disconnect_by_func (stage, (void *)grab_area_screenshot, (gpointer)screenshot_data); - result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, grab_area_screenshot); - g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL); - g_object_unref (result); -} - -/** - * shell_global_screenshot: - * @global: the #ShellGlobal - * @include_cursor: Whether to include the cursor or not - * @filename: The filename for the screenshot - * @callback: (scope async): function to call returning success or failure - * of the async grabbing - * - * Takes a screenshot of the whole screen - * in @filename as png image. - * - */ -void -shell_global_screenshot (ShellGlobal *global, - gboolean include_cursor, - const char *filename, - ShellGlobalScreenshotCallback callback) -{ - ClutterActor *stage; - _screenshot_data *data = g_new0 (_screenshot_data, 1); - - data->global = global; - data->filename = g_strdup (filename); - data->callback = callback; - data->include_cursor = include_cursor; - - stage = CLUTTER_ACTOR (meta_plugin_get_stage (global->plugin)); - - g_signal_connect_after (stage, "paint", G_CALLBACK (grab_screenshot), (gpointer)data); - - clutter_actor_queue_redraw (stage); -} - -/** - * shell_global_screenshot_area: - * @global: the #ShellGlobal - * @x: The X coordinate of the area - * @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 - * @callback: (scope async): function to call returning success or failure - * of the async grabbing - * - * Takes a screenshot of the passed in area and saves it - * in @filename as png image. - * - */ -void -shell_global_screenshot_area (ShellGlobal *global, - int x, - int y, - int width, - int height, - const char *filename, - ShellGlobalScreenshotCallback callback) -{ - ClutterActor *stage; - _screenshot_data *data = g_new0 (_screenshot_data, 1); - - data->global = global; - data->filename = g_strdup (filename); - data->screenshot_area.x = x; - data->screenshot_area.y = y; - data->screenshot_area.width = width; - data->screenshot_area.height = height; - data->callback = callback; - - stage = CLUTTER_ACTOR (meta_plugin_get_stage (global->plugin)); - - g_signal_connect_after (stage, "paint", G_CALLBACK (grab_area_screenshot), (gpointer)data); - - clutter_actor_queue_redraw (stage); -} - -/** - * shell_global_screenshot_window: - * @global: the #ShellGlobal - * @include_frame: Whether to include the frame or not - * @include_cursor: Whether to include the cursor or not - * - * @filename: The filename for the screenshot - * @callback: (scope async): function to call returning success or failure - * of the async grabbing - * - * Takes a screenshot of the focused window (optionally omitting the frame) - * in @filename as png image. - * - */ -void -shell_global_screenshot_window (ShellGlobal *global, - gboolean include_frame, - gboolean include_cursor, - const char *filename, - ShellGlobalScreenshotCallback callback) -{ - GSimpleAsyncResult *result; - - _screenshot_data *screenshot_data = g_new0 (_screenshot_data, 1); - - MetaScreen *screen = meta_plugin_get_screen (global->plugin); - MetaDisplay *display = meta_screen_get_display (screen); - MetaWindow *window = meta_display_get_focus_window (display); - ClutterActor *window_actor; - gfloat actor_x, actor_y; - MetaShapedTexture *stex; - MetaRectangle rect; - cairo_rectangle_int_t clip; - - screenshot_data->global = global; - screenshot_data->filename = g_strdup (filename); - screenshot_data->callback = callback; - - window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window)); - clutter_actor_get_position (window_actor, &actor_x, &actor_y); - - if (include_frame || !meta_window_get_frame (window)) - { - meta_window_get_outer_rect (window, &rect); - - screenshot_data->screenshot_area.x = rect.x; - screenshot_data->screenshot_area.y = rect.y; - - clip.x = rect.x - (gint) actor_x; - clip.y = rect.y - (gint) actor_y; - } - else - { - rect = *meta_window_get_rect (window); - - screenshot_data->screenshot_area.x = (gint) actor_x + rect.x; - screenshot_data->screenshot_area.y = (gint) actor_y + rect.y; - - clip.x = rect.x; - clip.y = rect.y; - } - - clip.width = screenshot_data->screenshot_area.width = rect.width; - clip.height = screenshot_data->screenshot_area.height = rect.height; - - stex = META_SHAPED_TEXTURE (meta_window_actor_get_texture (META_WINDOW_ACTOR (window_actor))); - screenshot_data->image = meta_shaped_texture_get_image (stex, &clip); - - if (include_cursor) - _draw_cursor_image (screenshot_data->image, screenshot_data->screenshot_area); - - result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, shell_global_screenshot_window); - g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL); - g_object_unref (result); -} - /** * shell_global_get_session_type: * @global: The #ShellGlobal. diff --git a/src/shell-global.h b/src/shell-global.h index 7e8345129..5a39e79e2 100644 --- a/src/shell-global.h +++ b/src/shell-global.h @@ -29,6 +29,7 @@ GType shell_global_get_type (void) G_GNUC_CONST; ShellGlobal *shell_global_get (void); +ClutterStage *shell_global_get_stage (ShellGlobal *global); MetaScreen *shell_global_get_screen (ShellGlobal *global); GdkScreen *shell_global_get_gdk_screen (ShellGlobal *global); MetaDisplay *shell_global_get_display (ShellGlobal *global); @@ -140,28 +141,6 @@ void shell_global_reexec_self (ShellGlobal *global); void shell_global_launch_calendar_server (ShellGlobal *global); -typedef void (*ShellGlobalScreenshotCallback) (ShellGlobal *global, - gboolean success, - cairo_rectangle_int_t *screenshot_area); - -void shell_global_screenshot_area (ShellGlobal *global, - int x, - int y, - int width, - int height, - const char *filename, - ShellGlobalScreenshotCallback callback); - -void shell_global_screenshot_window (ShellGlobal *global, - gboolean include_frame, - gboolean include_cursor, - const char *filename, - ShellGlobalScreenshotCallback callback); - -void shell_global_screenshot (ShellGlobal *global, - gboolean include_cursor, - const char *filename, - ShellGlobalScreenshotCallback callback); typedef enum { SHELL_SESSION_USER, SHELL_SESSION_GDM diff --git a/src/shell-screenshot.c b/src/shell-screenshot.c new file mode 100644 index 000000000..792e73749 --- /dev/null +++ b/src/shell-screenshot.c @@ -0,0 +1,401 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "shell-global.h" +#include "shell-screen-grabber.h" +#include "shell-screenshot.h" + +struct _ShellScreenshotClass +{ + GObjectClass parent_class; +}; + +struct _ShellScreenshot +{ + GObject parent_instance; + + ShellGlobal *global; +}; + +/* Used for async screenshot grabbing */ +typedef struct _screenshot_data { + ShellScreenshot *screenshot; + + char *filename; + + cairo_surface_t *image; + cairo_rectangle_int_t screenshot_area; + + gboolean include_cursor; + + ShellScreenshotCallback callback; +} _screenshot_data; + +G_DEFINE_TYPE(ShellScreenshot, shell_screenshot, G_TYPE_OBJECT); + +static void +shell_screenshot_finalize (GObject *gobject) +{ + +} + +static void +shell_screenshot_class_init (ShellScreenshotClass *screenshot_class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (screenshot_class); + + gobject_class->finalize = shell_screenshot_finalize; +} + +static void +shell_screenshot_init (ShellScreenshot *screenshot) +{ + screenshot->global = shell_global_get (); +} + +static void +on_screenshot_written (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + _screenshot_data *screenshot_data = (_screenshot_data*) user_data; + if (screenshot_data->callback) + screenshot_data->callback (screenshot_data->screenshot, + g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result)), + &screenshot_data->screenshot_area); + + cairo_surface_destroy (screenshot_data->image); + g_free (screenshot_data->filename); + g_free (screenshot_data); +} + +static void +write_screenshot_thread (GSimpleAsyncResult *result, + GObject *object, + GCancellable *cancellable) +{ + cairo_status_t status; + _screenshot_data *screenshot_data = g_async_result_get_user_data (G_ASYNC_RESULT (result)); + g_assert (screenshot_data != NULL); + + status = cairo_surface_write_to_png (screenshot_data->image, screenshot_data->filename); + g_simple_async_result_set_op_res_gboolean (result, status == CAIRO_STATUS_SUCCESS); +} + +static void +do_grab_screenshot (_screenshot_data *screenshot_data, + int x, + int y, + int width, + int height) +{ + ShellScreenGrabber *grabber; + static const cairo_user_data_key_t key; + guchar *data; + + grabber = shell_screen_grabber_new (); + data = shell_screen_grabber_grab (grabber, x, y, width, height); + g_object_unref (grabber); + + screenshot_data->image = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, + width, height, width * 4); + cairo_surface_set_user_data (screenshot_data->image, &key, + data, (cairo_destroy_func_t)g_free); +} + +static void +_draw_cursor_image (cairo_surface_t *surface, + cairo_rectangle_int_t area) +{ + XFixesCursorImage *cursor_image; + + cairo_surface_t *cursor_surface; + cairo_region_t *screenshot_region; + cairo_t *cr; + + guchar *data; + int stride; + int i, j; + + cursor_image = XFixesGetCursorImage (clutter_x11_get_default_display ()); + + if (!cursor_image) + return; + + screenshot_region = cairo_region_create_rectangle (&area); + + if (!cairo_region_contains_point (screenshot_region, cursor_image->x, cursor_image->y)) + { + XFree (cursor_image); + cairo_region_destroy (screenshot_region); + return; + } + + cursor_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, cursor_image->width, cursor_image->height); + + /* The pixel data (in typical Xlib breakage) is longs even on + * 64-bit platforms, so we have to data-convert there. For simplicity, + * just do it always + */ + data = cairo_image_surface_get_data (cursor_surface); + stride = cairo_image_surface_get_stride (cursor_surface); + for (i = 0; i < cursor_image->height; i++) + for (j = 0; j < cursor_image->width; j++) + *(guint32 *)(data + i * stride + 4 * j) = cursor_image->pixels[i * cursor_image->width + j]; + + cairo_surface_mark_dirty (cursor_surface); + + cr = cairo_create (surface); + cairo_set_source_surface (cr, + cursor_surface, + cursor_image->x - cursor_image->xhot - area.x, + cursor_image->y - cursor_image->yhot - area.y); + cairo_paint (cr); + + cairo_destroy (cr); + cairo_surface_destroy (cursor_surface); + cairo_region_destroy (screenshot_region); + XFree (cursor_image); +} + +static void +grab_screenshot (ClutterActor *stage, + _screenshot_data *screenshot_data) +{ + MetaScreen *screen = shell_global_get_screen (screenshot_data->screenshot->global); + int width, height; + GSimpleAsyncResult *result; + + meta_screen_get_size (screen, &width, &height); + + do_grab_screenshot (screenshot_data, 0, 0, width, height); + + if (meta_screen_get_n_monitors (screen) > 1) + { + cairo_region_t *screen_region = cairo_region_create (); + cairo_region_t *stage_region; + MetaRectangle monitor_rect; + cairo_rectangle_int_t stage_rect; + int i; + cairo_t *cr; + + for (i = meta_screen_get_n_monitors (screen) - 1; i >= 0; i--) + { + meta_screen_get_monitor_geometry (screen, i, &monitor_rect); + cairo_region_union_rectangle (screen_region, (const cairo_rectangle_int_t *) &monitor_rect); + } + + stage_rect.x = 0; + stage_rect.y = 0; + stage_rect.width = width; + stage_rect.height = height; + + stage_region = cairo_region_create_rectangle ((const cairo_rectangle_int_t *) &stage_rect); + cairo_region_xor (stage_region, screen_region); + cairo_region_destroy (screen_region); + + cr = cairo_create (screenshot_data->image); + + for (i = 0; i < cairo_region_num_rectangles (stage_region); i++) + { + cairo_rectangle_int_t rect; + cairo_region_get_rectangle (stage_region, i, &rect); + cairo_rectangle (cr, (double) rect.x, (double) rect.y, (double) rect.width, (double) rect.height); + cairo_fill (cr); + } + + cairo_destroy (cr); + cairo_region_destroy (stage_region); + } + + screenshot_data->screenshot_area.x = 0; + screenshot_data->screenshot_area.y = 0; + screenshot_data->screenshot_area.width = width; + screenshot_data->screenshot_area.height = height; + + if (screenshot_data->include_cursor) + _draw_cursor_image (screenshot_data->image, screenshot_data->screenshot_area); + + g_signal_handlers_disconnect_by_func (stage, (void *)grab_screenshot, (gpointer)screenshot_data); + + result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, grab_screenshot); + g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL); + g_object_unref (result); +} + +static void +grab_area_screenshot (ClutterActor *stage, + _screenshot_data *screenshot_data) +{ + GSimpleAsyncResult *result; + + do_grab_screenshot (screenshot_data, + screenshot_data->screenshot_area.x, + screenshot_data->screenshot_area.y, + screenshot_data->screenshot_area.width, + screenshot_data->screenshot_area.height); + + g_signal_handlers_disconnect_by_func (stage, (void *)grab_area_screenshot, (gpointer)screenshot_data); + result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, grab_area_screenshot); + g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL); + g_object_unref (result); +} + +/** + * shell_screenshot_screenshot: + * @screenshot: the #ShellScreenshot + * @include_cursor: Whether to include the cursor or not + * @filename: The filename for the screenshot + * @callback: (scope async): function to call returning success or failure + * of the async grabbing + * + * Takes a screenshot of the whole screen + * in @filename as png image. + * + */ +void +shell_screenshot_screenshot (ShellScreenshot *screenshot, + gboolean include_cursor, + const char *filename, + ShellScreenshotCallback callback) +{ + ClutterActor *stage; + _screenshot_data *data = g_new0 (_screenshot_data, 1); + + data->screenshot = screenshot; + data->filename = g_strdup (filename); + data->callback = callback; + data->include_cursor = include_cursor; + + stage = CLUTTER_ACTOR (shell_global_get_stage (screenshot->global)); + + g_signal_connect_after (stage, "paint", G_CALLBACK (grab_screenshot), (gpointer)data); + + clutter_actor_queue_redraw (stage); +} + +/** + * shell_screenshot_screenshot_area: + * @screenshot: the #ShellScreenshot + * @x: The X coordinate of the area + * @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 + * @callback: (scope async): function to call returning success or failure + * of the async grabbing + * + * Takes a screenshot of the passed in area and saves it + * in @filename as png image. + * + */ +void +shell_screenshot_screenshot_area (ShellScreenshot *screenshot, + int x, + int y, + int width, + int height, + const char *filename, + ShellScreenshotCallback callback) +{ + ClutterActor *stage; + _screenshot_data *data = g_new0 (_screenshot_data, 1); + + data->screenshot = screenshot; + data->filename = g_strdup (filename); + data->screenshot_area.x = x; + data->screenshot_area.y = y; + data->screenshot_area.width = width; + data->screenshot_area.height = height; + data->callback = callback; + + stage = CLUTTER_ACTOR (shell_global_get_stage (screenshot->global)); + + g_signal_connect_after (stage, "paint", G_CALLBACK (grab_area_screenshot), (gpointer)data); + + clutter_actor_queue_redraw (stage); +} + +/** + * shell_screenshot_screenshot_window: + * @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 + * @callback: (scope async): function to call returning success or failure + * of the async grabbing + * + * Takes a screenshot of the focused window (optionally omitting the frame) + * in @filename as png image. + * + */ +void +shell_screenshot_screenshot_window (ShellScreenshot *screenshot, + gboolean include_frame, + gboolean include_cursor, + const char *filename, + ShellScreenshotCallback callback) +{ + GSimpleAsyncResult *result; + + _screenshot_data *screenshot_data = g_new0 (_screenshot_data, 1); + + MetaScreen *screen = shell_global_get_screen (screenshot->global); + MetaDisplay *display = meta_screen_get_display (screen); + MetaWindow *window = meta_display_get_focus_window (display); + ClutterActor *window_actor; + gfloat actor_x, actor_y; + MetaShapedTexture *stex; + MetaRectangle rect; + cairo_rectangle_int_t clip; + + screenshot_data->screenshot = screenshot; + screenshot_data->filename = g_strdup (filename); + screenshot_data->callback = callback; + + window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window)); + clutter_actor_get_position (window_actor, &actor_x, &actor_y); + + if (include_frame || !meta_window_get_frame (window)) + { + meta_window_get_outer_rect (window, &rect); + + screenshot_data->screenshot_area.x = rect.x; + screenshot_data->screenshot_area.y = rect.y; + + clip.x = rect.x - (gint) actor_x; + clip.y = rect.y - (gint) actor_y; + } + else + { + rect = *meta_window_get_rect (window); + + screenshot_data->screenshot_area.x = (gint) actor_x + rect.x; + screenshot_data->screenshot_area.y = (gint) actor_y + rect.y; + + clip.x = rect.x; + clip.y = rect.y; + } + + clip.width = screenshot_data->screenshot_area.width = rect.width; + clip.height = screenshot_data->screenshot_area.height = rect.height; + + stex = META_SHAPED_TEXTURE (meta_window_actor_get_texture (META_WINDOW_ACTOR (window_actor))); + screenshot_data->image = meta_shaped_texture_get_image (stex, &clip); + + if (include_cursor) + _draw_cursor_image (screenshot_data->image, screenshot_data->screenshot_area); + + result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, shell_screenshot_screenshot_window); + g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL); + g_object_unref (result); +} diff --git a/src/shell-screenshot.h b/src/shell-screenshot.h new file mode 100644 index 000000000..9be1574bd --- /dev/null +++ b/src/shell-screenshot.h @@ -0,0 +1,51 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +#ifndef __SHELL_SCREENSHOT_H__ +#define __SHELL_SCREENSHOT_H__ + +/** + * SECTION:shell-screenshot + * @short_description: Grabs screenshots of areas and/or windows + * + * The #ShellScreenshot object is used to take screenshots of screen + * areas or windows and write them out as png files. + * + */ + +typedef struct _ShellScreenshot ShellScreenshot; +typedef struct _ShellScreenshotClass ShellScreenshotClass; + +#define SHELL_TYPE_SCREENSHOT (shell_screenshot_get_type ()) +#define SHELL_SCREENSHOT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SHELL_TYPE_SCREENSHOT, ShellScreenshot)) +#define SHELL_SCREENSHOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_SCREENSHOT, ShellScreenshotClass)) +#define SHELL_IS_SCREENSHOT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SHELL_TYPE_SCREENSHOT)) +#define SHELL_IS_SCREENSHOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_SCREENSHOT)) +#define SHELL_SCREENSHOT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_SCREENSHOT, ShellScreenshotClass)) + +GType shell_screenshot_get_type (void) G_GNUC_CONST; + +ShellScreenGrabber *shell_screenshot_new (void); + +typedef void (*ShellScreenshotCallback) (ShellScreenshot *screenshot, + gboolean success, + cairo_rectangle_int_t *screenshot_area); + +void shell_screenshot_screenshot_area (ShellScreenshot *screenshot, + int x, + int y, + int width, + int height, + const char *filename, + ShellScreenshotCallback callback); + +void shell_screenshot_screenshot_window (ShellScreenshot *screenshot, + gboolean include_frame, + gboolean include_cursor, + const char *filename, + ShellScreenshotCallback callback); + +void shell_screenshot_screenshot (ShellScreenshot *screenshot, + gboolean include_cursor, + const char *filename, + ShellScreenshotCallback callback); + +#endif /* ___SHELL_SCREENSHOT_H__ */