diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js index 78cd8a04f..03adae927 100644 --- a/js/ui/shellDBus.js +++ b/js/ui/shellDBus.js @@ -23,6 +23,18 @@ const GnomeShellIface = { { name: 'GetExtensionErrors', inSignature: 's', outSignature: 'as' + }, + { name: 'ScreenshotArea', + inSignature: 'iiiis', + outSignature: 'b' + }, + { name: 'ScreenshotWindow', + inSignature: 'bs', + outSignature: 'b' + }, + { name: 'Screenshot', + inSignature: 's', + outSignature: 'b' } ], signals: [], @@ -76,6 +88,50 @@ GnomeShell.prototype = { return [success, returnValue]; }, + /** + * ScreenshotArea: + * @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 + * + * Takes a screenshot of the passed in area and saves it + * in @filename as png image, it returns a boolean + * indicating whether the operation was successful or not. + * + */ + ScreenshotAreaAsync : function (x, y, width, height, filename, callback) { + global.screenshot_area (x, y, width, height, filename, function (obj, result) { callback(result); }); + }, + + /** + * ScreenshotWindow: + * @include_frame: Whether to include the frame or not + * @filename: The filename for the screenshot + * + * Takes a screenshot of the focused window (optionally omitting the frame) + * and saves it in @filename as png image, it returns a boolean + * indicating whether the operation was successful or not. + * + */ + ScreenshotWindow : function (include_frame, filename) { + return global.screenshot_window (include_frame, filename); + }, + + /** + * Screenshot: + * @filename: The filename for the screenshot + * + * Takes a screenshot of the whole screen and saves it + * in @filename as png image, it returns a boolean + * indicating whether the operation was successful or not. + * + */ + ScreenshotAsync : function (filename, callback) { + global.screenshot(filename, function (obj, result) { callback(result); }); + }, + ListExtensions: function() { return ExtensionSystem.extensionMeta; }, diff --git a/src/shell-global-private.h b/src/shell-global-private.h index 5512645a0..2dd698f94 100644 --- a/src/shell-global-private.h +++ b/src/shell-global-private.h @@ -13,4 +13,19 @@ GjsContext *_shell_global_get_gjs_context (ShellGlobal *global); gboolean _shell_global_check_xdnd_event (ShellGlobal *global, XEvent *xev); + +/* Used for async screenshot grabbing */ +typedef struct _screenshot_data { + ShellGlobal *global; + + char *filename; + + int x; + int y; + int width; + int height; + + ShellGlobalScreenshotCallback callback; +} _screenshot_data; + #endif /* __SHELL_GLOBAL_PRIVATE_H__ */ diff --git a/src/shell-global.c b/src/shell-global.c index c18d474a5..07a51a21a 100644 --- a/src/shell-global.c +++ b/src/shell-global.c @@ -1628,3 +1628,232 @@ shell_global_launch_calendar_server (ShellGlobal *global) g_free (calendar_server_exe); } + +static void +grab_screenshot (ClutterActor *stage, + _screenshot_data *screenshot_data) +{ + MetaScreen *screen = shell_global_get_screen (screenshot_data->global); + cairo_status_t status; + cairo_surface_t *image; + guchar *data; + int width, height; + + meta_plugin_query_screen_size (screenshot_data->global->plugin, &width, &height); + image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + data = cairo_image_surface_get_data (image); + + cogl_flush(); + + cogl_read_pixels (0, 0, width, height, COGL_READ_PIXELS_COLOR_BUFFER, CLUTTER_CAIRO_FORMAT_ARGB32, data); + + cairo_surface_mark_dirty (image); + + 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 (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); + } + + + status = cairo_surface_write_to_png (image, screenshot_data->filename); + cairo_surface_destroy (image); + + if (screenshot_data->callback) + screenshot_data->callback (screenshot_data->global, status == CAIRO_STATUS_SUCCESS); + + g_signal_handlers_disconnect_by_func (stage, (void *)grab_screenshot, (gpointer)screenshot_data); + g_free (screenshot_data->filename); + g_free (screenshot_data); +} + +static void +grab_area_screenshot (ClutterActor *stage, + _screenshot_data *screenshot_data) +{ + cairo_status_t status; + cairo_surface_t *image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, screenshot_data->width, screenshot_data->height); + guchar *data = cairo_image_surface_get_data (image); + + cogl_flush(); + + cogl_read_pixels (screenshot_data->x, screenshot_data->y, screenshot_data->width, screenshot_data->height, + COGL_READ_PIXELS_COLOR_BUFFER, CLUTTER_CAIRO_FORMAT_ARGB32, data); + + cairo_surface_mark_dirty (image); + status = cairo_surface_write_to_png (image, screenshot_data->filename); + cairo_surface_destroy (image); + + if (screenshot_data->callback) + screenshot_data->callback (screenshot_data->global, status == CAIRO_STATUS_SUCCESS); + + g_signal_handlers_disconnect_by_func (stage, (void *)grab_area_screenshot, (gpointer)screenshot_data); + g_free (screenshot_data->filename); + g_free (screenshot_data); +} + +/** + * shell_global_screenshot: + * @global: the #ShellGlobal + * @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, + 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; + + 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->x = x; + data->y = y; + data->width = width; + data->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 + * + * @filename: The filename for the screenshot + * + * Takes a screenshot of the focused window (optionally omitting the frame) + * in @filename as png image. + * + * Return value: success or failure. + */ +gboolean +shell_global_screenshot_window (ShellGlobal *global, + gboolean include_frame, + const char *filename) +{ + CoglHandle texture; + cairo_surface_t *image; + guchar *data; + + 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; + + cairo_status_t status; + + window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window)); + texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (meta_window_actor_get_texture (META_WINDOW_ACTOR (window_actor)))); + + if (!include_frame) + { + MetaRectangle *window_rect = meta_window_get_rect (window); + texture = cogl_texture_new_from_sub_texture (texture, + window_rect->x, + window_rect->y, + window_rect->width, + window_rect->height); + + image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + window_rect->width, + window_rect->height); + } + else + image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + clutter_actor_get_width (window_actor), + clutter_actor_get_height (window_actor)); + + data = cairo_image_surface_get_data (image); + + cogl_flush(); + + cogl_texture_get_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32, 0, data); + + cairo_surface_mark_dirty (image); + status = cairo_surface_write_to_png (image, filename); + cairo_surface_destroy (image); + + return status == CAIRO_STATUS_SUCCESS; +} diff --git a/src/shell-global.h b/src/shell-global.h index d5bca83ab..aa1941a6d 100644 --- a/src/shell-global.h +++ b/src/shell-global.h @@ -142,6 +142,25 @@ void shell_global_reexec_self (ShellGlobal *global); void shell_global_launch_calendar_server (ShellGlobal *global); + +typedef void (*ShellGlobalScreenshotCallback) (ShellGlobal *global, gboolean success); + +void shell_global_screenshot_area (ShellGlobal *global, + int x, + int y, + int width, + int height, + const char *filename, + ShellGlobalScreenshotCallback callback); + +gboolean shell_global_screenshot_window (ShellGlobal *global, + gboolean include_frame, + const char *filename); + +void shell_global_screenshot (ShellGlobal *global, + const char *filename, + ShellGlobalScreenshotCallback callback); + G_END_DECLS #endif /* __SHELL_GLOBAL_H__ */