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