summaryrefslogtreecommitdiff
path: root/src/xscanimage.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/xscanimage.c')
-rw-r--r--src/xscanimage.c2224
1 files changed, 2224 insertions, 0 deletions
diff --git a/src/xscanimage.c b/src/xscanimage.c
new file mode 100644
index 0000000..a36324f
--- /dev/null
+++ b/src/xscanimage.c
@@ -0,0 +1,2224 @@
+/* xscanimage -- a graphical scanner-oriented SANE frontend
+
+ Authors:
+ Andreas Beck <becka@sunserver1.rz.uni-duesseldorf.de>
+ Tristan Tarrant <ttarrant@etnoteam.it>
+ David Mosberger-Tang <davidm@azstarnet.com>
+
+ Copyright (C) 1997, 1998 Andreas Beck, Tristan Tarrant, and David
+ Mosberger
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef _AIX
+# include "../include/lalloca.h" /* MUST come first for AIX! */
+#endif
+
+#include "../include/sane/config.h"
+#include "../include/lalloca.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <gtkglue.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+
+#include <sane/sane.h>
+#include <sane/saneopts.h>
+#include "../include/sane/sanei.h"
+
+#define BACKEND_NAME xscanimage
+#include "../include/sane/sanei_debug.h"
+
+#include <progress.h>
+#include <preferences.h>
+#include <preview.h>
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define OUTFILENAME "out.pnm"
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+
+#include <libgimp/gimp.h>
+
+# ifdef HAVE_LIBGIMP_GIMPFEATURES_H
+# include <libgimp/gimpfeatures.h>
+# elif defined(ENABLE_GIMP_1_2)
+# define GIMP_CHECK_VERSION(major, minor, micro) 0
+# endif /* HAVE_LIBGIMP_GIMPFEATURES_H */
+
+#ifndef ENABLE_GIMP_1_2
+# define GIMP_HAVE_RESOLUTION_INFO
+#endif /* !ENABLE_GIMP_1_2 */
+
+# ifdef GIMP_CHECK_VERSION
+# if GIMP_CHECK_VERSION(1,1,25)
+/* ok, we have the new gimp interface */
+# else
+/* we have the old gimp interface and need the compatibility header file */
+# include "xscanimage-gimp-1_0-compat.h"
+# endif
+# else
+# ifdef ENABLE_GIMP_1_2
+/* we have the old gimp interface and need the compatibility header file */
+# include "xscanimage-gimp-1_0-compat.h"
+# endif /* ENABLE_GIMP_1_2 */
+# endif
+
+static void query (void);
+#ifndef ENABLE_GIMP_1_2
+static void run (const gchar * name, gint nparams, const GimpParam * param,
+ gint * nreturn_vals, GimpParam ** return_vals);
+#else
+static void run (char *name, int nparams, GimpParam * param,
+ int *nreturn_vals, GimpParam ** return_vals);
+#endif /* !ENABLE_GIMP_1_2 */
+
+GimpPlugInInfo PLUG_IN_INFO = {
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+#endif /* HAVE_LIBGIMP_GIMP_H */
+
+#define DBG_fatal 0
+#define DBG_error 1
+#define DBG_warning 2
+#define DBG_info 3
+#define DBG_debug 4
+
+enum
+{
+ STANDALONE, SANE_GIMP_EXTENSION
+};
+
+static struct
+{
+ GtkWidget *shell;
+ GtkWidget *menubar;
+ GtkWidget *hruler;
+ GtkWidget *vruler;
+ GtkWidget *info_label;
+ GtkWidget *preview_button;
+ GtkWidget *scan_button;
+ Preview *preview;
+ gint32 mode;
+ /* various scanning related state: */
+ size_t num_bytes;
+ size_t bytes_read;
+ Progress_t *progress;
+ int input_tag;
+ SANE_Parameters param;
+ int x, y;
+ /* for standalone mode: */
+ GtkWidget *filename_entry;
+ FILE *out;
+ long header_size;
+ gboolean have_odd_byte;
+ guint8 odd_byte;
+#ifdef HAVE_LIBGIMP_GIMP_H
+ /* for GIMP mode: */
+ gint32 image_ID;
+ GimpDrawable *drawable;
+ guchar *tile;
+ unsigned tile_offset;
+ GimpPixelRgn region;
+ int first_frame; /* used for RED/GREEN/BLUE frames */
+#endif
+}
+scan_win;
+
+static const char *prog_name;
+static GtkWidget *choose_device_dialog;
+static GSGDialog *dialog;
+static const SANE_Device **devlist;
+static gint seldev = -1; /* The selected device */
+static gint defdev = -1; /* The default device */
+static gint ndevs; /* The number of available devices */
+static gboolean little_endian; /* Is this computer little-endian ? */
+static struct option long_options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {0, 0, 0, 0}
+};
+
+
+static int gtk_quit_flag; /* Call gtk_main_quit() only if at least one device
+ device is found. */
+
+/* forward declarations: */
+
+int main (int argc, char **argv);
+static void interface (int argc, char **argv);
+static void scan_start (void);
+static void scan_done (void);
+
+/* Test if this machine is little endian (from coolscan.c) */
+static gboolean
+calc_little_endian (void)
+{
+ SANE_Int testvalue = 255;
+ u_int8_t *firstbyte = (u_int8_t *) & testvalue;
+
+ if (*firstbyte == 255)
+ return TRUE;
+ return FALSE;
+}
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+
+static int
+encode_devname (const char *devname, int n, char *buf)
+{
+ static const char hexdigit[] = "0123456789abcdef";
+ char *dst, *limit;
+ const char *src;
+ char ch;
+
+ DBG_INIT ();
+
+ DBG (DBG_debug, "encode_devname\n");
+ limit = buf + n;
+ for (src = devname, dst = buf; *src; ++src)
+ {
+ if (dst >= limit)
+ return -1;
+
+ ch = *src;
+ /* don't use the ctype.h macros here since we don't want to
+ allow anything non-ASCII here... */
+ if ((ch >= '0' && ch <= '9')
+ || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
+ *dst++ = ch;
+ else
+ {
+ /* encode */
+
+ if (dst + 4 >= limit)
+ return -1;
+
+ *dst++ = '-';
+ if (ch == '-')
+ *dst++ = '-';
+ else
+ {
+ *dst++ = hexdigit[(ch >> 4) & 0x0f];
+ *dst++ = hexdigit[(ch >> 0) & 0x0f];
+ *dst++ = '-';
+ }
+ }
+ }
+ if (dst >= limit)
+ return -1;
+ *dst = '\0';
+ DBG (DBG_debug, "encode_devname: %s --> %s\n", devname, buf);
+
+ return 0;
+}
+
+static int
+decode_devname (const char *encoded_devname, int n, char *buf)
+{
+ char *dst, *limit;
+ const char *src;
+ char ch, val;
+
+ DBG_INIT ();
+
+ DBG (DBG_debug, "decode_devname\n");
+ limit = buf + n;
+ for (src = encoded_devname, dst = buf; *src; ++dst)
+ {
+ if (dst >= limit)
+ return -1;
+
+ ch = *src++;
+ /* don't use the ctype.h macros here since we don't want to
+ allow anything non-ASCII here... */
+ if (ch != '-')
+ *dst = ch;
+ else
+ {
+ /* decode */
+
+ ch = *src++;
+ if (ch == '-')
+ *dst = ch;
+ else
+ {
+ if (ch >= 'a' && ch <= 'f')
+ val = (ch - 'a') + 10;
+ else
+ val = (ch - '0');
+ val <<= 4;
+
+ ch = *src++;
+ if (ch >= 'a' && ch <= 'f')
+ val |= (ch - 'a') + 10;
+ else
+ val |= (ch - '0');
+
+ *dst = val;
+
+ ++src; /* simply skip terminating '-' for now... */
+ }
+ }
+ }
+ if (dst >= limit)
+ return -1;
+ *dst = '\0';
+ DBG (DBG_debug, "deccode_devname: %s -> %s\n", encoded_devname, buf);
+ return 0;
+}
+
+static void
+query (void)
+{
+ static GimpParamDef args[] = {
+ {GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive"},
+ };
+ static GimpParamDef *return_vals = NULL;
+ static int nargs = sizeof (args) / sizeof (args[0]);
+ static int nreturn_vals = 0;
+ char mpath[1024];
+ char name[1024];
+ size_t len;
+ int i, j;
+ SANE_Status status;
+
+ DBG_INIT ();
+
+ DBG (DBG_debug, "query\n");
+ gimp_install_procedure ("xscanimage",
+ "Front-end to the SANE interface",
+ "This function provides access to scanners and other image acquisition "
+ "devices through the SANE (Scanner Access Now Easy) interface.",
+ "Andy Beck, Tristan Tarrant, and David Mosberger",
+ "Andy Beck, Tristan Tarrant, and David Mosberger",
+ "8th June 1997",
+ "<Toolbox>/File/Acquire/xscanimage/Device dialog...",
+ "RGB, GRAY",
+ GIMP_EXTENSION,
+ nargs, nreturn_vals, args, return_vals);
+
+ status = sane_init (0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_fatal, "query: sane_init failed: %s\n",
+ sane_strstatus (status));
+ exit (1);
+ }
+
+ status = sane_get_devices (&devlist, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_fatal, "query: sane_get_devices failed: %s\n",
+ sane_strstatus (status));
+ exit (1);
+ }
+
+ for (i = 0; devlist[i]; ++i)
+ {
+ strcpy (name, "xscanimage-");
+ if (encode_devname (devlist[i]->name, sizeof (name) - 11, name + 11) <
+ 0)
+ continue; /* name too long... */
+
+ strncpy (mpath, "<Toolbox>/File/Acquire/xscanimage/", sizeof (mpath));
+ len = strlen (mpath);
+ for (j = 0; devlist[i]->name[j]; ++j)
+ {
+ if (devlist[i]->name[j] == '/')
+ mpath[len++] = '\'';
+ else
+ mpath[len++] = devlist[i]->name[j];
+ }
+ mpath[len++] = '\0';
+
+ gimp_install_procedure
+ (name, "Front-end to the SANE interface",
+ "This function provides access to scanners and other image "
+ "acquisition devices through the SANE (Scanner Access Now Easy) "
+ "interface.",
+ "Andy Beck, Tristan Tarrant, and David Mosberger",
+ "Andy Beck, Tristan Tarrant, and David Mosberger",
+ "8th June 1997", mpath, "RGB, GRAY", GIMP_EXTENSION,
+ nargs, nreturn_vals, args, return_vals);
+ }
+ sane_exit ();
+ DBG (DBG_debug, "query: finished\n");
+}
+
+#ifndef ENABLE_GIMP_1_2
+static void
+run (const gchar * name, gint nparams, const GimpParam * param,
+ gint * nreturn_vals, GimpParam ** return_vals)
+#else
+static void
+run (char *name, int nparams, GimpParam * param,
+ int *nreturn_vals, GimpParam ** return_vals)
+#endif /* !ENABLE_GIMP_1_2 */
+{
+ static GimpParam values[2];
+#ifndef ENABLE_GIMP_1_2
+ GimpRunMode run_mode;
+#else
+ GimpRunModeType run_mode;
+#endif /* !ENABLE_GIMP_1_2 */
+ char devname[1024];
+ char *args[2];
+ int nargs;
+
+ DBG (DBG_debug, "run\n");
+ run_mode = param[0].data.d_int32;
+ scan_win.mode = SANE_GIMP_EXTENSION;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
+
+ nargs = 0;
+ args[nargs++] = "xscanimage";
+
+ seldev = -1;
+ if (strncmp (name, "xscanimage-", 11) == 0)
+ {
+ if (decode_devname (name + 11, sizeof (devname), devname) < 0)
+ return; /* name too long */
+ args[nargs++] = devname;
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+#ifndef ENABLE_GIMP_1_2
+ gimp_extension_ack ();
+#endif /* !ENABLE_GIMP_1_2 */
+ interface (nargs, args);
+ values[0].data.d_status = GIMP_PDB_SUCCESS;
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ break;
+
+ default:
+ break;
+ }
+ DBG (DBG_debug, "run: finished\n");
+}
+
+static void
+null_print_func (const gchar * msg)
+{
+}
+
+#endif /* HAVE_LIBGIMP_GIMP_H */
+
+static SANE_Word
+get_resolution (SANE_Handle dev)
+{
+ SANE_Status status;
+ SANE_Word resolution;
+ SANE_Int num_options, i;
+ const SANE_Option_Descriptor *option_desc;
+
+ DBG (DBG_debug, "get_resolution\n");
+ status =
+ sane_control_option (dev, 0, SANE_ACTION_GET_VALUE, &num_options, 0);
+ if (status != SANE_STATUS_GOOD)
+ return 0;
+
+ for (i = 1; i < num_options; i++)
+ {
+ option_desc = sane_get_option_descriptor (dev, i);
+ if (option_desc)
+ if (option_desc->name)
+ {
+ if (strncmp (option_desc->name, SANE_NAME_SCAN_RESOLUTION,
+ sizeof (SANE_NAME_SCAN_RESOLUTION)) == 0)
+ {
+ status = sane_control_option (dev, i, SANE_ACTION_GET_VALUE,
+ &resolution, 0);
+ if (status == SANE_STATUS_GOOD)
+ {
+ if (option_desc->type == SANE_TYPE_INT)
+ return resolution;
+ else if (option_desc->type == SANE_TYPE_FIXED)
+ return (SANE_Word) SANE_UNFIX (resolution);
+ }
+ return 0;
+ }
+ }
+ }
+ DBG (DBG_debug, "get_resolution: finished\n");
+ return 0;
+}
+
+static void
+update_preview (GSGDialog * dialog, void *arg)
+{
+ if (scan_win.preview)
+ preview_update (scan_win.preview);
+}
+
+/* Update the info line with the latest size information. */
+static void
+update_param (GSGDialog * dialog, void *arg)
+{
+ SANE_Parameters params;
+ gchar buf[200];
+
+ DBG (DBG_debug, "update_param\n");
+ if (!scan_win.info_label)
+ return;
+
+ if (sane_get_parameters (gsg_dialog_get_device (dialog), &params)
+ == SANE_STATUS_GOOD)
+ {
+ double size = (double) params.bytes_per_line * (double) params.lines;
+ const char *unit = "B";
+
+ if (params.lines == -1)
+ {
+ snprintf (buf, sizeof (buf), "%dxunknown: unknown size",
+ params.pixels_per_line);
+ }
+ else
+ {
+ if (params.format >= SANE_FRAME_RED
+ && params.format <= SANE_FRAME_BLUE)
+ size *= 3;
+
+ if (size >= 1024 * 1024)
+ {
+ size /= 1024 * 1024;
+ unit = "MB";
+ }
+ else if (size >= 1024)
+ {
+ size /= 1024;
+ unit = "KB";
+ }
+ snprintf (buf, sizeof (buf), "%dx%d: %1.1f %s",
+ params.pixels_per_line, params.lines, size, unit);
+ }
+ }
+ else
+ snprintf (buf, sizeof (buf), "Invalid parameters.");
+ gtk_label_set (GTK_LABEL (scan_win.info_label), buf);
+
+ if (scan_win.preview)
+ preview_update (scan_win.preview);
+ DBG (DBG_debug, "update_param: finished\n");
+}
+
+static void
+pref_xscanimage_save (void)
+{
+ char filename[PATH_MAX];
+ int fd;
+
+ DBG (DBG_debug, "pref_xscanimage_save\n");
+ /* first save xscanimage-specific preferences: */
+ gsg_make_path (sizeof (filename), filename, "xscanimage", "xscanimage",
+ 0, ".rc");
+ fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0)
+ {
+ char buf[256];
+
+ snprintf (buf, sizeof (buf), "Failed to create file: %s.",
+ strerror (errno));
+ gsg_error (buf);
+ return;
+ }
+ preferences_save (fd);
+ close (fd);
+ DBG (DBG_debug, "pref_xscanimage_save: finished\n");
+}
+
+static void
+pref_xscanimage_restore (void)
+{
+ char filename[PATH_MAX];
+ int fd;
+
+ DBG (DBG_debug, "pref_xscanimage_restore\n");
+ gsg_make_path (sizeof (filename), filename, "xscanimage", "xscanimage",
+ 0, ".rc");
+ fd = open (filename, O_RDONLY);
+ if (fd >= 0)
+ {
+ preferences_restore (fd);
+ close (fd);
+ }
+ if (!preferences.filename)
+ preferences.filename = strdup (OUTFILENAME);
+ DBG (DBG_debug, "pref_xscanimage_restore: finished\n");
+}
+
+static void
+quit_xscanimage (void)
+{
+ DBG (DBG_debug, "quit_xscanimage\n");
+ if (scan_win.preview)
+ {
+ Preview *preview = scan_win.preview;
+ scan_win.preview = 0;
+ preview_destroy (preview);
+ }
+ while (gsg_message_dialog_active)
+ {
+ if (!gtk_events_pending ())
+ usleep (100000);
+ gtk_main_iteration ();
+ }
+ pref_xscanimage_save ();
+ if (dialog && gsg_dialog_get_device (dialog))
+ sane_close (gsg_dialog_get_device (dialog));
+ sane_exit ();
+ if (gtk_quit_flag == 1)
+ gtk_main_quit ();
+#ifdef HAVE_LIBGIMP_GIMP_H
+ if (scan_win.mode == SANE_GIMP_EXTENSION)
+ gimp_quit ();
+#endif
+ DBG (DBG_debug, "quit_xscanimage: exiting\n");
+ exit (0);
+}
+
+/* Invoked when window manager's "delete" (or "close") function is
+ invoked. */
+static gint
+scan_win_delete (GtkWidget * w, gpointer data)
+{
+ quit_xscanimage ();
+ return FALSE;
+}
+
+static void
+preview_window_destroyed (GtkWidget * widget, gpointer call_data)
+{
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (call_data), FALSE);
+}
+
+/* Invoked when the preview button is pressed. */
+
+static void
+scan_preview (GtkWidget * widget, gpointer call_data)
+{
+ DBG (DBG_debug, "scan_preview\n");
+
+ if (GTK_TOGGLE_BUTTON (widget)->active)
+ {
+ if (!scan_win.preview)
+ {
+ scan_win.preview = preview_new (dialog);
+ if (scan_win.preview && scan_win.preview->top)
+ gtk_signal_connect (GTK_OBJECT (scan_win.preview->top),
+ "destroy",
+ GTK_SIGNAL_FUNC (preview_window_destroyed),
+ widget);
+ else
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (widget), FALSE);
+ if (scan_win.progress)
+ gtk_widget_set_sensitive (scan_win.preview->preview, FALSE);
+ }
+ }
+ else if (scan_win.preview)
+ {
+ preview_destroy (scan_win.preview);
+ scan_win.preview = 0;
+ }
+ DBG (DBG_debug, "scan_preview: finished\n");
+}
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+static void
+advance (void)
+{
+ DBG (DBG_debug, "advance\n");
+
+ if (++scan_win.x >= scan_win.param.pixels_per_line)
+ {
+ int tile_height = gimp_tile_height ();
+
+ scan_win.x = 0;
+ ++scan_win.y;
+ if (scan_win.y % tile_height == 0)
+ {
+ gimp_pixel_rgn_set_rect (&scan_win.region, scan_win.tile,
+ 0, scan_win.y - tile_height,
+ scan_win.param.pixels_per_line,
+ tile_height);
+ if (scan_win.param.format >= SANE_FRAME_RED
+ && scan_win.param.format <= SANE_FRAME_BLUE)
+ {
+ int height;
+
+ scan_win.tile_offset %= 3;
+ if (!scan_win.first_frame)
+ {
+ /* get the data for the existing tile: */
+ height = tile_height;
+ if (scan_win.y + height >= scan_win.param.lines)
+ height = scan_win.param.lines - scan_win.y;
+ gimp_pixel_rgn_get_rect (&scan_win.region, scan_win.tile,
+ 0, scan_win.y,
+ scan_win.param.pixels_per_line,
+ height);
+ }
+ }
+ else
+ scan_win.tile_offset = 0;
+ }
+ }
+ DBG (DBG_debug, "advance: finished\n");
+}
+
+#endif /* HAVE_LIBGIMP_GIMP_H */
+
+static void
+write_swapped_words (FILE * f, char *buf, guint len)
+{
+ char tmp_buf[2];
+ char tmp;
+ unsigned int i;
+
+ DBG (DBG_debug, "write_swapped_words\n");
+ if (!len)
+ return;
+ if (scan_win.have_odd_byte)
+ {
+ tmp_buf[0] = *buf++;
+ tmp_buf[1] = scan_win.odd_byte;
+ fwrite (tmp_buf, 1, 2, f);
+ --len;
+ scan_win.have_odd_byte = FALSE;
+ }
+ if (len)
+ {
+ for (i = 1; i < len; i += 2)
+ {
+ tmp = buf[i];
+ buf[i] = buf[i - 1];
+ buf[i - 1] = tmp;
+ }
+ fwrite (buf, 1, len & ~1, f);
+ if (len & 1)
+ {
+ scan_win.have_odd_byte = TRUE;
+ scan_win.odd_byte = buf[len - 1];
+ }
+ }
+ DBG (DBG_debug, "write_swapped_words: finished\n");
+}
+
+static void
+input_available (gpointer data, gint source, GdkInputCondition cond)
+{
+ SANE_Handle dev = gsg_dialog_get_device (dialog);
+ static char buf[32768];
+ SANE_Status status;
+ SANE_Int len;
+ int i;
+
+ DBG (DBG_debug, "input_available\n");
+ while (1)
+ {
+ status = sane_read (dev, (unsigned char *) buf, sizeof (buf), &len);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (status == SANE_STATUS_EOF)
+ {
+ if (!scan_win.param.last_frame)
+ {
+ if (scan_win.input_tag < 0)
+ {
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ }
+ else
+ {
+ gdk_input_remove (scan_win.input_tag);
+ scan_win.input_tag = -1;
+ }
+ scan_start ();
+ break;
+ }
+ }
+ else
+ {
+ snprintf (buf, sizeof (buf), "Error during read: %s.",
+ sane_strstatus (status));
+ gsg_error (buf);
+ }
+ scan_done ();
+ return;
+ }
+ if (!len) /* out of data for now */
+ {
+ if (scan_win.input_tag < 0)
+ {
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ continue;
+ }
+ else
+ break;
+ }
+
+ scan_win.bytes_read += len;
+ progress_update (scan_win.progress,
+ scan_win.bytes_read / (gfloat) scan_win.num_bytes);
+ if (scan_win.input_tag < 0)
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+
+ switch (scan_win.param.format)
+ {
+ case SANE_FRAME_GRAY:
+ if (scan_win.mode == STANDALONE)
+ {
+ if (scan_win.param.depth > 8 && little_endian)
+ write_swapped_words (scan_win.out, buf, len);
+ else
+ fwrite (buf, 1, len, scan_win.out);
+ }
+#ifdef HAVE_LIBGIMP_GIMP_H
+ else
+ {
+ switch (scan_win.param.depth)
+ {
+ case 1:
+ for (i = 0; i < len; ++i)
+ {
+ u_char mask;
+ int j;
+
+ mask = buf[i];
+ for (j = 7; j >= 0; --j)
+ {
+ u_char gl = (mask & (1 << j)) ? 0x00 : 0xff;
+ scan_win.tile[scan_win.tile_offset++] = gl;
+ advance ();
+ if (scan_win.x == 0)
+ break;
+ }
+ }
+ break;
+
+ case 8:
+ for (i = 0; i < len; ++i)
+ {
+ scan_win.tile[scan_win.tile_offset++] = buf[i];
+ advance ();
+ }
+ break;
+
+ default:
+ goto bad_depth;
+ }
+ }
+#endif /* HAVE_LIBGIMP_GIMP_H */
+ break;
+
+ case SANE_FRAME_RGB:
+ if (scan_win.mode == STANDALONE)
+ {
+ if (scan_win.param.depth > 8 && little_endian)
+ write_swapped_words (scan_win.out, buf, len);
+ else
+ fwrite (buf, 1, len, scan_win.out);
+ }
+#ifdef HAVE_LIBGIMP_GIMP_H
+ else
+ {
+ switch (scan_win.param.depth)
+ {
+ case 1:
+ if (scan_win.param.format == SANE_FRAME_RGB)
+ goto bad_depth;
+ for (i = 0; i < len; ++i)
+ {
+ u_char mask;
+ int j;
+
+ mask = buf[i];
+ for (j = 0; j < 8; ++j)
+ {
+ u_char gl = (mask & 1) ? 0xff : 0x00;
+ mask >>= 1;
+ scan_win.tile[scan_win.tile_offset++] = gl;
+ advance ();
+ if (scan_win.x == 0)
+ break;
+ }
+ }
+ break;
+
+ case 8:
+ for (i = 0; i < len; ++i)
+ {
+ scan_win.tile[scan_win.tile_offset++] = buf[i];
+ if (scan_win.tile_offset % 3 == 0)
+ advance ();
+ }
+ break;
+
+ default:
+ goto bad_depth;
+ }
+ }
+#endif /* HAVE_LIBGIMP_GIMP_H */
+ break;
+
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ if (scan_win.mode == STANDALONE)
+ for (i = 0; i < len; ++i)
+ {
+ fwrite (buf + i, 1, 1, scan_win.out);
+ fseek (scan_win.out, 2, SEEK_CUR);
+ }
+#ifdef HAVE_LIBGIMP_GIMP_H
+ else
+ {
+ switch (scan_win.param.depth)
+ {
+ case 1:
+ for (i = 0; i < len; ++i)
+ {
+ u_char mask;
+ int j;
+
+ mask = buf[i];
+ for (j = 0; j < 8; ++j)
+ {
+ u_char gl = (mask & 1) ? 0xff : 0x00;
+ mask >>= 1;
+ scan_win.tile[scan_win.tile_offset] = gl;
+ scan_win.tile_offset += 3;
+ advance ();
+ if (scan_win.x == 0)
+ break;
+ }
+ }
+ break;
+
+ case 8:
+ for (i = 0; i < len; ++i)
+ {
+ scan_win.tile[scan_win.tile_offset] = buf[i];
+ scan_win.tile_offset += 3;
+ advance ();
+ }
+ break;
+ }
+ }
+#endif /* HAVE_LIBGIMP_GIMP_H */
+ break;
+
+ default:
+ fprintf (stderr, "%s.input_available: bad frame format %d\n",
+ prog_name, scan_win.param.format);
+ scan_done ();
+ return;
+ }
+ }
+ DBG (DBG_debug, "input_available: finished\n");
+ return;
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+bad_depth:
+ snprintf (buf, sizeof (buf), "Cannot handle depth %d.",
+ scan_win.param.depth);
+ gsg_error (buf);
+ scan_done ();
+ return;
+#endif
+}
+
+static void
+scan_done (void)
+{
+ DBG (DBG_debug, "scan_done\n");
+ gsg_set_sensitivity (dialog, TRUE);
+ if (scan_win.preview)
+ gtk_widget_set_sensitive (scan_win.preview->preview, TRUE);
+ gtk_widget_set_sensitive (scan_win.scan_button, TRUE);
+ gtk_widget_set_sensitive (scan_win.menubar, TRUE);
+
+ if (scan_win.input_tag >= 0)
+ {
+ gdk_input_remove (scan_win.input_tag);
+ scan_win.input_tag = -1;
+ }
+
+ sane_cancel (gsg_dialog_get_device (dialog));
+
+ if (!scan_win.progress)
+ return;
+
+ progress_free (scan_win.progress);
+ scan_win.progress = 0;
+ scan_win.header_size = 0;
+
+ if (scan_win.mode == STANDALONE)
+ {
+ fclose (scan_win.out);
+ scan_win.out = 0;
+ }
+#ifdef HAVE_LIBGIMP_GIMP_H
+ else
+ {
+ int remaining;
+
+ /* GIMP mode */
+ if (scan_win.y > scan_win.param.lines)
+ scan_win.y = scan_win.param.lines;
+
+ remaining = scan_win.y % gimp_tile_height ();
+ if (remaining)
+ gimp_pixel_rgn_set_rect (&scan_win.region, scan_win.tile,
+ 0, scan_win.y - remaining,
+ scan_win.param.pixels_per_line, remaining);
+ gimp_drawable_flush (scan_win.drawable);
+ gimp_display_new (scan_win.image_ID);
+ gimp_drawable_detach (scan_win.drawable);
+ g_free (scan_win.tile);
+ scan_win.tile = 0;
+ }
+#endif /* HAVE_LIBGIMP_GIMP_H */
+ DBG (DBG_debug, "scan_done: finished\n");
+}
+
+static void
+progress_cancel (void)
+{
+ DBG (DBG_debug, "progress_cancel\n");
+ sane_cancel (gsg_dialog_get_device (dialog));
+ DBG (DBG_debug, "progress_cancel: done\n");
+}
+
+static void
+scan_start (void)
+{
+ SANE_Status status;
+ SANE_Handle dev = gsg_dialog_get_device (dialog);
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+ SANE_Word resolution;
+#endif /* HAVE_LIBGIMP_GIMP_H */
+
+ const char *frame_type = 0;
+ char buf[256];
+ int fd;
+
+ DBG (DBG_debug, "scan_start\n");
+
+ gsg_set_sensitivity (dialog, FALSE);
+ if (scan_win.preview)
+ gtk_widget_set_sensitive (scan_win.preview->preview, FALSE);
+ gtk_widget_set_sensitive (scan_win.scan_button, FALSE);
+ gtk_widget_set_sensitive (scan_win.menubar, FALSE);
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+ if (scan_win.mode == SANE_GIMP_EXTENSION && scan_win.tile)
+ {
+ int height, remaining;
+
+ /* write the last tile of the frame to the GIMP region: */
+
+ if (scan_win.y > scan_win.param.lines) /* sanity check */
+ scan_win.y = scan_win.param.lines;
+
+ remaining = scan_win.y % gimp_tile_height ();
+ if (remaining)
+ gimp_pixel_rgn_set_rect (&scan_win.region, scan_win.tile,
+ 0, scan_win.y - remaining,
+ scan_win.param.pixels_per_line, remaining);
+
+ /* initialize the tile with the first tile of the GIMP region: */
+
+ height = gimp_tile_height ();
+ if (height >= scan_win.param.lines)
+ height = scan_win.param.lines;
+ gimp_pixel_rgn_get_rect (&scan_win.region, scan_win.tile,
+ 0, 0, scan_win.param.pixels_per_line, height);
+ }
+#endif /* HAVE_LIBGIMP_GIMP_H */
+
+#ifdef GIMP_HAVE_RESOLUTION_INFO
+ resolution = get_resolution (dev);
+#endif
+
+ scan_win.x = scan_win.y = 0;
+
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ status = sane_start (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ gsg_set_sensitivity (dialog, TRUE);
+ snprintf (buf, sizeof (buf), "Failed to start scanner: %s",
+ sane_strstatus (status));
+ gsg_error (buf);
+ return;
+ }
+
+ status = sane_get_parameters (dev, &scan_win.param);
+ if (status != SANE_STATUS_GOOD)
+ {
+ gsg_set_sensitivity (dialog, TRUE);
+ snprintf (buf, sizeof (buf), "Failed to get parameters: %s",
+ sane_strstatus (status));
+ gsg_error (buf);
+ scan_done ();
+ return;
+ }
+
+ if (scan_win.param.lines == -1)
+ {
+ gsg_set_sensitivity (dialog, TRUE);
+ snprintf (buf, sizeof (buf), "Hand-Scanner mode not supported");
+ gsg_error (buf);
+ scan_done ();
+ return;
+ }
+
+ scan_win.num_bytes = scan_win.param.lines * scan_win.param.bytes_per_line;
+ scan_win.bytes_read = 0;
+ scan_win.have_odd_byte = FALSE;
+
+ switch (scan_win.param.format)
+ {
+ case SANE_FRAME_RGB:
+ frame_type = "RGB";
+ break;
+ case SANE_FRAME_RED:
+ frame_type = "red";
+ break;
+ case SANE_FRAME_GREEN:
+ frame_type = "green";
+ break;
+ case SANE_FRAME_BLUE:
+ frame_type = "blue";
+ break;
+ case SANE_FRAME_GRAY:
+ frame_type = "gray";
+ break;
+ }
+
+ if (scan_win.mode == STANDALONE)
+ { /* We are running in standalone mode */
+ if (!scan_win.header_size)
+ {
+ switch (scan_win.param.format)
+ {
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ if (scan_win.param.depth > 8)
+ {
+ gsg_set_sensitivity (dialog, TRUE);
+ snprintf (buf, sizeof (buf),
+ "Separate channel transfers are not supported "
+ "with %d bits/channel.", scan_win.param.depth);
+ gsg_error (buf);
+ scan_done ();
+ return;
+ }
+ /*FALLTHROUGH*/ case SANE_FRAME_RGB:
+ fprintf (scan_win.out, "P6\n# SANE data follows\n%d %d\n%d\n",
+ scan_win.param.pixels_per_line, scan_win.param.lines,
+ (scan_win.param.depth <= 8) ? 255 : 65535);
+ break;
+
+ case SANE_FRAME_GRAY:
+ if (scan_win.param.depth == 1)
+ fprintf (scan_win.out, "P4\n# SANE data follows\n%d %d\n",
+ scan_win.param.pixels_per_line,
+ scan_win.param.lines);
+ else
+ fprintf (scan_win.out, "P5\n# SANE data follows\n%d %d\n%d\n",
+ scan_win.param.pixels_per_line, scan_win.param.lines,
+ (scan_win.param.depth <= 8) ? 255 : 65535);
+ break;
+ }
+ scan_win.header_size = ftell (scan_win.out);
+ }
+ if (scan_win.param.format >= SANE_FRAME_RED
+ && scan_win.param.format <= SANE_FRAME_BLUE)
+ fseek (scan_win.out,
+ scan_win.header_size + scan_win.param.format - SANE_FRAME_RED,
+ SEEK_SET);
+ snprintf (buf, sizeof (buf), "Receiving %s data for `%s'...",
+ frame_type, preferences.filename);
+ }
+#ifdef HAVE_LIBGIMP_GIMP_H
+ else
+ {
+ size_t tile_size;
+
+ /* We are running under the GIMP */
+ /* Check whether the selected bit depth is supported by the Gimp */
+ if (scan_win.param.depth > 8)
+ {
+ gsg_set_sensitivity (dialog, TRUE);
+ snprintf (buf, sizeof (buf), "The Gimp doesn't support images "
+ "with %d bits/channel.", scan_win.param.depth);
+ gsg_error (buf);
+ return;
+ }
+
+ scan_win.tile_offset = 0;
+ tile_size = scan_win.param.pixels_per_line * gimp_tile_height ();
+ if (scan_win.param.format != SANE_FRAME_GRAY)
+ tile_size *= 3; /* 24 bits/pixel */
+ if (scan_win.tile)
+ scan_win.first_frame = 0;
+ else
+ {
+ GimpImageType image_type = GIMP_RGB;
+ GimpImageType drawable_type = GIMP_RGB_IMAGE;
+ gint32 layer_ID;
+
+ if (scan_win.param.format == SANE_FRAME_GRAY)
+ {
+ image_type = GIMP_GRAY;
+ drawable_type = GIMP_GRAY_IMAGE;
+ }
+
+ scan_win.image_ID = gimp_image_new (scan_win.param.pixels_per_line,
+ scan_win.param.lines,
+ image_type);
+
+/* the following is supported since gimp-1.1.0 */
+#ifdef GIMP_HAVE_RESOLUTION_INFO
+ if (resolution > 0)
+ gimp_image_set_resolution (scan_win.image_ID, resolution,
+ resolution);
+#endif
+
+ layer_ID = gimp_layer_new (scan_win.image_ID, "Background",
+ scan_win.param.pixels_per_line,
+ scan_win.param.lines,
+ drawable_type, 100, GIMP_NORMAL_MODE);
+ gimp_image_add_layer (scan_win.image_ID, layer_ID, 0);
+
+ scan_win.drawable = gimp_drawable_get (layer_ID);
+ gimp_pixel_rgn_init (&scan_win.region, scan_win.drawable, 0, 0,
+ scan_win.drawable->width,
+ scan_win.drawable->height, TRUE, FALSE);
+ scan_win.tile = g_new (guchar, tile_size);
+ scan_win.first_frame = 1;
+ }
+ if (scan_win.param.format >= SANE_FRAME_RED
+ && scan_win.param.format <= SANE_FRAME_BLUE)
+ scan_win.tile_offset = scan_win.param.format - SANE_FRAME_RED;
+ snprintf (buf, sizeof (buf), "Receiving %s data for GIMP...",
+ frame_type);
+ }
+#endif /* HAVE_LIBGIMP_GIMP_H */
+ if (scan_win.progress)
+ progress_free (scan_win.progress);
+ scan_win.progress = progress_new ("Scanning", buf,
+ (GtkSignalFunc) progress_cancel, 0);
+
+ scan_win.input_tag = -1;
+ if (sane_set_io_mode (dev, SANE_TRUE) == SANE_STATUS_GOOD
+ && sane_get_select_fd (dev, &fd) == SANE_STATUS_GOOD)
+ scan_win.input_tag =
+ gdk_input_add (fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
+ input_available, 0);
+ else
+ {
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ input_available (0, -1, GDK_INPUT_READ);
+ }
+ DBG (DBG_debug, "scan_start: finished\n");
+}
+
+/* Invoked when the scan button is pressed */
+static void
+scan_dialog (GtkWidget * widget, gpointer call_data)
+{
+ char buf[256];
+ char testfilename[256];
+
+ DBG (DBG_debug, "scan_dialog\n");
+ if (scan_win.mode == STANDALONE)
+ { /* We are running in standalone mode */
+ /* test for pnm formats */
+ strncpy (testfilename, preferences.filename, sizeof (testfilename));
+ testfilename[sizeof (testfilename)] = 0;
+ g_strreverse (testfilename);
+ if (!((!strncmp (testfilename, "mnp.", 4)) ||
+ (!strncmp (testfilename, "mgp.", 4)) ||
+ (!strncmp (testfilename, "mbp.", 4)) ||
+ (!strncmp (testfilename, "mpp.", 4)) ||
+ (!strncmp (testfilename, "MNP.", 4)) ||
+ (!strncmp (testfilename, "MGP.", 4)) ||
+ (!strncmp (testfilename, "MBP.", 4)) ||
+ (!strncmp (testfilename, "MPP.", 4))))
+ {
+ snprintf (buf, sizeof (buf),
+ "Failed to scan, wrong file extension, use pnm, pgm, pbm or ppm `%s'",
+ preferences.filename);
+ gsg_error (buf);
+ return;
+ }
+ scan_win.out = fopen (preferences.filename, "w");
+ if (!scan_win.out)
+ {
+ snprintf (buf, sizeof (buf), "Failed to open `%s': %s",
+ preferences.filename, strerror (errno));
+ gsg_error (buf);
+ return;
+ }
+ }
+ gsg_sync (dialog);
+ scan_start ();
+ DBG (DBG_debug, "scan_dialog: finished\n");
+}
+
+#if 0
+
+static void
+zoom_in_preview (GtkWidget * widget, gpointer data)
+{
+ if (Selection.x1 >= Selection.x2
+ || Selection.y1 >= Selection.y2 || !Selection.active)
+ return;
+
+ Selection.active = FALSE;
+ draw_selection (TRUE);
+
+ gtk_ruler_set_range (GTK_RULER (scan_win.hruler), Selection.x1,
+ Selection.x2, 0, 20);
+ gtk_ruler_set_range (GTK_RULER (scan_win.vruler), Selection.y1,
+ Selection.y2, 0, 20);
+}
+
+static void
+zoom_out_preview (GtkWidget * widget, gpointer data)
+{
+ gtk_ruler_set_range (GTK_RULER (scan_win.hruler), 0,
+ Preview.PhysWidth, 0, 20);
+ gtk_ruler_set_range (GTK_RULER (scan_win.vruler), 0,
+ Preview.PhysHeight, 0, 20);
+}
+
+#endif /* 0 */
+
+static void
+files_exit_callback (GtkWidget * widget, gpointer data)
+{
+ quit_xscanimage ();
+}
+
+static GtkWidget *
+files_build_menu (void)
+{
+ GtkWidget *menu, *item;
+
+ DBG (DBG_debug, "files_build_menu\n");
+ menu = gtk_menu_new ();
+
+ item = gtk_menu_item_new ();
+ gtk_container_add (GTK_CONTAINER (menu), item);
+ gtk_widget_show (item);
+
+ item = gtk_menu_item_new_with_label ("Exit");
+ gtk_container_add (GTK_CONTAINER (menu), item);
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ (GtkSignalFunc) files_exit_callback, 0);
+ gtk_widget_show (item);
+
+ DBG (DBG_debug, "files_build_menu: finished\n");
+ return menu;
+}
+
+static void
+pref_set_unit_callback (GtkWidget * widget, gpointer data)
+{
+ const char *unit = data;
+ double unit_conversion_factor = 1.0;
+
+ DBG (DBG_debug, "pref_set_unit_callback\n");
+
+ if (strcmp (unit, "cm") == 0)
+ unit_conversion_factor = 10.0;
+ else if (strcmp (unit, "in") == 0)
+ unit_conversion_factor = 25.4;
+
+ preferences.length_unit = unit_conversion_factor;
+
+ gsg_refresh_dialog (dialog);
+ if (scan_win.preview)
+ preview_update (scan_win.preview);
+
+ pref_xscanimage_save ();
+ DBG (DBG_debug, "pref_set_unit_callback: finished\n");
+}
+
+static void
+update_int_callback (GtkWidget * widget, gpointer data)
+{
+ int *valuep = data;
+
+ *valuep = (GTK_TOGGLE_BUTTON (widget)->active != 0);
+}
+
+static void
+update_double_callback (GtkWidget * widget, gpointer data)
+{
+ double *valuep = data;
+ const char *start;
+ char *end;
+ double v;
+
+ start = gtk_entry_get_text (GTK_ENTRY (widget));
+ if (!start)
+ return;
+
+ v = strtod (start, &end);
+ if (end > start)
+ *valuep = v;
+}
+
+static void
+preview_options_ok_callback (GtkWidget * widget, gpointer data)
+{
+ GtkWidget *dialog = data;
+ char buf[1024];
+
+ DBG (DBG_debug, "preview_options_ok_callback\n");
+
+ /* gamma min, max test */
+ if (preferences.preview_gamma < 0.0 || preferences.preview_gamma > 255.0)
+ {
+ snprintf (buf, sizeof (buf),
+ "Gamma value %g is < 0 or > 255, please change!",
+ preferences.preview_gamma);
+ gsg_warning (buf);
+ return;
+ }
+
+
+ gtk_widget_destroy (dialog);
+ pref_xscanimage_save ();
+
+
+ snprintf (buf, sizeof (buf),
+ "It is necessary to restart %s for the changes to take effect.",
+ prog_name);
+ gsg_warning (buf);
+ DBG (DBG_debug, "preview_options_ok_callback: finished\n");
+}
+
+static void
+preview_options_cancel_callback (GtkWidget * widget, gpointer data)
+{
+ GtkWidget *dialog = data;
+
+ gtk_widget_destroy (dialog);
+}
+
+static void
+preview_options_dialog (GtkWidget * widget, gpointer data)
+{
+ GtkWidget *dialog, *vbox, *hbox, *button, *label, *text;
+ char buf[64];
+
+ DBG (DBG_debug, "preview_options_dialog\n");
+ dialog = gtk_dialog_new ();
+ sprintf (buf, "%s preview options", prog_name);
+ gtk_window_set_title (GTK_WINDOW (dialog), buf);
+
+ vbox = GTK_DIALOG (dialog)->vbox;
+
+ /* preserve preview image: */
+
+ hbox = gtk_hbox_new ( /* homogeneous */ FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 2);
+ button = gtk_check_button_new_with_label ("Preserve preview image");
+ gtk_signal_connect (GTK_OBJECT (button), "toggled",
+ (GtkSignalFunc) update_int_callback,
+ &preferences.preserve_preview);
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button),
+ preferences.preserve_preview);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 2);
+
+ gtk_widget_show (button);
+ gtk_widget_show (hbox);
+
+ /* private colormap: */
+
+ hbox = gtk_hbox_new ( /* homogeneous */ FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 2);
+ button = gtk_check_button_new_with_label ("Use private colormap");
+ gtk_signal_connect (GTK_OBJECT (button), "toggled",
+ (GtkSignalFunc) update_int_callback,
+ &preferences.preview_own_cmap);
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button),
+ preferences.preview_own_cmap);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 2);
+
+ gtk_widget_show (button);
+ gtk_widget_show (hbox);
+
+ /* gamma correction value: */
+
+ hbox = gtk_hbox_new ( /* homogeneous */ FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 2);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new ("Gamma correction value");
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 2);
+ gtk_widget_show (label);
+
+ sprintf (buf, "%g", preferences.preview_gamma);
+ text = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (text), buf);
+ gtk_box_pack_start (GTK_BOX (hbox), text, TRUE, TRUE, 2);
+ gtk_signal_connect (GTK_OBJECT (text), "changed",
+ (GtkSignalFunc) update_double_callback,
+ &preferences.preview_gamma);
+ gtk_widget_show (text);
+
+ /* fill in action area: */
+ hbox = GTK_DIALOG (dialog)->action_area;
+
+ button = gtk_button_new_with_label ("OK");
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) preview_options_ok_callback, dialog);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+
+ button = gtk_button_new_with_label ("Cancel");
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) preview_options_cancel_callback,
+ dialog);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gtk_widget_show (dialog);
+ DBG (DBG_debug, "preview_options_dialog: finished\n");
+}
+
+static void
+pref_device_save (GtkWidget * widget, gpointer data)
+{
+ char filename[PATH_MAX];
+ int fd;
+
+ DBG (DBG_debug, "pref_device_save\n");
+ gsg_make_path (sizeof (filename), filename,
+ "xscanimage", 0, dialog->dev_name, ".rc");
+ fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0)
+ {
+ char buf[256];
+
+ snprintf (buf, sizeof (buf), "Failed to create file: %s.",
+ strerror (errno));
+ gsg_error (buf);
+ return;
+ }
+ gsg_sync (dialog);
+ sanei_save_values (fd, dialog->dev);
+ close (fd);
+ DBG (DBG_debug, "pref_device_save: finished\n");
+}
+
+static void
+pref_device_restore (void)
+{
+ char filename[PATH_MAX];
+ int fd;
+
+ DBG (DBG_debug, "pref_device_restore\n");
+ gsg_make_path (sizeof (filename), filename,
+ "xscanimage", 0, dialog->dev_name, ".rc");
+ fd = open (filename, O_RDONLY);
+ if (fd < 0)
+ return;
+ sanei_load_values (fd, dialog->dev);
+ close (fd);
+
+ gsg_refresh_dialog (dialog);
+ DBG (DBG_debug, "pref_device_restore: finished\n");
+}
+
+static void
+pref_toggle_advanced (GtkWidget * widget, gpointer data)
+{
+ preferences.advanced = (GTK_CHECK_MENU_ITEM (widget)->active != 0);
+ gsg_set_advanced (dialog, preferences.advanced);
+ pref_xscanimage_save ();
+}
+
+static void
+pref_toggle_tooltips (GtkWidget * widget, gpointer data)
+{
+ preferences.tooltips_enabled = (GTK_CHECK_MENU_ITEM (widget)->active != 0);
+ gsg_set_tooltips (dialog, preferences.tooltips_enabled);
+ pref_xscanimage_save ();
+}
+
+static void
+pref_toggle_twocolumn (GtkWidget * widget, gpointer data)
+{
+ preferences.twocolumn_enabled = (GTK_CHECK_MENU_ITEM (widget)->active != 0);
+ gsg_set_twocolumn (dialog, preferences.twocolumn_enabled);
+ pref_xscanimage_save ();
+}
+
+static GtkWidget *
+pref_build_menu (void)
+{
+ GtkWidget *menu, *item, *submenu;
+ GtkWidget *unit_mm, *unit_cm, *unit_in;
+ GSList *units_group = NULL;
+ double unit;
+
+ DBG (DBG_debug, "pref_build_menu\n");
+ menu = gtk_menu_new ();
+
+ /* advanced user option: */
+ item = gtk_check_menu_item_new_with_label ("Show advanced options");
+ gtk_check_menu_item_set_state (GTK_CHECK_MENU_ITEM (item),
+ preferences.advanced);
+ gtk_menu_append (GTK_MENU (menu), item);
+ gtk_widget_show (item);
+ gtk_signal_connect (GTK_OBJECT (item), "toggled",
+ (GtkSignalFunc) pref_toggle_advanced, 0);
+
+ /* tooltips submenu: */
+
+ item = gtk_check_menu_item_new_with_label ("Show tooltips");
+ gtk_check_menu_item_set_state (GTK_CHECK_MENU_ITEM (item),
+ preferences.tooltips_enabled);
+ gtk_menu_append (GTK_MENU (menu), item);
+ gtk_widget_show (item);
+ gtk_signal_connect (GTK_OBJECT (item), "toggled",
+ (GtkSignalFunc) pref_toggle_tooltips, 0);
+
+ /* two column submenu: */
+
+ item = gtk_check_menu_item_new_with_label ("Show two column display");
+ gtk_check_menu_item_set_state (GTK_CHECK_MENU_ITEM (item),
+ preferences.twocolumn_enabled);
+ gtk_menu_append (GTK_MENU (menu), item);
+ gtk_widget_show (item);
+ gtk_signal_connect (GTK_OBJECT (item), "toggled",
+ (GtkSignalFunc) pref_toggle_twocolumn, 0);
+
+ /* length unit submenu: */
+
+ item = gtk_menu_item_new_with_label ("Length unit");
+ gtk_menu_append (GTK_MENU (menu), item);
+ gtk_widget_show (item);
+
+ submenu = gtk_menu_new ();
+
+ unit_mm = gtk_radio_menu_item_new_with_label (units_group, "millimeters");
+ units_group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (unit_mm));
+ gtk_menu_append (GTK_MENU (submenu), unit_mm);
+ gtk_widget_show (unit_mm);
+
+ unit_cm = gtk_radio_menu_item_new_with_label (units_group, "centimeters");
+ units_group = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (unit_cm));
+ gtk_menu_append (GTK_MENU (submenu), unit_cm);
+ gtk_widget_show (unit_cm);
+
+ unit_in = gtk_radio_menu_item_new_with_label (units_group, "inches");
+ gtk_menu_append (GTK_MENU (submenu), unit_in);
+ gtk_widget_show (unit_in);
+
+ unit = preferences.length_unit;
+ if ((unit > 9.9) && (unit < 10.1))
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (unit_cm), TRUE);
+ else if ((unit > 25.3) && (unit < 25.5))
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (unit_in), TRUE);
+ else
+ {
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (unit_mm), TRUE);
+ preferences.length_unit = 1.0;
+ }
+ gtk_signal_connect (GTK_OBJECT (unit_mm), "toggled",
+ (GtkSignalFunc) pref_set_unit_callback, "mm");
+ gtk_signal_connect (GTK_OBJECT (unit_cm), "toggled",
+ (GtkSignalFunc) pref_set_unit_callback, "cm");
+ gtk_signal_connect (GTK_OBJECT (unit_in), "toggled",
+ (GtkSignalFunc) pref_set_unit_callback, "in");
+
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
+
+ /* preview options: */
+
+ item = gtk_menu_item_new_with_label ("Preview options...");
+ gtk_menu_append (GTK_MENU (menu), item);
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ (GtkSignalFunc) preview_options_dialog, 0);
+ gtk_widget_show (item);
+
+ /* insert separator: */
+ item = gtk_menu_item_new ();
+ gtk_menu_append (GTK_MENU (menu), item);
+ gtk_widget_show (item);
+
+ item = gtk_menu_item_new_with_label ("Save device settings");
+ gtk_menu_append (GTK_MENU (menu), item);
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ (GtkSignalFunc) pref_device_save, 0);
+ gtk_widget_show (item);
+
+ item = gtk_menu_item_new_with_label ("Restore device settings");
+ gtk_menu_append (GTK_MENU (menu), item);
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ (GtkSignalFunc) pref_device_restore, 0);
+ gtk_widget_show (item);
+
+ DBG (DBG_debug, "pref_build_menu: finished\n");
+ return menu;
+}
+
+static void
+browse_filename_callback (GtkWidget * widget, gpointer data)
+{
+ char filename[1024];
+
+ DBG (DBG_debug, "browse_filename_callback\n");
+ if (preferences.filename)
+ {
+ strncpy (filename, preferences.filename, sizeof (filename));
+ filename[sizeof (filename) - 1] = '\0';
+ }
+ else
+ strcpy (filename, OUTFILENAME);
+ gsg_get_filename ("Output Filename", filename, sizeof (filename), filename);
+ gtk_entry_set_text (GTK_ENTRY (scan_win.filename_entry), filename);
+
+ if (preferences.filename)
+ free ((void *) preferences.filename);
+ preferences.filename = strdup (filename);
+ DBG (DBG_debug, "browse_filename_callback: finished\n");
+}
+
+static void
+filename_changed_callback (GtkWidget * widget, gpointer data)
+{
+ if (preferences.filename)
+ free ((void *) preferences.filename);
+ preferences.filename = strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
+}
+
+/* Create the main dialog box. */
+static void
+device_dialog (void)
+{
+ GtkWidget *vbox, *hbox, *button, *frame, *scrolled_window, *dialog_window,
+ *label, *text;
+ GtkWidget *menubar_item;
+ const gchar *devname;
+
+ DBG (DBG_debug, "device_dialog\n");
+ /* first, restore xscanimage preferences */
+ pref_xscanimage_restore ();
+
+ devname = devlist[seldev]->name;
+
+ /* create the dialog box */
+ scan_win.shell = gtk_dialog_new ();
+ gtk_window_set_title (GTK_WINDOW (scan_win.shell), (char *) devname);
+ gtk_window_set_policy (GTK_WINDOW (scan_win.shell), FALSE, TRUE, TRUE);
+ gtk_window_set_default_size (GTK_WINDOW (scan_win.shell), 400, 400);
+ gtk_signal_connect (GTK_OBJECT (scan_win.shell), "delete_event",
+ GTK_SIGNAL_FUNC (scan_win_delete), NULL);
+
+ /* create the main vbox */
+ vbox = GTK_DIALOG (scan_win.shell)->vbox;
+
+ /* create the menubar */
+
+ scan_win.menubar = gtk_menu_bar_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), scan_win.menubar, FALSE, FALSE, 0);
+
+ /* "Files" submenu: */
+ menubar_item = gtk_menu_item_new_with_label ("File");
+ gtk_container_add (GTK_CONTAINER (scan_win.menubar), menubar_item);
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menubar_item),
+ files_build_menu ());
+ gtk_widget_show (menubar_item);
+
+ /* "Preferences" submenu: */
+ menubar_item = gtk_menu_item_new_with_label ("Preferences");
+ gtk_container_add (GTK_CONTAINER (scan_win.menubar), menubar_item);
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menubar_item),
+ pref_build_menu ());
+ gtk_widget_show (menubar_item);
+
+ gtk_widget_show (scan_win.menubar);
+
+ /* if we're running in standalone mode, provide a output filename box: */
+ if (scan_win.mode == STANDALONE)
+ {
+ frame = gtk_frame_new ("Output");
+ gtk_container_border_width (GTK_CONTAINER (frame), 4);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 2);
+ gtk_container_border_width (GTK_CONTAINER (hbox), 2);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+
+ label = gtk_label_new ("Filename");
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 2);
+
+ text = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (text), (char *) preferences.filename);
+ gtk_box_pack_start (GTK_BOX (hbox), text, TRUE, TRUE, 2);
+ gtk_signal_connect (GTK_OBJECT (text), "changed",
+ (GtkSignalFunc) filename_changed_callback, 0);
+
+ scan_win.filename_entry = text;
+
+ button = gtk_button_new_with_label ("Browse");
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 2);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) browse_filename_callback, 0);
+
+ gtk_widget_show (button);
+ gtk_widget_show (label);
+ gtk_widget_show (text);
+ gtk_widget_show (hbox);
+ gtk_widget_show (frame);
+ }
+
+ /* create a subwindow so the dialog keeps its position on rebuilds: */
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_placement (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_CORNER_TOP_RIGHT);
+ dialog_window = gtk_hbox_new ( /* homogeneous */ FALSE, 0);
+ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW
+ (scrolled_window), dialog_window);
+
+ gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
+ gtk_widget_show (scrolled_window);
+ gtk_widget_show (dialog_window);
+
+ dialog = gsg_create_dialog (dialog_window, devname,
+ update_preview, 0, update_param, 0);
+ if (!dialog)
+ return;
+
+ /* The info row */
+ hbox = gtk_hbox_new (FALSE, 5);
+ gtk_container_border_width (GTK_CONTAINER (hbox), 3);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ hbox = gtk_hbox_new (FALSE, 5);
+ gtk_container_border_width (GTK_CONTAINER (hbox), 2);
+ gtk_container_add (GTK_CONTAINER (frame), hbox);
+ gtk_widget_show (hbox);
+
+ scan_win.info_label = gtk_label_new ("0x0: 0KB");
+ gtk_box_pack_start (GTK_BOX (hbox), scan_win.info_label, FALSE, FALSE, 0);
+ gtk_widget_show (scan_win.info_label);
+
+ update_param (dialog, 0);
+
+ /* The bottom row of buttons */
+ hbox = GTK_DIALOG (scan_win.shell)->action_area;
+
+ /* The Scan button */
+ scan_win.scan_button = gtk_button_new_with_label ("Scan");
+ gtk_signal_connect (GTK_OBJECT (scan_win.scan_button), "clicked",
+ (GtkSignalFunc) scan_dialog, NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), scan_win.scan_button, TRUE, TRUE, 0);
+ gtk_widget_show (scan_win.scan_button);
+
+ /* The Preview button */
+ scan_win.preview_button =
+ gtk_toggle_button_new_with_label ("Preview Window");
+ gtk_signal_connect (GTK_OBJECT (scan_win.preview_button), "clicked",
+ (GtkSignalFunc) scan_preview, NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), scan_win.preview_button, TRUE, TRUE, 0);
+ gtk_widget_show (scan_win.preview_button);
+
+#if 0
+ /* The Zoom in button */
+ button = gtk_button_new_with_label ("Zoom");
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) zoom_in_preview, NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ /* The Zoom out button */
+ button = gtk_button_new_with_label ("Zoom out");
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) zoom_out_preview, NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+#endif
+
+ pref_device_restore (); /* restore device-settings */
+ gtk_widget_show (scan_win.shell);
+ DBG (DBG_debug, "device_dialog: finished\n");
+}
+
+static void
+ok_choose_dialog_callback (void)
+{
+ gtk_widget_destroy (choose_device_dialog);
+ device_dialog ();
+}
+
+static int
+select_device_callback (GtkWidget * widget, GdkEventButton * event,
+ gpointer data)
+{
+ seldev = (long) data;
+ if (event->type == GDK_2BUTTON_PRESS && event->button == 1)
+ ok_choose_dialog_callback ();
+
+ return 0;
+}
+
+static gint32
+choose_device (void)
+{
+ GtkWidget *main_vbox, *vbox, *hbox, *button;
+ GSList *owner;
+ const SANE_Device *adev;
+ gint i;
+
+ DBG (DBG_debug, "choose_device\n");
+ choose_device_dialog = gtk_dialog_new ();
+ gtk_window_set_title (GTK_WINDOW (choose_device_dialog), "Select device");
+ gtk_signal_connect (GTK_OBJECT (choose_device_dialog), "delete_event",
+ GTK_SIGNAL_FUNC (files_exit_callback), NULL);
+
+ main_vbox = GTK_DIALOG (choose_device_dialog)->vbox;
+
+ /* The list of drivers */
+ vbox = gtk_vbox_new (FALSE, 5);
+ gtk_container_border_width (GTK_CONTAINER (vbox), 3);
+ gtk_box_pack_start (GTK_BOX (main_vbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ /* The radio buttons */
+ owner = NULL;
+ for (i = 0; i < ndevs; i++)
+ {
+ adev = devlist[i];
+
+ button = gtk_radio_button_new_with_label (owner, (char *) adev->name);
+ gtk_signal_connect (GTK_OBJECT (button), "button_press_event",
+ (GtkSignalFunc) select_device_callback,
+ (void *) (long) i);
+ gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+ owner = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
+ if (i == defdev)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+ }
+
+ /* The bottom row of buttons */
+ hbox = GTK_DIALOG (choose_device_dialog)->action_area;
+
+ /* The OK button */
+ button = gtk_button_new_with_label ("OK");
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) ok_choose_dialog_callback, NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+
+ /* The Cancel button */
+ button = gtk_button_new_with_label ("Cancel");
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) files_exit_callback, NULL);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gtk_widget_show (choose_device_dialog);
+ DBG (DBG_debug, "choose_device: finished\n");
+ return 0;
+}
+
+static void
+usage (void)
+{
+ printf ("Usage: %s [OPTION]... [DEVICE]\n\
+\n\
+Start up graphical user interface to access SANE (Scanner Access Now\n\
+Easy) devices.\n\
+\n\
+-h, --help display this help message and exit\n\
+-V, --version print version information\n", prog_name);
+}
+
+static void
+init (int argc, char **argv)
+{
+ char filename[PATH_MAX];
+ struct stat st;
+ SANE_Status status;
+
+ DBG_INIT ();
+
+ DBG (DBG_debug, "init\n");
+ gtk_init (&argc, &argv);
+#ifdef HAVE_LIBGIMP_GIMP_H
+ gtk_rc_parse (gimp_gtkrc ());
+
+# ifndef ENABLE_GIMP_1_2
+ /* GIMP 2.0 defines gimp_use_xshm() as a macro always returning TRUE
+ * (in the compat header) */
+ gdk_set_use_xshm (TRUE);
+# else
+ gdk_set_use_xshm (gimp_use_xshm ());
+# endif /* !ENABLE_GIMP_1_2 */
+#endif
+
+ gsg_make_path (sizeof (filename), filename, 0, "sane-style", 0, ".rc");
+ if (stat (filename, &st) >= 0)
+ gtk_rc_parse (filename);
+ else
+ {
+ strncpy (filename, STRINGIFY (PATH_SANE_DATA_DIR) "/sane-style.rc",
+ sizeof (filename));
+ filename[sizeof (filename) - 1] = '\0';
+ if (stat (filename, &st) >= 0)
+ gtk_rc_parse (filename);
+ }
+
+ status = sane_init (0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_fatal, "init: sane_init failed: %s\n",
+ sane_strstatus (status));
+ exit (1);
+ }
+
+ if (argc > 1)
+ {
+ static SANE_Device dev;
+ static const SANE_Device *device_list[] = { &dev, 0 };
+ int ch;
+
+ while ((ch = getopt_long (argc, argv, "ghV", long_options, 0)) != EOF)
+ {
+ switch (ch)
+ {
+ case 'g':
+ printf ("%s: GIMP support missing.\n", argv[0]);
+ exit (0);
+
+ case 'V':
+ printf ("xscanimage (%s) %s\n", PACKAGE, VERSION);
+ exit (0);
+
+ case 'h':
+ default:
+ usage ();
+ exit (0);
+ }
+ }
+
+ if (optind < argc)
+ {
+ memset (&dev, 0, sizeof (dev));
+ dev.name = argv[argc - 1];
+ dev.vendor = "Unknown";
+ dev.type = "unknown";
+ dev.model = "unknown";
+
+ devlist = device_list;
+ seldev = 0;
+ }
+ }
+
+ if (seldev < 0)
+ {
+ char *defdevname;
+
+ status = sane_get_devices (&devlist, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_fatal, "init: sane_get_devices failed: %s\n",
+ sane_strstatus (status));
+ exit (1);
+ }
+
+ if ((defdevname = getenv ("SANE_DEFAULT_DEVICE")) != NULL)
+ {
+ int i;
+
+ for (i = 0; devlist[i] != 0; i++)
+ {
+ if (strcmp (devlist[i]->name, defdevname) == 0)
+ {
+ defdev = i;
+ break;
+ }
+ }
+ if (defdev < 0)
+ DBG (DBG_error, "default device is `%s' wasn't found by "
+ "sane_get_devices() \n", defdevname);
+ else
+ DBG (DBG_info, "default device is `%s'\n", defdevname);
+ }
+ }
+ DBG (DBG_debug, "init: finished\n");
+}
+
+static void
+interface (int argc, char **argv)
+{
+ scan_win.info_label = NULL;
+
+ DBG (DBG_debug, "interface\n");
+ init (argc, argv);
+
+ for (ndevs = 0; devlist[ndevs]; ++ndevs);
+
+ if (seldev >= 0)
+ {
+ if (seldev >= ndevs)
+ {
+ fprintf (stderr, "%s: device %d is unavailable.\n",
+ prog_name, seldev);
+ quit_xscanimage ();
+ }
+ device_dialog ();
+ if (!dialog)
+ quit_xscanimage ();
+ }
+ else
+ {
+ if (ndevs > 0)
+ {
+ seldev = 0;
+ if (ndevs == 1)
+ {
+ device_dialog ();
+ if (!dialog)
+ quit_xscanimage ();
+ }
+ else
+ choose_device ();
+ }
+ else
+ {
+ DBG (DBG_fatal,
+ "No scanners were identified. If you were expecting something\n"
+ " different, check that the scanner is plugged in, turned on and\n"
+ " detected by sane-find-scanner (if appropriate). Please read\n"
+ " the documentation which came with this software (README, FAQ,\n"
+ " manpages).\n");
+ quit_xscanimage ();
+ }
+ }
+ gtk_quit_flag = 1;
+ DBG (DBG_debug, "interface: now running gtk_main ()\n");
+ gtk_main ();
+ sane_exit ();
+ DBG (DBG_debug, "interface: finished\n");
+}
+
+int
+main (int argc, char **argv)
+{
+ DBG_INIT ();
+ DBG (DBG_debug, "main\n");
+ DBG (DBG_error, "xscanimage (version: %s, package: %s) starting\n", VERSION,
+ PACKAGE);
+ little_endian = calc_little_endian ();
+ scan_win.mode = STANDALONE;
+ gtk_quit_flag = 0;
+ prog_name = strrchr (argv[0], '/');
+ if (prog_name)
+ ++prog_name;
+ else
+ prog_name = argv[0];
+
+#ifdef HAVE_LIBGIMP_GIMP_H
+ {
+ GPrintFunc old_print_func;
+ GPrintFunc old_printerr_func;
+ int result;
+
+ /* Temporarily install a print function that discards all output.
+ This is to avoid annoying "you must run this program under
+ gimp" messages when xscanimage gets invoked in stand-alone
+ mode. */
+ old_print_func = g_set_print_handler (null_print_func);
+ old_printerr_func = g_set_printerr_handler (null_print_func);
+ /* gimp_main () returns 1 if xscanimage wasn't invoked by GIMP */
+# ifdef HAVE_OS2_H
+ set_gimp_PLUG_IN_INFO (&PLUG_IN_INFO);
+# endif
+# ifndef ENABLE_GIMP_1_2
+ result = gimp_main (&PLUG_IN_INFO, argc, argv);
+# else
+ result = gimp_main (argc, argv);
+# endif /* !ENABLE_GIMP_1_2 */
+ g_set_print_handler (old_print_func);
+ g_set_printerr_handler (old_printerr_func);
+ if (result)
+ interface (argc, argv);
+ }
+#else
+ interface (argc, argv);
+#endif /* HAVE_LIBGIMP_GIMP_H */
+ DBG (DBG_debug, "main: finished\n");
+ return 0;
+}