plug-ins: port screenshot to GimpProcedureDialog and gimp_procedure_new2().

A lot less lines for the same functionalities!
This includes improvements too:

* "include-pointer" as new PDB argument;
* settings storage thanks to GimpProcedureDialog;
* it should hopefully work better in non-interactive (though it's untested).

The macOS and Windows screenshots are untested after this change, though they
should still work the same. Please report if there is any problem!
This commit is contained in:
Jehan 2023-06-15 04:12:45 +02:00
parent 60aeff5623
commit f6da799c11
10 changed files with 377 additions and 583 deletions

View File

@ -123,8 +123,7 @@ screenshot_freedesktop_dbus_signal (GDBusProxy *proxy,
}
GimpPDBStatusType
screenshot_freedesktop_shoot (ScreenshotValues *shootvals,
GdkMonitor *monitor,
screenshot_freedesktop_shoot (GdkMonitor *monitor,
GimpImage **image,
GError **error)
{
@ -149,15 +148,6 @@ screenshot_freedesktop_shoot (ScreenshotValues *shootvals,
}
#endif
if (shootvals->shoot_type != SHOOT_ROOT)
{
/* This should not happen. */
return GIMP_PDB_EXECUTION_ERROR;
}
if (shootvals->screenshot_delay > 0)
screenshot_delay (shootvals->screenshot_delay);
options = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
/* "interactive" option will display the options first (otherwise, it
* makes a screenshot first, then proposes to tweak. Since version 2

View File

@ -23,8 +23,7 @@ gboolean screenshot_freedesktop_available (void);
ScreenshotCapabilities screenshot_freedesktop_get_capabilities (void);
GimpPDBStatusType screenshot_freedesktop_shoot (ScreenshotValues *shootvals,
GdkMonitor *monitor,
GimpPDBStatusType screenshot_freedesktop_shoot (GdkMonitor *monitor,
GimpImage **image,
GError **error);

View File

@ -74,8 +74,11 @@ screenshot_osx_get_capabilities (void)
}
GimpPDBStatusType
screenshot_osx_shoot (ScreenshotValues *shootvals,
GdkScreen *screen,
screenshot_osx_shoot (ShootType shoot_type,
guint select_delay,
guint screenshot_delay,
gboolean decorate,
gboolean show_cursor,
GimpImage **image,
GError **error)
{
@ -86,30 +89,30 @@ screenshot_osx_shoot (ScreenshotValues *shootvals,
gchar *quoted;
gchar *command = NULL;
switch (shootvals->shoot_type)
switch (shoot_type)
{
case SHOOT_REGION:
if (shootvals->select_delay > 0)
screenshot_delay (shootvals->select_delay);
if (select_delay > 0)
screenshot_wait_delay (select_delay);
mode = "-is";
break;
case SHOOT_WINDOW:
if (shootvals->select_delay > 0)
screenshot_delay (shootvals->select_delay);
if (select_delay > 0)
screenshot_wait_delay (select_delay);
if (shootvals->decorate)
if (decorate)
mode = "-iwo";
else
mode = "-iw";
if (shootvals->show_cursor)
if (show_cursor)
cursor = "-C";
break;
case SHOOT_ROOT:
mode = " ";
if (shootvals->show_cursor)
if (show_cursor)
cursor = "-C";
break;
@ -118,7 +121,7 @@ screenshot_osx_shoot (ScreenshotValues *shootvals,
break;
}
delay = g_strdup_printf ("-T %i", shootvals->screenshot_delay);
delay = g_strdup_printf ("-T %i", screenshot_delay);
tmpfile = gimp_temp_file ("png");
quoted = g_shell_quote (g_file_peek_path (tmpfile));

View File

@ -25,8 +25,11 @@ gboolean screenshot_osx_available (void);
ScreenshotCapabilities screenshot_osx_get_capabilities (void);
GimpPDBStatusType screenshot_osx_shoot (ScreenshotValues *shootvals,
GdkScreen *screen,
GimpPDBStatusType screenshot_osx_shoot (ShootType shoot_type,
guint select_delay,
guint screenshot_delay,
gboolean decorate,
gboolean show_cursor,
GimpImage **image,
GError **error);

View File

@ -164,33 +164,35 @@ screenshot_win32_get_capabilities (void)
}
GimpPDBStatusType
screenshot_win32_shoot (ScreenshotValues *shootvals,
screenshot_win32_shoot (ShootType shoot_type,
guint screenshot_delay,
gboolean show_cursor,
GdkMonitor *monitor,
GimpImage **_image,
GError **error)
{
GimpPDBStatusType status = GIMP_PDB_EXECUTION_ERROR;
/* leave "shootvals->monitor" alone until somebody patches the code
/* leave "monitor" alone until somebody patches the code
* to be able to get a monitor's color profile
*/
image = _image;
winsnapvals.delay = shootvals->screenshot_delay;
capturePointer = shootvals->show_cursor;
winsnapvals.delay = screenshot_delay;
capturePointer = show_cursor;
if (shootvals->shoot_type == SHOOT_ROOT)
if (shoot_type == SHOOT_ROOT)
{
doCapture (0);
status = GIMP_PDB_SUCCESS;
}
else if (shootvals->shoot_type == SHOOT_WINDOW)
else if (shoot_type == SHOOT_WINDOW)
{
status = 0 == doWindowCapture () ? GIMP_PDB_CANCEL : GIMP_PDB_SUCCESS;
}
else if (shootvals->shoot_type == SHOOT_REGION)
else if (shoot_type == SHOOT_REGION)
{
/* FIXME */
}

View File

@ -33,7 +33,9 @@ gboolean screenshot_win32_available (void);
ScreenshotCapabilities screenshot_win32_get_capabilities (void);
GimpPDBStatusType screenshot_win32_shoot (ScreenshotValues *shootvals,
GimpPDBStatusType screenshot_win32_shoot (ShootType shoot_type,
guint screenshot_delay,
gboolean show_cursor,
GdkMonitor *monitor,
GimpImage **image,
GError **error);

View File

@ -48,7 +48,7 @@
#include "libgimp/stdplugins-intl.h"
static guint32 select_window (ScreenshotValues *shootvals,
static guint32 select_window (GimpProcedureConfig *config,
GdkMonitor *monitor);
static GimpImage * create_image (cairo_surface_t *surface,
cairo_region_t *shape,
@ -58,7 +58,7 @@ static GimpImage * create_image (cairo_surface_t *surface,
/* Allow the user to select a window or a region with the mouse */
static guint32
select_window (ScreenshotValues *shootvals,
select_window (GimpProcedureConfig *config,
GdkMonitor *monitor)
{
Display *x_dpy = GDK_DISPLAY_XDISPLAY (gdk_monitor_get_display (monitor));
@ -76,7 +76,21 @@ select_window (ScreenshotValues *shootvals,
gint mask = ButtonPressMask | ButtonReleaseMask;
gboolean cancel = FALSE;
if (shootvals->shoot_type == SHOOT_REGION)
ShootType shoot_type;
guint screenshot_delay;
gboolean decorate;
gint x1 = 0;
gint y1 = 0;
gint x2 = 0;
gint y2 = 0;
g_object_get (config,
"shoot-type", &shoot_type,
"screenshot-delay", &screenshot_delay,
"include-decoration", &decorate,
NULL);
if (shoot_type == SHOOT_REGION)
mask |= PointerMotionMask;
status = XGrabPointer (x_dpy, x_root, False,
@ -95,7 +109,7 @@ select_window (ScreenshotValues *shootvals,
g_message (_("Error selecting the window"));
}
if (shootvals->shoot_type == SHOOT_REGION)
if (shoot_type == SHOOT_REGION)
{
XGCValues gc_values;
@ -176,12 +190,12 @@ select_window (ScreenshotValues *shootvals,
if (x_win == None)
x_win = x_root;
#ifdef HAVE_X11_XMU_WINUTIL_H
else if (! shootvals->decorate)
else if (! 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;
x2 = x1 = x_event.xbutton.x_root;
y2 = y1 = x_event.xbutton.y_root;
}
buttons++;
@ -191,39 +205,39 @@ select_window (ScreenshotValues *shootvals,
if (buttons > 0)
buttons--;
if (! buttons && shootvals->shoot_type == SHOOT_REGION)
if (! buttons && 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);
x = MIN (x1, x2);
y = MIN (y1, y2);
w = ABS (x2 - x1);
h = ABS (y2 - 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;
x2 = x_event.xbutton.x_root;
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);
x = MIN (x1, x2);
y = MIN (y1, y2);
w = ABS (x2 - x1);
h = ABS (y2 - 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;
x2 = x_event.xmotion.x_root;
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);
x = MIN (x1, x2);
y = MIN (y1, y2);
w = ABS (x2 - x1);
h = ABS (y2 - y1);
if (w > 0 && h > 0)
XDrawRectangle (x_dpy, x_root, x_gc, x, y, w, h);
@ -283,6 +297,8 @@ select_window (ScreenshotValues *shootvals,
if (x_gc != NULL)
XFreeGC (x_dpy, x_gc);
g_object_set (config, "x1", x1, "x2", x2, "y1", y1, "y2", y2, NULL);
return x_win;
}
@ -556,7 +572,7 @@ screenshot_x11_get_capabilities (void)
}
GimpPDBStatusType
screenshot_x11_shoot (ScreenshotValues *shootvals,
screenshot_x11_shoot (GimpProcedureConfig *config,
GdkMonitor *monitor,
GimpImage **image,
GError **error)
@ -573,33 +589,55 @@ screenshot_x11_shoot (ScreenshotValues *shootvals,
gchar *name = NULL;
gint x, y;
ShootType shoot_type;
guint screenshot_delay;
guint select_delay;
gboolean show_cursor;
guint window_id = 0;
g_object_get (config,
"shoot-type", &shoot_type,
"screenshot-delay", &screenshot_delay,
"selection-delay", &select_delay,
"include-pointer", &show_cursor,
NULL);
/* use default screen if we are running non-interactively */
if (monitor == NULL)
monitor = gdk_display_get_monitor (gdk_display_get_default (), 0);
if (shootvals->shoot_type != SHOOT_ROOT && ! shootvals->window_id)
if (shoot_type != SHOOT_ROOT)
{
if (shootvals->select_delay > 0)
screenshot_delay (shootvals->select_delay);
if (select_delay > 0)
screenshot_wait_delay (select_delay);
shootvals->window_id = select_window (shootvals, monitor);
window_id = select_window (config, monitor);
if (! shootvals->window_id)
if (! window_id)
return GIMP_PDB_CANCEL;
}
if (shootvals->screenshot_delay > 0)
screenshot_delay (shootvals->screenshot_delay);
if (screenshot_delay > 0)
screenshot_wait_delay (screenshot_delay);
display = gdk_monitor_get_display (monitor);
screen = gdk_display_get_default_screen (display);
if (shootvals->shoot_type == SHOOT_REGION)
if (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);
gint x1;
gint y1;
gint x2;
gint y2;
g_object_get (config,
"x1", &x1, "y1", &y1,
"x2", &x2, "y2", &y2,
NULL);
rect.x = MIN (x1, x2);
rect.y = MIN (y1, y2);
rect.width = ABS (x2 - x1);
rect.height = ABS (y2 - y1);
monitor = gdk_display_get_monitor_at_point (display,
rect.x + rect.width / 2,
@ -607,7 +645,7 @@ screenshot_x11_shoot (ScreenshotValues *shootvals,
}
else
{
if (shootvals->shoot_type == SHOOT_ROOT)
if (shoot_type == SHOOT_ROOT)
{
window = gdk_screen_get_root_window (screen);
@ -615,9 +653,7 @@ screenshot_x11_shoot (ScreenshotValues *shootvals,
}
else
{
window = gdk_x11_window_foreign_new_for_display (display,
shootvals->window_id);
window = gdk_x11_window_foreign_new_for_display (display, window_id);
monitor = gdk_display_get_monitor_at_window (display, window);
}
@ -658,11 +694,11 @@ screenshot_x11_shoot (ScreenshotValues *shootvals,
gdk_display_beep (display);
if (shootvals->shoot_type == SHOOT_WINDOW)
if (shoot_type == SHOOT_WINDOW)
{
name = window_get_title (display, shootvals->window_id);
name = window_get_title (display, window_id);
shape = window_get_shape (monitor, shootvals->window_id);
shape = window_get_shape (monitor, window_id);
if (shape)
cairo_region_translate (shape, x - rect.x, y - rect.y);
@ -680,8 +716,7 @@ screenshot_x11_shoot (ScreenshotValues *shootvals,
/* 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->shoot_type == SHOOT_WINDOW) && shootvals->show_cursor)
if ((shoot_type == SHOOT_ROOT || shoot_type == SHOOT_WINDOW) && show_cursor)
add_cursor_image (*image, display);
profile = gimp_monitor_get_color_profile (monitor);

View File

@ -25,7 +25,7 @@ gboolean screenshot_x11_available (void);
ScreenshotCapabilities screenshot_x11_get_capabilities (void);
GimpPDBStatusType screenshot_x11_shoot (ScreenshotValues *shootvals,
GimpPDBStatusType screenshot_x11_shoot (GimpProcedureConfig *config,
GdkMonitor *monitor,
GimpImage **image,
GError **error);

View File

@ -64,14 +64,17 @@ static GimpProcedure * screenshot_create_procedure (GimpPlugIn *plug_
const gchar *name);
static GimpValueArray * screenshot_run (GimpProcedure *procedure,
const GimpValueArray *args,
GimpProcedureConfig *config,
gpointer run_data);
static GimpPDBStatusType shoot (GdkMonitor *monitor,
GimpImage **image,
GimpProcedureConfig *config,
GError **error);
static gboolean shoot_dialog (GdkMonitor **monitor);
static gboolean shoot_dialog (GimpProcedure *procedure,
GimpProcedureConfig *config,
GdkMonitor **monitor);
static gboolean shoot_quit_timeout (gpointer data);
static gboolean shoot_delay_timeout (gpointer data);
@ -84,24 +87,6 @@ DEFINE_STD_SET_I18N
static ScreenshotBackend backend = SCREENSHOT_BACKEND_NONE;
static ScreenshotCapabilities capabilities = 0;
static GtkWidget *select_delay_grid = NULL;
static GtkWidget *shot_delay_grid = NULL;
static ScreenshotValues shootvals =
{
SHOOT_WINDOW, /* root window */
TRUE, /* include WM decorations */
0, /* window ID */
0, /* select delay */
0, /* screenshot delay */
0, /* coords of region dragged out by pointer */
0,
0,
0,
FALSE, /* show cursor */
SCREENSHOT_PROFILE_POLICY_MONITOR
};
static void
screenshot_class_init (ScreenshotClass *klass)
@ -132,7 +117,7 @@ screenshot_create_procedure (GimpPlugIn *plug_in,
if (! strcmp (name, PLUG_IN_PROC))
{
procedure = gimp_procedure_new (plug_in, name,
procedure = gimp_procedure_new2 (plug_in, name,
GIMP_PDB_PROC_TYPE_PLUGIN,
screenshot_run, NULL, NULL);
@ -176,10 +161,10 @@ screenshot_create_procedure (GimpPlugIn *plug_in,
G_PARAM_READWRITE);
GIMP_PROC_ARG_INT (procedure, "shoot-type",
"Shoot type",
_("Shoot _area"),
"The shoot type { SHOOT-WINDOW (0), SHOOT-ROOT (1), "
"SHOOT-REGION (2) }",
0, 2, 0,
0, 2, SHOOT_WINDOW,
G_PARAM_READWRITE);
GIMP_PROC_ARG_INT (procedure, "x1",
@ -206,6 +191,42 @@ screenshot_create_procedure (GimpPlugIn *plug_in,
G_MININT, G_MAXINT, 0,
G_PARAM_READWRITE);
GIMP_PROC_ARG_BOOLEAN (procedure, "include-pointer",
"Include _mouse pointer",
"Your pointing device's cursor will be part of the image",
FALSE,
G_PARAM_READWRITE);
/* Since no backends allow window screenshot non-interactively so far, no
* need to expose this argument to the API.
*/
GIMP_PROC_AUX_ARG_BOOLEAN (procedure, "include-decoration",
"Include window _decoration",
"Title bar, window borders and shadow will be part of the image",
#ifdef PLATFORM_OSX
/* on OS X, this just means shoot the shadow, default to nope */
FALSE,
#else
TRUE,
#endif
G_PARAM_READWRITE);
GIMP_PROC_AUX_ARG_INT (procedure, "selection-delay",
"Selection d_elay",
"Delay before selection of the window or the region",
0, 20, 0,
G_PARAM_READWRITE);
GIMP_PROC_AUX_ARG_INT (procedure, "screenshot-delay",
"Screenshot dela_y",
"Delay before snapping the screenshot",
0, 20, 0,
G_PARAM_READWRITE);
GIMP_PROC_AUX_ARG_INT (procedure, "color-profile",
_("Color _Profile"),
"{ SCREENSHOT_PROFILE_POLICY_MONITOR, (0), "
"SCREENSHOT_PROFILE_POLICY_MONITOR, (1) } ",
0, 1, SCREENSHOT_PROFILE_POLICY_MONITOR,
G_PARAM_READWRITE);
GIMP_PROC_VAL_IMAGE (procedure, "image",
"Image",
"Output image",
@ -218,7 +239,7 @@ screenshot_create_procedure (GimpPlugIn *plug_in,
static GimpValueArray *
screenshot_run (GimpProcedure *procedure,
const GimpValueArray *args,
GimpProcedureConfig *config,
gpointer run_data)
{
GimpValueArray *return_vals;
@ -228,19 +249,29 @@ screenshot_run (GimpProcedure *procedure,
GimpImage *image = NULL;
GError *error = NULL;
ShootType shoot_type;
ScreenshotProfilePolicy profile_policy;
gegl_init (NULL, NULL);
run_mode = GIMP_VALUES_GET_ENUM (args, 0);
gimp_ui_init (PLUG_IN_BINARY);
g_object_get (config,
"run-mode", &run_mode,
"shoot-type", &shoot_type,
NULL);
if (! gdk_init_check (NULL, NULL))
{
g_set_error_literal (&error, GIMP_PLUG_IN_ERROR, 0, _("GDK initialization failed."));
return gimp_procedure_new_return_values (procedure,
GIMP_PDB_EXECUTION_ERROR,
error);
}
#ifdef PLATFORM_OSX
if (! backend && screenshot_osx_available ())
{
backend = SCREENSHOT_BACKEND_OSX;
capabilities = screenshot_osx_get_capabilities ();
/* on OS X, this just means shoot the shadow, default to nope */
shootvals.decorate = FALSE;
}
#endif
@ -265,24 +296,36 @@ screenshot_run (GimpProcedure *procedure,
capabilities = screenshot_freedesktop_get_capabilities ();
}
if (backend == SCREENSHOT_BACKEND_NONE)
{
g_set_error_literal (&error, GIMP_PLUG_IN_ERROR, 0,
_("No screenshot backends available."));
return gimp_procedure_new_return_values (procedure,
GIMP_PDB_EXECUTION_ERROR,
error);
}
/* how are we running today? */
switch (run_mode)
{
case GIMP_RUN_INTERACTIVE:
/* Possibly retrieve data from a previous run */
gimp_get_data (PLUG_IN_PROC, &shootvals);
shootvals.window_id = 0;
gimp_ui_init (PLUG_IN_BINARY);
if ((shootvals.shoot_type == SHOOT_WINDOW &&
/* In interactive mode, coords are always reset. */
g_object_set (config,
"x1", 0, "y1", 0,
"x2", 0, "y2", 0,
NULL);
if ((shoot_type == SHOOT_WINDOW &&
! (capabilities & SCREENSHOT_CAN_SHOOT_WINDOW)) ||
(shootvals.shoot_type == SHOOT_REGION &&
(shoot_type == SHOOT_REGION &&
! (capabilities & SCREENSHOT_CAN_SHOOT_REGION)))
{
/* Shoot root is the only type of shoot which is definitely
* shared by all screenshot backends (basically just snap the
* whole display setup).
*/
shootvals.shoot_type = SHOOT_ROOT;
g_object_set (config, "shoot-type", SHOOT_ROOT, NULL);
}
/* Get information from the dialog. Freedesktop portal comes with
@ -290,7 +333,7 @@ screenshot_run (GimpProcedure *procedure,
*/
if (backend != SCREENSHOT_BACKEND_FREEDESKTOP)
{
if (! shoot_dialog (&monitor))
if (! shoot_dialog (procedure, config, &monitor))
status = GIMP_PDB_CANCEL;
}
else
@ -304,30 +347,20 @@ screenshot_run (GimpProcedure *procedure,
break;
case GIMP_RUN_NONINTERACTIVE:
shootvals.shoot_type = GIMP_VALUES_GET_INT (args, 1);
shootvals.window_id = GIMP_VALUES_GET_INT (args, 2);
shootvals.select_delay = 0;
shootvals.x1 = GIMP_VALUES_GET_INT (args, 3);
shootvals.y1 = GIMP_VALUES_GET_INT (args, 4);
shootvals.x2 = GIMP_VALUES_GET_INT (args, 5);
shootvals.y2 = GIMP_VALUES_GET_INT (args, 6);
if (! gdk_init_check (NULL, NULL))
status = GIMP_PDB_CALLING_ERROR;
if (! (capabilities & SCREENSHOT_CAN_PICK_NONINTERACTIVELY))
{
if (shootvals.shoot_type == SHOOT_WINDOW ||
shootvals.shoot_type == SHOOT_REGION)
if (backend == SCREENSHOT_BACKEND_FREEDESKTOP)
{
/* With portals, even the basic full screenshot is interactive. */
status = GIMP_PDB_CALLING_ERROR;
}
else if (! (capabilities & SCREENSHOT_CAN_PICK_NONINTERACTIVELY))
{
if (shoot_type == SHOOT_WINDOW ||
shoot_type == SHOOT_REGION)
status = GIMP_PDB_CALLING_ERROR;
}
break;
case GIMP_RUN_WITH_LAST_VALS:
/* Possibly retrieve data from a previous run */
gimp_get_data (PLUG_IN_PROC, &shootvals);
break;
default:
@ -335,9 +368,7 @@ screenshot_run (GimpProcedure *procedure,
}
if (status == GIMP_PDB_SUCCESS)
{
status = shoot (monitor, &image, &error);
}
status = shoot (monitor, &image, config, &error);
if (status == GIMP_PDB_SUCCESS)
{
@ -345,7 +376,14 @@ screenshot_run (GimpProcedure *procedure,
gimp_image_undo_disable (image);
if (shootvals.profile_policy == SCREENSHOT_PROFILE_POLICY_SRGB)
g_object_get (config,
"color-profile", &profile_policy,
NULL);
if (run_mode == GIMP_RUN_NONINTERACTIVE)
profile_policy = SCREENSHOT_PROFILE_POLICY_MONITOR;
if (profile_policy == SCREENSHOT_PROFILE_POLICY_SRGB)
{
GimpColorProfile *srgb_profile = gimp_color_profile_new_rgb_srgb ();
@ -374,13 +412,15 @@ screenshot_run (GimpProcedure *procedure,
if (run_mode == GIMP_RUN_INTERACTIVE)
{
/* Store variable states for next run */
gimp_set_data (PLUG_IN_PROC, &shootvals, sizeof (ScreenshotValues));
guint select_delay;
gimp_display_new (image);
g_object_get (config,
"selection-delay", &select_delay,
NULL);
/* Give some sort of feedback that the shot is done */
if (shootvals.select_delay > 0)
if (select_delay > 0)
{
gdk_display_beep (gdk_monitor_get_display (monitor));
/* flush so the beep makes it to the server */
@ -403,24 +443,43 @@ screenshot_run (GimpProcedure *procedure,
static GimpPDBStatusType
shoot (GdkMonitor *monitor,
GimpImage **image,
GimpProcedureConfig *config,
GError **error)
{
ShootType shoot_type;
gboolean decorate;
guint select_delay;
guint screenshot_delay;
gboolean show_cursor;
g_object_get (config,
"shoot-type", &shoot_type,
"screenshot-delay", &screenshot_delay,
"selection-delay", &select_delay,
"include-decoration", &decorate,
"include-pointer", &show_cursor,
NULL);
#ifdef PLATFORM_OSX
if (backend == SCREENSHOT_BACKEND_OSX)
return screenshot_osx_shoot (&shootvals, monitor, image, error);
return screenshot_osx_shoot (shoot_type,
select_delay, screenshot_delay,
decorate, show_cursor,
image, error);
#endif
#ifdef G_OS_WIN32
if (backend == SCREENSHOT_BACKEND_WIN32)
return screenshot_win32_shoot (&shootvals, monitor, image, error);
return screenshot_win32_shoot (shoot_type, screenshot_delay, show_cursor,
monitor, image, error);
#endif
if (backend == SCREENSHOT_BACKEND_FREEDESKTOP)
return screenshot_freedesktop_shoot (&shootvals, monitor, image, error);
return screenshot_freedesktop_shoot (monitor, image, error);
#ifdef GDK_WINDOWING_X11
if (backend == SCREENSHOT_BACKEND_X11)
return screenshot_x11_shoot (&shootvals, monitor, image, error);
return screenshot_x11_shoot (config, monitor, image, error);
#endif
return GIMP_PDB_CALLING_ERROR; /* silence compiler */
@ -429,416 +488,133 @@ shoot (GdkMonitor *monitor,
/* Screenshot dialog */
static void
shoot_dialog_add_hint (GtkNotebook *notebook,
ShootType type,
const gchar *hint)
{
GtkWidget *label;
label = g_object_new (GTK_TYPE_LABEL,
"label", hint,
"wrap", TRUE,
"justify", GTK_JUSTIFY_LEFT,
"xalign", 0.0,
"yalign", 0.0,
NULL);
gimp_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
-1);
gtk_notebook_insert_page (notebook, label, NULL, type);
gtk_widget_show (label);
}
static void
shoot_radio_button_toggled (GtkWidget *widget,
GtkWidget *notebook)
{
gimp_radio_button_update (widget, &shootvals.shoot_type);
if (select_delay_grid)
{
if (shootvals.shoot_type == SHOOT_ROOT ||
(shootvals.shoot_type == SHOOT_WINDOW &&
! (capabilities & SCREENSHOT_CAN_PICK_WINDOW)))
{
gtk_widget_hide (select_delay_grid);
}
else
{
gtk_widget_show (select_delay_grid);
}
}
if (shot_delay_grid)
{
if (shootvals.shoot_type == SHOOT_WINDOW &&
(capabilities & SCREENSHOT_CAN_PICK_WINDOW) &&
! (capabilities & SCREENSHOT_CAN_DELAY_WINDOW_SHOT))
{
gtk_widget_hide (shot_delay_grid);
}
else
{
gtk_widget_show (shot_delay_grid);
}
}
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), shootvals.shoot_type);
}
static gboolean
shoot_dialog (GdkMonitor **monitor)
shoot_dialog (GimpProcedure *procedure,
GimpProcedureConfig *config,
GdkMonitor **monitor)
{
GtkWidget *dialog;
GtkWidget *main_vbox;
GtkWidget *notebook1;
GtkWidget *notebook2;
GtkWidget *frame;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *label;
GtkWidget *button;
GtkWidget *toggle;
GtkWidget *spinner;
GtkWidget *grid;
GSList *radio_group = NULL;
GtkAdjustment *adj;
GtkListStore *store;
GimpValueArray *values;
GValue value = G_VALUE_INIT;
gboolean run;
GtkWidget *cursor_toggle = NULL;
dialog = gimp_dialog_new (_("Screenshot"), PLUG_IN_ROLE,
NULL, 0,
gimp_standard_help_func, PLUG_IN_PROC,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("S_nap"), GTK_RESPONSE_OK,
dialog = gimp_procedure_dialog_new (procedure,
GIMP_PROCEDURE_CONFIG (config),
_("Screenshot"));
gimp_procedure_dialog_set_ok_label (GIMP_PROCEDURE_DIALOG (dialog), _("S_nap"));
store = gimp_int_store_new (_("Take a screenshot of a single window"), SHOOT_WINDOW,
_("Take a screenshot of the entire screen"), SHOOT_ROOT,
_("Select a region to grab"), SHOOT_REGION,
NULL);
gimp_procedure_dialog_get_int_combo (GIMP_PROCEDURE_DIALOG (dialog),
"shoot-type", GIMP_INT_STORE (store));
gimp_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
-1);
main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
main_vbox, FALSE, FALSE, 0);
gtk_widget_show (main_vbox);
/* Create delay hints notebooks early */
notebook1 = g_object_new (GTK_TYPE_NOTEBOOK,
"show-border", FALSE,
"show-tabs", FALSE,
NULL);
notebook2 = g_object_new (GTK_TYPE_NOTEBOOK,
"show-border", FALSE,
"show-tabs", FALSE,
NULL);
/* Area */
frame = gimp_frame_new (_("Area"));
gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_container_add (GTK_CONTAINER (frame), vbox);
gtk_widget_show (vbox);
/* Single window */
if (capabilities & SCREENSHOT_CAN_SHOOT_WINDOW)
if (capabilities & SCREENSHOT_CAN_SHOOT_POINTER ||
capabilities & SCREENSHOT_CAN_SHOOT_DECORATIONS)
{
button = gtk_radio_button_new_with_mnemonic (radio_group,
_("Take a screenshot of "
"a single _window"));
radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
if (! (capabilities & SCREENSHOT_CAN_SHOOT_POINTER))
gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dialog),
"contents-box",
"include-decoration",
NULL);
else if (! (capabilities & SCREENSHOT_CAN_SHOOT_DECORATIONS))
gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dialog),
"contents-box",
"include-pointer",
NULL);
else
gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dialog),
"contents-box",
"include-decoration",
"include-pointer",
NULL);
g_object_set_data (G_OBJECT (button), "gimp-item-data",
GINT_TO_POINTER (SHOOT_WINDOW));
gimp_procedure_dialog_get_label (GIMP_PROCEDURE_DIALOG (dialog),
"contents-frame-title",
_("Contents"));
gimp_procedure_dialog_fill_frame (GIMP_PROCEDURE_DIALOG (dialog),
"contents-frame", "contents-frame-title",
FALSE, "contents-box");
g_signal_connect (button, "toggled",
G_CALLBACK (shoot_radio_button_toggled),
notebook1);
g_signal_connect (button, "toggled",
G_CALLBACK (shoot_radio_button_toggled),
notebook2);
/* Window decorations */
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);
values = gimp_value_array_new (1);
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, SHOOT_WINDOW);
gimp_value_array_append (values, &value);
g_value_unset (&value);
gimp_procedure_dialog_set_sensitive_if_in (GIMP_PROCEDURE_DIALOG (dialog),
"include-decoration",
NULL, "shoot-type",
values, TRUE);
}
/* Mouse pointer */
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);
cursor_toggle = gtk_check_button_new_with_mnemonic (_("Include _mouse pointer"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cursor_toggle),
shootvals.show_cursor);
gtk_box_pack_start (GTK_BOX (hbox), cursor_toggle, TRUE, TRUE, 24);
gtk_widget_show (cursor_toggle);
g_signal_connect (cursor_toggle, "toggled",
G_CALLBACK (gimp_toggle_button_update),
&shootvals.show_cursor);
g_object_bind_property (button, "active",
cursor_toggle, "sensitive",
G_BINDING_SYNC_CREATE);
values = gimp_value_array_new (1);
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, SHOOT_REGION);
gimp_value_array_append (values, &value);
g_value_unset (&value);
gimp_procedure_dialog_set_sensitive_if_in (GIMP_PROCEDURE_DIALOG (dialog),
"include-pointer",
NULL, "shoot-type",
values, FALSE);
}
}
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
shootvals.shoot_type == SHOOT_WINDOW);
}
/* Whole screen */
button = gtk_radio_button_new_with_mnemonic (radio_group,
_("Take a screenshot of "
"the entire _screen"));
radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_object_set_data (G_OBJECT (button), "gimp-item-data",
GINT_TO_POINTER (SHOOT_ROOT));
g_signal_connect (button, "toggled",
G_CALLBACK (shoot_radio_button_toggled),
notebook1);
g_signal_connect (button, "toggled",
G_CALLBACK (shoot_radio_button_toggled),
notebook2);
/* Mouse pointer */
if (capabilities & SCREENSHOT_CAN_SHOOT_POINTER)
if (capabilities & SCREENSHOT_CAN_DELAY_WINDOW_SHOT)
{
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);
if (cursor_toggle)
{
g_object_bind_property (cursor_toggle, "active",
toggle, "active",
G_BINDING_BIDIRECTIONAL);
}
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);
/* Dragged region */
if (capabilities & SCREENSHOT_CAN_SHOOT_REGION)
{
button = gtk_radio_button_new_with_mnemonic (radio_group,
_("Select a _region to grab"));
radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
shootvals.shoot_type == SHOOT_REGION);
gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_object_set_data (G_OBJECT (button), "gimp-item-data",
GINT_TO_POINTER (SHOOT_REGION));
g_signal_connect (button, "toggled",
G_CALLBACK (shoot_radio_button_toggled),
notebook1);
g_signal_connect (button, "toggled",
G_CALLBACK (shoot_radio_button_toggled),
notebook2);
}
frame = gimp_frame_new (_("Delay"));
gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
gtk_widget_show (frame);
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
gtk_container_add (GTK_CONTAINER (frame), vbox);
gtk_widget_show (vbox);
/* Selection delay */
grid = gtk_grid_new ();
select_delay_grid = grid;
gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
/* Check if this delay must be hidden from start. */
if (shootvals.shoot_type == SHOOT_REGION ||
(shootvals.shoot_type == SHOOT_WINDOW &&
capabilities & SCREENSHOT_CAN_PICK_WINDOW))
{
gtk_widget_show (select_delay_grid);
}
label = gtk_label_new (_("Selection delay: "));
gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
gtk_widget_show (label);
adj = gtk_adjustment_new (shootvals.select_delay,
0.0, 100.0, 1.0, 5.0, 0.0);
spinner = gimp_spin_button_new (adj, 0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
gtk_grid_attach (GTK_GRID (grid), spinner, 1, 0, 1, 1);
gtk_widget_show (spinner);
g_signal_connect (adj, "value-changed",
G_CALLBACK (gimp_int_adjustment_update),
&shootvals.select_delay);
/* translators: this is the unit label of a spinbutton */
label = gtk_label_new (_("seconds"));
gtk_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
gtk_widget_set_hexpand (label, TRUE);
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_show (label);
/* Selection delay hints */
gtk_grid_attach (GTK_GRID (grid), notebook1, 0, 1, 3, 1);
gtk_widget_show (notebook1);
/* No selection delay for full-screen. */
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_ROOT, "");
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_REGION,
_("After the delay, drag your mouse to select "
"the region for the screenshot."));
#ifdef G_OS_WIN32
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_WINDOW,
_("Click in a window to snap it after delay."));
#else
if (capabilities & SCREENSHOT_CAN_PICK_WINDOW)
{
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_WINDOW,
_("At the end of the delay, click in a window "
"to snap it."));
}
else
{
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook1), SHOOT_WINDOW, "");
}
#endif
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook1), shootvals.shoot_type);
/* Screenshot delay */
grid = gtk_grid_new ();
shot_delay_grid = grid;
gtk_box_pack_start (GTK_BOX (vbox), grid, FALSE, FALSE, 0);
if (shootvals.shoot_type != SHOOT_WINDOW ||
! (capabilities & SCREENSHOT_CAN_PICK_WINDOW) ||
(capabilities & SCREENSHOT_CAN_DELAY_WINDOW_SHOT))
{
gtk_widget_show (grid);
}
label = gtk_label_new_with_mnemonic (_("Screenshot dela_y: "));
gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
gtk_widget_show (label);
adj = gtk_adjustment_new (shootvals.screenshot_delay,
0.0, 100.0, 1.0, 5.0, 0.0);
spinner = gimp_spin_button_new (adj, 0, 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
gtk_grid_attach (GTK_GRID (grid), spinner, 1, 0, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), GTK_WIDGET (spinner));
gtk_widget_show (spinner);
g_signal_connect (adj, "value-changed",
G_CALLBACK (gimp_int_adjustment_update),
&shootvals.screenshot_delay);
/* translators: this is the unit label of a spinbutton */
label = gtk_label_new (_("seconds"));
gtk_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
gtk_widget_set_hexpand (label, TRUE);
gtk_widget_set_halign (label, GTK_ALIGN_START);
gtk_widget_show (label);
/* Screenshot delay hints */
gtk_grid_attach (GTK_GRID (grid), notebook2, 0, 1, 3, 1);
gtk_widget_show (notebook2);
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_ROOT,
_("After the delay, the screenshot is taken."));
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_REGION,
_("Once the region is selected, it will be "
"captured after this delay."));
if (capabilities & SCREENSHOT_CAN_PICK_WINDOW)
{
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_WINDOW,
_("Once the window is selected, it will be "
"captured after this delay."));
}
else
{
shoot_dialog_add_hint (GTK_NOTEBOOK (notebook2), SHOOT_WINDOW,
_("After the delay, the active window "
"will be captured."));
}
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook2), shootvals.shoot_type);
/* Color profile */
frame = gimp_int_radio_group_new (TRUE,
_("Color Profile"),
G_CALLBACK (gimp_radio_button_update),
&shootvals.profile_policy, NULL,
shootvals.profile_policy,
_("Tag image with _monitor profile"),
SCREENSHOT_PROFILE_POLICY_MONITOR,
NULL,
_("Convert image to sR_GB"),
SCREENSHOT_PROFILE_POLICY_SRGB,
NULL,
gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dialog),
"delay-box",
"selection-delay",
"screenshot-delay",
NULL);
gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
gtk_widget_show (dialog);
run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
if (run)
{
/* get the screen on which we are running */
*monitor = gimp_widget_get_monitor (dialog);
values = gimp_value_array_new (1);
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, SHOOT_ROOT);
gimp_value_array_append (values, &value);
g_value_unset (&value);
gimp_procedure_dialog_set_sensitive_if_in (GIMP_PROCEDURE_DIALOG (dialog),
"selection-delay",
NULL, "shoot-type",
values, FALSE);
}
else
{
gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dialog),
"delay-box", "screenshot-delay", NULL);
}
gimp_procedure_dialog_get_label (GIMP_PROCEDURE_DIALOG (dialog),
"delay-frame-title",
_("Delay"));
gimp_procedure_dialog_fill_frame (GIMP_PROCEDURE_DIALOG (dialog),
"delay-frame", "delay-frame-title",
FALSE, "delay-box");
}
gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog), "shoot-type", NULL);
store = gimp_int_store_new (_("Tag image with monitor profile"), SCREENSHOT_PROFILE_POLICY_MONITOR,
_("Convert image with sRGB"), SCREENSHOT_PROFILE_POLICY_SRGB,
NULL);
gimp_procedure_dialog_get_int_combo (GIMP_PROCEDURE_DIALOG (dialog),
"color-profile", GIMP_INT_STORE (store));
if ((capabilities & SCREENSHOT_CAN_SHOOT_POINTER) ||
(capabilities & SCREENSHOT_CAN_SHOOT_DECORATIONS))
gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog), "contents-frame", NULL);
if (capabilities & SCREENSHOT_CAN_DELAY_WINDOW_SHOT)
gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog), "delay-frame", NULL);
gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog), "color-profile", NULL);
run = gimp_procedure_dialog_run (GIMP_PROCEDURE_DIALOG (dialog));
gtk_widget_destroy (dialog);
@ -880,7 +656,7 @@ shoot_delay_timeout (gpointer data)
/* public functions */
void
screenshot_delay (gint seconds)
screenshot_wait_delay (gint seconds)
{
g_timeout_add (1000, shoot_delay_timeout, &seconds);
gtk_main ();

View File

@ -34,7 +34,7 @@ typedef enum
SCREENSHOT_CAN_SHOOT_POINTER = 0x1 << 1,
SCREENSHOT_CAN_PICK_NONINTERACTIVELY = 0x1 << 2,
SCREENSHOT_CAN_SHOOT_REGION = 0x1 << 3,
/* SHOOT_WINDOW mode only: it window selection requires active click. */
/* SHOOT_WINDOW mode only: if window selection requires active click. */
SCREENSHOT_CAN_PICK_WINDOW = 0x1 << 4,
/* SHOOT_WINDOW + SCREENSHOT_CAN_PICK_WINDOW only: if a delay can be
* inserted in-between selection click and actual snapshot. */
@ -55,24 +55,8 @@ typedef enum
SHOOT_REGION
} ShootType;
typedef struct
{
ShootType shoot_type;
gboolean decorate;
guint window_id;
GdkMonitor *monitor;
guint select_delay;
guint screenshot_delay;
gint x1;
gint y1;
gint x2;
gint y2;
gboolean show_cursor;
ScreenshotProfilePolicy profile_policy;
} ScreenshotValues;
void screenshot_delay (gint seconds);
void screenshot_wait_delay (gint seconds);
#endif /* __SCREENSHOT_H__ */