[gnome-shell] screenshot: Split into separate file / class



commit 4516e4cc3bd9a7ea1b79a9d3d4e3ba6fa2a1127b
Author: Adel Gadllah <adel gadllah gmail com>
Date:   Tue Feb 14 20:33:56 2012 +0100

    screenshot: Split into separate file / class
    
    Split the screenshot functionality from ShellGlobal into its own class.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=670086

 js/ui/shellDBus.js         |   15 +-
 src/Makefile.am            |    2 +
 src/shell-global-private.h |   14 --
 src/shell-global.c         |  351 ++-------------------------------------
 src/shell-global.h         |   23 +---
 src/shell-screenshot.c     |  401 ++++++++++++++++++++++++++++++++++++++++++++
 src/shell-screenshot.h     |   51 ++++++
 7 files changed, 475 insertions(+), 382 deletions(-)
---
diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js
index 96c75f1..5233383 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 835ce5f..b512b41 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 27e7829..62f7c26 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 8588f37..ed73f29 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"
@@ -706,6 +705,17 @@ shell_global_set_stage_input_region (ShellGlobal *global,
 }
 
 /**
+ * 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:
  *
  * Return value: (transfer none): The default #MetaScreen
@@ -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 7e83451..5a39e79 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 0000000..792e737
--- /dev/null
+++ b/src/shell-screenshot.c
@@ -0,0 +1,401 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+#include <X11/extensions/Xfixes.h>
+#include <clutter/x11/clutter-x11.h>
+#include <clutter/clutter.h>
+#include <cogl/cogl.h>
+#include <meta/display.h>
+#include <meta/util.h>
+#include <meta/meta-plugin.h>
+#include <meta/meta-shaped-texture.h>
+
+#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 0000000..9be1574
--- /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__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]