gnome-shell-3.6.3.1/src/shell-screenshot.c

No issues found

  1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
  2 
  3 #include <X11/extensions/Xfixes.h>
  4 #include <clutter/x11/clutter-x11.h>
  5 #include <clutter/clutter.h>
  6 #include <cogl/cogl.h>
  7 #include <meta/display.h>
  8 #include <meta/util.h>
  9 #include <meta/meta-plugin.h>
 10 #include <meta/meta-shaped-texture.h>
 11 
 12 #include "shell-global.h"
 13 #include "shell-screen-grabber.h"
 14 #include "shell-screenshot.h"
 15 
 16 struct _ShellScreenshotClass
 17 {
 18   GObjectClass parent_class;
 19 };
 20 
 21 struct _ShellScreenshot
 22 {
 23   GObject parent_instance;
 24 
 25   ShellGlobal *global;
 26 };
 27 
 28 /* Used for async screenshot grabbing */
 29 typedef struct _screenshot_data {
 30   ShellScreenshot  *screenshot;
 31 
 32   char *filename;
 33 
 34   cairo_surface_t *image;
 35   cairo_rectangle_int_t screenshot_area;
 36 
 37   gboolean include_cursor;
 38 
 39   ShellScreenshotCallback callback;
 40 } _screenshot_data;
 41 
 42 G_DEFINE_TYPE(ShellScreenshot, shell_screenshot, G_TYPE_OBJECT);
 43 
 44 static void
 45 shell_screenshot_class_init (ShellScreenshotClass *screenshot_class)
 46 {
 47   (void) screenshot_class;
 48 }
 49 
 50 static void
 51 shell_screenshot_init (ShellScreenshot *screenshot)
 52 {
 53   screenshot->global = shell_global_get ();
 54 }
 55 
 56 static void
 57 on_screenshot_written (GObject *source,
 58                        GAsyncResult *result,
 59                        gpointer user_data)
 60 {
 61   _screenshot_data *screenshot_data = (_screenshot_data*) user_data;
 62   if (screenshot_data->callback)
 63     screenshot_data->callback (screenshot_data->screenshot,
 64                                g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result)),
 65                                &screenshot_data->screenshot_area);
 66 
 67   cairo_surface_destroy (screenshot_data->image);
 68   g_object_unref (screenshot_data->screenshot);
 69   g_free (screenshot_data->filename);
 70   g_free (screenshot_data);
 71 }
 72 
 73 static void
 74 write_screenshot_thread (GSimpleAsyncResult *result,
 75                          GObject *object,
 76                          GCancellable *cancellable)
 77 {
 78   cairo_status_t status;
 79   _screenshot_data *screenshot_data = g_async_result_get_user_data (G_ASYNC_RESULT (result));
 80   g_assert (screenshot_data != NULL);
 81 
 82   status = cairo_surface_write_to_png (screenshot_data->image, screenshot_data->filename);
 83   g_simple_async_result_set_op_res_gboolean (result, status == CAIRO_STATUS_SUCCESS);
 84 }
 85 
 86 static void
 87 do_grab_screenshot (_screenshot_data *screenshot_data,
 88                     int               x,
 89                     int               y,
 90                     int               width,
 91                     int               height)
 92 {
 93   ShellScreenGrabber *grabber;
 94   static const cairo_user_data_key_t key;
 95   guchar *data;
 96 
 97   grabber = shell_screen_grabber_new ();
 98   data = shell_screen_grabber_grab (grabber, x, y, width, height);
 99   g_object_unref (grabber);
100 
101   screenshot_data->image = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24,
102                                                                width, height, width * 4);
103   cairo_surface_set_user_data (screenshot_data->image, &key,
104                                data, (cairo_destroy_func_t)g_free);
105 }
106 
107 static void
108 _draw_cursor_image (cairo_surface_t *surface,
109                     cairo_rectangle_int_t area)
110 {
111   XFixesCursorImage *cursor_image;
112 
113   cairo_surface_t *cursor_surface;
114   cairo_region_t *screenshot_region;
115   cairo_t *cr;
116 
117   guchar *data;
118   int stride;
119   int i, j;
120 
121   cursor_image = XFixesGetCursorImage (clutter_x11_get_default_display ());
122 
123   if (!cursor_image)
124     return;
125 
126   screenshot_region = cairo_region_create_rectangle (&area);
127 
128   if (!cairo_region_contains_point (screenshot_region, cursor_image->x, cursor_image->y))
129     {
130        XFree (cursor_image);
131        cairo_region_destroy (screenshot_region);
132        return;
133     }
134 
135   cursor_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, cursor_image->width, cursor_image->height);
136 
137   /* The pixel data (in typical Xlib breakage) is longs even on
138    * 64-bit platforms, so we have to data-convert there. For simplicity,
139    * just do it always
140    */
141   data = cairo_image_surface_get_data (cursor_surface);
142   stride = cairo_image_surface_get_stride (cursor_surface);
143   for (i = 0; i < cursor_image->height; i++)
144     for (j = 0; j < cursor_image->width; j++)
145       *(guint32 *)(data + i * stride + 4 * j) = cursor_image->pixels[i * cursor_image->width + j];
146 
147   cairo_surface_mark_dirty (cursor_surface);
148 
149   cr = cairo_create (surface);
150   cairo_set_source_surface (cr,
151                             cursor_surface,
152                             cursor_image->x - cursor_image->xhot - area.x,
153                             cursor_image->y - cursor_image->yhot - area.y);
154   cairo_paint (cr);
155 
156   cairo_destroy (cr);
157   cairo_surface_destroy (cursor_surface);
158   cairo_region_destroy (screenshot_region);
159   XFree (cursor_image);
160 }
161 
162 static void
163 grab_screenshot (ClutterActor *stage,
164                  _screenshot_data *screenshot_data)
165 {
166   MetaScreen *screen = shell_global_get_screen (screenshot_data->screenshot->global);
167   int width, height;
168   GSimpleAsyncResult *result;
169 
170   meta_screen_get_size (screen, &width, &height);
171 
172   do_grab_screenshot (screenshot_data, 0, 0, width, height);
173 
174   if (meta_screen_get_n_monitors (screen) > 1)
175     {
176       cairo_region_t *screen_region = cairo_region_create ();
177       cairo_region_t *stage_region;
178       MetaRectangle monitor_rect;
179       cairo_rectangle_int_t stage_rect;
180       int i;
181       cairo_t *cr;
182 
183       for (i = meta_screen_get_n_monitors (screen) - 1; i >= 0; i--)
184         {
185           meta_screen_get_monitor_geometry (screen, i, &monitor_rect);
186           cairo_region_union_rectangle (screen_region, (const cairo_rectangle_int_t *) &monitor_rect);
187         }
188 
189       stage_rect.x = 0;
190       stage_rect.y = 0;
191       stage_rect.width = width;
192       stage_rect.height = height;
193 
194       stage_region = cairo_region_create_rectangle ((const cairo_rectangle_int_t *) &stage_rect);
195       cairo_region_xor (stage_region, screen_region);
196       cairo_region_destroy (screen_region);
197 
198       cr = cairo_create (screenshot_data->image);
199 
200       for (i = 0; i < cairo_region_num_rectangles (stage_region); i++)
201         {
202           cairo_rectangle_int_t rect;
203           cairo_region_get_rectangle (stage_region, i, &rect);
204           cairo_rectangle (cr, (double) rect.x, (double) rect.y, (double) rect.width, (double) rect.height);
205           cairo_fill (cr);
206         }
207 
208       cairo_destroy (cr);
209       cairo_region_destroy (stage_region);
210     }
211 
212   screenshot_data->screenshot_area.x = 0;
213   screenshot_data->screenshot_area.y = 0;
214   screenshot_data->screenshot_area.width = width;
215   screenshot_data->screenshot_area.height = height;
216 
217   if (screenshot_data->include_cursor)
218     _draw_cursor_image (screenshot_data->image, screenshot_data->screenshot_area);
219 
220   g_signal_handlers_disconnect_by_func (stage, (void *)grab_screenshot, (gpointer)screenshot_data);
221 
222   result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, grab_screenshot);
223   g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL);
224   g_object_unref (result);
225 }
226 
227 static void
228 grab_area_screenshot (ClutterActor *stage,
229                       _screenshot_data *screenshot_data)
230 {
231   GSimpleAsyncResult *result;
232 
233   do_grab_screenshot (screenshot_data,
234                       screenshot_data->screenshot_area.x,
235                       screenshot_data->screenshot_area.y,
236                       screenshot_data->screenshot_area.width,
237                       screenshot_data->screenshot_area.height);
238 
239   g_signal_handlers_disconnect_by_func (stage, (void *)grab_area_screenshot, (gpointer)screenshot_data);
240   result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, grab_area_screenshot);
241   g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL);
242   g_object_unref (result);
243 }
244 
245 /**
246  * shell_screenshot_screenshot:
247  * @screenshot: the #ShellScreenshot
248  * @include_cursor: Whether to include the cursor or not
249  * @filename: The filename for the screenshot
250  * @callback: (scope async): function to call returning success or failure
251  * of the async grabbing
252  *
253  * Takes a screenshot of the whole screen
254  * in @filename as png image.
255  *
256  */
257 void
258 shell_screenshot_screenshot (ShellScreenshot *screenshot,
259                              gboolean include_cursor,
260                              const char *filename,
261                              ShellScreenshotCallback callback)
262 {
263   ClutterActor *stage;
264   _screenshot_data *data = g_new0 (_screenshot_data, 1);
265 
266   data->screenshot = g_object_ref (screenshot);
267   data->filename = g_strdup (filename);
268   data->callback = callback;
269   data->include_cursor = include_cursor;
270 
271   stage = CLUTTER_ACTOR (shell_global_get_stage (screenshot->global));
272 
273   g_signal_connect_after (stage, "paint", G_CALLBACK (grab_screenshot), (gpointer)data);
274 
275   clutter_actor_queue_redraw (stage);
276 }
277 
278 /**
279  * shell_screenshot_screenshot_area:
280  * @screenshot: the #ShellScreenshot
281  * @x: The X coordinate of the area
282  * @y: The Y coordinate of the area
283  * @width: The width of the area
284  * @height: The height of the area
285  * @filename: The filename for the screenshot
286  * @callback: (scope async): function to call returning success or failure
287  * of the async grabbing
288  *
289  * Takes a screenshot of the passed in area and saves it
290  * in @filename as png image.
291  *
292  */
293 void
294 shell_screenshot_screenshot_area (ShellScreenshot *screenshot,
295                                   int x,
296                                   int y,
297                                   int width,
298                                   int height,
299                                   const char *filename,
300                                   ShellScreenshotCallback callback)
301 {
302   ClutterActor *stage;
303   _screenshot_data *data = g_new0 (_screenshot_data, 1);
304 
305   data->screenshot = g_object_ref (screenshot);
306   data->filename = g_strdup (filename);
307   data->screenshot_area.x = x;
308   data->screenshot_area.y = y;
309   data->screenshot_area.width = width;
310   data->screenshot_area.height = height;
311   data->callback = callback;
312 
313   stage = CLUTTER_ACTOR (shell_global_get_stage (screenshot->global));
314 
315   g_signal_connect_after (stage, "paint", G_CALLBACK (grab_area_screenshot), (gpointer)data);
316 
317   clutter_actor_queue_redraw (stage);
318 }
319 
320 /**
321  * shell_screenshot_screenshot_window:
322  * @screenshot: the #ShellScreenshot
323  * @include_frame: Whether to include the frame or not
324  * @include_cursor: Whether to include the cursor or not
325  * @filename: The filename for the screenshot
326  * @callback: (scope async): function to call returning success or failure
327  * of the async grabbing
328  *
329  * Takes a screenshot of the focused window (optionally omitting the frame)
330  * in @filename as png image.
331  *
332  */
333 void
334 shell_screenshot_screenshot_window (ShellScreenshot *screenshot,
335                                     gboolean include_frame,
336                                     gboolean include_cursor,
337                                     const char *filename,
338                                     ShellScreenshotCallback callback)
339 {
340   GSimpleAsyncResult *result;
341 
342   _screenshot_data *screenshot_data = g_new0 (_screenshot_data, 1);
343 
344   MetaScreen *screen = shell_global_get_screen (screenshot->global);
345   MetaDisplay *display = meta_screen_get_display (screen);
346   MetaWindow *window = meta_display_get_focus_window (display);
347   ClutterActor *window_actor;
348   gfloat actor_x, actor_y;
349   MetaShapedTexture *stex;
350   MetaRectangle rect;
351   cairo_rectangle_int_t clip;
352 
353   screenshot_data->screenshot = g_object_ref (screenshot);
354   screenshot_data->filename = g_strdup (filename);
355   screenshot_data->callback = callback;
356 
357   window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window));
358   clutter_actor_get_position (window_actor, &actor_x, &actor_y);
359 
360   if (include_frame || !meta_window_get_frame (window))
361     {
362       meta_window_get_outer_rect (window, &rect);
363 
364       screenshot_data->screenshot_area.x = rect.x;
365       screenshot_data->screenshot_area.y = rect.y;
366 
367       clip.x = rect.x - (gint) actor_x;
368       clip.y = rect.y - (gint) actor_y;
369     }
370   else
371     {
372       rect = *meta_window_get_rect (window);
373 
374       screenshot_data->screenshot_area.x = (gint) actor_x + rect.x;
375       screenshot_data->screenshot_area.y = (gint) actor_y + rect.y;
376 
377       clip.x = rect.x;
378       clip.y = rect.y;
379     }
380 
381   clip.width = screenshot_data->screenshot_area.width = rect.width;
382   clip.height = screenshot_data->screenshot_area.height = rect.height;
383 
384   stex = META_SHAPED_TEXTURE (meta_window_actor_get_texture (META_WINDOW_ACTOR (window_actor)));
385   screenshot_data->image = meta_shaped_texture_get_image (stex, &clip);
386 
387   if (include_cursor)
388     _draw_cursor_image (screenshot_data->image, screenshot_data->screenshot_area);
389 
390   result = g_simple_async_result_new (NULL, on_screenshot_written, (gpointer)screenshot_data, shell_screenshot_screenshot_window);
391   g_simple_async_result_run_in_thread (result, write_screenshot_thread, G_PRIORITY_DEFAULT, NULL);
392   g_object_unref (result);
393 }
394 
395 ShellScreenshot *
396 shell_screenshot_new (void)
397 {
398   return g_object_new (SHELL_TYPE_SCREENSHOT, NULL);
399 }