[gimp] Bug 757687 - Screenshot functionality broken under Wayland
- From: Michael Natterer <mitch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] Bug 757687 - Screenshot functionality broken under Wayland
- Date: Sat, 7 Nov 2015 21:40:47 +0000 (UTC)
commit e4ff6b40da511fae4b6864f280e5e9981799b509
Author: Michael Natterer <mitch gimp org>
Date: Sat Nov 7 22:21:53 2015 +0100
Bug 757687 - Screenshot functionality broken under Wayland
Move OS X and X11 specific code to its own files, to prepare for more
shooting backends being added. Also remove unimplemented G_OS_WIN32
stubs, if anyone feels like implementing a Windows backend, by all
means go ahead.
plug-ins/screenshot/Makefile.am | 7 +-
plug-ins/screenshot/screenshot-osx.c | 118 +++++
plug-ins/screenshot/screenshot-osx.h | 33 ++
plug-ins/screenshot/screenshot-x11.c | 702 ++++++++++++++++++++++++
plug-ins/screenshot/screenshot-x11.h | 33 ++
plug-ins/screenshot/screenshot.c | 966 +++-------------------------------
plug-ins/screenshot/screenshot.h | 49 ++
po-plug-ins/POTFILES.in | 1 +
8 files changed, 1019 insertions(+), 890 deletions(-)
---
diff --git a/plug-ins/screenshot/Makefile.am b/plug-ins/screenshot/Makefile.am
index 301c131..9cb0629 100644
--- a/plug-ins/screenshot/Makefile.am
+++ b/plug-ins/screenshot/Makefile.am
@@ -48,4 +48,9 @@ libexec_PROGRAMS = screenshot
EXTRA_PROGRAMS = screenshot
screenshot_SOURCES = \
- screenshot.c
+ screenshot.c \
+ screenshot.h \
+ screenshot-osx.c \
+ screenshot-osx.h \
+ screenshot-x11.c \
+ screenshot-x11.h
diff --git a/plug-ins/screenshot/screenshot-osx.c b/plug-ins/screenshot/screenshot-osx.c
new file mode 100644
index 0000000..b5add01
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-osx.c
@@ -0,0 +1,118 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Screenshot plug-in
+ * Copyright 1998-2007 Sven Neumann <sven gimp org>
+ * Copyright 2003 Henrik Brix Andersen <brix gimp org>
+ * Copyright 2012 Simone Karin Lehmann - OS X patches
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#ifdef PLATFORM_OSX
+
+#include <stdlib.h> /* for system() on OSX */
+#include <string.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "screenshot.h"
+#include "screenshot-osx.h"
+
+
+/*
+ * Mac OS X uses a rootless X server. This won't let us use
+ * gdk_pixbuf_get_from_drawable() and similar function on the root
+ * window to get the entire screen contents. With a nytive OS X build
+ * we have to do this without X as well.
+ *
+ * Since Mac OS X 10.2 a system utility for screencapturing is
+ * included. We can safely use this, since it's available on every OS
+ * X version GIMP is running on.
+ *
+ * The main drawbacks are that it's not possible to shoot windows or
+ * regions in scripts in noninteractive mode, and that windows always
+ * include decorations, since decorations are different between X11
+ * windows and native OS X app windows. But we can use this switch
+ * to capture the shadow of a window, which is indeed very Mac-ish.
+ *
+ * This routines works well with X11 and as a navtive build
+ */
+
+ScreenshotCapabilities
+screenshot_osx_get_capabilities (void)
+{
+ return (SCREENSHOT_CAN_SHOOT_DECORATIONS ||
+ SCREENSHOT_CAN_SHOOT_POINTER);
+}
+
+GimpPDBStatusType
+screenshot_osx_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID)
+{
+ gchar *mode = " ";
+ gchar *delay = NULL;
+ gchar *cursor = " ";
+ gchar *command = NULL;
+
+ switch (shootvals->shoot_type)
+ {
+ case SHOOT_REGION:
+ mode = "-is";
+ break;
+
+ case SHOOT_WINDOW:
+ mode = "-iwo";
+ if (shootvals->decorate)
+ mode = "-iw";
+ break;
+
+ case SHOOT_ROOT:
+ mode = " ";
+ break;
+
+ default:
+ break;
+ }
+
+ delay = g_strdup_printf ("-T %i", shootvals->select_delay);
+
+ if (shootvals->show_cursor)
+ cursor = "-C";
+
+ command = g_strjoin (" ",
+ "/usr/sbin/screencapture",
+ mode,
+ cursor,
+ delay,
+ "/tmp/screenshot.png",
+ NULL);
+
+ system ((const char *) command);
+
+ g_free (command);
+ g_free (delay);
+
+ *image_ID = gimp_file_load (GIMP_RUN_NONINTERACTIVE,
+ "/tmp/screenshot.png", "/tmp/screenshot.png");
+ gimp_image_set_filename (image, "screenshot.png");
+
+ return GIMP_PDB_SUCCESS;
+}
+
+#endif /* PLATFORM_OSX */
diff --git a/plug-ins/screenshot/screenshot-osx.h b/plug-ins/screenshot/screenshot-osx.h
new file mode 100644
index 0000000..9bd2dfc
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-osx.h
@@ -0,0 +1,33 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCREENSHOT_OSX_H__
+#define __SCREENSHOT_OSX_H__
+
+
+#ifdef PLATFORM_OSX
+
+ScreenshotCapabilities screenshot_osx_get_capabilities (void);
+
+GimpPDBStatusType screenshot_osx_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID);
+
+#endif /* PLATFORM_OSX */
+
+
+#endif /* __SCREENSHOT_OSX_H__ */
diff --git a/plug-ins/screenshot/screenshot-x11.c b/plug-ins/screenshot/screenshot-x11.c
new file mode 100644
index 0000000..48e0516
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-x11.c
@@ -0,0 +1,702 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Screenshot plug-in
+ * Copyright 1998-2007 Sven Neumann <sven gimp org>
+ * Copyright 2003 Henrik Brix Andersen <brix gimp org>
+ * Copyright 2012 Simone Karin Lehmann - OS X patches
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#ifdef GDK_WINDOWING_X11
+
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+
+#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
+#include <X11/extensions/shape.h>
+#endif
+
+#ifdef HAVE_X11_XMU_WINUTIL_H
+#include <X11/Xmu/WinUtil.h>
+#endif
+
+#ifdef HAVE_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif
+
+#include "screenshot.h"
+#include "screenshot-x11.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+static guint32 select_window (ScreenshotValues *shootvals,
+ GdkScreen *screen);
+static gint32 create_image (cairo_surface_t *surface,
+ cairo_region_t *shape,
+ const gchar *name);
+
+static void shoot_delay (gint32 delay);
+static gboolean shoot_delay_callback (gpointer data);
+
+
+/* Allow the user to select a window or a region with the mouse */
+
+static guint32
+select_window (ScreenshotValues *shootvals,
+ GdkScreen *screen)
+{
+ Display *x_dpy = GDK_SCREEN_XDISPLAY (screen);
+ gint x_scr = GDK_SCREEN_XNUMBER (screen);
+ Window x_root = RootWindow (x_dpy, x_scr);
+ Window x_win = None;
+ GC x_gc = NULL;
+ Cursor x_cursor = XCreateFontCursor (x_dpy, GDK_CROSSHAIR);
+ GdkKeymap *keymap;
+ GdkKeymapKey *keys = NULL;
+ gint status;
+ gint num_keys;
+ gint i;
+ gint buttons = 0;
+ gint mask = ButtonPressMask | ButtonReleaseMask;
+ gboolean cancel = FALSE;
+
+ if (shootvals->shoot_type == SHOOT_REGION)
+ mask |= PointerMotionMask;
+
+ status = XGrabPointer (x_dpy, x_root, False,
+ mask, GrabModeSync, GrabModeAsync,
+ x_root, x_cursor, CurrentTime);
+
+ if (status != GrabSuccess)
+ {
+ gint x, y;
+ guint xmask;
+
+ /* if we can't grab the pointer, return the window under the pointer */
+ XQueryPointer (x_dpy, x_root, &x_root, &x_win, &x, &y, &x, &y, &xmask);
+
+ if (x_win == None || x_win == x_root)
+ g_message (_("Error selecting the window"));
+ }
+
+ if (shootvals->shoot_type == SHOOT_REGION)
+ {
+ XGCValues gc_values;
+
+ gc_values.function = GXxor;
+ gc_values.plane_mask = AllPlanes;
+ gc_values.foreground = WhitePixel (x_dpy, x_scr);
+ gc_values.background = BlackPixel (x_dpy, x_scr);
+ gc_values.line_width = 0;
+ gc_values.line_style = LineSolid;
+ gc_values.fill_style = FillSolid;
+ gc_values.cap_style = CapButt;
+ gc_values.join_style = JoinMiter;
+ gc_values.graphics_exposures = FALSE;
+ gc_values.clip_x_origin = 0;
+ gc_values.clip_y_origin = 0;
+ gc_values.clip_mask = None;
+ gc_values.subwindow_mode = IncludeInferiors;
+
+ x_gc = XCreateGC (x_dpy, x_root,
+ GCFunction | GCPlaneMask | GCForeground | GCLineWidth |
+ GCLineStyle | GCCapStyle | GCJoinStyle |
+ GCGraphicsExposures | GCBackground | GCFillStyle |
+ GCClipXOrigin | GCClipYOrigin | GCClipMask |
+ GCSubwindowMode,
+ &gc_values);
+ }
+
+ keymap = gdk_keymap_get_for_display (gdk_screen_get_display (screen));
+
+ if (gdk_keymap_get_entries_for_keyval (keymap, GDK_KEY_Escape,
+ &keys, &num_keys))
+ {
+ gdk_error_trap_push ();
+
+#define X_GRAB_KEY(index, modifiers) \
+ XGrabKey (x_dpy, keys[index].keycode, modifiers, x_root, False, \
+ GrabModeAsync, GrabModeAsync)
+
+ for (i = 0; i < num_keys; i++)
+ {
+ X_GRAB_KEY (i, 0);
+ X_GRAB_KEY (i, LockMask); /* CapsLock */
+ X_GRAB_KEY (i, Mod2Mask); /* NumLock */
+ X_GRAB_KEY (i, Mod5Mask); /* ScrollLock */
+ X_GRAB_KEY (i, LockMask | Mod2Mask); /* CapsLock + NumLock */
+ X_GRAB_KEY (i, LockMask | Mod5Mask); /* CapsLock + ScrollLock */
+ X_GRAB_KEY (i, Mod2Mask | Mod5Mask); /* NumLock + ScrollLock */
+ X_GRAB_KEY (i, LockMask | Mod2Mask | Mod5Mask); /* all */
+ }
+
+#undef X_GRAB_KEY
+
+ gdk_flush ();
+
+ if (gdk_error_trap_pop ())
+ {
+ /* ignore errors */
+ }
+ }
+
+ while (! cancel && ((x_win == None) || (buttons != 0)))
+ {
+ XEvent x_event;
+ gint x, y, w, h;
+
+ XAllowEvents (x_dpy, SyncPointer, CurrentTime);
+ XWindowEvent (x_dpy, x_root, mask | KeyPressMask, &x_event);
+
+ switch (x_event.type)
+ {
+ case ButtonPress:
+ if (x_win == None)
+ {
+ x_win = x_event.xbutton.subwindow;
+
+ if (x_win == None)
+ x_win = x_root;
+#ifdef HAVE_X11_XMU_WINUTIL_H
+ else if (! shootvals->decorate)
+ x_win = XmuClientWindow (x_dpy, x_win);
+#endif
+
+ shootvals->x2 = shootvals->x1 = x_event.xbutton.x_root;
+ shootvals->y2 = shootvals->y1 = x_event.xbutton.y_root;
+ }
+
+ buttons++;
+ break;
+
+ case ButtonRelease:
+ if (buttons > 0)
+ buttons--;
+
+ if (! buttons && shootvals->shoot_type == SHOOT_REGION)
+ {
+ x = MIN (shootvals->x1, shootvals->x2);
+ y = MIN (shootvals->y1, shootvals->y2);
+ w = ABS (shootvals->x2 - shootvals->x1);
+ h = ABS (shootvals->y2 - shootvals->y1);
+
+ if (w > 0 && h > 0)
+ XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
+
+ shootvals->x2 = x_event.xbutton.x_root;
+ shootvals->y2 = x_event.xbutton.y_root;
+ }
+ break;
+
+ case MotionNotify:
+ if (buttons > 0)
+ {
+ x = MIN (shootvals->x1, shootvals->x2);
+ y = MIN (shootvals->y1, shootvals->y2);
+ w = ABS (shootvals->x2 - shootvals->x1);
+ h = ABS (shootvals->y2 - shootvals->y1);
+
+ if (w > 0 && h > 0)
+ XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
+
+ shootvals->x2 = x_event.xmotion.x_root;
+ shootvals->y2 = x_event.xmotion.y_root;
+
+ x = MIN (shootvals->x1, shootvals->x2);
+ y = MIN (shootvals->y1, shootvals->y2);
+ w = ABS (shootvals->x2 - shootvals->x1);
+ h = ABS (shootvals->y2 - shootvals->y1);
+
+ if (w > 0 && h > 0)
+ XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
+ }
+ break;
+
+ case KeyPress:
+ {
+ guint *keyvals;
+ gint n;
+
+ if (gdk_keymap_get_entries_for_keycode (NULL, x_event.xkey.keycode,
+ NULL, &keyvals, &n))
+ {
+ gint i;
+
+ for (i = 0; i < n && ! cancel; i++)
+ if (keyvals[i] == GDK_KEY_Escape)
+ cancel = TRUE;
+
+ g_free (keyvals);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (keys)
+ {
+#define X_UNGRAB_KEY(index, modifiers) \
+ XUngrabKey (x_dpy, keys[index].keycode, modifiers, x_root)
+
+ for (i = 0; i < num_keys; i++)
+ {
+ X_UNGRAB_KEY (i, 0);
+ X_UNGRAB_KEY (i, LockMask); /* CapsLock */
+ X_UNGRAB_KEY (i, Mod2Mask); /* NumLock */
+ X_UNGRAB_KEY (i, Mod5Mask); /* ScrollLock */
+ X_UNGRAB_KEY (i, LockMask | Mod2Mask); /* CapsLock + NumLock */
+ X_UNGRAB_KEY (i, LockMask | Mod5Mask); /* CapsLock + ScrollLock */
+ X_UNGRAB_KEY (i, Mod2Mask | Mod5Mask); /* NumLock + ScrollLock */
+ X_UNGRAB_KEY (i, LockMask | Mod2Mask | Mod5Mask); /* all */
+ }
+#undef X_UNGRAB_KEY
+
+ g_free (keys);
+ }
+
+ if (status == GrabSuccess)
+ XUngrabPointer (x_dpy, CurrentTime);
+
+ XFreeCursor (x_dpy, x_cursor);
+
+ if (x_gc != NULL)
+ XFreeGC (x_dpy, x_gc);
+
+ return x_win;
+}
+
+static gchar *
+window_get_utf8_property (GdkDisplay *display,
+ guint32 window,
+ const gchar *name)
+{
+ gchar *retval = NULL;
+ Atom utf8_string;
+ Atom type = None;
+ guchar *val = NULL;
+ gulong nitems = 0;
+ gulong after = 0;
+ gint format = 0;
+
+ utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
+
+ XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), window,
+ gdk_x11_get_xatom_by_name_for_display (display, name),
+ 0, G_MAXLONG, False, utf8_string,
+ &type, &format, &nitems, &after, &val);
+
+ if (type != utf8_string || format != 8 || nitems == 0)
+ {
+ if (val)
+ XFree (val);
+ return NULL;
+ }
+
+ if (g_utf8_validate ((const gchar *) val, nitems, NULL))
+ retval = g_strndup ((const gchar *) val, nitems);
+
+ XFree (val);
+
+ return retval;
+}
+
+static gchar *
+window_get_title (GdkDisplay *display,
+ guint window)
+{
+#ifdef HAVE_X11_XMU_WINUTIL_H
+ window = XmuClientWindow (GDK_DISPLAY_XDISPLAY (display), window);
+#endif
+
+ return window_get_utf8_property (display, window, "_NET_WM_NAME");
+}
+
+static cairo_region_t *
+window_get_shape (GdkScreen *screen,
+ guint32 window)
+{
+ cairo_region_t *shape = NULL;
+
+#if defined(HAVE_X11_EXTENSIONS_SHAPE_H)
+ XRectangle *rects;
+ gint rect_count;
+ gint rect_order;
+
+ rects = XShapeGetRectangles (GDK_SCREEN_XDISPLAY (screen), window,
+ ShapeBounding,
+ &rect_count, &rect_order);
+
+ if (rects)
+ {
+ if (rect_count > 1)
+ {
+ gint i;
+
+ shape = cairo_region_create ();
+
+ for (i = 0; i < rect_count; i++)
+ {
+ cairo_rectangle_int_t rect = { rects[i].x,
+ rects[i].y,
+ rects[i].width,
+ rects[i].height };
+
+ cairo_region_union_rectangle (shape, &rect);
+ }
+ }
+
+ XFree (rects);
+ }
+#endif
+
+ return shape;
+}
+
+static void
+image_select_shape (gint32 image,
+ cairo_region_t *shape)
+{
+ gint num_rects;
+ gint i;
+
+ gimp_selection_none (image);
+
+ num_rects = cairo_region_num_rectangles (shape);
+
+ for (i = 0; i < num_rects; i++)
+ {
+ cairo_rectangle_int_t rect;
+
+ cairo_region_get_rectangle (shape, i, &rect);
+
+ gimp_image_select_rectangle (image, GIMP_CHANNEL_OP_ADD,
+ rect.x, rect.y,
+ rect.width, rect.height);
+ }
+
+ gimp_selection_invert (image);
+}
+
+
+/* Create a GimpImage from a GdkPixbuf */
+
+static gint32
+create_image (cairo_surface_t *surface,
+ cairo_region_t *shape,
+ const gchar *name)
+{
+ gint32 image;
+ gint32 layer;
+ gdouble xres, yres;
+ gchar *comment;
+ gint width, height;
+
+ gimp_progress_init (_("Importing screenshot"));
+
+ width = cairo_image_surface_get_width (surface);
+ height = cairo_image_surface_get_height (surface);
+
+ image = gimp_image_new (width, height, GIMP_RGB);
+ gimp_image_undo_disable (image);
+
+ gimp_get_monitor_resolution (&xres, &yres);
+ gimp_image_set_resolution (image, xres, yres);
+
+ comment = gimp_get_default_comment ();
+ if (comment)
+ {
+ GimpParasite *parasite;
+
+ parasite = gimp_parasite_new ("gimp-comment", GIMP_PARASITE_PERSISTENT,
+ strlen (comment) + 1, comment);
+
+ gimp_image_attach_parasite (image, parasite);
+ gimp_parasite_free (parasite);
+
+ g_free (comment);
+ }
+
+ layer = gimp_layer_new_from_surface (image,
+ name ? name : _("Screenshot"),
+ surface,
+ 0.0, 1.0);
+ gimp_image_insert_layer (image, layer, -1, 0);
+
+ if (shape && ! cairo_region_is_empty (shape))
+ {
+ image_select_shape (image, shape);
+
+ if (! gimp_selection_is_empty (image))
+ {
+ gimp_layer_add_alpha (layer);
+ gimp_edit_clear (layer);
+ gimp_selection_none (image);
+ }
+ }
+
+ gimp_image_undo_enable (image);
+
+ return image;
+}
+
+static void
+add_cursor_image (gint32 image,
+ GdkDisplay *display)
+{
+#ifdef HAVE_XFIXES
+ XFixesCursorImage *cursor;
+ GeglBuffer *buffer;
+ GeglBufferIterator *iter;
+ GeglRectangle *roi;
+ gint32 layer;
+ gint32 active;
+
+ cursor = XFixesGetCursorImage (GDK_DISPLAY_XDISPLAY (display));
+
+ if (!cursor)
+ return;
+
+ active = gimp_image_get_active_layer (image);
+
+ layer = gimp_layer_new (image, _("Mouse Pointer"),
+ cursor->width, cursor->height,
+ GIMP_RGBA_IMAGE, 100.0, GIMP_NORMAL_MODE);
+
+ buffer = gimp_drawable_get_buffer (layer);
+
+ iter = gegl_buffer_iterator_new (buffer,
+ GEGL_RECTANGLE (0, 0,
+ gimp_drawable_width (layer),
+ gimp_drawable_height (layer)),
+ 0, babl_format ("R'G'B'A u8"),
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
+ roi = &iter->roi[0];
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const gulong *src = cursor->pixels + roi->y * cursor->width + roi->x;
+ guchar *dest = iter->data[0];
+ gint x, y;
+
+ for (y = 0; y < roi->height; y++)
+ {
+ const gulong *s = src;
+ guchar *d = dest;
+
+ for (x = 0; x < roi->width; x++)
+ {
+ /* the cursor pixels are pre-multiplied ARGB */
+ guint a = (*s >> 24) & 0xff;
+ guint r = (*s >> 16) & 0xff;
+ guint g = (*s >> 8) & 0xff;
+ guint b = (*s >> 0) & 0xff;
+
+ d[0] = a ? (r * 255) / a : r;
+ d[1] = a ? (g * 255) / a : g;
+ d[2] = a ? (b * 255) / a : b;
+ d[3] = a;
+
+ s++;
+ d += 4;
+ }
+
+ src += cursor->width;
+ dest += 4 * roi->width;
+ }
+ }
+
+ g_object_unref (buffer);
+
+ gimp_image_insert_layer (image, layer, -1, -1);
+ gimp_layer_set_offsets (layer,
+ cursor->x - cursor->xhot, cursor->y - cursor->yhot);
+
+ gimp_image_set_active_layer (image, active);
+#endif
+}
+
+
+/* The main Screenshot function */
+
+ScreenshotCapabilities
+screenshot_x11_get_capabilities (void)
+{
+ ScreenshotCapabilities capabilities = 0;
+
+#ifdef HAVE_X11_XMU_WINUTIL_H
+ capabilities |= SCREENSHOT_CAN_SHOOT_DECORATIONS;
+#endif
+
+#ifdef HAVE_XFIXES
+ capabilities |= SCREENSHOT_CAN_SHOOT_POINTER;
+#endif
+
+ return capabilities;
+}
+
+GimpPDBStatusType
+screenshot_x11_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID)
+{
+ GdkDisplay *display;
+ GdkWindow *window;
+ cairo_surface_t *screenshot;
+ cairo_region_t *shape = NULL;
+ cairo_t *cr;
+ GdkRectangle rect;
+ GdkRectangle screen_rect;
+ gchar *name = NULL;
+ gint screen_x;
+ gint screen_y;
+ gint x, y;
+
+ /* use default screen if we are running non-interactively */
+ if (screen == NULL)
+ screen = gdk_screen_get_default ();
+
+ if (shootvals->select_delay > 0)
+ shoot_delay (shootvals->select_delay);
+
+ if (shootvals->shoot_type != SHOOT_ROOT && ! shootvals->window_id)
+ {
+ shootvals->window_id = select_window (shootvals, screen);
+
+ if (! shootvals->window_id)
+ return GIMP_PDB_CANCEL;
+ }
+
+ display = gdk_screen_get_display (screen);
+
+ screen_rect.x = 0;
+ screen_rect.y = 0;
+ screen_rect.width = gdk_screen_get_width (screen);
+ screen_rect.height = gdk_screen_get_height (screen);
+
+ if (shootvals->shoot_type == SHOOT_REGION)
+ {
+ rect.x = MIN (shootvals->x1, shootvals->x2);
+ rect.y = MIN (shootvals->y1, shootvals->y2);
+ rect.width = ABS (shootvals->x2 - shootvals->x1);
+ rect.height = ABS (shootvals->y2 - shootvals->y1);
+ }
+ else
+ {
+ if (shootvals->shoot_type == SHOOT_ROOT)
+ {
+ window = gdk_screen_get_root_window (screen);
+ }
+ else
+ {
+ window = gdk_x11_window_foreign_new_for_display (display,
+ shootvals->window_id);
+ }
+
+ if (! window)
+ {
+ g_message (_("Specified window not found"));
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ rect.width = gdk_window_get_width (window);
+ rect.height = gdk_window_get_height (window);
+ gdk_window_get_origin (window, &x, &y);
+
+ rect.x = x;
+ rect.y = y;
+ }
+
+ if (! gdk_rectangle_intersect (&rect, &screen_rect, &rect))
+ return GIMP_PDB_EXECUTION_ERROR;
+
+ window = gdk_screen_get_root_window (screen);
+ gdk_window_get_origin (window, &screen_x, &screen_y);
+
+ screenshot = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+ rect.width, rect.height);
+
+ cr = cairo_create (screenshot);
+
+ gdk_cairo_set_source_window (cr, window,
+ - (rect.x - screen_x),
+ - (rect.y - screen_y));
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ gdk_display_beep (display);
+
+ if (shootvals->shoot_type == SHOOT_WINDOW)
+ {
+ name = window_get_title (display, shootvals->window_id);
+
+ shape = window_get_shape (screen, shootvals->window_id);
+
+ if (shape)
+ cairo_region_translate (shape, x - rect.x, y - rect.y);
+ }
+
+ *image_ID = create_image (screenshot, shape, name);
+
+ cairo_surface_destroy (screenshot);
+
+ if (shape)
+ cairo_region_destroy (shape);
+
+ g_free (name);
+
+ /* FIXME: Some time might have passed until we get here.
+ * The cursor image should be grabbed together with the screenshot.
+ */
+ if (shootvals->shoot_type == SHOOT_ROOT && shootvals->show_cursor)
+ add_cursor_image (*image_ID, display);
+
+ return GIMP_PDB_SUCCESS;
+}
+
+
+/* delay functions */
+
+static void
+shoot_delay (gint delay)
+{
+ g_timeout_add (1000, shoot_delay_callback, &delay);
+ gtk_main ();
+}
+
+static gboolean
+shoot_delay_callback (gpointer data)
+{
+ gint *seconds_left = data;
+
+ (*seconds_left)--;
+
+ if (!*seconds_left)
+ gtk_main_quit ();
+
+ return *seconds_left;
+}
+
+#endif /* GDK_WINDOWING_X11 */
diff --git a/plug-ins/screenshot/screenshot-x11.h b/plug-ins/screenshot/screenshot-x11.h
new file mode 100644
index 0000000..0a76c34
--- /dev/null
+++ b/plug-ins/screenshot/screenshot-x11.h
@@ -0,0 +1,33 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCREENSHOT_X11_H__
+#define __SCREENSHOT_X11_H__
+
+
+#ifdef GDK_WINDOWING_X11
+
+ScreenshotCapabilities screenshot_x11_get_capabilities (void);
+
+GimpPDBStatusType screenshot_x11_shoot (ScreenshotValues *shootvals,
+ GdkScreen *screen,
+ gint32 *image_ID);
+
+#endif /* GDK_WINDOWING_X11 */
+
+
+#endif /* __SCREENSHOT_X11_H__ */
diff --git a/plug-ins/screenshot/screenshot.c b/plug-ins/screenshot/screenshot.c
index 8213852..2f91204 100644
--- a/plug-ins/screenshot/screenshot.c
+++ b/plug-ins/screenshot/screenshot.c
@@ -1,6 +1,11 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
+ * Screenshot plug-in
+ * Copyright 1998-2007 Sven Neumann <sven gimp org>
+ * Copyright 2003 Henrik Brix Andersen <brix gimp org>
+ * Copyright 2012 Simone Karin Lehmann - OS X patches
+ *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
@@ -15,44 +20,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/*
- * Screenshot plug-in
- * Copyright 1998-2007 Sven Neumann <sven gimp org>
- * Copyright 2003 Henrik Brix Andersen <brix gimp org>
- * Copyright 2012 Simone Karin Lehmann - OS X patches
- *
- * Any suggestions, bug-reports or patches are very welcome.
- *
- */
-
#include "config.h"
-#include <stdlib.h> /* for system() on OSX */
-#include <string.h>
-
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
-#include <gdk/gdkkeysyms.h>
-
-#if defined(GDK_WINDOWING_X11)
-#include <gdk/gdkx.h>
-
-#ifdef HAVE_X11_EXTENSIONS_SHAPE_H
-#include <X11/extensions/shape.h>
-#endif
-
-#ifdef HAVE_X11_XMU_WINUTIL_H
-#include <X11/Xmu/WinUtil.h>
-#endif
-
-#ifdef HAVE_XFIXES
-#include <X11/extensions/Xfixes.h>
-#endif
-
-#elif defined(GDK_WINDOWING_WIN32)
-#include <windows.h>
-#endif
+#include "screenshot.h"
+#include "screenshot-osx.h"
+#include "screenshot-x11.h"
#include "libgimp/stdplugins-intl.h"
@@ -152,25 +127,6 @@ static const guint8 screenshot_icon[] =
#endif
#endif
-typedef enum
-{
- SHOOT_ROOT,
- SHOOT_REGION,
- SHOOT_WINDOW
-} ShootType;
-
-typedef struct
-{
- ShootType shoot_type;
- gboolean decorate;
- guint window_id;
- guint select_delay;
- gint x1;
- gint y1;
- gint x2;
- gint y2;
- gboolean show_cursor;
-} ScreenshotValues;
static ScreenshotValues shootvals =
{
@@ -190,30 +146,24 @@ static ScreenshotValues shootvals =
};
-static void query (void);
-static void run (const gchar *name,
- gint nparams,
- const GimpParam *param,
- gint *nreturn_vals,
- GimpParam **return_vals);
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
-static guint32 select_window (GdkScreen *screen);
-static gint32 create_image (cairo_surface_t *surface,
- cairo_region_t *shape,
- const gchar *name);
+static GimpPDBStatusType shoot (GdkScreen *screen,
+ gint32 *image_ID);
-static gint32 shoot (GdkScreen *screen);
-static gint32 shoot_main (GdkScreen *screen);
-#ifdef PLATFORM_OSX
-static gint32 shoot_osx (GdkScreen *screen);
-#endif
-static gboolean shoot_dialog (GdkScreen **screen);
-static void shoot_delay (gint32 delay);
-static gboolean shoot_delay_callback (gpointer data);
-static gboolean shoot_quit_timeout (gpointer data);
+static gboolean shoot_dialog (GdkScreen **screen);
+static gboolean shoot_quit_timeout (gpointer data);
/* Global Variables */
+
+static ScreenshotCapabilities capabilities = 0;
+
const GimpPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
@@ -305,6 +255,12 @@ run (const gchar *name,
values[0].type = GIMP_PDB_STATUS;
values[0].data.d_status = status;
+#ifdef PLATFORM_OSX
+ capabilities = screenshot_osx_get_capabilities ();
+#elif defined (GDK_WINDOWING_X11)
+ capabilities = screenshot_x11_get_capabilities ();
+#endif
+
/* how are we running today? */
switch (run_mode)
{
@@ -361,28 +317,9 @@ run (const gchar *name,
break;
}
-#ifndef PLATFORM_OSX
- if (status == GIMP_PDB_SUCCESS)
- {
- if (shootvals.select_delay > 0)
- shoot_delay (shootvals.select_delay);
-
- if (shootvals.shoot_type != SHOOT_ROOT && ! shootvals.window_id)
- {
- shootvals.window_id = select_window (screen);
-
- if (! shootvals.window_id)
- status = GIMP_PDB_CANCEL;
- }
- }
-#endif
-
if (status == GIMP_PDB_SUCCESS)
{
- image_ID = shoot (screen);
-
- if (image_ID == -1)
- status = GIMP_PDB_EXECUTION_ERROR;
+ status = shoot (screen, &image_ID);
}
if (status == GIMP_PDB_SUCCESS)
@@ -413,748 +350,22 @@ run (const gchar *name,
}
-/* Allow the user to select a window or a region with the mouse */
-
-#ifdef GDK_WINDOWING_X11
-
-static guint32
-select_window_x11 (GdkScreen *screen)
-{
- Display *x_dpy = GDK_SCREEN_XDISPLAY (screen);
- gint x_scr = GDK_SCREEN_XNUMBER (screen);
- Window x_root = RootWindow (x_dpy, x_scr);
- Window x_win = None;
- GC x_gc = NULL;
- Cursor x_cursor = XCreateFontCursor (x_dpy, GDK_CROSSHAIR);
- GdkKeymap *keymap;
- GdkKeymapKey *keys = NULL;
- gint status;
- gint i, num_keys;
- gint buttons = 0;
- gint mask = ButtonPressMask | ButtonReleaseMask;
- gboolean cancel = FALSE;
-
- if (shootvals.shoot_type == SHOOT_REGION)
- mask |= PointerMotionMask;
-
- status = XGrabPointer (x_dpy, x_root, False,
- mask, GrabModeSync, GrabModeAsync,
- x_root, x_cursor, CurrentTime);
-
- if (status != GrabSuccess)
- {
- gint x, y;
- guint xmask;
-
- /* if we can't grab the pointer, return the window under the pointer */
- XQueryPointer (x_dpy, x_root, &x_root, &x_win, &x, &y, &x, &y, &xmask);
-
- if (x_win == None || x_win == x_root)
- g_message (_("Error selecting the window"));
- }
-
- if (shootvals.shoot_type == SHOOT_REGION)
- {
- XGCValues gc_values;
-
- gc_values.function = GXxor;
- gc_values.plane_mask = AllPlanes;
- gc_values.foreground = WhitePixel (x_dpy, x_scr);
- gc_values.background = BlackPixel (x_dpy, x_scr);
- gc_values.line_width = 0;
- gc_values.line_style = LineSolid;
- gc_values.fill_style = FillSolid;
- gc_values.cap_style = CapButt;
- gc_values.join_style = JoinMiter;
- gc_values.graphics_exposures = FALSE;
- gc_values.clip_x_origin = 0;
- gc_values.clip_y_origin = 0;
- gc_values.clip_mask = None;
- gc_values.subwindow_mode = IncludeInferiors;
-
- x_gc = XCreateGC (x_dpy, x_root,
- GCFunction | GCPlaneMask | GCForeground | GCLineWidth |
- GCLineStyle | GCCapStyle | GCJoinStyle |
- GCGraphicsExposures | GCBackground | GCFillStyle |
- GCClipXOrigin | GCClipYOrigin | GCClipMask |
- GCSubwindowMode,
- &gc_values);
- }
-
- keymap = gdk_keymap_get_for_display (gdk_screen_get_display (screen));
-
- if (gdk_keymap_get_entries_for_keyval (keymap, GDK_KEY_Escape,
- &keys, &num_keys))
- {
- gdk_error_trap_push ();
-
-#define X_GRAB_KEY(index, modifiers) \
- XGrabKey (x_dpy, keys[index].keycode, modifiers, x_root, False, \
- GrabModeAsync, GrabModeAsync)
-
- for (i = 0; i < num_keys; i++)
- {
- X_GRAB_KEY (i, 0);
- X_GRAB_KEY (i, LockMask); /* CapsLock */
- X_GRAB_KEY (i, Mod2Mask); /* NumLock */
- X_GRAB_KEY (i, Mod5Mask); /* ScrollLock */
- X_GRAB_KEY (i, LockMask | Mod2Mask); /* CapsLock + NumLock */
- X_GRAB_KEY (i, LockMask | Mod5Mask); /* CapsLock + ScrollLock */
- X_GRAB_KEY (i, Mod2Mask | Mod5Mask); /* NumLock + ScrollLock */
- X_GRAB_KEY (i, LockMask | Mod2Mask | Mod5Mask); /* all */
- }
-
-#undef X_GRAB_KEY
-
- gdk_flush ();
-
- if (gdk_error_trap_pop ())
- {
- /* ignore errors */
- }
- }
-
- while (! cancel && ((x_win == None) || (buttons != 0)))
- {
- XEvent x_event;
- gint x, y, w, h;
-
- XAllowEvents (x_dpy, SyncPointer, CurrentTime);
- XWindowEvent (x_dpy, x_root, mask | KeyPressMask, &x_event);
-
- switch (x_event.type)
- {
- case ButtonPress:
- if (x_win == None)
- {
- x_win = x_event.xbutton.subwindow;
-
- if (x_win == None)
- x_win = x_root;
-#ifdef HAVE_X11_XMU_WINUTIL_H
- else if (! shootvals.decorate)
- x_win = XmuClientWindow (x_dpy, x_win);
-#endif
-
- shootvals.x2 = shootvals.x1 = x_event.xbutton.x_root;
- shootvals.y2 = shootvals.y1 = x_event.xbutton.y_root;
- }
-
- buttons++;
- break;
-
- case ButtonRelease:
- if (buttons > 0)
- buttons--;
-
- if (! buttons && shootvals.shoot_type == SHOOT_REGION)
- {
- x = MIN (shootvals.x1, shootvals.x2);
- y = MIN (shootvals.y1, shootvals.y2);
- w = ABS (shootvals.x2 - shootvals.x1);
- h = ABS (shootvals.y2 - shootvals.y1);
-
- if (w > 0 && h > 0)
- XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
-
- shootvals.x2 = x_event.xbutton.x_root;
- shootvals.y2 = x_event.xbutton.y_root;
- }
- break;
-
- case MotionNotify:
- if (buttons > 0)
- {
- x = MIN (shootvals.x1, shootvals.x2);
- y = MIN (shootvals.y1, shootvals.y2);
- w = ABS (shootvals.x2 - shootvals.x1);
- h = ABS (shootvals.y2 - shootvals.y1);
-
- if (w > 0 && h > 0)
- XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
-
- shootvals.x2 = x_event.xmotion.x_root;
- shootvals.y2 = x_event.xmotion.y_root;
-
- x = MIN (shootvals.x1, shootvals.x2);
- y = MIN (shootvals.y1, shootvals.y2);
- w = ABS (shootvals.x2 - shootvals.x1);
- h = ABS (shootvals.y2 - shootvals.y1);
-
- if (w > 0 && h > 0)
- XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
- }
- break;
-
- case KeyPress:
- {
- guint *keyvals;
- gint n;
-
- if (gdk_keymap_get_entries_for_keycode (NULL, x_event.xkey.keycode,
- NULL, &keyvals, &n))
- {
- gint i;
-
- for (i = 0; i < n && ! cancel; i++)
- if (keyvals[i] == GDK_KEY_Escape)
- cancel = TRUE;
-
- g_free (keyvals);
- }
- }
- break;
-
- default:
- break;
- }
- }
-
- if (keys)
- {
-#define X_UNGRAB_KEY(index, modifiers) \
- XUngrabKey (x_dpy, keys[index].keycode, modifiers, x_root)
-
- for (i = 0; i < num_keys; i++)
- {
- X_UNGRAB_KEY (i, 0);
- X_UNGRAB_KEY (i, LockMask); /* CapsLock */
- X_UNGRAB_KEY (i, Mod2Mask); /* NumLock */
- X_UNGRAB_KEY (i, Mod5Mask); /* ScrollLock */
- X_UNGRAB_KEY (i, LockMask | Mod2Mask); /* CapsLock + NumLock */
- X_UNGRAB_KEY (i, LockMask | Mod5Mask); /* CapsLock + ScrollLock */
- X_UNGRAB_KEY (i, Mod2Mask | Mod5Mask); /* NumLock + ScrollLock */
- X_UNGRAB_KEY (i, LockMask | Mod2Mask | Mod5Mask); /* all */
- }
-#undef X_UNGRAB_KEY
-
- g_free (keys);
- }
-
- if (status == GrabSuccess)
- XUngrabPointer (x_dpy, CurrentTime);
-
- XFreeCursor (x_dpy, x_cursor);
-
- if (x_gc != NULL)
- XFreeGC (x_dpy, x_gc);
-
- return x_win;
-}
-
-#endif
-
-
-#ifdef GDK_WINDOWING_WIN32
-
-static guint32
-select_window_win32 (GdkScreen *screen)
-{
- /* MS Windows specific code goes here (yet to be written) */
-
- /* basically the code should grab the pointer using a crosshair
- * cursor, allow the user to click on a window and return the
- * obtained HWND (as a GdkNativeWindow) - for more details consult
- * the X11 specific code above
- */
-
- /* note to self: take a look at the winsnap plug-in for example code */
-
-#ifdef __GNUC__
-#warning Win32 screenshot window chooser not implemented yet
-#else
-#pragma message("Win32 screenshot window chooser not implemented yet")
-#endif
-
- return 0;
-}
-
-#endif
-
-
-static guint32
-select_window (GdkScreen *screen)
-{
-#if defined(GDK_WINDOWING_X11)
- return select_window_x11 (screen);
-#elif defined(GDK_WINDOWING_WIN32)
- return select_window_win32 (screen);
-#else
-#warning screenshot window chooser not implemented yet for this GDK backend
- return 0;
-#endif
-}
-
-static gchar *
-window_get_utf8_property (GdkDisplay *display,
- guint32 window,
- const gchar *name)
-{
- gchar *retval = NULL;
-
-#if defined(GDK_WINDOWING_X11)
- Atom utf8_string;
- Atom type = None;
- guchar *val = NULL;
- gulong nitems = 0;
- gulong after = 0;
- gint format = 0;
-
- utf8_string = gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING");
-
- XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), window,
- gdk_x11_get_xatom_by_name_for_display (display, name),
- 0, G_MAXLONG, False, utf8_string,
- &type, &format, &nitems, &after, &val);
-
- if (type != utf8_string || format != 8 || nitems == 0)
- {
- if (val)
- XFree (val);
- return NULL;
- }
-
- if (g_utf8_validate ((const gchar *) val, nitems, NULL))
- retval = g_strndup ((const gchar *) val, nitems);
-
- XFree (val);
-
-#endif
- return retval;
-}
-
-static gchar *
-window_get_title (GdkDisplay *display,
- guint window)
-{
-#if defined(GDK_WINDOWING_X11)
-#ifdef HAVE_X11_XMU_WINUTIL_H
- window = XmuClientWindow (GDK_DISPLAY_XDISPLAY (display), window);
-#endif
-
- return window_get_utf8_property (display, window, "_NET_WM_NAME");
-#else
- return NULL;
-#endif
-}
-
-static cairo_region_t *
-window_get_shape (GdkScreen *screen,
- guint32 window)
-{
- cairo_region_t *shape = NULL;
-
-#if defined(GDK_WINDOWING_X11) && defined(HAVE_X11_EXTENSIONS_SHAPE_H)
- XRectangle *rects;
- gint rect_count;
- gint rect_order;
-
- rects = XShapeGetRectangles (GDK_SCREEN_XDISPLAY (screen), window,
- ShapeBounding,
- &rect_count, &rect_order);
-
- if (rects)
- {
- if (rect_count > 1)
- {
- gint i;
-
- shape = cairo_region_create ();
-
- for (i = 0; i < rect_count; i++)
- {
- cairo_rectangle_int_t rect = { rects[i].x,
- rects[i].y,
- rects[i].width,
- rects[i].height };
-
- cairo_region_union_rectangle (shape, &rect);
- }
- }
-
- XFree (rects);
- }
-#endif
-
- return shape;
-}
-
-static void
-image_select_shape (gint32 image,
- cairo_region_t *shape)
-{
- gint num_rects;
- gint i;
-
- gimp_selection_none (image);
-
- num_rects = cairo_region_num_rectangles (shape);
-
- for (i = 0; i < num_rects; i++)
- {
- cairo_rectangle_int_t rect;
-
- cairo_region_get_rectangle (shape, i, &rect);
-
- gimp_image_select_rectangle (image, GIMP_CHANNEL_OP_ADD,
- rect.x, rect.y,
- rect.width, rect.height);
- }
-
- gimp_selection_invert (image);
-}
-
-
-/* Create a GimpImage from a GdkPixbuf */
-
-static gint32
-create_image (cairo_surface_t *surface,
- cairo_region_t *shape,
- const gchar *name)
-{
- gint32 image;
- gint32 layer;
- gdouble xres, yres;
- gchar *comment;
- gint width, height;
-
- gimp_progress_init (_("Importing screenshot"));
-
- width = cairo_image_surface_get_width (surface);
- height = cairo_image_surface_get_height (surface);
-
- image = gimp_image_new (width, height, GIMP_RGB);
- gimp_image_undo_disable (image);
-
- gimp_get_monitor_resolution (&xres, &yres);
- gimp_image_set_resolution (image, xres, yres);
-
- comment = gimp_get_default_comment ();
- if (comment)
- {
- GimpParasite *parasite;
-
- parasite = gimp_parasite_new ("gimp-comment", GIMP_PARASITE_PERSISTENT,
- strlen (comment) + 1, comment);
-
- gimp_image_attach_parasite (image, parasite);
- gimp_parasite_free (parasite);
-
- g_free (comment);
- }
-
- layer = gimp_layer_new_from_surface (image,
- name ? name : _("Screenshot"),
- surface,
- 0.0, 1.0);
- gimp_image_insert_layer (image, layer, -1, 0);
-
- if (shape && ! cairo_region_is_empty (shape))
- {
- image_select_shape (image, shape);
-
- if (! gimp_selection_is_empty (image))
- {
- gimp_layer_add_alpha (layer);
- gimp_edit_clear (layer);
- gimp_selection_none (image);
- }
- }
-
- gimp_image_undo_enable (image);
-
- return image;
-}
-
-static void
-add_cursor_image (gint32 image,
- GdkDisplay *display)
-{
-#ifdef HAVE_XFIXES
- XFixesCursorImage *cursor;
- GeglBuffer *buffer;
- GeglBufferIterator *iter;
- GeglRectangle *roi;
- gint32 layer;
- gint32 active;
-
- cursor = XFixesGetCursorImage (GDK_DISPLAY_XDISPLAY (display));
-
- if (!cursor)
- return;
-
- active = gimp_image_get_active_layer (image);
-
- layer = gimp_layer_new (image, _("Mouse Pointer"),
- cursor->width, cursor->height,
- GIMP_RGBA_IMAGE, 100.0, GIMP_NORMAL_MODE);
-
- buffer = gimp_drawable_get_buffer (layer);
-
- iter = gegl_buffer_iterator_new (buffer,
- GEGL_RECTANGLE (0, 0,
- gimp_drawable_width (layer),
- gimp_drawable_height (layer)),
- 0, babl_format ("R'G'B'A u8"),
- GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
- roi = &iter->roi[0];
-
- while (gegl_buffer_iterator_next (iter))
- {
- const gulong *src = cursor->pixels + roi->y * cursor->width + roi->x;
- guchar *dest = iter->data[0];
- gint x, y;
-
- for (y = 0; y < roi->height; y++)
- {
- const gulong *s = src;
- guchar *d = dest;
-
- for (x = 0; x < roi->width; x++)
- {
- /* the cursor pixels are pre-multiplied ARGB */
- guint a = (*s >> 24) & 0xff;
- guint r = (*s >> 16) & 0xff;
- guint g = (*s >> 8) & 0xff;
- guint b = (*s >> 0) & 0xff;
-
- d[0] = a ? (r * 255) / a : r;
- d[1] = a ? (g * 255) / a : g;
- d[2] = a ? (b * 255) / a : b;
- d[3] = a;
-
- s++;
- d += 4;
- }
-
- src += cursor->width;
- dest += 4 * roi->width;
- }
- }
-
- g_object_unref (buffer);
-
- gimp_image_insert_layer (image, layer, -1, -1);
- gimp_layer_set_offsets (layer,
- cursor->x - cursor->xhot, cursor->y - cursor->yhot);
-
- gimp_image_set_active_layer (image, active);
-#endif
-}
-
-static GdkWindow *
-get_foreign_window (GdkDisplay *display,
- guint32 window)
-{
-#ifdef GDK_WINDOWING_X11
- return gdk_x11_window_foreign_new_for_display (display, window);
-#endif
-
-#ifdef GDK_WINDOWING_WIN32
- return gdk_win32_window_foreign_new_for_display (display, window);
-#endif
-
- return NULL;
-}
-
-
/* The main Screenshot function */
-static gint32
-shoot (GdkScreen *screen)
+static GimpPDBStatusType
+shoot (GdkScreen *screen,
+ gint32 *image_ID)
{
#ifdef PLATFORM_OSX
- /* on Mac OS X, either with X11 (which is a rootless X server) or
- * as a native quartz build, we have to implement it differently,
- * without using X and just use the standard OS X screenshot
- * utility.
- */
- return shoot_osx (screen);
-#else
- return shoot_main (screen);
+ return screenshot_osx_shoot (&shootvals, screen, image_ID);
#endif
-}
-
-static gint32
-shoot_main (GdkScreen *screen)
-{
- GdkDisplay *display;
- GdkWindow *window;
- cairo_surface_t *screenshot;
- cairo_region_t *shape = NULL;
- cairo_t *cr;
- GdkRectangle rect;
- GdkRectangle screen_rect;
- gchar *name = NULL;
- gint32 image;
- gint screen_x;
- gint screen_y;
- gint x, y;
-
- /* use default screen if we are running non-interactively */
- if (screen == NULL)
- screen = gdk_screen_get_default ();
-
- display = gdk_screen_get_display (screen);
-
- screen_rect.x = 0;
- screen_rect.y = 0;
- screen_rect.width = gdk_screen_get_width (screen);
- screen_rect.height = gdk_screen_get_height (screen);
-
- if (shootvals.shoot_type == SHOOT_REGION)
- {
- rect.x = MIN (shootvals.x1, shootvals.x2);
- rect.y = MIN (shootvals.y1, shootvals.y2);
- rect.width = ABS (shootvals.x2 - shootvals.x1);
- rect.height = ABS (shootvals.y2 - shootvals.y1);
- }
- else
- {
- if (shootvals.shoot_type == SHOOT_ROOT)
- {
- window = gdk_screen_get_root_window (screen);
- }
- else
- {
- window = get_foreign_window (display, shootvals.window_id);
- }
- if (! window)
- {
- g_message (_("Specified window not found"));
- return -1;
- }
-
- rect.width = gdk_window_get_width (window);
- rect.height = gdk_window_get_height (window);
- gdk_window_get_origin (window, &x, &y);
-
- rect.x = x;
- rect.y = y;
- }
-
- if (! gdk_rectangle_intersect (&rect, &screen_rect, &rect))
- return -1;
-
- window = gdk_screen_get_root_window (screen);
- gdk_window_get_origin (window, &screen_x, &screen_y);
-
- screenshot = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
- rect.width, rect.height);
-
- cr = cairo_create (screenshot);
-
- gdk_cairo_set_source_window (cr, window,
- - (rect.x - screen_x),
- - (rect.y - screen_y));
- cairo_paint (cr);
-
- cairo_destroy (cr);
-
- gdk_display_beep (display);
-
- if (shootvals.shoot_type == SHOOT_WINDOW)
- {
- name = window_get_title (display, shootvals.window_id);
-
- shape = window_get_shape (screen, shootvals.window_id);
-
- if (shape)
- cairo_region_translate (shape, x - rect.x, y - rect.y);
- }
-
- image = create_image (screenshot, shape, name);
-
- cairo_surface_destroy (screenshot);
-
- if (shape)
- cairo_region_destroy (shape);
-
- g_free (name);
-
- /* FIXME: Some time might have passed until we get here.
- * The cursor image should be grabbed together with the screenshot.
- */
- if (shootvals.shoot_type == SHOOT_ROOT && shootvals.show_cursor)
- add_cursor_image (image, display);
-
- return image;
-}
-
-#ifdef PLATFORM_OSX
-/*
- * Mac OS X uses a rootless X server. This won't let us use
- * gdk_pixbuf_get_from_drawable() and similar function on the root
- * window to get the entire screen contents. With a nytive OS X build
- * we have to do this without X as well.
- *
- * Since Mac OS X 10.2 a system utility for screencapturing is
- * included. We can safely use this, since it's available on every OS
- * X version GIMP is running on.
- *
- * The main drawbacks are that it's not possible to shoot windows or
- * regions in scripts in noninteractive mode, and that windows always
- * include decorations, since decorations are different between X11
- * windows and native OS X app windows. But we can use this switch
- * to capture the shadow of a window, which is indeed very Mac-ish.
- *
- * This routines works well with X11 and as a navtive build
- */
-static gint32
-shoot_osx (GdkScreen *screen)
-{
- gint32 image;
- gchar *mode = " ";
- gchar *delay = NULL;
- gchar *cursor = " ";
- gchar *command = NULL;
-
- switch (shootvals.shoot_type)
- {
- case SHOOT_REGION:
- mode = "-is";
- break;
-
- case SHOOT_WINDOW:
- mode = "-iwo";
- if (shootvals.decorate)
- mode = "-iw";
- break;
-
- case SHOOT_ROOT:
- mode = " ";
- break;
-
- default:
- break;
- }
-
- delay = g_strdup_printf ("-T %i", shootvals.select_delay);
-
- if (shootvals.show_cursor)
- cursor = "-C";
-
- command = g_strjoin (" ",
- "/usr/sbin/screencapture",
- mode,
- cursor,
- delay,
- "/tmp/screenshot.png",
- NULL);
-
- system ((const char *) command);
-
- g_free (command);
- g_free (delay);
-
- image = gimp_file_load (GIMP_RUN_NONINTERACTIVE,
- "/tmp/screenshot.png", "/tmp/screenshot.png");
- gimp_image_set_filename (image, "screenshot.png");
+#ifdef GDK_WINDOWING_X11
+ return screenshot_x11_shoot (&shootvals, screen, image_ID);
+#endif
- return image;
+ return GIMP_PDB_CALLING_ERROR; /* silence compiler */
}
-#endif /* PLATFORM_OSX */
/* Screenshot dialog */
@@ -1201,9 +412,7 @@ shoot_dialog (GdkScreen **screen)
GtkWidget *hbox;
GtkWidget *label;
GtkWidget *button;
-#if (defined (HAVE_XFIXES) || defined (HAVE_X11_XMU_WINUTIL_H) || defined (PLATFORM_OSX))
GtkWidget *toggle;
-#endif
GtkWidget *spinner;
GdkPixbuf *pixbuf;
GSList *radio_group = NULL;
@@ -1286,26 +495,27 @@ shoot_dialog (GdkScreen **screen)
G_CALLBACK (shoot_radio_button_toggled),
notebook);
-#if (defined (HAVE_X11_XMU_WINUTIL_H) || defined (PLATFORM_OSX))
/* window decorations */
- hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
- gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
- gtk_widget_show (hbox);
-
- toggle = gtk_check_button_new_with_mnemonic (_("Include window _decoration"));
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), shootvals.decorate);
- gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 24);
- gtk_widget_show (toggle);
-
- g_signal_connect (toggle, "toggled",
- G_CALLBACK (gimp_toggle_button_update),
- &shootvals.decorate);
-
- g_object_bind_property (button, "active",
- toggle, "sensitive",
- G_BINDING_SYNC_CREATE);
-
-#endif /* HAVE_X11_XMU_WINUTIL_H */
+ if (capabilities & SCREENSHOT_CAN_SHOOT_DECORATIONS)
+ {
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("Include window _decoration"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ shootvals.decorate);
+ gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 24);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &shootvals.decorate);
+
+ g_object_bind_property (button, "active",
+ toggle, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ }
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
shootvals.shoot_type == SHOOT_WINDOW);
@@ -1325,27 +535,27 @@ shoot_dialog (GdkScreen **screen)
G_CALLBACK (shoot_radio_button_toggled),
notebook);
-#if (defined (HAVE_XFIXES) || defined (PLATFORM_OSX))
/* mouse pointer */
- hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
- gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
- gtk_widget_show (hbox);
-
- toggle = gtk_check_button_new_with_mnemonic (_("Include _mouse pointer"));
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
- shootvals.show_cursor);
- gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 24);
- gtk_widget_show (toggle);
-
- g_signal_connect (toggle, "toggled",
- G_CALLBACK (gimp_toggle_button_update),
- &shootvals.show_cursor);
-
- g_object_bind_property (button, "active",
- toggle, "sensitive",
- G_BINDING_SYNC_CREATE);
-
-#endif
+ if (capabilities & SCREENSHOT_CAN_SHOOT_POINTER)
+ {
+ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ toggle = gtk_check_button_new_with_mnemonic (_("Include _mouse pointer"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ shootvals.show_cursor);
+ gtk_box_pack_start (GTK_BOX (hbox), toggle, TRUE, TRUE, 24);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &shootvals.show_cursor);
+
+ g_object_bind_property (button, "active",
+ toggle, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ }
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
shootvals.shoot_type == SHOOT_ROOT);
@@ -1420,32 +630,10 @@ shoot_dialog (GdkScreen **screen)
return run;
}
-
-/* delay functions */
-
-static void
-shoot_delay (gint delay)
-{
- g_timeout_add (1000, shoot_delay_callback, &delay);
- gtk_main ();
-}
-
-static gboolean
-shoot_delay_callback (gpointer data)
-{
- gint *seconds_left = data;
-
- (*seconds_left)--;
-
- if (!*seconds_left)
- gtk_main_quit ();
-
- return *seconds_left;
-}
-
static gboolean
shoot_quit_timeout (gpointer data)
{
gtk_main_quit ();
+
return FALSE;
}
diff --git a/plug-ins/screenshot/screenshot.h b/plug-ins/screenshot/screenshot.h
new file mode 100644
index 0000000..8841c0b
--- /dev/null
+++ b/plug-ins/screenshot/screenshot.h
@@ -0,0 +1,49 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __SCREENSHOT_H__
+#define __SCREENSHOT_H__
+
+
+typedef enum
+{
+ SCREENSHOT_CAN_SHOOT_DECORATIONS = 0x1 << 0,
+ SCREENSHOT_CAN_SHOOT_POINTER = 0x1 << 1
+} ScreenshotCapabilities;
+
+typedef enum
+{
+ SHOOT_ROOT,
+ SHOOT_REGION,
+ SHOOT_WINDOW
+} ShootType;
+
+typedef struct
+{
+ ShootType shoot_type;
+ gboolean decorate;
+ guint window_id;
+ guint select_delay;
+ gint x1;
+ gint y1;
+ gint x2;
+ gint y2;
+ gboolean show_cursor;
+} ScreenshotValues;
+
+
+#endif /* __SCREENSHOT_H__ */
diff --git a/po-plug-ins/POTFILES.in b/po-plug-ins/POTFILES.in
index 067bb15..ac3749e 100644
--- a/po-plug-ins/POTFILES.in
+++ b/po-plug-ins/POTFILES.in
@@ -215,6 +215,7 @@ plug-ins/print/print-draw-page.c
plug-ins/print/print-page-layout.c
plug-ins/print/print.c
plug-ins/screenshot/screenshot.c
+plug-ins/screenshot/screenshot-x11.c
plug-ins/selection-to-path/pxl-outline.c
plug-ins/selection-to-path/selection-to-path.c
plug-ins/selection-to-path/selection-to-path-dialog.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]