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 }