1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * Screenshot plug-in
5 * Copyright 1998-2007 Sven Neumann <sven@gimp.org>
6 * Copyright 2003 Henrik Brix Andersen <brix@gimp.org>
7 * Copyright 2012 Simone Karin Lehmann - OS X patches
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23 #include "config.h"
24
25 #include <libgimp/gimp.h>
26 #include <libgimp/gimpui.h>
27
28 #ifdef GDK_WINDOWING_X11
29
30 #include <gdk/gdkkeysyms.h>
31 #include <gdk/gdkx.h>
32
33 #ifdef HAVE_X11_EXTENSIONS_SHAPE_H
34 #include <X11/extensions/shape.h>
35 #endif
36
37 #ifdef HAVE_X11_XMU_WINUTIL_H
38 #include <X11/Xmu/WinUtil.h>
39 #endif
40
41 #ifdef HAVE_XFIXES
42 #include <X11/extensions/Xfixes.h>
43 #endif
44
45 #include "screenshot.h"
46 #include "screenshot-x11.h"
47
48 #include "libgimp/stdplugins-intl.h"
49
50
51 static guint32 select_window (ScreenshotValues *shootvals,
52 GdkScreen *screen);
53 static gint32 create_image (cairo_surface_t *surface,
54 cairo_region_t *shape,
55 const gchar *name);
56
57
58 /* Allow the user to select a window or a region with the mouse */
59
60 static guint32
select_window(ScreenshotValues * shootvals,GdkScreen * screen)61 select_window (ScreenshotValues *shootvals,
62 GdkScreen *screen)
63 {
64 Display *x_dpy = GDK_SCREEN_XDISPLAY (screen);
65 gint x_scr = GDK_SCREEN_XNUMBER (screen);
66 Window x_root = RootWindow (x_dpy, x_scr);
67 Window x_win = None;
68 GC x_gc = NULL;
69 Cursor x_cursor = XCreateFontCursor (x_dpy, GDK_CROSSHAIR);
70 GdkKeymap *keymap;
71 GdkKeymapKey *keys = NULL;
72 gint status;
73 gint num_keys;
74 gint i;
75 gint buttons = 0;
76 gint mask = ButtonPressMask | ButtonReleaseMask;
77 gboolean cancel = FALSE;
78
79 if (shootvals->shoot_type == SHOOT_REGION)
80 mask |= PointerMotionMask;
81
82 status = XGrabPointer (x_dpy, x_root, False,
83 mask, GrabModeSync, GrabModeAsync,
84 x_root, x_cursor, CurrentTime);
85
86 if (status != GrabSuccess)
87 {
88 gint x, y;
89 guint xmask;
90
91 /* if we can't grab the pointer, return the window under the pointer */
92 XQueryPointer (x_dpy, x_root, &x_root, &x_win, &x, &y, &x, &y, &xmask);
93
94 if (x_win == None || x_win == x_root)
95 g_message (_("Error selecting the window"));
96 }
97
98 if (shootvals->shoot_type == SHOOT_REGION)
99 {
100 XGCValues gc_values;
101
102 gc_values.function = GXxor;
103 gc_values.plane_mask = AllPlanes;
104 gc_values.foreground = WhitePixel (x_dpy, x_scr);
105 gc_values.background = BlackPixel (x_dpy, x_scr);
106 gc_values.line_width = 0;
107 gc_values.line_style = LineSolid;
108 gc_values.fill_style = FillSolid;
109 gc_values.cap_style = CapButt;
110 gc_values.join_style = JoinMiter;
111 gc_values.graphics_exposures = FALSE;
112 gc_values.clip_x_origin = 0;
113 gc_values.clip_y_origin = 0;
114 gc_values.clip_mask = None;
115 gc_values.subwindow_mode = IncludeInferiors;
116
117 x_gc = XCreateGC (x_dpy, x_root,
118 GCFunction | GCPlaneMask | GCForeground | GCLineWidth |
119 GCLineStyle | GCCapStyle | GCJoinStyle |
120 GCGraphicsExposures | GCBackground | GCFillStyle |
121 GCClipXOrigin | GCClipYOrigin | GCClipMask |
122 GCSubwindowMode,
123 &gc_values);
124 }
125
126 keymap = gdk_keymap_get_for_display (gdk_screen_get_display (screen));
127
128 if (gdk_keymap_get_entries_for_keyval (keymap, GDK_KEY_Escape,
129 &keys, &num_keys))
130 {
131 gdk_error_trap_push ();
132
133 #define X_GRAB_KEY(index, modifiers) \
134 XGrabKey (x_dpy, keys[index].keycode, modifiers, x_root, False, \
135 GrabModeAsync, GrabModeAsync)
136
137 for (i = 0; i < num_keys; i++)
138 {
139 X_GRAB_KEY (i, 0);
140 X_GRAB_KEY (i, LockMask); /* CapsLock */
141 X_GRAB_KEY (i, Mod2Mask); /* NumLock */
142 X_GRAB_KEY (i, Mod5Mask); /* ScrollLock */
143 X_GRAB_KEY (i, LockMask | Mod2Mask); /* CapsLock + NumLock */
144 X_GRAB_KEY (i, LockMask | Mod5Mask); /* CapsLock + ScrollLock */
145 X_GRAB_KEY (i, Mod2Mask | Mod5Mask); /* NumLock + ScrollLock */
146 X_GRAB_KEY (i, LockMask | Mod2Mask | Mod5Mask); /* all */
147 }
148
149 #undef X_GRAB_KEY
150
151 gdk_flush ();
152
153 if (gdk_error_trap_pop ())
154 {
155 /* ignore errors */
156 }
157 }
158
159 while (! cancel && ((x_win == None) || (buttons != 0)))
160 {
161 XEvent x_event;
162 gint x, y, w, h;
163
164 XAllowEvents (x_dpy, SyncPointer, CurrentTime);
165 XWindowEvent (x_dpy, x_root, mask | KeyPressMask, &x_event);
166
167 switch (x_event.type)
168 {
169 case ButtonPress:
170 if (x_win == None)
171 {
172 x_win = x_event.xbutton.subwindow;
173
174 if (x_win == None)
175 x_win = x_root;
176 #ifdef HAVE_X11_XMU_WINUTIL_H
177 else if (! shootvals->decorate)
178 x_win = XmuClientWindow (x_dpy, x_win);
179 #endif
180
181 shootvals->x2 = shootvals->x1 = x_event.xbutton.x_root;
182 shootvals->y2 = shootvals->y1 = x_event.xbutton.y_root;
183 }
184
185 buttons++;
186 break;
187
188 case ButtonRelease:
189 if (buttons > 0)
190 buttons--;
191
192 if (! buttons && shootvals->shoot_type == SHOOT_REGION)
193 {
194 x = MIN (shootvals->x1, shootvals->x2);
195 y = MIN (shootvals->y1, shootvals->y2);
196 w = ABS (shootvals->x2 - shootvals->x1);
197 h = ABS (shootvals->y2 - shootvals->y1);
198
199 if (w > 0 && h > 0)
200 XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
201
202 shootvals->x2 = x_event.xbutton.x_root;
203 shootvals->y2 = x_event.xbutton.y_root;
204 }
205 break;
206
207 case MotionNotify:
208 if (buttons > 0)
209 {
210 x = MIN (shootvals->x1, shootvals->x2);
211 y = MIN (shootvals->y1, shootvals->y2);
212 w = ABS (shootvals->x2 - shootvals->x1);
213 h = ABS (shootvals->y2 - shootvals->y1);
214
215 if (w > 0 && h > 0)
216 XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
217
218 shootvals->x2 = x_event.xmotion.x_root;
219 shootvals->y2 = x_event.xmotion.y_root;
220
221 x = MIN (shootvals->x1, shootvals->x2);
222 y = MIN (shootvals->y1, shootvals->y2);
223 w = ABS (shootvals->x2 - shootvals->x1);
224 h = ABS (shootvals->y2 - shootvals->y1);
225
226 if (w > 0 && h > 0)
227 XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
228 }
229 break;
230
231 case KeyPress:
232 {
233 guint *keyvals;
234 gint n;
235
236 if (gdk_keymap_get_entries_for_keycode (NULL, x_event.xkey.keycode,
237 NULL, &keyvals, &n))
238 {
239 gint i;
240
241 for (i = 0; i < n && ! cancel; i++)
242 if (keyvals[i] == GDK_KEY_Escape)
243 cancel = TRUE;
244
245 g_free (keyvals);
246 }
247 }
248 break;
249
250 default:
251 break;
252 }
253 }
254
255 if (keys)
256 {
257 #define X_UNGRAB_KEY(index, modifiers) \
258 XUngrabKey (x_dpy, keys[index].keycode, modifiers, x_root)
259
260 for (i = 0; i < num_keys; i++)
261 {
262 X_UNGRAB_KEY (i, 0);
263 X_UNGRAB_KEY (i, LockMask); /* CapsLock */
264 X_UNGRAB_KEY (i, Mod2Mask); /* NumLock */
265 X_UNGRAB_KEY (i, Mod5Mask); /* ScrollLock */
266 X_UNGRAB_KEY (i, LockMask | Mod2Mask); /* CapsLock + NumLock */
267 X_UNGRAB_KEY (i, LockMask | Mod5Mask); /* CapsLock + ScrollLock */
268 X_UNGRAB_KEY (i, Mod2Mask | Mod5Mask); /* NumLock + ScrollLock */
269 X_UNGRAB_KEY (i, LockMask | Mod2Mask | Mod5Mask); /* all */
270 }
271 #undef X_UNGRAB_KEY
272
273 g_free (keys);
274 }
275
276 if (status == GrabSuccess)
277 XUngrabPointer (x_dpy, CurrentTime);
278
279 XFreeCursor (x_dpy, x_cursor);
280
281 if (x_gc != NULL)
282 XFreeGC (x_dpy, x_gc);
283
284 return x_win;
285 }
286
287 static gchar *
window_get_utf8_property(GdkDisplay * display,guint32 window,const gchar * name)288 window_get_utf8_property (GdkDisplay *display,
289 guint32 window,
290 const gchar *name)
291 {
292 gchar *retval = NULL;
293 Atom utf8_string;
294 Atom type = None;
295 guchar *val = NULL;
296 gulong nitems = 0;
297 gulong after = 0;
298 gint format = 0;
299
300 utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
301
302 XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), window,
303 gdk_x11_get_xatom_by_name_for_display (display, name),
304 0, G_MAXLONG, False, utf8_string,
305 &type, &format, &nitems, &after, &val);
306
307 if (type != utf8_string || format != 8 || nitems == 0)
308 {
309 if (val)
310 XFree (val);
311 return NULL;
312 }
313
314 if (g_utf8_validate ((const gchar *) val, nitems, NULL))
315 retval = g_strndup ((const gchar *) val, nitems);
316
317 XFree (val);
318
319 return retval;
320 }
321
322 static gchar *
window_get_title(GdkDisplay * display,guint window)323 window_get_title (GdkDisplay *display,
324 guint window)
325 {
326 #ifdef HAVE_X11_XMU_WINUTIL_H
327 window = XmuClientWindow (GDK_DISPLAY_XDISPLAY (display), window);
328 #endif
329
330 return window_get_utf8_property (display, window, "_NET_WM_NAME");
331 }
332
333 static cairo_region_t *
window_get_shape(GdkScreen * screen,guint32 window)334 window_get_shape (GdkScreen *screen,
335 guint32 window)
336 {
337 cairo_region_t *shape = NULL;
338
339 #if defined(HAVE_X11_EXTENSIONS_SHAPE_H)
340 XRectangle *rects;
341 gint rect_count;
342 gint rect_order;
343
344 rects = XShapeGetRectangles (GDK_SCREEN_XDISPLAY (screen), window,
345 ShapeBounding,
346 &rect_count, &rect_order);
347
348 if (rects)
349 {
350 if (rect_count > 1)
351 {
352 gint i;
353
354 shape = cairo_region_create ();
355
356 for (i = 0; i < rect_count; i++)
357 {
358 cairo_rectangle_int_t rect = { rects[i].x,
359 rects[i].y,
360 rects[i].width,
361 rects[i].height };
362
363 cairo_region_union_rectangle (shape, &rect);
364 }
365 }
366
367 XFree (rects);
368 }
369 #endif
370
371 return shape;
372 }
373
374 static void
image_select_shape(gint32 image,cairo_region_t * shape)375 image_select_shape (gint32 image,
376 cairo_region_t *shape)
377 {
378 gint num_rects;
379 gint i;
380
381 gimp_selection_none (image);
382
383 num_rects = cairo_region_num_rectangles (shape);
384
385 for (i = 0; i < num_rects; i++)
386 {
387 cairo_rectangle_int_t rect;
388
389 cairo_region_get_rectangle (shape, i, &rect);
390
391 gimp_image_select_rectangle (image, GIMP_CHANNEL_OP_ADD,
392 rect.x, rect.y,
393 rect.width, rect.height);
394 }
395
396 gimp_selection_invert (image);
397 }
398
399
400 /* Create a GimpImage from a GdkPixbuf */
401
402 static gint32
create_image(cairo_surface_t * surface,cairo_region_t * shape,const gchar * name)403 create_image (cairo_surface_t *surface,
404 cairo_region_t *shape,
405 const gchar *name)
406 {
407 gint32 image;
408 gint32 layer;
409 gdouble xres, yres;
410 gint width, height;
411
412 gimp_progress_init (_("Importing screenshot"));
413
414 width = cairo_image_surface_get_width (surface);
415 height = cairo_image_surface_get_height (surface);
416
417 image = gimp_image_new (width, height, GIMP_RGB);
418 gimp_image_undo_disable (image);
419
420 gimp_get_monitor_resolution (&xres, &yres);
421 gimp_image_set_resolution (image, xres, yres);
422
423 layer = gimp_layer_new_from_surface (image,
424 name ? name : _("Screenshot"),
425 surface,
426 0.0, 1.0);
427 gimp_image_insert_layer (image, layer, -1, 0);
428
429 if (shape && ! cairo_region_is_empty (shape))
430 {
431 image_select_shape (image, shape);
432
433 if (! gimp_selection_is_empty (image))
434 {
435 gimp_layer_add_alpha (layer);
436 gimp_drawable_edit_clear (layer);
437 gimp_selection_none (image);
438 }
439 }
440
441 gimp_image_undo_enable (image);
442
443 return image;
444 }
445
446 static void
add_cursor_image(gint32 image,GdkDisplay * display)447 add_cursor_image (gint32 image,
448 GdkDisplay *display)
449 {
450 #ifdef HAVE_XFIXES
451 XFixesCursorImage *cursor;
452 GeglBuffer *buffer;
453 GeglBufferIterator *iter;
454 GeglRectangle *roi;
455 gint32 layer;
456 gint32 active;
457
458 cursor = XFixesGetCursorImage (GDK_DISPLAY_XDISPLAY (display));
459
460 if (!cursor)
461 return;
462
463 active = gimp_image_get_active_layer (image);
464
465 layer = gimp_layer_new (image, _("Mouse Pointer"),
466 cursor->width, cursor->height,
467 GIMP_RGBA_IMAGE,
468 100.0,
469 gimp_image_get_default_new_layer_mode (image));
470
471 buffer = gimp_drawable_get_buffer (layer);
472
473 iter = gegl_buffer_iterator_new (buffer,
474 GEGL_RECTANGLE (0, 0,
475 gimp_drawable_width (layer),
476 gimp_drawable_height (layer)),
477 0, babl_format ("R'G'B'A u8"),
478 GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
479 roi = &iter->items[0].roi;
480
481 while (gegl_buffer_iterator_next (iter))
482 {
483 const gulong *src = cursor->pixels + roi->y * cursor->width + roi->x;
484 guchar *dest = iter->items[0].data;
485 gint x, y;
486
487 for (y = 0; y < roi->height; y++)
488 {
489 const gulong *s = src;
490 guchar *d = dest;
491
492 for (x = 0; x < roi->width; x++)
493 {
494 /* the cursor pixels are pre-multiplied ARGB */
495 guint a = (*s >> 24) & 0xff;
496 guint r = (*s >> 16) & 0xff;
497 guint g = (*s >> 8) & 0xff;
498 guint b = (*s >> 0) & 0xff;
499
500 d[0] = a ? (r * 255) / a : r;
501 d[1] = a ? (g * 255) / a : g;
502 d[2] = a ? (b * 255) / a : b;
503 d[3] = a;
504
505 s++;
506 d += 4;
507 }
508
509 src += cursor->width;
510 dest += 4 * roi->width;
511 }
512 }
513
514 g_object_unref (buffer);
515
516 gimp_image_insert_layer (image, layer, -1, -1);
517 gimp_layer_set_offsets (layer,
518 cursor->x - cursor->xhot, cursor->y - cursor->yhot);
519
520 gimp_image_set_active_layer (image, active);
521 #endif
522 }
523
524
525 /* The main Screenshot function */
526
527 gboolean
screenshot_x11_available(void)528 screenshot_x11_available (void)
529 {
530 return TRUE;
531 }
532
533 ScreenshotCapabilities
screenshot_x11_get_capabilities(void)534 screenshot_x11_get_capabilities (void)
535 {
536 ScreenshotCapabilities capabilities = SCREENSHOT_CAN_PICK_NONINTERACTIVELY;
537
538 #ifdef HAVE_X11_XMU_WINUTIL_H
539 capabilities |= SCREENSHOT_CAN_SHOOT_DECORATIONS;
540 #endif
541
542 #ifdef HAVE_XFIXES
543 capabilities |= SCREENSHOT_CAN_SHOOT_POINTER;
544 #endif
545
546 capabilities |= SCREENSHOT_CAN_SHOOT_REGION |
547 SCREENSHOT_CAN_SHOOT_WINDOW |
548 SCREENSHOT_CAN_PICK_WINDOW |
549 SCREENSHOT_CAN_DELAY_WINDOW_SHOT;
550
551 return capabilities;
552 }
553
554 GimpPDBStatusType
screenshot_x11_shoot(ScreenshotValues * shootvals,GdkScreen * screen,gint32 * image_ID,GError ** error)555 screenshot_x11_shoot (ScreenshotValues *shootvals,
556 GdkScreen *screen,
557 gint32 *image_ID,
558 GError **error)
559 {
560 GdkDisplay *display;
561 GdkWindow *window;
562 cairo_surface_t *screenshot;
563 cairo_region_t *shape = NULL;
564 cairo_t *cr;
565 GimpColorProfile *profile;
566 GdkRectangle rect;
567 GdkRectangle screen_rect;
568 gchar *name = NULL;
569 gint screen_x;
570 gint screen_y;
571 gint monitor = shootvals->monitor;
572 gint x, y;
573
574 /* use default screen if we are running non-interactively */
575 if (screen == NULL)
576 screen = gdk_screen_get_default ();
577
578 if (shootvals->shoot_type != SHOOT_ROOT && ! shootvals->window_id)
579 {
580 if (shootvals->select_delay > 0)
581 screenshot_delay (shootvals->select_delay);
582
583 shootvals->window_id = select_window (shootvals, screen);
584
585 if (! shootvals->window_id)
586 return GIMP_PDB_CANCEL;
587 }
588
589 if (shootvals->screenshot_delay > 0)
590 screenshot_delay (shootvals->screenshot_delay);
591
592 display = gdk_screen_get_display (screen);
593
594 screen_rect.x = 0;
595 screen_rect.y = 0;
596 screen_rect.width = gdk_screen_get_width (screen);
597 screen_rect.height = gdk_screen_get_height (screen);
598
599 if (shootvals->shoot_type == SHOOT_REGION)
600 {
601 rect.x = MIN (shootvals->x1, shootvals->x2);
602 rect.y = MIN (shootvals->y1, shootvals->y2);
603 rect.width = ABS (shootvals->x2 - shootvals->x1);
604 rect.height = ABS (shootvals->y2 - shootvals->y1);
605
606 monitor = gdk_screen_get_monitor_at_point (screen,
607 rect.x + rect.width / 2,
608 rect.y + rect.height / 2);
609 }
610 else
611 {
612 if (shootvals->shoot_type == SHOOT_ROOT)
613 {
614 window = gdk_screen_get_root_window (screen);
615
616 /* FIXME: figure monitor */
617 }
618 else
619 {
620 window = gdk_x11_window_foreign_new_for_display (display,
621 shootvals->window_id);
622
623 monitor = gdk_screen_get_monitor_at_window (screen, window);
624 }
625
626 if (! window)
627 {
628 g_set_error_literal (error, 0, 0, _("Specified window not found"));
629 return GIMP_PDB_EXECUTION_ERROR;
630 }
631
632 rect.width = gdk_window_get_width (window);
633 rect.height = gdk_window_get_height (window);
634 gdk_window_get_origin (window, &x, &y);
635
636 rect.x = x;
637 rect.y = y;
638 }
639
640 if (! gdk_rectangle_intersect (&rect, &screen_rect, &rect))
641 return GIMP_PDB_EXECUTION_ERROR;
642
643 window = gdk_screen_get_root_window (screen);
644 gdk_window_get_origin (window, &screen_x, &screen_y);
645
646 screenshot = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
647 rect.width, rect.height);
648
649 cr = cairo_create (screenshot);
650
651 gdk_cairo_set_source_window (cr, window,
652 - (rect.x - screen_x),
653 - (rect.y - screen_y));
654 cairo_paint (cr);
655
656 cairo_destroy (cr);
657
658 gdk_display_beep (display);
659
660 if (shootvals->shoot_type == SHOOT_WINDOW)
661 {
662 name = window_get_title (display, shootvals->window_id);
663
664 shape = window_get_shape (screen, shootvals->window_id);
665
666 if (shape)
667 cairo_region_translate (shape, x - rect.x, y - rect.y);
668 }
669
670 *image_ID = create_image (screenshot, shape, name);
671
672 cairo_surface_destroy (screenshot);
673
674 if (shape)
675 cairo_region_destroy (shape);
676
677 g_free (name);
678
679 /* FIXME: Some time might have passed until we get here.
680 * The cursor image should be grabbed together with the screenshot.
681 */
682 if ((shootvals->shoot_type == SHOOT_ROOT ||
683 shootvals->shoot_type == SHOOT_WINDOW) && shootvals->show_cursor)
684 add_cursor_image (*image_ID, display);
685
686 profile = gimp_screen_get_color_profile (screen, monitor);
687
688 if (profile)
689 {
690 gimp_image_set_color_profile (*image_ID, profile);
691 g_object_unref (profile);
692 }
693
694 return GIMP_PDB_SUCCESS;
695 }
696
697 #endif /* GDK_WINDOWING_X11 */
698