summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2014-12-02 20:17:04 +0100
committerJörg Frings-Fürst <debian@jff-webhosting.net>2014-12-02 20:17:04 +0100
commit7d8191b83e163d76bb05e13b373638e4eeb7da95 (patch)
treefe29c36a3cb4ef2267b2253da4dde8ce360b3cb5 /src
Initial import of sane-frontends version 1.0.14-9
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.in120
-rw-r--r--src/font_6x11.h3337
-rw-r--r--src/gtkglue.c1495
-rw-r--r--src/gtkglue.h121
-rw-r--r--src/preferences.c185
-rw-r--r--src/preferences.h25
-rw-r--r--src/preview.c1536
-rw-r--r--src/preview.h87
-rw-r--r--src/progress.c93
-rw-r--r--src/progress.h34
-rw-r--r--src/sane-style.rc21
-rw-r--r--src/scanadf.c1590
-rw-r--r--src/xcam.c1871
-rw-r--r--src/xscanimage-gimp-1_0-compat.h47
-rw-r--r--src/xscanimage.c2224
15 files changed, 12786 insertions, 0 deletions
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..af064d0
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,120 @@
+SHELL = /bin/sh
+
+VPATH = @srcdir@
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+top_builddir = ..
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+distdir = $(top_srcdir)/$(PACKAGE)-$(VERSION)
+
+sane_prefix = @SANE_PREFIX@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+configdir = ${sysconfdir}/sane.d
+sanedatadir = ${datadir}/sane
+
+MKDIR = $(top_srcdir)/mkinstalldirs
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+CC = @CC@
+INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include \
+ @INCLUDES@ @GTK_CFLAGS@ @GIMP_CFLAGS@
+DEFS = @DEFS@
+CPPFLAGS = @CPPFLAGS@
+CFLAGS = @CFLAGS@ @SANE_CFLAGS@
+LDFLAGS = @LDFLAGS@ @SANE_LDFLAGS@
+LIBS = @LIBS@ @SANE_LIBS@
+GTK_LIBS = @GTK_LIBS@
+GTK_CFLAGS = @GTK_CFLAGS@
+GIMP_LIBS = @GIMP_LIBS@
+
+BINPROGS = @BINPROGS@
+
+COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $(GTK_CFLAGS)
+LINK = $(CC) $(LDFLAGS) -o $@
+
+@SET_MAKE@
+
+PROGRAMS = $(BINPROGS)
+LIBLIB = ../lib/liblib.a
+LIBSANEI = ../sanei/libsanei.a
+
+XSCANIMAGE_OBJS = xscanimage.o progress.o preview.o preferences.o gtkglue.o
+XCAM_OBJS = xcam.o preferences.o gtkglue.o
+SCANADF_OBJS = scanadf.o
+
+DISTFILES = Makefile.in gtkglue.c gtkglue.h preferences.c preferences.h \
+ preview.c preview.h progress.c progress.h sane-style.rc xcam.c \
+ xscanimage-gimp-1_0-compat.h xscanimage.c scanadf.c font_6x11.h
+
+.PHONY: all clean depend dist distclean install uninstall
+
+.c.o:
+ $(COMPILE) $<
+
+all: $(PROGRAMS)
+
+install: $(PROGRAMS)
+ @if test -z "$(BINPROGS)" ; then \
+ echo "*** The list of frontends to install is empty." ; \
+ echo "*** Check the output of configure and the file config.log," ; \
+ echo "*** maybe the GTK libraries weren't found?" ; \
+ exit 1 ; \
+ fi
+ $(MKDIR) $(DESTDIR)$(bindir) $(DESTDIR)$(datadir) $(DESTDIR)$(sanedatadir)
+ @for program in $(BINPROGS); do \
+ echo installing $${program} in $(bindir)/$${program}... ; \
+ $(INSTALL_PROGRAM) $${program} \
+ $(DESTDIR)$(bindir)/$${program}; \
+ done
+ $(INSTALL_DATA) $(srcdir)/sane-style.rc \
+ $(DESTDIR)$(sanedatadir)/sane-style.rc
+
+uninstall:
+ @for program in $(BINPROGS); do \
+ echo removing $(bindir)/$${program}...; \
+ rm -f $(bindir)/$${program}; \
+ done
+ rm -f $(sanedatadir)/sane-style.rc
+
+xscanimage: $(XSCANIMAGE_OBJS) $(LIBSANEI) $(LIBLIB)
+ $(LINK) $(XSCANIMAGE_OBJS) $(LIBSANEI) \
+ $(LIBLIB) $(GIMP_LIBS) $(GTK_LIBS) $(LIBS)
+
+xcam: $(XCAM_OBJS) $(LIBSANEI) $(LIBLIB)
+ $(LINK) $(XCAM_OBJS) $(LIBSANEI) \
+ $(LIBLIB) $(GTK_LIBS) $(LIBS)
+
+scanadf: $(SCANADF_OBJS) $(LIBLIB)
+ $(LINK) $(SCANADF_OBJS) $(LIBLIB) $(LIBS)
+
+clean:
+ rm -f *.o *~ .*~ *.bak
+ rm -rf .libs
+
+distclean: clean
+ rm -f Makefile $(PROGRAMS)
+
+depend:
+ makedepend $(INCLUDES) *.c
+
+dist: $(DISTFILES)
+ for file in $(DISTFILES); do \
+ ln $$file $(distdir)/src 2> /dev/null \
+ || cp -p $$file $(distdir)/src ; \
+ done
diff --git a/src/font_6x11.h b/src/font_6x11.h
new file mode 100644
index 0000000..844ff4f
--- /dev/null
+++ b/src/font_6x11.h
@@ -0,0 +1,3337 @@
+/**********************************************/
+/* */
+/* Font file generated by rthelen */
+/* */
+/**********************************************/
+
+static unsigned char fontdata[] = {
+
+ /* 0 0x00 '^A' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 1 0x01 '^B' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 2 0x02 '^C' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 3 0x03 '^D' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 4 0x04 '^E' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 5 0x05 '^F' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 6 0x06 '^G' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 7 0x07 '^H' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 8 0x08 '^I' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 9 0x09 '^J' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 10 0x0a '^K' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 11 0x0b '^L' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 12 0x0c '^M' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 13 0x0d '^N' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 14 0x0e '^O' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 15 0x0f '^P' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 16 0x10 '^Q' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 17 0x11 '^R' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x28, /* 00 0 000 */
+ 0x54, /* 0 0 0 00 */
+ 0x38, /* 00 000 */
+ 0x54, /* 0 0 0 00 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 18 0x12 '^S' */
+ 0x04, /* 00000 00 */
+ 0x04, /* 00000 00 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x50, /* 0 0 0000 */
+ 0x50, /* 0 0 0000 */
+ 0x20, /* 00 00000 */
+ 0x20, /* 00 00000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 19 0x13 '^T' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x38, /* 00 000 */
+ 0x7c, /* 0 00 */
+ 0x38, /* 00 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 20 0x14 '^U' */
+ 0x18, /* 000 000 */
+ 0x10, /* 000 0000 */
+ 0x28, /* 00 0 000 */
+ 0x7c, /* 0 00 */
+ 0x78, /* 0 000 */
+ 0x78, /* 0 000 */
+ 0x7c, /* 0 00 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 21 0x15 '^V' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 22 0x16 '^W' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 23 0x17 '^X' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 24 0x18 '^Y' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 25 0x19 '^Z' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 26 0x1a '^[' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 27 0x1b '^\' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 28 0x1c '^]' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 29 0x1d '^^' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 30 0x1e '^_' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 31 0x1f '^`' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 32 0x20 ' ' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 33 0x21 '!' */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 34 0x22 '"' */
+ 0x28, /* 00 0 000 */
+ 0x28, /* 00 0 000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 35 0x23 '#' */
+ 0x00, /* 00000000 */
+ 0x28, /* 00 0 000 */
+ 0x7c, /* 0 00 */
+ 0x28, /* 00 0 000 */
+ 0x7c, /* 0 00 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 36 0x24 '$' */
+ 0x10, /* 000 0000 */
+ 0x38, /* 00 000 */
+ 0x54, /* 0 0 0 00 */
+ 0x50, /* 0 0 0000 */
+ 0x38, /* 00 000 */
+ 0x14, /* 000 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x38, /* 00 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 37 0x25 '%' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x58, /* 0 0 000 */
+ 0x28, /* 00 0 000 */
+ 0x34, /* 00 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x48, /* 0 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 38 0x26 '&' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00 0000 */
+ 0x48, /* 0 00 000 */
+ 0x50, /* 0 0 0000 */
+ 0x20, /* 00 00000 */
+ 0x54, /* 0 0 0 00 */
+ 0x48, /* 0 00 000 */
+ 0x34, /* 00 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 39 0x27 ''' */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 40 0x28 '(' */
+ 0x04, /* 00000 00 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x08, /* 0000 000 */
+ 0x04, /* 00000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 41 0x29 ')' */
+ 0x20, /* 00 00000 */
+ 0x10, /* 000 0000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x20, /* 00 00000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 42 0x2a '*' */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x54, /* 0 0 0 00 */
+ 0x38, /* 00 000 */
+ 0x54, /* 0 0 0 00 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 43 0x2b '+' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x7c, /* 0 00 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 44 0x2c ',' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00 0000 */
+ 0x30, /* 00 0000 */
+ 0x10, /* 000 0000 */
+ 0x20, /* 00 00000 */
+ 0x00, /* 00000000 */
+
+ /* 45 0x2d '-' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 46 0x2e '.' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 000 000 */
+ 0x18, /* 000 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 47 0x2f '/' */
+ 0x04, /* 00000 00 */
+ 0x04, /* 00000 00 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x20, /* 00 00000 */
+ 0x20, /* 00 00000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x00, /* 00000000 */
+
+ /* 48 0x30 '0' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x4c, /* 0 00 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x64, /* 0 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 49 0x31 '1' */
+ 0x00, /* 00000000 */
+ 0x08, /* 0000 000 */
+ 0x18, /* 000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x1c, /* 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 50 0x32 '2' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x04, /* 00000 00 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x20, /* 00 00000 */
+ 0x7c, /* 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 51 0x33 '3' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x04, /* 00000 00 */
+ 0x18, /* 000 000 */
+ 0x04, /* 00000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 52 0x34 '4' */
+ 0x00, /* 00000000 */
+ 0x08, /* 0000 000 */
+ 0x18, /* 000 000 */
+ 0x28, /* 00 0 000 */
+ 0x48, /* 0 00 000 */
+ 0x7c, /* 0 00 */
+ 0x08, /* 0000 000 */
+ 0x1c, /* 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 53 0x35 '5' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x40, /* 0 000000 */
+ 0x78, /* 0 000 */
+ 0x04, /* 00000 00 */
+ 0x04, /* 00000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 54 0x36 '6' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x40, /* 0 000000 */
+ 0x78, /* 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 55 0x37 '7' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x04, /* 00000 00 */
+ 0x04, /* 00000 00 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 56 0x38 '8' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 57 0x39 '9' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x3c, /* 00 00 */
+ 0x04, /* 00000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 58 0x3a ':' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 000 000 */
+ 0x18, /* 000 000 */
+ 0x00, /* 00000000 */
+ 0x18, /* 000 000 */
+ 0x18, /* 000 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 59 0x3b ';' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00 0000 */
+ 0x30, /* 00 0000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00 0000 */
+ 0x30, /* 00 0000 */
+ 0x10, /* 000 0000 */
+ 0x20, /* 00 00000 */
+ 0x00, /* 00000000 */
+
+ /* 60 0x3c '<' */
+ 0x00, /* 00000000 */
+ 0x04, /* 00000 00 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x20, /* 00 00000 */
+ 0x10, /* 000 0000 */
+ 0x08, /* 0000 000 */
+ 0x04, /* 00000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 61 0x3d '=' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 62 0x3e '>' */
+ 0x00, /* 00000000 */
+ 0x20, /* 00 00000 */
+ 0x10, /* 000 0000 */
+ 0x08, /* 0000 000 */
+ 0x04, /* 00000 00 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x20, /* 00 00000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 63 0x3f '?' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x04, /* 00000 00 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 64 0x40 '@' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x74, /* 0 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x78, /* 0 000 */
+ 0x40, /* 0 000000 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 65 0x41 'A' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x7c, /* 0 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 66 0x42 'B' */
+ 0x00, /* 00000000 */
+ 0x78, /* 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x78, /* 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x78, /* 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 67 0x43 'C' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 68 0x44 'D' */
+ 0x00, /* 00000000 */
+ 0x78, /* 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x78, /* 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 69 0x45 'E' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x78, /* 0 000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x7c, /* 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 70 0x46 'F' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x78, /* 0 000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 71 0x47 'G' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x40, /* 0 000000 */
+ 0x4c, /* 0 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 72 0x48 'H' */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x7c, /* 0 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 73 0x49 'I' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 74 0x4a 'J' */
+ 0x00, /* 00000000 */
+ 0x04, /* 00000 00 */
+ 0x04, /* 00000 00 */
+ 0x04, /* 00000 00 */
+ 0x04, /* 00000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 75 0x4b 'K' */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x48, /* 0 00 000 */
+ 0x50, /* 0 0 0000 */
+ 0x60, /* 0 00000 */
+ 0x50, /* 0 0 0000 */
+ 0x48, /* 0 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 76 0x4c 'L' */
+ 0x00, /* 00000000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x7c, /* 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 77 0x4d 'M' */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x6c, /* 0 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 78 0x4e 'N' */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x64, /* 0 00 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x4c, /* 0 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 79 0x4f 'O' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 80 0x50 'P' */
+ 0x00, /* 00000000 */
+ 0x78, /* 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x78, /* 0 000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 81 0x51 'Q' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x38, /* 00 000 */
+ 0x04, /* 00000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 82 0x52 'R' */
+ 0x00, /* 00000000 */
+ 0x78, /* 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x78, /* 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 83 0x53 'S' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x40, /* 0 000000 */
+ 0x38, /* 00 000 */
+ 0x04, /* 00000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 84 0x54 'T' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 85 0x55 'U' */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 86 0x56 'V' */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x28, /* 00 0 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 87 0x57 'W' */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x6c, /* 0 0 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 88 0x58 'X' */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x28, /* 00 0 000 */
+ 0x10, /* 000 0000 */
+ 0x28, /* 00 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 89 0x59 'Y' */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x28, /* 00 0 000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 90 0x5a 'Z' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x04, /* 00000 00 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x20, /* 00 00000 */
+ 0x40, /* 0 000000 */
+ 0x7c, /* 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 91 0x5b '[' */
+ 0x0c, /* 0000 00 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x0c, /* 0000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 92 0x5c '\' */
+ 0x20, /* 00 00000 */
+ 0x20, /* 00 00000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x04, /* 00000 00 */
+ 0x04, /* 00000 00 */
+ 0x02, /* 000000 0 */
+ 0x02, /* 000000 0 */
+ 0x00, /* 00000000 */
+
+ /* 93 0x5d ']' */
+ 0x30, /* 00 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x30, /* 00 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 94 0x5e '^' */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x28, /* 00 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 95 0x5f '_' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 0 0 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 96 0x60 '`' */
+ 0x20, /* 00 00000 */
+ 0x10, /* 000 0000 */
+ 0x08, /* 0000 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 97 0x61 'a' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x4c, /* 0 00 00 */
+ 0x34, /* 00 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 98 0x62 'b' */
+ 0x00, /* 00000000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x78, /* 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x78, /* 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 99 0x63 'c' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x40, /* 0 000000 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 100 0x64 'd' */
+ 0x00, /* 00000000 */
+ 0x04, /* 00000 00 */
+ 0x04, /* 00000 00 */
+ 0x3c, /* 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 101 0x65 'e' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x7c, /* 0 00 */
+ 0x40, /* 0 000000 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 102 0x66 'f' */
+ 0x00, /* 00000000 */
+ 0x0c, /* 0000 00 */
+ 0x10, /* 000 0000 */
+ 0x38, /* 00 000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 103 0x67 'g' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x3c, /* 00 00 */
+ 0x04, /* 00000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+
+ /* 104 0x68 'h' */
+ 0x00, /* 00000000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x78, /* 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 105 0x69 'i' */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 106 0x6a 'j' */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x30, /* 00 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x60, /* 0 00000 */
+ 0x00, /* 00000000 */
+
+ /* 107 0x6b 'k' */
+ 0x00, /* 00000000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x48, /* 0 00 000 */
+ 0x50, /* 0 0 0000 */
+ 0x70, /* 0 0000 */
+ 0x48, /* 0 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 108 0x6c 'l' */
+ 0x00, /* 00000000 */
+ 0x30, /* 00 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 109 0x6d 'm' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 0 000 */
+ 0x54, /* 0 0 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 110 0x6e 'n' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x58, /* 0 0 000 */
+ 0x64, /* 0 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 111 0x6f 'o' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 112 0x70 'p' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x78, /* 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x78, /* 0 000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x00, /* 00000000 */
+
+ /* 113 0x71 'q' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x3c, /* 00 00 */
+ 0x04, /* 00000 00 */
+ 0x04, /* 00000 00 */
+ 0x00, /* 00000000 */
+
+ /* 114 0x72 'r' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x58, /* 0 0 000 */
+ 0x64, /* 0 00 00 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 115 0x73 's' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x40, /* 0 000000 */
+ 0x38, /* 00 000 */
+ 0x04, /* 00000 00 */
+ 0x78, /* 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 116 0x74 't' */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x38, /* 00 000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x0c, /* 0000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 117 0x75 'u' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x4c, /* 0 00 00 */
+ 0x34, /* 00 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 118 0x76 'v' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x28, /* 00 0 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 119 0x77 'w' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x54, /* 0 0 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 120 0x78 'x' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x28, /* 00 0 000 */
+ 0x10, /* 000 0000 */
+ 0x28, /* 00 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 121 0x79 'y' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x3c, /* 00 00 */
+ 0x04, /* 00000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+
+ /* 122 0x7a 'z' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x20, /* 00 00000 */
+ 0x7c, /* 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 123 0x7b '{' */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x04, /* 00000 00 */
+ 0x00, /* 00000000 */
+
+ /* 124 0x7c '|' */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 125 0x7d '}' */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x20, /* 00 00000 */
+ 0x00, /* 00000000 */
+
+ /* 126 0x7e '~' */
+ 0x00, /* 00000000 */
+ 0x34, /* 00 0 00 */
+ 0x58, /* 0 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 127 0x7f '^?' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 128 0x80 '\200' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x7c, /* 0 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 129 0x81 '\201' */
+ 0x28, /* 00 0 000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x7c, /* 0 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 130 0x82 '\202' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 131 0x83 '\203' */
+ 0x10, /* 000 0000 */
+ 0x7c, /* 0 00 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x78, /* 0 000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x7c, /* 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 132 0x84 '\204' */
+ 0x58, /* 0 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x64, /* 0 00 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x4c, /* 0 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 133 0x85 '\205' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 134 0x86 '\206' */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 135 0x87 '\207' */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x4c, /* 0 00 00 */
+ 0x34, /* 00 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 136 0x88 '\210' */
+ 0x10, /* 000 0000 */
+ 0x08, /* 0000 000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x4c, /* 0 00 00 */
+ 0x34, /* 00 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 137 0x89 '\211' */
+ 0x10, /* 000 0000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x4c, /* 0 00 00 */
+ 0x34, /* 00 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 138 0x8a '\212' */
+ 0x00, /* 00000000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x4c, /* 0 00 00 */
+ 0x34, /* 00 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 139 0x8b '\213' */
+ 0x34, /* 00 0 00 */
+ 0x58, /* 0 0 000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x4c, /* 0 00 00 */
+ 0x34, /* 00 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 140 0x8c '\214' */
+ 0x18, /* 000 000 */
+ 0x24, /* 00 00 00 */
+ 0x18, /* 000 000 */
+ 0x3c, /* 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x4c, /* 0 00 00 */
+ 0x34, /* 00 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 141 0x8d '\215' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x3c, /* 00 00 */
+ 0x10, /* 000 0000 */
+ 0x20, /* 00 00000 */
+ 0x00, /* 00000000 */
+
+ /* 142 0x8e '\216' */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x7c, /* 0 00 */
+ 0x40, /* 0 000000 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 143 0x8f '\217' */
+ 0x20, /* 00 00000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x7c, /* 0 00 */
+ 0x40, /* 0 000000 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 144 0x90 '\220' */
+ 0x10, /* 000 0000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x7c, /* 0 00 */
+ 0x40, /* 0 000000 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 145 0x91 '\221' */
+ 0x00, /* 00000000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x7c, /* 0 00 */
+ 0x40, /* 0 000000 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 146 0x92 '\222' */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 147 0x93 '\223' */
+ 0x20, /* 00 00000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 148 0x94 '\224' */
+ 0x10, /* 000 0000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 149 0x95 '\225' */
+ 0x00, /* 00000000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 150 0x96 '\226' */
+ 0x34, /* 00 0 00 */
+ 0x58, /* 0 0 000 */
+ 0x00, /* 00000000 */
+ 0x58, /* 0 0 000 */
+ 0x64, /* 0 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 151 0x97 '\227' */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 152 0x98 '\230' */
+ 0x20, /* 00 00000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 153 0x99 '\231' */
+ 0x10, /* 000 0000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 154 0x9a '\232' */
+ 0x00, /* 00000000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 155 0x9b '\233' */
+ 0x34, /* 00 0 00 */
+ 0x58, /* 0 0 000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 156 0x9c '\234' */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x4c, /* 0 00 00 */
+ 0x34, /* 00 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 157 0x9d '\235' */
+ 0x20, /* 00 00000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x4c, /* 0 00 00 */
+ 0x34, /* 00 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 158 0x9e '\236' */
+ 0x10, /* 000 0000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x4c, /* 0 00 00 */
+ 0x34, /* 00 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 159 0x9f '\237' */
+ 0x00, /* 00000000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x4c, /* 0 00 00 */
+ 0x34, /* 00 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 160 0xa0 '\240' */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x38, /* 00 000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 161 0xa1 '\241' */
+ 0x18, /* 000 000 */
+ 0x24, /* 00 00 00 */
+ 0x24, /* 00 00 00 */
+ 0x18, /* 000 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 162 0xa2 '\242' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x38, /* 00 000 */
+ 0x54, /* 0 0 0 00 */
+ 0x50, /* 0 0 0000 */
+ 0x54, /* 0 0 0 00 */
+ 0x38, /* 00 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 163 0xa3 '\243' */
+ 0x30, /* 00 0000 */
+ 0x48, /* 0 00 000 */
+ 0x40, /* 0 000000 */
+ 0x70, /* 0 0000 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x44, /* 0 000 00 */
+ 0x78, /* 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 164 0xa4 '\244' */
+ 0x44, /* 0 000 00 */
+ 0x24, /* 00 00 00 */
+ 0x50, /* 0 0 0000 */
+ 0x48, /* 0 00 000 */
+ 0x24, /* 00 00 00 */
+ 0x14, /* 000 0 00 */
+ 0x48, /* 0 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 165 0xa5 '\245' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x7c, /* 0 00 */
+ 0x7c, /* 0 00 */
+ 0x7c, /* 0 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 166 0xa6 '\246' */
+ 0x3c, /* 00 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x3c, /* 00 00 */
+ 0x14, /* 000 0 00 */
+ 0x14, /* 000 0 00 */
+ 0x14, /* 000 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 167 0xa7 '\247' */
+ 0x18, /* 000 000 */
+ 0x24, /* 00 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x48, /* 0 00 000 */
+ 0x48, /* 0 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x58, /* 0 0 000 */
+ 0x40, /* 0 000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 168 0xa8 '\250' */
+ 0x00, /* 00000000 */
+ 0x70, /* 0 0000 */
+ 0x08, /* 0000 000 */
+ 0x64, /* 0 00 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x64, /* 0 00 00 */
+ 0x58, /* 0 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 169 0xa9 '\251' */
+ 0x00, /* 00000000 */
+ 0x70, /* 0 0000 */
+ 0x08, /* 0000 000 */
+ 0x34, /* 00 0 00 */
+ 0x44, /* 0 000 00 */
+ 0x34, /* 00 0 00 */
+ 0x08, /* 0000 000 */
+ 0x70, /* 0 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 170 0xaa '\252' */
+ 0x00, /* 00000000 */
+ 0x7a, /* 0 0 0 */
+ 0x2e, /* 00 0 0 */
+ 0x2e, /* 00 0 0 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 171 0xab '\253' */
+ 0x00, /* 00000000 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 172 0xac '\254' */
+ 0x00, /* 00000000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 173 0xad '\255' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x08, /* 0000 000 */
+ 0x7c, /* 0 00 */
+ 0x10, /* 000 0000 */
+ 0x7c, /* 0 00 */
+ 0x20, /* 00 00000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 174 0xae '\256' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x50, /* 0 0 0000 */
+ 0x50, /* 0 0 0000 */
+ 0x78, /* 0 000 */
+ 0x50, /* 0 0 0000 */
+ 0x50, /* 0 0 0000 */
+ 0x5c, /* 0 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 175 0xaf '\257' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x4c, /* 0 00 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x64, /* 0 00 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 176 0xb0 '\260' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x6c, /* 0 0 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x6c, /* 0 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 177 0xb1 '\261' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x7c, /* 0 00 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x7c, /* 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 178 0xb2 '\262' */
+ 0x00, /* 00000000 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x20, /* 00 00000 */
+ 0x10, /* 000 0000 */
+ 0x08, /* 0000 000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 179 0xb3 '\263' */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x08, /* 0000 000 */
+ 0x04, /* 00000 00 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x1c, /* 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 180 0xb4 '\264' */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x28, /* 00 0 000 */
+ 0x7c, /* 0 00 */
+ 0x10, /* 000 0000 */
+ 0x7c, /* 0 00 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 181 0xb5 '\265' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x48, /* 0 00 000 */
+ 0x48, /* 0 00 000 */
+ 0x48, /* 0 00 000 */
+ 0x48, /* 0 00 000 */
+ 0x74, /* 0 0 00 */
+ 0x40, /* 0 000000 */
+ 0x40, /* 0 000000 */
+ 0x00, /* 00000000 */
+
+ /* 182 0xb6 '\266' */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x08, /* 0000 000 */
+ 0x0c, /* 0000 00 */
+ 0x14, /* 000 0 00 */
+ 0x24, /* 00 00 00 */
+ 0x24, /* 00 00 00 */
+ 0x18, /* 000 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 183 0xb7 '\267' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x24, /* 00 00 00 */
+ 0x10, /* 000 0000 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x24, /* 00 00 00 */
+ 0x7c, /* 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 184 0xb8 '\270' */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x28, /* 00 0 000 */
+ 0x28, /* 00 0 000 */
+ 0x28, /* 00 0 000 */
+ 0x28, /* 00 0 000 */
+ 0x28, /* 00 0 000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 185 0xb9 '\271' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x28, /* 00 0 000 */
+ 0x28, /* 00 0 000 */
+ 0x28, /* 00 0 000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 186 0xba '\272' */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x60, /* 0 00000 */
+ 0x00, /* 00000000 */
+
+ /* 187 0xbb '\273' */
+ 0x00, /* 00000000 */
+ 0x1c, /* 000 00 */
+ 0x24, /* 00 00 00 */
+ 0x24, /* 00 00 00 */
+ 0x1c, /* 000 00 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 188 0xbc '\274' */
+ 0x00, /* 00000000 */
+ 0x18, /* 000 000 */
+ 0x24, /* 00 00 00 */
+ 0x24, /* 00 00 00 */
+ 0x18, /* 000 000 */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 189 0xbd '\275' */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x28, /* 00 0 000 */
+ 0x6c, /* 0 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 190 0xbe '\276' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x54, /* 0 0 0 00 */
+ 0x5c, /* 0 0 00 */
+ 0x50, /* 0 0 0000 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 191 0xbf '\277' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x4c, /* 0 00 00 */
+ 0x54, /* 0 0 0 00 */
+ 0x64, /* 0 00 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 192 0xc0 '\300' */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x20, /* 00 00000 */
+ 0x40, /* 0 000000 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 193 0xc1 '\301' */
+ 0x00, /* 00000000 */
+ 0x08, /* 0000 000 */
+ 0x00, /* 00000000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x08, /* 0000 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 194 0xc2 '\302' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x04, /* 00000 00 */
+ 0x04, /* 00000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 195 0xc3 '\303' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x0c, /* 0000 00 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x50, /* 0 0 0000 */
+ 0x20, /* 00 00000 */
+ 0x20, /* 00 00000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 196 0xc4 '\304' */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x38, /* 00 000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x60, /* 0 00000 */
+ 0x00, /* 00000000 */
+
+ /* 197 0xc5 '\305' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x04, /* 00000 00 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x40, /* 0 000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 198 0xc6 '\306' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x10, /* 000 0000 */
+ 0x28, /* 00 0 000 */
+ 0x28, /* 00 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x7c, /* 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 199 0xc7 '\307' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x24, /* 00 00 00 */
+ 0x48, /* 0 00 000 */
+ 0x48, /* 0 00 000 */
+ 0x24, /* 00 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 200 0xc8 '\310' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x48, /* 0 00 000 */
+ 0x24, /* 00 00 00 */
+ 0x24, /* 00 00 00 */
+ 0x48, /* 0 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 201 0xc9 '\311' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x54, /* 0 0 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 202 0xca '\312' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 203 0xcb '\313' */
+ 0x10, /* 000 0000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x7c, /* 0 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 204 0xcc '\314' */
+ 0x58, /* 0 0 000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x7c, /* 0 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 205 0xcd '\315' */
+ 0x58, /* 0 0 000 */
+ 0x38, /* 00 000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 206 0xce '\316' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x50, /* 0 0 0000 */
+ 0x50, /* 0 0 0000 */
+ 0x58, /* 0 0 000 */
+ 0x50, /* 0 0 0000 */
+ 0x50, /* 0 0 0000 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 207 0xcf '\317' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x28, /* 00 0 000 */
+ 0x54, /* 0 0 0 00 */
+ 0x5c, /* 0 0 00 */
+ 0x50, /* 0 0 0000 */
+ 0x2c, /* 00 0 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 208 0xd0 '\320' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 209 0xd1 '\321' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 0 0 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 210 0xd2 '\322' */
+ 0x00, /* 00000000 */
+ 0x14, /* 000 0 00 */
+ 0x28, /* 00 0 000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 211 0xd3 '\323' */
+ 0x00, /* 00000000 */
+ 0x14, /* 000 0 00 */
+ 0x14, /* 000 0 00 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 212 0xd4 '\324' */
+ 0x00, /* 00000000 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x18, /* 000 000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 213 0xd5 '\325' */
+ 0x00, /* 00000000 */
+ 0x18, /* 000 000 */
+ 0x08, /* 0000 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 214 0xd6 '\326' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x7c, /* 0 00 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 215 0xd7 '\327' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x10, /* 000 0000 */
+ 0x28, /* 00 0 000 */
+ 0x44, /* 0 000 00 */
+ 0x28, /* 00 0 000 */
+ 0x10, /* 000 0000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 216 0xd8 '\330' */
+ 0x00, /* 00000000 */
+ 0x28, /* 00 0 000 */
+ 0x00, /* 00000000 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x44, /* 0 000 00 */
+ 0x3c, /* 00 00 */
+ 0x04, /* 00000 00 */
+ 0x38, /* 00 000 */
+ 0x00, /* 00000000 */
+
+ /* 217 0xd9 '\331' */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 0 0 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 0 0 */
+ 0x00, /* 00000000 */
+ 0x7e, /* 0 0 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 218 0xda '\332' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 219 0xdb '\333' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 220 0xdc '\334' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 221 0xdd '\335' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 222 0xde '\336' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 223 0xdf '\337' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 224 0xe0 '\340' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 225 0xe1 '\341' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 226 0xe2 '\342' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 227 0xe3 '\343' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 228 0xe4 '\344' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 229 0xe5 '\345' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 230 0xe6 '\346' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 231 0xe7 '\347' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 232 0xe8 '\350' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 233 0xe9 '\351' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 234 0xea '\352' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 235 0xeb '\353' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 236 0xec '\354' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 237 0xed '\355' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 238 0xee '\356' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 239 0xef '\357' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 240 0xf0 '\360' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 241 0xf1 '\361' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 242 0xf2 '\362' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 243 0xf3 '\363' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 244 0xf4 '\364' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 245 0xf5 '\365' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 246 0xf6 '\366' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 247 0xf7 '\367' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 248 0xf8 '\370' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 249 0xf9 '\371' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 250 0xfa '\372' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 251 0xfb '\373' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 252 0xfc '\374' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 253 0xfd '\375' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 254 0xfe '\376' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+ /* 255 0xff '\377' */
+ 0x00, /* 00000000 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x3c, /* 00 00 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+ 0x00, /* 00000000 */
+
+};
diff --git a/src/gtkglue.c b/src/gtkglue.c
new file mode 100644
index 0000000..ba5cbf5
--- /dev/null
+++ b/src/gtkglue.c
@@ -0,0 +1,1495 @@
+/* gtk/SANE-glue -- gtk interfacing routines for SANE
+ Uses the SANE library.
+ Copyright (C) 1997 David Mosberger and Tristan Tarrant
+
+ 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 <assert.h>
+#include <errno.h>
+#include <memory.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h> /* Apollo/DomainOS needs this _before_ sys/stat.h */
+#include <sys/stat.h>
+
+#include "gtkglue.h"
+
+#include "preferences.h"
+#include <sane/sane.h>
+#include <sane/saneopts.h>
+
+#define DBG_fatal 0
+#define DBG_error 1
+#define DBG_warning 2
+#define DBG_info 3
+#define DBG_debug 4
+
+#define BACKEND_NAME gtkglue
+#include "../include/sane/sanei_debug.h"
+
+int gsg_message_dialog_active = 0;
+
+/* forward declarations: */
+static void panel_rebuild (GSGDialog * dialog);
+
+static const char *
+unit_string (SANE_Unit unit)
+{
+ double d;
+
+ switch (unit)
+ {
+ case SANE_UNIT_NONE:
+ return "none";
+ case SANE_UNIT_PIXEL:
+ return "pixel";
+ case SANE_UNIT_BIT:
+ return "bit";
+ case SANE_UNIT_DPI:
+ return "dpi";
+ case SANE_UNIT_PERCENT:
+ return "%";
+ case SANE_UNIT_MM:
+ d = preferences.length_unit;
+ if (d > 9.9 && d < 10.1)
+ return "cm";
+ else if (d > 25.3 && d < 25.5)
+ return "in";
+ return "mm";
+ case SANE_UNIT_MICROSECOND:
+ return "us";
+ }
+ return 0;
+}
+
+static void
+set_tooltip (GtkTooltips * tooltips, GtkWidget * widget, const char *desc)
+{
+ if (desc && desc[0])
+#ifdef HAVE_GTK_TOOLTIPS_SET_TIPS
+ /* pre 0.99.4: */
+ gtk_tooltips_set_tips (tooltips, widget, (char *) desc);
+#else
+ gtk_tooltips_set_tip (tooltips, widget, desc, 0);
+#endif
+}
+
+int
+gsg_make_path (size_t buf_size, char *buf,
+ const char *prog_name,
+ const char *prefix, const char *dev_name, const char *postfix)
+{
+ struct passwd *pw;
+ size_t len, extra;
+ int i;
+
+ /* first, make sure ~/.sane exists: */
+ pw = getpwuid (getuid ());
+ if (!pw)
+ {
+ snprintf (buf, buf_size, "Failed to determine home directory: %s.",
+ strerror (errno));
+ gsg_error (buf);
+ return -1;
+ }
+ snprintf (buf, buf_size, "%s/.sane", pw->pw_dir);
+ mkdir (buf, 0777); /* ensure ~/.sane directory exists */
+
+ len = strlen (buf);
+
+ if (prog_name)
+ {
+ extra = strlen (prog_name);
+ if (len + extra + 1 >= buf_size)
+ goto filename_too_long;
+
+ buf[len++] = '/';
+ memcpy (buf + len, prog_name, extra);
+ len += extra;
+ buf[len] = '\0';
+ mkdir (buf, 0777); /* ensure ~/.sane/PROG_NAME directory exists */
+ }
+ if (len >= buf_size)
+ goto filename_too_long;
+
+ buf[len++] = '/';
+
+ if (prefix)
+ {
+ extra = strlen (prefix);
+ if (len + extra >= buf_size)
+ goto filename_too_long;
+
+ memcpy (buf + len, prefix, extra);
+ len += extra;
+ }
+
+ if (dev_name)
+ {
+ /* Turn devicename into valid filename by replacing slashes by
+ "+-". A lonely `+' gets translated into "++" so we can tell
+ it from a substituted slash. */
+
+ for (i = 0; dev_name[i]; ++i)
+ {
+ if (len + 2 >= buf_size)
+ goto filename_too_long;
+
+ switch (dev_name[i])
+ {
+ case '/':
+ buf[len++] = '+';
+ buf[len++] = '-';
+ break;
+
+#ifdef HAVE_OS2_H
+ case ':': /* OS2 can not handle colons in filenames */
+ buf[len++] = '+';
+ buf[len++] = '_';
+ break;
+#endif
+
+ case '+':
+ buf[len++] = '+';
+ default:
+ buf[len++] = dev_name[i];
+ break;
+ }
+ }
+ }
+
+ if (postfix)
+ {
+ extra = strlen (postfix);
+ if (len + extra >= buf_size)
+ goto filename_too_long;
+ memcpy (buf + len, postfix, extra);
+ len += extra;
+ }
+ if (len >= buf_size)
+ goto filename_too_long;
+
+ buf[len++] = '\0';
+ return 0;
+
+filename_too_long:
+ gsg_error ("Filename too long.");
+ errno = E2BIG;
+ return -1;
+}
+
+static void
+set_option (GSGDialog * dialog, int opt_num, void *val, SANE_Action action)
+{
+ SANE_Status status;
+ SANE_Int info;
+ char buf[256];
+
+ status = sane_control_option (dialog->dev, opt_num, action, val, &info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ snprintf (buf, sizeof (buf), "Failed to set value of option %s: %s.",
+ sane_get_option_descriptor (dialog->dev, opt_num)->name,
+ sane_strstatus (status));
+ gsg_error (buf);
+ return;
+ }
+ if (info & SANE_INFO_RELOAD_OPTIONS)
+ {
+ panel_rebuild (dialog);
+ if (dialog->option_reload_callback)
+ (*dialog->option_reload_callback) (dialog, dialog->option_reload_arg);
+ }
+ if ((info & SANE_INFO_RELOAD_PARAMS) && dialog->param_change_callback)
+ (*dialog->param_change_callback) (dialog, dialog->param_change_arg);
+}
+
+void
+gsg_close_dialog_callback (GtkWidget * widget, gpointer data)
+{
+ gtk_widget_destroy (data);
+ gsg_message_dialog_active = 0;
+}
+
+void
+gsg_message (gchar * title, gchar * message)
+{
+ GtkWidget *main_vbox, *label;
+ GtkWidget *button, *message_dialog;
+
+ if (gsg_message_dialog_active)
+ {
+ fprintf (stderr, "%s: %s\n", title, message);
+ return;
+ }
+ gsg_message_dialog_active = 1;
+ message_dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_position (GTK_WINDOW (message_dialog), GTK_WIN_POS_MOUSE);
+ gtk_window_set_title (GTK_WINDOW (message_dialog), title);
+
+ /* create the main vbox */
+ main_vbox = gtk_vbox_new (TRUE, 5);
+ gtk_container_border_width (GTK_CONTAINER (main_vbox), 5);
+ gtk_widget_show (main_vbox);
+
+ gtk_container_add (GTK_CONTAINER (message_dialog), main_vbox);
+
+ /* the message */
+ label = gtk_label_new (message);
+ gtk_container_add (GTK_CONTAINER (main_vbox), label);
+ gtk_widget_show (label);
+
+ /* the confirmation button */
+ button = gtk_button_new_with_label ("OK");
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) gsg_close_dialog_callback,
+ message_dialog);
+ gtk_container_add (GTK_CONTAINER (main_vbox), button);
+
+ gtk_widget_show (button);
+ gtk_widget_show (message_dialog);
+}
+
+void
+gsg_error (gchar * error)
+{
+ gsg_message ("Error", error);
+}
+
+void
+gsg_warning (gchar * warning)
+{
+ gsg_message ("Warning", warning);
+}
+
+static void
+get_filename_button_clicked (GtkWidget * w, gpointer data)
+{
+ int *clicked = data;
+ *clicked = 1;
+}
+
+int
+gsg_get_filename (const char *label, const char *default_name,
+ size_t max_len, char *filename)
+{
+ int cancel = 0, ok = 0;
+ GtkWidget *filesel;
+
+ filesel = gtk_file_selection_new ((char *) label);
+
+ gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
+
+ gtk_signal_connect (GTK_OBJECT
+ (GTK_FILE_SELECTION (filesel)->cancel_button),
+ "clicked", (GtkSignalFunc) get_filename_button_clicked,
+ &cancel);
+ gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button),
+ "clicked", (GtkSignalFunc) get_filename_button_clicked,
+ &ok);
+ if (default_name)
+ gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel),
+ (char *) default_name);
+
+ gtk_widget_show (filesel);
+
+ while (!cancel && !ok)
+ {
+ if (!gtk_events_pending ())
+ usleep (100000);
+ gtk_main_iteration ();
+ }
+
+ if (ok)
+ {
+ size_t len, cwd_len;
+ char *cwd;
+
+ strncpy (filename,
+ gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)),
+ max_len - 1);
+ filename[max_len - 1] = '\0';
+
+ len = strlen (filename);
+ cwd = alloca (len + 2);
+ getcwd (cwd, len + 1);
+ cwd_len = strlen (cwd);
+ cwd[cwd_len++] = '/';
+ cwd[cwd_len] = '\0';
+ if (strncmp (filename, cwd, cwd_len) == 0)
+ memcpy (filename, filename + cwd_len, len - cwd_len + 1);
+ }
+ gtk_widget_destroy (filesel);
+ return cancel ? -1 : 0;
+}
+
+static gint
+autobutton_update (GtkWidget * widget, GSGDialogElement * elem)
+{
+ GSGDialog *dialog = elem->dialog;
+ int opt_num = elem - dialog->element;
+ const SANE_Option_Descriptor *opt;
+ SANE_Status status;
+ SANE_Word val;
+ char buf[256];
+
+ opt = sane_get_option_descriptor (dialog->dev, opt_num);
+ if (GTK_TOGGLE_BUTTON (widget)->active)
+ set_option (dialog, opt_num, 0, SANE_ACTION_SET_AUTO);
+ else
+ {
+ status = sane_control_option (dialog->dev, opt_num,
+ SANE_ACTION_GET_VALUE, &val, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ snprintf (buf, sizeof (buf),
+ "Failed to obtain value of option %s: %s.",
+ opt->name, sane_strstatus (status));
+ gsg_error (buf);
+ }
+ set_option (dialog, opt_num, &val, SANE_ACTION_SET_VALUE);
+ }
+ return FALSE;
+}
+
+static void
+autobutton_new (GtkWidget * parent, GSGDialogElement * elem,
+ GtkWidget * label, GtkTooltips * tooltips)
+{
+ GtkWidget *button, *alignment;
+
+ button = gtk_check_button_new ();
+ gtk_container_border_width (GTK_CONTAINER (button), 0);
+ gtk_widget_set_usize (button, 20, 20);
+ gtk_signal_connect (GTK_OBJECT (button), "toggled",
+ (GtkSignalFunc) autobutton_update, elem);
+ set_tooltip (tooltips, button, "Turns on automatic mode.");
+
+ alignment = gtk_alignment_new (0.0, 1.0, 0.5, 0.5);
+ gtk_container_add (GTK_CONTAINER (alignment), button);
+
+ gtk_box_pack_end (GTK_BOX (parent), label, FALSE, FALSE, 0);
+ gtk_box_pack_end (GTK_BOX (parent), alignment, FALSE, FALSE, 2);
+
+ gtk_widget_show (alignment);
+ gtk_widget_show (button);
+}
+
+static gint
+button_update (GtkWidget * widget, GSGDialogElement * elem)
+{
+ GSGDialog *dialog = elem->dialog;
+ int opt_num = elem - dialog->element;
+ const SANE_Option_Descriptor *opt;
+ SANE_Word val = SANE_FALSE;
+
+ opt = sane_get_option_descriptor (dialog->dev, opt_num);
+ if (GTK_TOGGLE_BUTTON (widget)->active)
+ val = SANE_TRUE;
+ set_option (dialog, opt_num, &val, SANE_ACTION_SET_VALUE);
+ return FALSE;
+}
+
+static void
+button_new (GtkWidget * parent, const char *name, SANE_Word val,
+ GSGDialogElement * elem, GtkTooltips * tooltips, const char *desc,
+ gint is_settable)
+{
+ GtkWidget *button;
+
+ button = gtk_check_button_new_with_label ((char *) name);
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), val);
+ gtk_signal_connect (GTK_OBJECT (button), "toggled",
+ (GtkSignalFunc) button_update, elem);
+ gtk_box_pack_start (GTK_BOX (parent), button, FALSE, TRUE, 0);
+ if (!is_settable)
+ gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
+ gtk_widget_show (button);
+ set_tooltip (tooltips, button, desc);
+
+ elem->widget = button;
+}
+
+static void
+scale_update (GtkAdjustment * adj_data, GSGDialogElement * elem)
+{
+ const SANE_Option_Descriptor *opt;
+ GSGDialog *dialog = elem->dialog;
+ SANE_Word val, new_val;
+ int opt_num;
+ double d;
+ SANE_Status status;
+
+ DBG_INIT ();
+
+ DBG (DBG_debug, "scale_update\n");
+
+
+ opt_num = elem - dialog->element;
+ opt = sane_get_option_descriptor (dialog->dev, opt_num);
+ switch (opt->type)
+ {
+ case SANE_TYPE_INT:
+ val = adj_data->value + 0.5;
+ break;
+
+ case SANE_TYPE_FIXED:
+ d = adj_data->value;
+ if (opt->unit == SANE_UNIT_MM)
+ d *= preferences.length_unit;
+ val = SANE_FIX (d);
+ break;
+
+ default:
+ fprintf (stderr, "scale_update: unknown type %d\n", opt->type);
+ return;
+ }
+ set_option (dialog, opt_num, &val, SANE_ACTION_SET_VALUE);
+ status =
+ sane_control_option (dialog->dev, opt_num, SANE_ACTION_GET_VALUE,
+ &new_val, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_fatal, "scale_update: sane_control_option failed: %s\n",
+ sane_strstatus (status));
+ return;
+ }
+
+ if (new_val != val)
+ {
+ val = new_val;
+ goto value_changed;
+ }
+ return; /* value didn't change */
+
+value_changed:
+ switch (opt->type)
+ {
+ case SANE_TYPE_INT:
+ adj_data->value = val;
+ break;
+
+ case SANE_TYPE_FIXED:
+ d = SANE_UNFIX (val);
+ if (opt->unit == SANE_UNIT_MM)
+ d /= preferences.length_unit;
+ adj_data->value = d;
+ break;
+
+ default:
+ break;
+ }
+ /* Let widget know that value changed _again_. This must converge
+ quickly---otherwise things would get very slow very quickly (as
+ in "infinite recursion"): */
+ gtk_signal_emit_by_name (GTK_OBJECT (adj_data), "value_changed");
+ return;
+}
+
+static void
+scale_new (GtkWidget * parent, const char *name, gfloat val,
+ gfloat min, gfloat max, gfloat quant, int automatic,
+ GSGDialogElement * elem, GtkTooltips * tooltips, const char *desc,
+ gint is_settable)
+{
+ GtkWidget *hbox, *label, *scale;
+
+ hbox = gtk_hbox_new (FALSE, 2);
+ gtk_container_border_width (GTK_CONTAINER (hbox), 0);
+ gtk_box_pack_start (GTK_BOX (parent), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new ((char *) name);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 2);
+
+ elem->data = gtk_adjustment_new (val, min, max, quant, 1.0, 0.0);
+ scale = gtk_hscale_new (GTK_ADJUSTMENT (elem->data));
+ set_tooltip (tooltips, scale, desc);
+ gtk_widget_set_usize (scale, 200, 0);
+
+ if (automatic)
+ autobutton_new (hbox, elem, scale, tooltips);
+ else
+ gtk_box_pack_end (GTK_BOX (hbox), scale, FALSE, FALSE, 0);
+
+ gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_CONTINUOUS);
+ gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
+ if (quant - (int) quant == 0.0)
+ gtk_scale_set_digits (GTK_SCALE (scale), 0);
+ else
+ /* one place behind decimal point */
+ gtk_scale_set_digits (GTK_SCALE (scale), 1);
+
+ gtk_signal_connect (elem->data, "value_changed",
+ (GtkSignalFunc) scale_update, elem);
+
+ gtk_widget_show (label);
+ gtk_widget_show (scale);
+ if (!is_settable)
+ gtk_widget_set_sensitive (GTK_WIDGET (hbox), FALSE);
+
+ gtk_widget_show (hbox);
+
+ elem->widget = scale;
+}
+
+static void
+push_button_callback (GtkWidget * widget, gpointer data)
+{
+ GSGDialogElement *elem = data;
+ GSGDialog *dialog = elem->dialog;
+ int opt_num;
+
+ opt_num = elem - dialog->element;
+ set_option (dialog, opt_num, 0, SANE_ACTION_SET_VALUE);
+}
+
+static int
+option_menu_lookup (GSGMenuItem menu_items[], const char *string)
+{
+ int i;
+
+ for (i = 0; strcmp (menu_items[i].label, string) != 0; ++i);
+ return i;
+}
+
+static void
+option_menu_callback (GtkWidget * widget, gpointer data)
+{
+ GSGMenuItem *menu_item = data;
+ GSGDialogElement *elem = menu_item->elem;
+ const SANE_Option_Descriptor *opt;
+ GSGDialog *dialog = elem->dialog;
+ int opt_num;
+ double dval;
+ SANE_Word val;
+ void *valp = &val;
+
+ opt_num = elem - dialog->element;
+ opt = sane_get_option_descriptor (dialog->dev, opt_num);
+ switch (opt->type)
+ {
+ case SANE_TYPE_INT:
+ sscanf (menu_item->label, "%d", &val);
+ break;
+
+ case SANE_TYPE_FIXED:
+ sscanf (menu_item->label, "%lg", &dval);
+ val = SANE_FIX (dval);
+ break;
+
+ case SANE_TYPE_STRING:
+ valp = menu_item->label;
+ break;
+
+ default:
+ fprintf (stderr, "option_menu_callback: unexpected type %d\n",
+ opt->type);
+ break;
+ }
+ set_option (dialog, opt_num, valp, SANE_ACTION_SET_VALUE);
+}
+
+static void
+option_menu_new (GtkWidget * parent, const char *name, char *str_list[],
+ const char *val, GSGDialogElement * elem,
+ GtkTooltips * tooltips, const char *desc, gint is_settable)
+{
+ GtkWidget *hbox, *label, *option_menu, *menu, *item;
+ GSGMenuItem *menu_items;
+ int i, num_items;
+
+ hbox = gtk_hbox_new (FALSE, 2);
+ gtk_container_border_width (GTK_CONTAINER (hbox), 0);
+ gtk_box_pack_start (GTK_BOX (parent), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new ((char *) name);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 2);
+
+ for (num_items = 0; str_list[num_items]; ++num_items);
+ menu_items = malloc (num_items * sizeof (menu_items[0]));
+
+ menu = gtk_menu_new ();
+ for (i = 0; i < num_items; ++i)
+ {
+ item = gtk_menu_item_new_with_label (str_list[i]);
+ gtk_container_add (GTK_CONTAINER (menu), item);
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ (GtkSignalFunc) option_menu_callback,
+ menu_items + i);
+
+ gtk_widget_show (item);
+
+ menu_items[i].label = str_list[i];
+ menu_items[i].elem = elem;
+ menu_items[i].index = i;
+ }
+
+ option_menu = gtk_option_menu_new ();
+ gtk_box_pack_end (GTK_BOX (hbox), option_menu, FALSE, FALSE, 2);
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), menu);
+ gtk_option_menu_set_history (GTK_OPTION_MENU (option_menu),
+ option_menu_lookup (menu_items, val));
+ set_tooltip (tooltips, option_menu, desc);
+
+ gtk_widget_show (label);
+ gtk_widget_show (option_menu);
+ if (!is_settable)
+ gtk_widget_set_sensitive (GTK_WIDGET (hbox), FALSE);
+ gtk_widget_show (hbox);
+
+ elem->widget = option_menu;
+ elem->menu_size = num_items;
+ elem->menu = menu_items;
+}
+
+static void
+text_entry_callback (GtkWidget * w, gpointer data)
+{
+ GSGDialogElement *elem = data;
+ const SANE_Option_Descriptor *opt;
+ GSGDialog *dialog = elem->dialog;
+ const gchar *text;
+ int opt_num;
+ char *buf;
+
+ opt_num = elem - dialog->element;
+ opt = sane_get_option_descriptor (dialog->dev, opt_num);
+
+ buf = alloca (opt->size);
+ buf[0] = '\0';
+
+ text = gtk_entry_get_text (GTK_ENTRY (elem->widget));
+ if (text)
+ strncpy (buf, text, opt->size);
+ buf[opt->size - 1] = '\0';
+
+ set_option (dialog, opt_num, buf, SANE_ACTION_SET_VALUE);
+
+ if (strcmp (buf, text) != 0)
+ /* the backend modified the option value; update widget: */
+ gtk_entry_set_text (GTK_ENTRY (elem->widget), buf);
+}
+
+static void
+text_entry_new (GtkWidget * parent, const char *name, const char *val,
+ GSGDialogElement * elem,
+ GtkTooltips * tooltips, const char *desc, gint is_settable)
+{
+ GtkWidget *hbox, *text, *label;
+
+ hbox = gtk_hbox_new (FALSE, 2);
+ gtk_container_border_width (GTK_CONTAINER (hbox), 0);
+ gtk_box_pack_start (GTK_BOX (parent), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new ((char *) name);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 2);
+
+ text = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (text), (char *) val);
+ gtk_box_pack_start (GTK_BOX (hbox), text, FALSE, TRUE, 0);
+ gtk_signal_connect (GTK_OBJECT (text), "changed",
+ (GtkSignalFunc) text_entry_callback, elem);
+ set_tooltip (tooltips, text, desc);
+
+ gtk_widget_show (hbox);
+ if (!is_settable)
+ gtk_widget_set_sensitive (GTK_WIDGET (hbox), FALSE);
+ gtk_widget_show (label);
+ gtk_widget_show (text);
+
+ elem->widget = text;
+}
+
+static GtkWidget *
+group_new (GtkWidget * parent, const char *title)
+{
+ GtkWidget *frame, *vbox;
+
+ frame = gtk_frame_new ((char *) title);
+ 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 (parent), frame, FALSE, FALSE, 0);
+
+ vbox = gtk_vbox_new (FALSE, 4);
+ gtk_container_border_width (GTK_CONTAINER (vbox), 2);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+ gtk_widget_show (vbox);
+ return vbox;
+}
+
+static GtkWidget *
+curve_new (GSGDialog * dialog, int optnum)
+{
+ const SANE_Option_Descriptor *opt;
+ gfloat fmin, fmax, val, *vector;
+ SANE_Word *optval, min, max;
+ GtkWidget *curve, *gamma;
+ SANE_Status status;
+ SANE_Handle dev;
+ int i, optlen;
+
+ gamma = gtk_gamma_curve_new ();
+ curve = GTK_GAMMA_CURVE (gamma)->curve;
+ dev = dialog->dev;
+
+ opt = sane_get_option_descriptor (dev, optnum);
+ optlen = opt->size / sizeof (SANE_Word);
+ vector = alloca (optlen * (sizeof (vector[0]) + sizeof (optval[0])));
+ optval = (SANE_Word *) (vector + optlen);
+
+ min = max = 0;
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ min = opt->constraint.range->min;
+ max = opt->constraint.range->max;
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ if (opt->constraint.word_list[0] > 1)
+ {
+ min = max = opt->constraint.word_list[1];
+ for (i = 2; i < opt->constraint.word_list[0]; ++i)
+ {
+ if (opt->constraint.word_list[i] < min)
+ min = opt->constraint.word_list[i];
+ if (opt->constraint.word_list[i] > max)
+ max = opt->constraint.word_list[i];
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (min == max)
+ {
+ fprintf (stderr,
+ "curve_new: warning: option `%s' has no value constraint\n",
+ opt->name);
+ fmin = 0;
+ fmax = 255;
+ }
+ else if (opt->type == SANE_TYPE_FIXED)
+ {
+ fmin = SANE_UNFIX (min);
+ fmax = SANE_UNFIX (max);
+ }
+ else
+ {
+ fmin = min;
+ fmax = max;
+ }
+ gtk_curve_set_range (GTK_CURVE (curve), 0, optlen - 1, fmin, fmax);
+
+ status = sane_control_option (dev, optnum, SANE_ACTION_GET_VALUE,
+ optval, 0);
+ if (status == SANE_STATUS_GOOD)
+ {
+ for (i = 0; i < optlen; ++i)
+ {
+ if (opt->type == SANE_TYPE_FIXED)
+ val = SANE_UNFIX (optval[i]);
+ else
+ val = optval[i];
+ vector[i] = val;
+ }
+ gtk_curve_set_vector (GTK_CURVE (curve), optlen, vector);
+ }
+ else
+ gtk_widget_set_sensitive (gamma, FALSE);
+
+ return gamma;
+}
+
+static void
+vector_new (GSGDialog * dialog, GtkWidget * vbox, int num_vopts, int *vopts)
+{
+ GtkWidget *notebook, *label, *curve;
+ const SANE_Option_Descriptor *opt;
+ int i;
+
+ notebook = gtk_notebook_new ();
+ gtk_container_border_width (GTK_CONTAINER (notebook), 4);
+ gtk_box_pack_start (GTK_BOX (vbox), notebook, TRUE, TRUE, 0);
+
+ for (i = 0; i < num_vopts; ++i)
+ {
+ opt = sane_get_option_descriptor (dialog->dev, vopts[i]);
+
+ label = gtk_label_new ((char *) opt->title);
+ vbox = gtk_vbox_new ( /* homogeneous */ FALSE, 0);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, label);
+ gtk_widget_show (vbox);
+ gtk_widget_show (label);
+
+ curve = curve_new (dialog, vopts[i]);
+ gtk_container_border_width (GTK_CONTAINER (curve), 4);
+ gtk_box_pack_start (GTK_BOX (vbox), curve, TRUE, TRUE, 0);
+ gtk_widget_show (curve);
+
+ dialog->element[vopts[i]].widget = curve;
+ }
+ gtk_widget_show (notebook);
+}
+
+static void
+panel_destroy (GSGDialog * dialog)
+{
+ const SANE_Option_Descriptor *opt;
+ GSGDialogElement *elem;
+ int i, j;
+
+#ifdef HAVE_GTK_TOOLTIPS_SET_TIPS
+ /* pre 0.99.4: */
+ gtk_tooltips_unref (dialog->tooltips);
+#else
+# if GTK_MAJOR_VERSION == 2
+ gtk_object_sink (GTK_OBJECT (dialog->tooltips));
+# else
+ gtk_object_unref (GTK_OBJECT (dialog->tooltips));
+# endif /* GTK_MAJOR_VERSION == 2 */
+#endif
+ gtk_widget_destroy (dialog->main_hbox);
+
+ /* free the menu labels of integer/fix-point word-lists: */
+ for (i = 0; i < dialog->num_elements; ++i)
+ {
+ if (dialog->element[i].menu)
+ {
+ opt = sane_get_option_descriptor (dialog->dev, i);
+ elem = dialog->element + i;
+ if (opt->type != SANE_TYPE_STRING)
+ for (j = 0; j < elem->menu_size; ++j)
+ if (elem->menu[j].label)
+ {
+ free (elem->menu[j].label);
+ elem->menu[j].label = 0;
+ }
+ free (elem->menu);
+ elem->menu = 0;
+ }
+ }
+ memset (dialog->element, 0,
+ dialog->num_elements * sizeof (dialog->element[0]));
+}
+
+static void
+panel_build (GSGDialog * dialog)
+{
+ GtkWidget *main_hbox, *standard_vbox, *advanced_vbox, *option_vbox;
+ GtkWidget *parent, *vbox, *button, *label;
+ const SANE_Option_Descriptor *opt;
+ SANE_Handle dev = dialog->dev;
+ double dval, dmin, dmax, dquant;
+ char *buf, str[16], title[256];
+ GSGDialogElement *elem;
+ SANE_Word quant, val;
+ SANE_Status status;
+ SANE_Int num_words;
+ char **str_list;
+ int i, j;
+ int num_vector_opts = 0, *vector_opts;
+
+ main_hbox = gtk_hbox_new (FALSE, 2);
+
+ if (dialog->twocolumn)
+ {
+ option_vbox = gtk_hbox_new (FALSE, 2); /* two column display */
+ }
+ else
+ {
+ option_vbox = gtk_vbox_new (FALSE, 2); /* one column display */
+ }
+
+ gtk_box_pack_start (GTK_BOX (main_hbox), option_vbox, FALSE, FALSE, 0);
+ gtk_widget_show (option_vbox);
+
+ /* standard options vbox */
+
+ standard_vbox = gtk_vbox_new ( /* homogeneous */ FALSE, 0);
+ gtk_widget_show (standard_vbox);
+ gtk_box_pack_start (GTK_BOX (option_vbox), standard_vbox, FALSE, FALSE, 0);
+
+ /* advanced options page */
+
+ advanced_vbox = gtk_vbox_new ( /* homogeneous */ FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (option_vbox), advanced_vbox, TRUE, TRUE, 0);
+
+ /* use black as foreground: */
+ dialog->tooltips = gtk_tooltips_new ();
+ dialog->tooltips_fg.red = 0;
+ dialog->tooltips_fg.green = 0;
+ dialog->tooltips_fg.blue = 0;
+ /* postit yellow (khaki) as background: */
+ gdk_color_alloc (gtk_widget_get_colormap (main_hbox), &dialog->tooltips_fg);
+ dialog->tooltips_bg.red = 61669;
+ dialog->tooltips_bg.green = 59113;
+ dialog->tooltips_bg.blue = 35979;
+ gdk_color_alloc (gtk_widget_get_colormap (main_hbox), &dialog->tooltips_bg);
+
+ /* GTK2 doesn't have the function (seems like it's not needed with GTK2 anyway) */
+#if GTK_MAJOR_VERSION < 2
+ gtk_tooltips_set_colors (dialog->tooltips,
+ &dialog->tooltips_bg, &dialog->tooltips_fg);
+#endif /* GTK_MAJOR_VERSION < 2 */
+
+ gsg_set_tooltips (dialog, preferences.tooltips_enabled);
+
+ gtk_container_add (GTK_CONTAINER (dialog->window), main_hbox);
+ dialog->main_hbox = main_hbox;
+ dialog->advanced_vbox = advanced_vbox;
+
+ /* reset well-known options: */
+ dialog->well_known.preview = -1;
+ dialog->well_known.dpi = -1;
+ dialog->well_known.coord[GSG_TL_X] = -1;
+ dialog->well_known.coord[GSG_TL_Y] = -1;
+ dialog->well_known.coord[GSG_BR_X] = -1;
+ dialog->well_known.coord[GSG_BR_Y] = -1;
+
+ vector_opts = alloca (dialog->num_elements * sizeof (int));
+
+ parent = standard_vbox;
+ for (i = 1; i < dialog->num_elements; ++i)
+ {
+ opt = sane_get_option_descriptor (dev, i);
+ if (!SANE_OPTION_IS_ACTIVE (opt->cap))
+ continue;
+ /* pick up well-known options as we go: */
+ if (opt->name)
+ {
+ if (strcmp (opt->name, SANE_NAME_PREVIEW) == 0
+ && opt->type == SANE_TYPE_BOOL)
+ {
+ dialog->well_known.preview = i;
+ continue;
+ }
+ else if (strcmp (opt->name, SANE_NAME_SCAN_RESOLUTION) == 0
+ && opt->unit == SANE_UNIT_DPI
+ && (opt->type == SANE_TYPE_INT
+ || opt->type == SANE_TYPE_FIXED))
+ dialog->well_known.dpi = i;
+ else if (strcmp (opt->name, SANE_NAME_SCAN_TL_X) == 0)
+ dialog->well_known.coord[GSG_TL_X] = i;
+ else if (strcmp (opt->name, SANE_NAME_SCAN_TL_Y) == 0)
+ dialog->well_known.coord[GSG_TL_Y] = i;
+ else if (strcmp (opt->name, SANE_NAME_SCAN_BR_X) == 0)
+ dialog->well_known.coord[GSG_BR_X] = i;
+ else if (strcmp (opt->name, SANE_NAME_SCAN_BR_Y) == 0)
+ dialog->well_known.coord[GSG_BR_Y] = i;
+ }
+
+ elem = dialog->element + i;
+ elem->dialog = dialog;
+
+ if (opt->unit == SANE_UNIT_NONE)
+ strncpy (title, opt->title, sizeof (title));
+ else
+ snprintf (title, sizeof (title),
+ "%s [%s]", opt->title, unit_string (opt->unit));
+
+ switch (opt->type)
+ {
+ case SANE_TYPE_GROUP:
+ /* group a set of options */
+ vbox = standard_vbox;
+ if (opt->cap & SANE_CAP_ADVANCED)
+ vbox = advanced_vbox;
+ parent = group_new (vbox, title);
+ elem->widget = parent;
+ break;
+
+ case SANE_TYPE_BOOL:
+ if ((opt->cap & SANE_CAP_ADVANCED) && !dialog->advanced)
+ break;
+ if (!(opt->cap & SANE_CAP_SOFT_DETECT))
+ break;
+ assert (opt->size == sizeof (SANE_Word));
+ status = sane_control_option (dialog->dev, i, SANE_ACTION_GET_VALUE,
+ &val, 0);
+ if (status != SANE_STATUS_GOOD)
+ goto get_value_failed;
+
+ button_new (parent, title, val, elem, dialog->tooltips, opt->desc,
+ SANE_OPTION_IS_SETTABLE (opt->cap));
+ gtk_widget_show (parent->parent);
+ break;
+
+ case SANE_TYPE_INT:
+ if ((opt->cap & SANE_CAP_ADVANCED) && !dialog->advanced)
+ break;
+ if (!(opt->cap & SANE_CAP_SOFT_DETECT))
+ break;
+ if (opt->size != sizeof (SANE_Word))
+ {
+ vector_opts[num_vector_opts++] = i;
+ break;
+ }
+ status = sane_control_option (dialog->dev, i, SANE_ACTION_GET_VALUE,
+ &val, 0);
+ if (status != SANE_STATUS_GOOD)
+ goto get_value_failed;
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ /* use a scale */
+ quant = opt->constraint.range->quant;
+ if (quant == 0)
+ quant = 1;
+ scale_new (parent, title, val,
+ opt->constraint.range->min,
+ opt->constraint.range->max, quant,
+ (opt->cap & SANE_CAP_AUTOMATIC), elem,
+ dialog->tooltips, opt->desc,
+ SANE_OPTION_IS_SETTABLE (opt->cap));
+ gtk_widget_show (parent->parent);
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ /* use a "list-selection" widget */
+ num_words = opt->constraint.word_list[0];
+ str_list = malloc ((num_words + 1) * sizeof (str_list[0]));
+ for (j = 0; j < num_words; ++j)
+ {
+ sprintf (str, "%d", opt->constraint.word_list[j + 1]);
+ str_list[j] = strdup (str);
+ }
+ str_list[j] = 0;
+ sprintf (str, "%d", val);
+ option_menu_new (parent, title, str_list, str, elem,
+ dialog->tooltips, opt->desc,
+ SANE_OPTION_IS_SETTABLE (opt->cap));
+ free (str_list);
+ gtk_widget_show (parent->parent);
+ break;
+
+ case SANE_CONSTRAINT_NONE:
+ /* having no constraint for an int is strange but allowed
+ by the SANE standard so we just ignore such options here */
+ break;
+
+ default:
+ fprintf (stderr, "panel_build: unknown constraint %d!\n",
+ opt->constraint_type);
+ break;
+ }
+ break;
+
+ case SANE_TYPE_FIXED:
+ if ((opt->cap & SANE_CAP_ADVANCED) && !dialog->advanced)
+ break;
+ if (!(opt->cap & SANE_CAP_SOFT_DETECT))
+ break;
+ if (opt->size != sizeof (SANE_Word))
+ {
+ vector_opts[num_vector_opts++] = i;
+ break;
+ }
+ status = sane_control_option (dialog->dev, i, SANE_ACTION_GET_VALUE,
+ &val, 0);
+ if (status != SANE_STATUS_GOOD)
+ goto get_value_failed;
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ /* use a scale */
+ quant = opt->constraint.range->quant;
+ if (quant == 0)
+ quant = 1;
+ dval = SANE_UNFIX (val);
+ dmin = SANE_UNFIX (opt->constraint.range->min);
+ dmax = SANE_UNFIX (opt->constraint.range->max);
+ dquant = SANE_UNFIX (quant);
+ if (opt->unit == SANE_UNIT_MM)
+ {
+ dval /= preferences.length_unit;
+ dmin /= preferences.length_unit;
+ dmax /= preferences.length_unit;
+ dquant /= preferences.length_unit;
+ }
+ scale_new (parent, title, dval, dmin, dmax, dquant,
+ (opt->cap & SANE_CAP_AUTOMATIC), elem,
+ dialog->tooltips, opt->desc,
+ SANE_OPTION_IS_SETTABLE (opt->cap));
+ gtk_widget_show (parent->parent);
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ /* use a "list-selection" widget */
+ num_words = opt->constraint.word_list[0];
+ str_list = malloc ((num_words + 1) * sizeof (str_list[0]));
+ for (j = 0; j < num_words; ++j)
+ {
+ sprintf (str, "%g",
+ SANE_UNFIX (opt->constraint.word_list[j + 1]));
+ str_list[j] = strdup (str);
+ }
+ str_list[j] = 0;
+ sprintf (str, "%g", SANE_UNFIX (val));
+ option_menu_new (parent, title, str_list, str, elem,
+ dialog->tooltips, opt->desc,
+ SANE_OPTION_IS_SETTABLE (opt->cap));
+ free (str_list);
+ gtk_widget_show (parent->parent);
+ break;
+
+ case SANE_CONSTRAINT_NONE:
+ /* having no constraint for a fixed is strange but allowed
+ by the SANE standard so we just ignore such options here */
+ break;
+
+ default:
+ fprintf (stderr, "panel_build: unknown constraint %d!\n",
+ opt->constraint_type);
+ break;
+ }
+ break;
+
+ case SANE_TYPE_STRING:
+ if ((opt->cap & SANE_CAP_ADVANCED) && !dialog->advanced)
+ break;
+ if (!(opt->cap & SANE_CAP_SOFT_DETECT))
+ break;
+ buf = malloc (opt->size);
+ status = sane_control_option (dialog->dev, i, SANE_ACTION_GET_VALUE,
+ buf, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ free (buf);
+ goto get_value_failed;
+ }
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_STRING_LIST:
+ /* use a "list-selection" widget */
+ option_menu_new (parent, title,
+ (char **) opt->constraint.string_list, buf,
+ elem, dialog->tooltips, opt->desc,
+ SANE_OPTION_IS_SETTABLE (opt->cap));
+ gtk_widget_show (parent->parent);
+ break;
+
+ case SANE_CONSTRAINT_NONE:
+ text_entry_new (parent, title, buf, elem,
+ dialog->tooltips, opt->desc,
+ SANE_OPTION_IS_SETTABLE (opt->cap));
+ gtk_widget_show (parent->parent);
+ break;
+
+ default:
+ fprintf (stderr, "panel_build: unknown constraint %d!\n",
+ opt->constraint_type);
+ break;
+ }
+ free (buf);
+ break;
+
+ case SANE_TYPE_BUTTON:
+ if ((opt->cap & SANE_CAP_ADVANCED) && !dialog->advanced)
+ break;
+ button = gtk_button_new ();
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) push_button_callback, elem);
+ set_tooltip (dialog->tooltips, button, opt->desc);
+
+ label = gtk_label_new (title);
+ gtk_container_add (GTK_CONTAINER (button), label);
+
+ gtk_box_pack_start (GTK_BOX (parent), button, FALSE, TRUE, 0);
+
+ gtk_widget_show (label);
+ gtk_widget_show (button);
+
+ elem->widget = button;
+ gtk_widget_show (parent->parent);
+ break;
+
+ default:
+ fprintf (stderr, "panel_build: Unknown type %d\n", opt->type);
+ break;
+ }
+ continue;
+
+ get_value_failed:
+ {
+ char msg[256];
+
+ sprintf (msg, "Failed to obtain value of option %s: %s.",
+ opt->name, sane_strstatus (status));
+ gsg_error (msg);
+ }
+ }
+
+ /* now add in vector editor, if necessary: */
+
+ if (num_vector_opts)
+ vector_new (dialog, main_hbox, num_vector_opts, vector_opts);
+
+ if (dialog->advanced)
+ gtk_widget_show (dialog->advanced_vbox);
+ else
+ gtk_widget_hide (dialog->advanced_vbox);
+ gtk_widget_show (main_hbox);
+}
+
+/* When an setting an option changes the dialog, everything may
+ change: the option titles, the activity-status of the option, its
+ constraints or what not. Thus, rather than trying to be clever in
+ detecting what exactly changed, we use a brute-force method of
+ rebuilding the entire dialog. */
+static void
+panel_rebuild (GSGDialog * dialog)
+{
+ panel_destroy (dialog);
+ panel_build (dialog);
+}
+
+GSGDialog *
+gsg_create_dialog (GtkWidget * window, const char *device_name,
+ GSGCallback option_reload_callback,
+ void *option_reload_arg, GSGCallback param_change_callback,
+ void *param_change_arg)
+{
+ SANE_Int num_elements;
+ GSGDialog *dialog;
+ SANE_Status status;
+ SANE_Handle dev;
+ char buf[256];
+
+ status = sane_open (device_name, &dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ sprintf (buf, "Failed to open device `%s': %s.",
+ device_name, sane_strstatus (status));
+ gsg_error (buf);
+ return 0;
+ }
+
+ if (sane_control_option (dev, 0, SANE_ACTION_GET_VALUE, &num_elements, 0)
+ != SANE_STATUS_GOOD)
+ {
+ gsg_error ("Error obtaining option count.");
+ sane_close (dev);
+ return 0;
+ }
+
+ dialog = malloc (sizeof (*dialog));
+ memset (dialog, 0, sizeof (*dialog));
+
+ dialog->window = window;
+ dialog->dev = dev;
+ dialog->dev_name = strdup (device_name);
+ dialog->num_elements = num_elements;
+ dialog->option_reload_callback = option_reload_callback;
+ dialog->option_reload_arg = option_reload_arg;
+ dialog->param_change_callback = param_change_callback;
+ dialog->param_change_arg = param_change_arg;
+ dialog->advanced = preferences.advanced;
+ dialog->twocolumn = preferences.twocolumn_enabled;
+
+ dialog->element = malloc (num_elements * sizeof (dialog->element[0]));
+ memset (dialog->element, 0, num_elements * sizeof (dialog->element[0]));
+
+ panel_build (dialog);
+ return dialog;
+}
+
+void
+gsg_refresh_dialog (GSGDialog * dialog)
+{
+ panel_rebuild (dialog);
+ if (dialog->param_change_callback)
+ (*dialog->param_change_callback) (dialog, dialog->param_change_arg);
+}
+
+void
+gsg_update_scan_window (GSGDialog * dialog)
+{
+ const SANE_Option_Descriptor *opt;
+ double old_val, new_val;
+ GSGDialogElement *elem;
+ SANE_Status status;
+ SANE_Word word;
+ int i, optnum;
+ char str[64];
+
+ for (i = 0; i < 4; ++i)
+ if (dialog->well_known.coord[i] > 0)
+ {
+ optnum = dialog->well_known.coord[i];
+ elem = dialog->element + optnum;
+ opt = sane_get_option_descriptor (dialog->dev, optnum);
+
+ status = sane_control_option (dialog->dev, optnum,
+ SANE_ACTION_GET_VALUE, &word, 0);
+ if (status != SANE_STATUS_GOOD)
+ continue; /* sliently ignore errors */
+
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ if (opt->type == SANE_TYPE_INT)
+ {
+ old_val = GTK_ADJUSTMENT (elem->data)->value;
+ new_val = word;
+ GTK_ADJUSTMENT (elem->data)->value = new_val;
+ }
+ else
+ {
+ old_val = GTK_ADJUSTMENT (elem->data)->value;
+ new_val = SANE_UNFIX (word);
+ if (opt->unit == SANE_UNIT_MM)
+ new_val /= preferences.length_unit;
+ GTK_ADJUSTMENT (elem->data)->value = new_val;
+ }
+ if (old_val != new_val)
+ gtk_signal_emit_by_name (GTK_OBJECT (elem->data),
+ "value_changed");
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ if (opt->type == SANE_TYPE_INT)
+ sprintf (str, "%d", word);
+ else
+ sprintf (str, "%g", SANE_UNFIX (word));
+ /* XXX maybe we should call this only when the value changes... */
+ gtk_option_menu_set_history (GTK_OPTION_MENU (elem->widget),
+ option_menu_lookup (elem->menu,
+ str));
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/* Ensure sure the device has up-to-date option values. Except for
+ vectors, all option values are kept current. Vectors are
+ downloaded into the device during this call. */
+void
+gsg_sync (GSGDialog * dialog)
+{
+ const SANE_Option_Descriptor *opt;
+ gfloat val, *vector;
+ SANE_Word *optval;
+ int i, j, optlen;
+ GtkWidget *curve;
+
+ for (i = 1; i < dialog->num_elements; ++i)
+ {
+ opt = sane_get_option_descriptor (dialog->dev, i);
+ if (!SANE_OPTION_IS_ACTIVE (opt->cap))
+ continue;
+
+ if (opt->type != SANE_TYPE_INT && opt->type != SANE_TYPE_FIXED)
+ continue;
+
+ if (opt->size == sizeof (SANE_Word))
+ continue;
+
+ /* ok, we're dealing with an active vector */
+
+ optlen = opt->size / sizeof (SANE_Word);
+ optval = alloca (optlen * sizeof (optval[0]));
+ vector = alloca (optlen * sizeof (vector[0]));
+
+ curve = GTK_GAMMA_CURVE (dialog->element[i].widget)->curve;
+ gtk_curve_get_vector (GTK_CURVE (curve), optlen, vector);
+ for (j = 0; j < optlen; ++j)
+ {
+ val = vector[j];
+ if (opt->type == SANE_TYPE_FIXED)
+ optval[j] = SANE_FIX (val);
+ else
+ optval[j] = val + 0.5;
+ }
+
+ set_option (dialog, i, optval, SANE_ACTION_SET_VALUE);
+ }
+}
+
+void
+gsg_set_advanced (GSGDialog * dialog, int advanced)
+{
+ dialog->advanced = advanced;
+ panel_rebuild (dialog);
+}
+
+void
+gsg_set_tooltips (GSGDialog * dialog, int enable)
+{
+ if (!dialog->tooltips)
+ return;
+
+ if (enable)
+ gtk_tooltips_enable (dialog->tooltips);
+ else
+ gtk_tooltips_disable (dialog->tooltips);
+}
+
+void
+gsg_set_twocolumn (GSGDialog * dialog, int twocolumn)
+{
+ dialog->twocolumn = twocolumn;
+ panel_rebuild (dialog);
+}
+
+void
+gsg_set_sensitivity (GSGDialog * dialog, int sensitive)
+{
+ const SANE_Option_Descriptor *opt;
+ int i;
+
+ for (i = 0; i < dialog->num_elements; ++i)
+ {
+ opt = sane_get_option_descriptor (dialog->dev, i);
+
+ if (!SANE_OPTION_IS_ACTIVE (opt->cap)
+ || opt->type == SANE_TYPE_GROUP || !dialog->element[i].widget)
+ continue;
+
+ if (!(opt->cap & SANE_CAP_ALWAYS_SETTABLE))
+ gtk_widget_set_sensitive (dialog->element[i].widget, sensitive);
+ }
+}
+
+void
+gsg_destroy_dialog (GSGDialog * dialog)
+{
+ SANE_Handle dev = dialog->dev;
+
+ panel_destroy (dialog);
+ free ((void *) dialog->dev_name);
+ free (dialog->element);
+ free (dialog);
+
+ sane_close (dev);
+}
diff --git a/src/gtkglue.h b/src/gtkglue.h
new file mode 100644
index 0000000..00b1666
--- /dev/null
+++ b/src/gtkglue.h
@@ -0,0 +1,121 @@
+#ifndef gtkglue_h
+#define gtkglue_h
+
+#include <sys/types.h>
+
+#include <gtk/gtk.h>
+
+#include "../include/sane/config.h"
+#include <sane/sane.h>
+
+struct GSGDialog;
+
+typedef void (*GSGCallback) (struct GSGDialog *dialog, void *arg);
+
+typedef enum
+ {
+ GSG_TL_X, /* top-left x */
+ GSG_TL_Y, /* top-left y */
+ GSG_BR_X, /* bottom-right x */
+ GSG_BR_Y /* bottom-right y */
+ }
+GSGCornerCoordinates;
+
+typedef struct
+ {
+ /* The option number of the well-known options. Each of these may
+ be -1 in case the backend doesn't define the respective option. */
+ int preview;
+ int dpi;
+ int coord[4];
+ }
+GSGWellKnownOptions;
+
+typedef struct
+ {
+ gchar *label;
+ struct GSGDialogElement *elem;
+ gint index;
+ }
+GSGMenuItem;
+
+typedef struct GSGDialogElement
+ {
+ struct GSGDialog *dialog; /* wasteful, but is there a better solution? */
+ GtkWidget *automatic; /* auto button for options that support this */
+ GtkWidget *widget;
+ GtkObject *data;
+ int menu_size; /* # of items in menu (if any) */
+ GSGMenuItem *menu;
+ }
+GSGDialogElement;
+
+typedef struct GSGDialog
+ {
+ GtkWidget *window;
+ GtkWidget *main_hbox;
+ GtkWidget *advanced_vbox;
+ int twocolumn;
+ GtkTooltips *tooltips;
+ GdkColor tooltips_fg;
+ GdkColor tooltips_bg;
+ SANE_Handle *dev;
+ const char *dev_name;
+ GSGWellKnownOptions well_known;
+ int num_elements;
+ GSGDialogElement *element;
+ gint idle_id;
+ u_int rebuild : 1;
+ u_int advanced : 1;
+ /* This callback gets invoked whenever the backend notifies us
+ that the option descriptors have changed. */
+ GSGCallback option_reload_callback;
+ void *option_reload_arg;
+ /* This callback gets invoked whenever the backend notifies us
+ that the parameters have changed. */
+ GSGCallback param_change_callback;
+ void *param_change_arg;
+ }
+GSGDialog;
+
+
+extern int gsg_message_dialog_active;
+
+/* Construct the path and return it in filename_ret (this buffer must
+ be at least max_len bytes long). The path is constructed as
+ follows:
+
+ ~/.sane/${PROG_NAME}/${PREFIX}${DEV_NAME}${POSTFIX}
+
+ If PROG_NAME is NULL, an empty string is used and the leading slash
+ is removed. On success, 0 is returned, on error a negative number and
+ ERRNO is set to the appropriate value. */
+extern int gsg_make_path (size_t max_len, char *filename_ret,
+ const char *prog_name,
+ const char *prefix, const char *dev_name,
+ const char *postfix);
+
+extern void gsg_message (gchar *title, gchar * message);
+extern void gsg_error (gchar * error_message);
+extern void gsg_warning (gchar * warning_message);
+extern int gsg_get_filename (const char *label, const char *default_name,
+ size_t max_len, char *filename);
+
+extern GSGDialog *gsg_create_dialog (GtkWidget *window,
+ const char *device_name,
+ GSGCallback option_reload_callback,
+ void *option_reload_arg,
+ GSGCallback param_callback,
+ void *param_arg);
+extern void gsg_sync (GSGDialog *dialog);
+extern void gsg_refresh_dialog (GSGDialog *dialog);
+extern void gsg_update_scan_window (GSGDialog *dialog);
+extern void gsg_set_advanced (GSGDialog *dialog, int advanced);
+extern void gsg_set_tooltips (GSGDialog *dialog, int enable);
+extern void gsg_set_twocolumn (GSGDialog *dialog, int enable);
+extern void gsg_set_sensitivity (GSGDialog *dialog, int sensitive);
+extern void gsg_destroy_dialog (GSGDialog * dialog);
+
+#define gsg_dialog_get_device(dialog) ((dialog)->dev)
+
+#endif /* gtkglue_h */
diff --git a/src/preferences.c b/src/preferences.c
new file mode 100644
index 0000000..16c3839
--- /dev/null
+++ b/src/preferences.c
@@ -0,0 +1,185 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 David Mosberger-Tang
+ This file is part of the SANE package.
+
+ SANE 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.
+
+ SANE 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 sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <preferences.h>
+#include <sane/sane.h>
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_wire.h"
+#include "../include/sane/sanei_codec_ascii.h"
+
+#define POFFSET(field) ((char *) &((Preferences *) 0)->field - (char *) 0)
+#define PFIELD(p,offset,type) (*((type *)(((char *)(p)) + (offset))))
+
+Preferences preferences =
+ {
+ 0, /* no preferred device (must be 0 or malloced!) */
+ 0, /* no default filename */
+ 0, /* advanced user */
+ 1, /* tooltips enabled */
+ 1, /* two column enabled */
+ 10.0, /* length unit */
+ 1, /* preserve_preview */
+ 0, /* preview_own_cmap */
+ 1.0 /* preview_gamma */
+ };
+
+static void w_string (Wire *w, Preferences *p, long offset);
+static void w_double (Wire *w, Preferences *p, long offset);
+static void w_int (Wire *w, Preferences *p, long offset);
+
+static struct
+ {
+ SANE_String name;
+ void (*codec) (Wire *w, Preferences *p, long offset);
+ long offset;
+ }
+desc[] =
+ {
+ {"device", w_string, POFFSET(device)},
+ {"filename", w_string, POFFSET(filename)},
+ {"advanced", w_int, POFFSET(advanced)},
+ {"tool-tips", w_int, POFFSET(tooltips_enabled)},
+ {"twocolumn", w_int, POFFSET(twocolumn_enabled)},
+ {"length-unit", w_double, POFFSET(length_unit)},
+ {"preserve-preview", w_int, POFFSET(preserve_preview)},
+ {"preview-own-cmap", w_int, POFFSET(preview_own_cmap)},
+ {"preview-gamma", w_double, POFFSET(preview_gamma)},
+ };
+
+static void
+w_string (Wire *w, Preferences *p, long offset)
+{
+ SANE_String string;
+
+ if (w->direction == WIRE_ENCODE)
+ string = PFIELD (p, offset, char *);
+
+ sanei_w_string (w, &string);
+
+ if (w->direction == WIRE_DECODE)
+ {
+ if (w->status == 0)
+ {
+ const char **field;
+
+ field = &PFIELD (p, offset, const char *);
+ if (*field)
+ free ((char *) *field);
+ *field = string ? strdup (string) : 0;
+ }
+ sanei_w_free (w, (WireCodecFunc) sanei_w_string, &string);
+ }
+}
+
+static void
+w_double (Wire *w, Preferences *p, long offset)
+{
+ SANE_Word word;
+
+ if (w->direction == WIRE_ENCODE)
+ word = SANE_FIX (PFIELD (p, offset, double));
+
+ sanei_w_word (w, &word);
+
+ if (w->direction == WIRE_DECODE)
+ {
+ if (w->status == 0)
+ PFIELD (p, offset, double) = SANE_UNFIX (word);
+ sanei_w_free (w, (WireCodecFunc) sanei_w_word, &word);
+ }
+}
+
+static void
+w_int (Wire *w, Preferences *p, long offset)
+{
+ SANE_Word word;
+
+ if (w->direction == WIRE_ENCODE)
+ word = PFIELD (p, offset, int);
+
+ sanei_w_word (w, &word);
+
+ if (w->direction == WIRE_DECODE)
+ {
+ if (w->status == 0)
+ PFIELD (p, offset, int) = word;
+ sanei_w_free (w, (WireCodecFunc) sanei_w_word, &word);
+ }
+}
+
+void
+preferences_save (int fd)
+{
+ Wire w;
+ int i;
+
+ w.io.fd = fd;
+ w.io.read = read;
+ w.io.write = write;
+ sanei_w_init (&w, sanei_codec_ascii_init);
+ sanei_w_set_dir (&w, WIRE_ENCODE);
+
+ for (i = 0; i < NELEMS (desc); ++i)
+ {
+ sanei_w_string (&w, &desc[i].name);
+ (*desc[i].codec) (&w, &preferences, desc[i].offset);
+ }
+
+ sanei_w_set_dir (&w, WIRE_DECODE); /* flush it out */
+}
+
+void
+preferences_restore (int fd)
+{
+ SANE_String name;
+ Wire w;
+ int i;
+
+ w.io.fd = fd;
+ w.io.read = read;
+ w.io.write = write;
+ sanei_w_init (&w, sanei_codec_ascii_init);
+ sanei_w_set_dir (&w, WIRE_DECODE);
+
+ while (1)
+ {
+ sanei_w_space (&w, 3);
+ if (w.status)
+ return;
+
+ sanei_w_string (&w, &name);
+ if (w.status || !name)
+ return;
+
+ for (i = 0; i < NELEMS (desc); ++i)
+ {
+ if (strcmp (name, desc[i].name) == 0)
+ {
+ (*desc[i].codec) (&w, &preferences, desc[i].offset);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/preferences.h b/src/preferences.h
new file mode 100644
index 0000000..5c58df3
--- /dev/null
+++ b/src/preferences.h
@@ -0,0 +1,25 @@
+#ifndef preferences_h
+#define preferences_h
+
+#include <sane/sane.h>
+
+typedef struct
+ {
+ const char *device; /* name of preferred device (or NULL) */
+ const char *filename; /* default filename */
+ int advanced; /* advanced user? */
+ int tooltips_enabled; /* should tooltips be disabled? */
+ int twocolumn_enabled; /* should two column be disabled? */
+ double length_unit; /* 1.0==mm, 10.0==cm, 25.4==inches, etc. */
+ int preserve_preview; /* save/restore preview image(s)? */
+ int preview_own_cmap; /* install colormap for preview */
+ double preview_gamma; /* gamma value for previews */
+ }
+Preferences;
+
+extern Preferences preferences;
+
+extern void preferences_save (int fd);
+extern void preferences_restore (int fd);
+
+#endif /* preferences_h */
diff --git a/src/preview.c b/src/preview.c
new file mode 100644
index 0000000..6fb21c0
--- /dev/null
+++ b/src/preview.c
@@ -0,0 +1,1536 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 David Mosberger-Tang and Tristan Tarrant
+ This file is part of the SANE package.
+
+ SANE 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.
+
+ SANE 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 sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
+ The preview strategy is as follows:
+
+ 1) A preview always acquires an image that covers the entire
+ scan surface. This is necessary so the user can see not
+ only what is, but also what isn't selected.
+
+ 2) The preview must be zoomable so the user can precisely pick
+ the selection area even for small scans on a large scan
+ surface. The user also should have the option of resizing
+ the preview window.
+
+ 3) We let the user/backend pick whether a preview is in color,
+ grayscale, lineart or what not. The only options that the
+ preview may (temporarily) modify are:
+
+ - resolution (set so the preview fills the window)
+ - scan area options (top-left corner, bottom-right corner)
+ - preview option (to let the backend know we're doing a preview)
+
+ 4) The size of the scan surface is determined based on the constraints
+ of the four corner coordinates. Missing constraints are replaced
+ by +/-INF as appropriate (-INF form top-left, +INF for bottom-right
+ coords).
+
+ 5) The size of the preview window is determined based on the scan
+ surface size:
+
+ If the surface area is specified in pixels and if that size
+ fits on the screen, we use that size. In all other cases,
+ we make the window of a size so that neither the width nor
+ the height is bigger than some fraction of the screen-size
+ while preserving the aspect ratio (a surface width or height
+ of INF implies an aspect ratio of 1).
+
+ 6) Given the preview window size and the scan surface size, we
+ select the resolution so the acquired preview image just fits
+ in the preview window. The resulting resolution may be out
+ of range in which case we pick the minum/maximum if there is
+ a range or word-list constraint or a default value if there is
+ no such constraint.
+
+ 7) Once a preview image has been acquired, we know the size of the
+ preview image (in pixels). An initial scale factor is chosen
+ so the image fits into the preview window.
+
+ */
+
+#include "../include/sane/config.h"
+
+#include <assert.h>
+#include <math.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <sys/param.h>
+
+#include "gtkglue.h"
+#include "preview.h"
+#include "preferences.h"
+
+#define DBG_fatal 0
+#define DBG_error 1
+#define DBG_warning 2
+#define DBG_info 3
+#define DBG_debug 4
+
+#define BACKEND_NAME preview
+#include "../include/sane/sanei_debug.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+/* Anything bigger than 2G will do, since SANE coordinates are 32 bit
+ values. */
+#define INF 5.0e9
+
+#define MM_PER_INCH 25.4
+
+/* Cut fp conversion routines some slack: */
+#define GROSSLY_DIFFERENT(f1,f2) (fabs ((f1) - (f2)) > 1e-3)
+
+#ifdef __alpha__
+ /* This seems to be necessary for at least some XFree86 3.1.2
+ servers. It's known to be necessary for the XF86_TGA server for
+ Linux/Alpha. Fortunately, it's no great loss so we turn this on
+ by default for now. */
+# define XSERVER_WITH_BUGGY_VISUALS
+#endif
+
+/* forward declarations */
+static void scan_start (Preview * p);
+static void scan_done (Preview * p);
+
+static void
+draw_rect (GdkWindow * win, GdkGC * gc, int coord[4])
+{
+ gint x, y, w, h;
+
+ x = coord[0];
+ y = coord[1];
+ w = coord[2] - x;
+ h = coord[3] - y;
+ if (w < 0)
+ {
+ x = coord[2];
+ w = -w;
+ }
+ if (h < 0)
+ {
+ y = coord[3];
+ h = -h;
+ }
+ gdk_draw_rectangle (win, gc, FALSE, x, y, w + 1, h + 1);
+}
+
+static void
+draw_selection (Preview * p)
+{
+ if (!p->gc)
+ /* window isn't mapped yet */
+ return;
+
+ if (p->previous_selection.active)
+ draw_rect (p->window->window, p->gc, p->previous_selection.coord);
+
+ if (p->selection.active)
+ draw_rect (p->window->window, p->gc, p->selection.coord);
+
+ p->previous_selection = p->selection;
+}
+
+static void
+update_selection (Preview * p)
+{
+ float min, max, normal, dev_selection[4];
+ const SANE_Option_Descriptor *opt;
+ SANE_Status status;
+ SANE_Word val;
+ int i, optnum;
+
+ p->previous_selection = p->selection;
+
+ memcpy (dev_selection, p->surface, sizeof (dev_selection));
+ for (i = 0; i < 4; ++i)
+ {
+ optnum = p->dialog->well_known.coord[i];
+ if (optnum > 0)
+ {
+ opt = sane_get_option_descriptor (p->dialog->dev, optnum);
+ status = sane_control_option (p->dialog->dev, optnum,
+ SANE_ACTION_GET_VALUE, &val, 0);
+ if (status != SANE_STATUS_GOOD)
+ continue;
+ if (opt->type == SANE_TYPE_FIXED)
+ dev_selection[i] = SANE_UNFIX (val);
+ else
+ dev_selection[i] = val;
+ }
+ }
+ for (i = 0; i < 2; ++i)
+ {
+ min = p->surface[i];
+ if (min <= -INF)
+ min = 0.0;
+ max = p->surface[i + 2];
+ if (max >= INF)
+ max = p->preview_width;
+
+ normal = ((i == 0) ? p->preview_width : p->preview_height) - 1;
+ normal /= (max - min);
+ p->selection.active = TRUE;
+ p->selection.coord[i] = ((dev_selection[i] - min) * normal) + 0.5;
+ p->selection.coord[i + 2] =
+ ((dev_selection[i + 2] - min) * normal) + 0.5;
+ if (p->selection.coord[i + 2] < p->selection.coord[i])
+ p->selection.coord[i + 2] = p->selection.coord[i];
+ }
+ draw_selection (p);
+}
+
+static void
+get_image_scale (Preview * p, float *xscalep, float *yscalep)
+{
+ float xscale, yscale;
+
+ if (p->image_width == 0)
+ xscale = 1.0;
+ else
+ {
+ xscale = p->image_width / (float) p->preview_width;
+ if (p->image_height > 0 && p->preview_height * xscale < p->image_height)
+ xscale = p->image_height / (float) p->preview_height;
+ }
+ yscale = xscale;
+
+ if (p->surface_unit == SANE_UNIT_PIXEL
+ && p->image_width <= p->preview_width
+ && p->image_height <= p->preview_height)
+ {
+ float swidth, sheight;
+
+ assert (p->surface_type == SANE_TYPE_INT);
+ swidth = (p->surface[GSG_BR_X] - p->surface[GSG_TL_X] + 1);
+ sheight = (p->surface[GSG_BR_Y] - p->surface[GSG_TL_Y] + 1);
+ xscale = 1.0;
+ yscale = 1.0;
+ if (p->image_width > 0 && swidth < INF)
+ xscale = p->image_width / swidth;
+ if (p->image_height > 0 && sheight < INF)
+ yscale = p->image_height / sheight;
+ }
+ *xscalep = xscale;
+ *yscalep = yscale;
+}
+
+static void
+paint_image (Preview * p)
+{
+ float xscale, yscale, src_x, src_y;
+ int dst_x, dst_y, height, x, y, src_offset;
+ gint gwidth, gheight;
+
+ gwidth = p->preview_width;
+ gheight = p->preview_height;
+
+ get_image_scale (p, &xscale, &yscale);
+
+ memset (p->preview_row, 0xff, 3 * gwidth);
+
+ /* don't draw last line unless it's complete: */
+ height = p->image_y;
+ if (p->image_x == 0 && height < p->image_height)
+ ++height;
+
+ /* for now, use simple nearest-neighbor interpolation: */
+ src_offset = 0;
+ src_x = src_y = 0.0;
+ for (dst_y = 0; dst_y < gheight; ++dst_y)
+ {
+ y = (int) (src_y + 0.5);
+ if (y >= height)
+ break;
+ src_offset = y * 3 * p->image_width;
+
+ if (p->image_data)
+ for (dst_x = 0; dst_x < gwidth; ++dst_x)
+ {
+ x = (int) (src_x + 0.5);
+ if (x >= p->image_width)
+ break;
+
+ p->preview_row[3 * dst_x + 0] =
+ p->image_data[src_offset + 3 * x + 0];
+ p->preview_row[3 * dst_x + 1] =
+ p->image_data[src_offset + 3 * x + 1];
+ p->preview_row[3 * dst_x + 2] =
+ p->image_data[src_offset + 3 * x + 2];
+ src_x += xscale;
+ }
+ gtk_preview_draw_row (GTK_PREVIEW (p->window), p->preview_row,
+ 0, dst_y, gwidth);
+ src_x = 0.0;
+ src_y += yscale;
+ }
+}
+
+static void
+display_partial_image (Preview * p)
+{
+ paint_image (p);
+
+ if (GTK_WIDGET_DRAWABLE (p->window))
+ {
+ GtkPreview *preview = GTK_PREVIEW (p->window);
+ int src_x, src_y;
+
+ src_x = (p->window->allocation.width - preview->buffer_width) / 2;
+ src_y = (p->window->allocation.height - preview->buffer_height) / 2;
+ gtk_preview_put (preview, p->window->window, p->window->style->black_gc,
+ src_x, src_y,
+ 0, 0, p->preview_width, p->preview_height);
+ }
+}
+
+static void
+display_maybe (Preview * p)
+{
+ time_t now;
+
+ time (&now);
+ if (now > p->image_last_time_updated)
+ {
+ p->image_last_time_updated = now;
+ display_partial_image (p);
+ }
+}
+
+static void
+display_image (Preview * p)
+{
+ if (p->params.lines <= 0 && p->image_y < p->image_height)
+ {
+ p->image_height = p->image_y;
+ p->image_data = realloc (p->image_data,
+ 3 * p->image_width * p->image_height);
+ assert (p->image_data);
+ }
+ display_partial_image (p);
+ scan_done (p);
+}
+
+static void
+preview_area_resize (GtkWidget * widget)
+{
+ float min_x, max_x, min_y, max_y, xscale, yscale, f;
+ Preview *p;
+
+ p = gtk_object_get_data (GTK_OBJECT (widget), "PreviewPointer");
+
+ p->preview_width = widget->allocation.width;
+ p->preview_height = widget->allocation.height;
+
+ if (p->preview_row)
+ p->preview_row = realloc (p->preview_row, 3 * p->preview_width);
+ else
+ p->preview_row = malloc (3 * p->preview_width);
+
+ /* set the ruler ranges: */
+
+ min_x = p->surface[GSG_TL_X];
+ if (min_x <= -INF)
+ min_x = 0.0;
+
+ max_x = p->surface[GSG_BR_X];
+ if (max_x >= INF)
+ max_x = p->preview_width - 1;
+
+ min_y = p->surface[GSG_TL_Y];
+ if (min_y <= -INF)
+ min_y = 0.0;
+
+ max_y = p->surface[GSG_BR_Y];
+ if (max_y >= INF)
+ max_y = p->preview_height - 1;
+
+ /* convert mm to inches if that's what the user wants: */
+
+ if (p->surface_unit == SANE_UNIT_MM)
+ {
+ double factor = 1.0 / preferences.length_unit;
+ min_x *= factor;
+ max_x *= factor;
+ min_y *= factor;
+ max_y *= factor;
+ }
+
+ get_image_scale (p, &xscale, &yscale);
+
+ if (p->surface_unit == SANE_UNIT_PIXEL)
+ f = 1.0 / xscale;
+ else if (p->image_width)
+ f = xscale * p->preview_width / p->image_width;
+ else
+ f = 1.0;
+ gtk_ruler_set_range (GTK_RULER (p->hruler), f * min_x, f * max_x, f * min_x,
+ /* max_size */ 20);
+
+ if (p->surface_unit == SANE_UNIT_PIXEL)
+ f = 1.0 / yscale;
+ else if (p->image_height)
+ f = yscale * p->preview_height / p->image_height;
+ else
+ f = 1.0;
+ gtk_ruler_set_range (GTK_RULER (p->vruler), f * min_y, f * max_y, f * min_y,
+ /* max_size */ 20);
+
+ paint_image (p);
+ update_selection (p);
+}
+
+static void
+get_bounds (const SANE_Option_Descriptor * opt, float *minp, float *maxp)
+{
+ float min, max;
+ int i;
+
+ min = -INF;
+ max = INF;
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ min = opt->constraint.range->min;
+ max = opt->constraint.range->max;
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ min = INF;
+ max = -INF;
+ for (i = 1; i <= opt->constraint.word_list[0]; ++i)
+ {
+ if (opt->constraint.word_list[i] < min)
+ min = opt->constraint.word_list[i];
+ if (opt->constraint.word_list[i] > max)
+ max = opt->constraint.word_list[i];
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ if (min > -INF && min < INF)
+ min = SANE_UNFIX (min);
+ if (max > -INF && max < INF)
+ max = SANE_UNFIX (max);
+ }
+ *minp = min;
+ *maxp = max;
+}
+
+static void
+save_option (Preview * p, int option, SANE_Word * save_loc, int *valid)
+{
+ SANE_Status status;
+
+ if (option <= 0)
+ {
+ *valid = 0;
+ return;
+ }
+ status = sane_control_option (p->dialog->dev, option, SANE_ACTION_GET_VALUE,
+ save_loc, 0);
+ *valid = (status == SANE_STATUS_GOOD);
+}
+
+static void
+restore_option (Preview * p, int option, SANE_Word saved_value, int valid)
+{
+ const SANE_Option_Descriptor *opt;
+ SANE_Status status;
+ SANE_Handle dev;
+
+ if (!valid)
+ return;
+
+ dev = p->dialog->dev;
+ status = sane_control_option (dev, option, SANE_ACTION_SET_VALUE,
+ &saved_value, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ char buf[256];
+ opt = sane_get_option_descriptor (dev, option);
+ snprintf (buf, sizeof (buf), "Failed restore value of option %s: %s.",
+ opt->name, sane_strstatus (status));
+ gsg_error (buf);
+ }
+}
+
+static SANE_Status
+set_option_float (Preview * p, int option, float value)
+{
+ const SANE_Option_Descriptor *opt;
+ SANE_Handle dev;
+ SANE_Word word;
+ SANE_Status status;
+
+ if (option <= 0 || value <= -INF || value >= INF)
+ return SANE_STATUS_INVAL;
+
+ dev = p->dialog->dev;
+ opt = sane_get_option_descriptor (dev, option);
+ if (opt->type == SANE_TYPE_FIXED)
+ word = SANE_FIX (value) + 0.5;
+ else
+ word = value + 0.5;
+ status = sane_control_option (dev, option, SANE_ACTION_SET_VALUE, &word, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ char buf[256];
+ opt = sane_get_option_descriptor (dev, option);
+ snprintf (buf, sizeof (buf), "Failed to set option %s: %s.",
+ opt->name, sane_strstatus (status));
+ gsg_error (buf);
+ return status;
+ }
+ return SANE_STATUS_GOOD;
+}
+
+static void
+set_option_bool (Preview * p, int option, SANE_Bool value)
+{
+ SANE_Handle dev;
+ SANE_Status status;
+
+ if (option <= 0)
+ return;
+
+ dev = p->dialog->dev;
+ status =
+ sane_control_option (dev, option, SANE_ACTION_SET_VALUE, &value, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_fatal, "set_option_bool: sane_control_option failed: %s\n",
+ sane_strstatus (status));
+ return;
+ }
+}
+
+static int
+increment_image_y (Preview * p)
+{
+ size_t extra_size, offset;
+ char buf[256];
+
+ p->image_x = 0;
+ ++p->image_y;
+ if (p->params.lines <= 0 && p->image_y >= p->image_height)
+ {
+ offset = 3 * p->image_width * p->image_height;
+ extra_size = 3 * 32 * p->image_width;
+ p->image_height += 32;
+ p->image_data = realloc (p->image_data, offset + extra_size);
+ if (!p->image_data)
+ {
+ snprintf (buf, sizeof (buf),
+ "Failed to reallocate image memory: %s.",
+ strerror (errno));
+ gsg_error (buf);
+ scan_done (p);
+ return -1;
+ }
+ memset (p->image_data + offset, 0xff, extra_size);
+ }
+ return 0;
+}
+
+static void
+input_available (gpointer data, gint source, GdkInputCondition cond)
+{
+ SANE_Status status;
+ Preview *p = data;
+ u_char buf[8192];
+ SANE_Handle dev;
+ SANE_Int len;
+ int i, j;
+
+ DBG_INIT ();
+
+ DBG (DBG_debug, "input_available: enter\n");
+
+ dev = p->dialog->dev;
+ while (1)
+ {
+ status = sane_read (dev, buf, sizeof (buf), &len);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (status == SANE_STATUS_EOF)
+ {
+ if (p->params.last_frame)
+ display_image (p);
+ else
+ {
+ if (p->input_tag < 0)
+ {
+ display_maybe (p);
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ }
+ else
+ {
+ gdk_input_remove (p->input_tag);
+ p->input_tag = -1;
+ }
+ scan_start (p);
+ break;
+ }
+ }
+ else
+ {
+ snprintf (buf, sizeof (buf), "Error during read: %s.",
+ sane_strstatus (status));
+ gsg_error (buf);
+ }
+ scan_done (p);
+ return;
+ }
+ if (!len) /* out of data for now */
+ {
+ if (p->input_tag < 0)
+ {
+ display_maybe (p);
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ continue;
+ }
+ else
+ break;
+ }
+
+ switch (p->params.format)
+ {
+ case SANE_FRAME_RGB:
+ switch (p->params.depth)
+ {
+ case 1:
+ for (i = 0; i < len; ++i)
+ {
+ u_char mask = buf[i];
+
+ for (j = 7; j >= 0; --j)
+ {
+ u_char gl = (mask & (1 << j)) ? 0xff : 0x00;
+
+ p->image_data[p->image_offset] = gl;
+ if (j != 0)
+ p->image_offset += 3;
+ else
+ {
+ if ((p->image_offset % 3) != 2)
+ p->image_offset -= 20;
+ else
+ p->image_offset++;
+ }
+ if (p->image_offset % 3 == 0)
+ {
+ if (++p->image_x >= p->image_width)
+ {
+ if (increment_image_y (p) < 0)
+ return;
+ }
+ }
+ }
+ }
+ break;
+
+ case 8:
+ for (i = 0; i < len; ++i)
+ {
+ p->image_data[p->image_offset++] = buf[i];
+ if (p->image_offset % 3 == 0)
+ {
+ if (++p->image_x >= p->image_width
+ && increment_image_y (p) < 0)
+ return;
+ }
+ }
+ break;
+ case 16:
+ for (i = 0; i < len; ++i)
+ {
+ guint16 value = buf[i];
+ if ((i % 2) == 1)
+ p->image_data[p->image_offset++] = *(guint8 *) (&value);
+ if (p->image_offset % 6 == 0)
+ {
+ if (++p->image_x >= p->image_width
+ && increment_image_y (p) < 0)
+ return;
+ }
+ }
+ break;
+ default:
+ goto bad_depth;
+ }
+ break;
+
+ case SANE_FRAME_GRAY:
+ switch (p->params.depth)
+ {
+ case 1:
+ for (i = 0; i < len; ++i)
+ {
+ u_char mask = buf[i];
+
+ for (j = 7; j >= 0; --j)
+ {
+ u_char gl = (mask & (1 << j)) ? 0x00 : 0xff;
+ p->image_data[p->image_offset++] = gl;
+ p->image_data[p->image_offset++] = gl;
+ p->image_data[p->image_offset++] = gl;
+ if (++p->image_x >= p->image_width)
+ {
+ if (increment_image_y (p) < 0)
+ return;
+ break; /* skip padding bits */
+ }
+ }
+ }
+ break;
+
+ case 8:
+ for (i = 0; i < len; ++i)
+ {
+ u_char gl = buf[i];
+ p->image_data[p->image_offset++] = gl;
+ p->image_data[p->image_offset++] = gl;
+ p->image_data[p->image_offset++] = gl;
+ if (++p->image_x >= p->image_width
+ && increment_image_y (p) < 0)
+ return;
+ }
+ break;
+ case 16:
+ for (i = 0; i < len; ++i)
+ {
+ guint16 value = buf[i];
+ if ((i % 2) == 1)
+ {
+ p->image_data[p->image_offset++] = *(guint8 *) (&value);
+ p->image_data[p->image_offset++] = *(guint8 *) (&value);
+ p->image_data[p->image_offset++] = *(guint8 *) (&value);
+ }
+ if (p->image_offset % 2 == 0)
+ {
+ if (++p->image_x >= p->image_width
+ && increment_image_y (p) < 0)
+ return;
+ }
+ }
+ break;
+
+ default:
+ goto bad_depth;
+ }
+ break;
+
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ switch (p->params.depth)
+ {
+ case 1:
+ for (i = 0; i < len; ++i)
+ {
+ u_char mask = buf[i];
+
+ for (j = 7; j >= 0; --j)
+ {
+ u_char gl = (mask & (1 << j)) ? 0xff : 0x00;
+ p->image_data[p->image_offset] = gl;
+ p->image_offset += 3;
+ if (++p->image_x >= p->image_width
+ && increment_image_y (p) < 0)
+ return;
+ }
+ }
+ break;
+
+ case 8:
+ for (i = 0; i < len; ++i)
+ {
+ p->image_data[p->image_offset] = buf[i];
+ p->image_offset += 3;
+ if (++p->image_x >= p->image_width
+ && increment_image_y (p) < 0)
+ return;
+ }
+ break;
+
+ case 16:
+ for (i = 0; i < len; ++i)
+ {
+ guint16 value = buf[i];
+ if ((i % 2) == 1)
+ {
+ p->image_data[p->image_offset] = *(guint8 *) (&value);
+ p->image_offset += 3;
+ }
+ if (p->image_offset % 2 == 0)
+ {
+ if (++p->image_x >= p->image_width
+ && increment_image_y (p) < 0)
+ return;
+ }
+ }
+ break;
+
+ default:
+ goto bad_depth;
+ }
+ break;
+
+ default:
+ fprintf (stderr, "preview.input_available: bad frame format %d\n",
+ p->params.format);
+ scan_done (p);
+ return;
+ }
+ if (p->input_tag < 0)
+ {
+ display_maybe (p);
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
+ }
+ }
+ display_maybe (p);
+ return;
+
+bad_depth:
+ snprintf (buf, sizeof (buf), "Preview cannot handle depth %d.",
+ p->params.depth);
+ gsg_error (buf);
+ scan_done (p);
+ return;
+}
+
+static void
+scan_done (Preview * p)
+{
+ int i;
+
+ p->scanning = FALSE;
+ if (p->input_tag >= 0)
+ {
+ gdk_input_remove (p->input_tag);
+ p->input_tag = -1;
+ }
+ sane_cancel (p->dialog->dev);
+
+ restore_option (p, p->dialog->well_known.dpi,
+ p->saved_dpi, p->saved_dpi_valid);
+ for (i = 0; i < 4; ++i)
+ restore_option (p, p->dialog->well_known.coord[i],
+ p->saved_coord[i], p->saved_coord_valid[i]);
+ set_option_bool (p, p->dialog->well_known.preview, SANE_FALSE);
+
+ gtk_widget_set_sensitive (p->cancel, FALSE);
+ gtk_widget_set_sensitive (p->preview, TRUE);
+ gsg_set_sensitivity (p->dialog, TRUE);
+ gtk_widget_set_sensitive (p->dialog->window->parent->parent->parent, TRUE);
+}
+
+static void
+scan_start (Preview * p)
+{
+ SANE_Handle dev = p->dialog->dev;
+ SANE_Status status;
+ char buf[256];
+ int fd, y;
+
+ gtk_widget_set_sensitive (p->cancel, TRUE);
+ gtk_widget_set_sensitive (p->preview, FALSE);
+ gsg_set_sensitivity (p->dialog, FALSE);
+ gtk_widget_set_sensitive (p->dialog->window->parent->parent->parent, FALSE);
+
+ /* clear old preview: */
+ memset (p->preview_row, 0xff, 3 * p->preview_width);
+ for (y = 0; y < p->preview_height; ++y)
+ gtk_preview_draw_row (GTK_PREVIEW (p->window), p->preview_row,
+ 0, y, p->preview_width);
+
+ if (p->input_tag >= 0)
+ {
+ gdk_input_remove (p->input_tag);
+ p->input_tag = -1;
+ }
+
+ gsg_sync (p->dialog);
+
+ status = sane_start (dev);
+ if (status != SANE_STATUS_GOOD)
+ {
+ snprintf (buf, sizeof (buf),
+ "Failed to start scanner: %s.", sane_strstatus (status));
+ gsg_error (buf);
+ scan_done (p);
+ return;
+ }
+
+ status = sane_get_parameters (dev, &p->params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ snprintf (buf, sizeof (buf),
+ "Failed to obtain parameters: %s.", sane_strstatus (status));
+ gsg_error (buf);
+ scan_done (p);
+ return;
+ }
+
+ if ((p->params.format >= SANE_FRAME_RGB &&
+ p->params.format <= SANE_FRAME_BLUE) &&
+ p->params.depth == 1 && p->params.pixels_per_line % 8 != 0)
+ {
+ snprintf (buf, sizeof (buf),
+ "Can't handle unaligned 1 bit RGB or three-pass mode.");
+ gsg_error (buf);
+ scan_done (p);
+ return;
+ }
+
+ p->image_offset = p->image_x = p->image_y = 0;
+
+ if (p->params.format >= SANE_FRAME_RED
+ && p->params.format <= SANE_FRAME_BLUE)
+ p->image_offset = p->params.format - SANE_FRAME_RED;
+
+ if (!p->image_data || p->params.pixels_per_line != p->image_width
+ || (p->params.lines >= 0 && p->params.lines != p->image_height))
+ {
+ /* image size changed */
+ if (p->image_data)
+ free (p->image_data);
+
+ p->image_width = p->params.pixels_per_line;
+ p->image_height = p->params.lines;
+ if (p->image_height < 0)
+ p->image_height = 32; /* may have to adjust as we go... */
+
+ p->image_data = malloc (p->image_width * p->image_height * 3);
+ if (!p->image_data)
+ {
+ snprintf (buf, sizeof (buf),
+ "Failed to allocate image memory: %s.", strerror (errno));
+ gsg_error (buf);
+ scan_done (p);
+ return;
+ }
+ memset (p->image_data, 0xff, 3 * p->image_width * p->image_height);
+ }
+
+ if (p->selection.active)
+ {
+ p->previous_selection = p->selection;
+ p->selection.active = FALSE;
+ draw_selection (p);
+ }
+ p->scanning = TRUE;
+
+ if (sane_set_io_mode (dev, SANE_TRUE) == SANE_STATUS_GOOD
+ && sane_get_select_fd (dev, &fd) == SANE_STATUS_GOOD)
+ p->input_tag =
+ gdk_input_add (fd, GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
+ input_available, p);
+ else
+ input_available (p, -1, GDK_INPUT_READ);
+}
+
+static void
+establish_selection (Preview * p)
+{
+ float min, max, normal, dev_selection[4];
+ int i;
+
+ memcpy (dev_selection, p->surface, sizeof (dev_selection));
+ if (p->selection.active)
+ for (i = 0; i < 2; ++i)
+ {
+ min = p->surface[i];
+ if (min <= -INF)
+ min = 0.0;
+ max = p->surface[i + 2];
+ if (max >= INF)
+ max = p->preview_width;
+
+ normal =
+ 1.0 / (((i == 0) ? p->preview_width : p->preview_height) - 1);
+ normal *= (max - min);
+ dev_selection[i] = p->selection.coord[i] * normal + min;
+ dev_selection[i + 2] = p->selection.coord[i + 2] * normal + min;
+ }
+ for (i = 0; i < 4; ++i)
+ set_option_float (p, p->dialog->well_known.coord[i], dev_selection[i]);
+ gsg_update_scan_window (p->dialog);
+ if (p->dialog->param_change_callback)
+ (*p->dialog->param_change_callback) (p->dialog,
+ p->dialog->param_change_arg);
+}
+
+static int
+make_preview_image_path (Preview * p, size_t filename_size, char *filename)
+{
+ return gsg_make_path (filename_size, filename, 0, "preview-",
+ p->dialog->dev_name, ".ppm");
+}
+
+static void
+restore_preview_image (Preview * p)
+{
+ u_int psurface_type, psurface_unit;
+ char filename[PATH_MAX];
+ int width, height;
+ float psurface[4];
+ size_t nread;
+ FILE *in;
+
+ /* See whether there is a saved preview and load it if present: */
+
+ if (make_preview_image_path (p, sizeof (filename), filename) < 0)
+ return;
+
+ in = fopen (filename, "r");
+ if (!in)
+ return;
+
+ /* Be careful about consuming too many bytes after the final newline
+ (e.g., consider an image whose first image byte is 13 (`\r'). */
+ if (fscanf (in, "P6\n# surface: %g %g %g %g %u %u\n%d %d\n255%*[\n]",
+ psurface + 0, psurface + 1, psurface + 2, psurface + 3,
+ &psurface_type, &psurface_unit, &width, &height) != 8)
+ return;
+
+ if (GROSSLY_DIFFERENT (psurface[0], p->surface[0])
+ || GROSSLY_DIFFERENT (psurface[1], p->surface[1])
+ || GROSSLY_DIFFERENT (psurface[2], p->surface[2])
+ || GROSSLY_DIFFERENT (psurface[3], p->surface[3])
+ || psurface_type != p->surface_type || psurface_unit != p->surface_unit)
+ /* ignore preview image that was acquired for/with a different surface */
+ return;
+
+ p->image_width = width;
+ p->image_height = height;
+ if ((width == 0) || (height == 0))
+ return;
+ p->image_data = malloc (3 * width * height);
+ if (!p->image_data)
+ return;
+
+ nread = fread (p->image_data, 3, width * height, in);
+
+ p->image_y = nread / width;
+ p->image_x = nread % width;
+}
+
+/* This is executed _after_ the gtkpreview's expose routine. */
+static gint
+expose_handler (GtkWidget * window, GdkEvent * event, gpointer data)
+{
+ Preview *p = data;
+
+ p->selection.active = FALSE;
+ update_selection (p);
+ return FALSE;
+}
+
+static gint
+event_handler (GtkWidget * window, GdkEvent * event, gpointer data)
+{
+ Preview *p = data;
+ int i, tmp;
+
+ if (event->type == GDK_EXPOSE)
+ {
+ if (!p->gc)
+ {
+ p->gc = gdk_gc_new (p->window->window);
+ gdk_gc_set_function (p->gc, GDK_INVERT);
+ gdk_gc_set_line_attributes (p->gc, 1, GDK_LINE_ON_OFF_DASH,
+ GDK_CAP_BUTT, GDK_JOIN_MITER);
+ paint_image (p);
+ }
+ else
+ {
+ p->selection.active = FALSE;
+ draw_selection (p);
+ }
+ }
+ else if (!p->scanning)
+ switch (event->type)
+ {
+ case GDK_UNMAP:
+ case GDK_MAP:
+ break;
+
+ case GDK_BUTTON_PRESS:
+ p->selection.coord[0] = event->button.x;
+ p->selection.coord[1] = event->button.y;
+ p->selection_drag = TRUE;
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ if (!p->selection_drag)
+ break;
+ p->selection_drag = FALSE;
+
+ p->selection.coord[2] = event->button.x;
+ p->selection.coord[3] = event->button.y;
+ p->selection.active =
+ (p->selection.coord[0] != p->selection.coord[2]
+ || p->selection.coord[1] != p->selection.coord[3]);
+
+ if (p->selection.active)
+ {
+ for (i = 0; i < 2; i += 1)
+ if (p->selection.coord[i] > p->selection.coord[i + 2])
+ {
+ tmp = p->selection.coord[i];
+ p->selection.coord[i] = p->selection.coord[i + 2];
+ p->selection.coord[i + 2] = tmp;
+ }
+ if (p->selection.coord[0] < 0)
+ p->selection.coord[0] = 0;
+ if (p->selection.coord[1] < 0)
+ p->selection.coord[1] = 0;
+ if (p->selection.coord[2] >= p->preview_width)
+ p->selection.coord[2] = p->preview_width - 1;
+ if (p->selection.coord[3] >= p->preview_height)
+ p->selection.coord[3] = p->preview_height - 1;
+ }
+ draw_selection (p);
+ establish_selection (p);
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ if (p->selection_drag)
+ {
+ p->selection.active = TRUE;
+ p->selection.coord[2] = event->motion.x;
+ p->selection.coord[3] = event->motion.y;
+ draw_selection (p);
+ }
+ break;
+
+ default:
+#if 0
+ fprintf (stderr, "event_handler: unhandled event type %d\n",
+ event->type);
+#endif
+ break;
+ }
+ return FALSE;
+}
+
+static void
+start_button_clicked (GtkWidget * widget, gpointer data)
+{
+ preview_scan (data);
+}
+
+static void
+cancel_button_clicked (GtkWidget * widget, gpointer data)
+{
+ scan_done (data);
+}
+
+static void
+top_destroyed (GtkWidget * widget, gpointer call_data)
+{
+ Preview *p = call_data;
+
+ p->top = NULL;
+}
+
+Preview *
+preview_new (GSGDialog * dialog)
+{
+ static int first_time = 1;
+ GtkWidget *table, *frame;
+ GtkSignalFunc signal_func;
+ GtkWidgetClass *class;
+ GtkBox *vbox, *hbox;
+ Preview *p;
+
+ p = malloc (sizeof (*p));
+ if (!p)
+ return 0;
+ memset (p, 0, sizeof (*p));
+
+ p->dialog = dialog;
+ p->input_tag = -1;
+
+ if (first_time)
+ {
+ first_time = 0;
+ gtk_preview_set_gamma (preferences.preview_gamma);
+ gtk_preview_set_install_cmap (preferences.preview_own_cmap);
+ }
+
+#ifndef XSERVER_WITH_BUGGY_VISUALS
+ gtk_widget_push_visual (gtk_preview_get_visual ());
+#endif
+ gtk_widget_push_colormap (gtk_preview_get_cmap ());
+
+ p->top = gtk_dialog_new ();
+ gtk_signal_connect (GTK_OBJECT (p->top), "destroy",
+ GTK_SIGNAL_FUNC (top_destroyed), p);
+ gtk_window_set_title (GTK_WINDOW (p->top), "xscanimage preview");
+ vbox = GTK_BOX (GTK_DIALOG (p->top)->vbox);
+ hbox = GTK_BOX (GTK_DIALOG (p->top)->action_area);
+
+ /* construct the preview area (table with sliders & preview window) */
+ table = gtk_table_new (2, 2, /* homogeneous */ FALSE);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 0, 1);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 0, 1);
+ gtk_container_border_width (GTK_CONTAINER (table), 2);
+ gtk_box_pack_start (vbox, table, /* expand */ TRUE, /* fill */ TRUE,
+ /* padding */ 0);
+
+ /* the empty box in the top-left corner */
+ frame = gtk_frame_new ( /* label */ 0);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
+ gtk_table_attach (GTK_TABLE (table), frame, 0, 1, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+
+ /* the horizontal ruler */
+ p->hruler = gtk_hruler_new ();
+ gtk_table_attach (GTK_TABLE (table), p->hruler, 1, 2, 0, 1,
+ GTK_FILL, 0, 0, 0);
+
+ /* the vertical ruler */
+ p->vruler = gtk_vruler_new ();
+ gtk_table_attach (GTK_TABLE (table), p->vruler, 0, 1, 1, 2, 0,
+ GTK_FILL, 0, 0);
+
+ /* the preview area */
+
+ p->window = gtk_preview_new (GTK_PREVIEW_COLOR);
+ gtk_preview_set_expand (GTK_PREVIEW (p->window), TRUE);
+ gtk_widget_set_events (p->window,
+ GDK_EXPOSURE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+ gtk_signal_connect (GTK_OBJECT (p->window), "event",
+ (GtkSignalFunc) event_handler, p);
+ gtk_signal_connect_after (GTK_OBJECT (p->window), "expose_event",
+ (GtkSignalFunc) expose_handler, p);
+ gtk_signal_connect_after (GTK_OBJECT (p->window), "size_allocate",
+ (GtkSignalFunc) preview_area_resize, 0);
+ gtk_object_set_data (GTK_OBJECT (p->window), "PreviewPointer", p);
+
+ /* Connect the motion-notify events of the preview area with the
+ rulers. Nifty stuff! */
+
+#if GTK_MAJOR_VERSION == 2
+ class = GTK_WIDGET_CLASS (GTK_OBJECT_GET_CLASS (GTK_OBJECT (p->hruler)));
+#else
+ class = GTK_WIDGET_CLASS (GTK_OBJECT (p->hruler)->klass);
+#endif /* GTK_MAJOR_VERSION == 2 */
+ signal_func = (GtkSignalFunc) class->motion_notify_event;
+ gtk_signal_connect_object (GTK_OBJECT (p->window), "motion_notify_event",
+ signal_func, GTK_OBJECT (p->hruler));
+
+#if GTK_MAJOR_VERSION == 2
+ class = GTK_WIDGET_CLASS (GTK_OBJECT_GET_CLASS (GTK_OBJECT (p->vruler)));
+#else
+ class = GTK_WIDGET_CLASS (GTK_OBJECT (p->vruler)->klass);
+#endif /* GTK_MAJOR_VERSION == 2 */
+ signal_func = (GtkSignalFunc) class->motion_notify_event;
+ gtk_signal_connect_object (GTK_OBJECT (p->window), "motion_notify_event",
+ signal_func, GTK_OBJECT (p->vruler));
+
+ p->viewport = gtk_frame_new ( /* label */ 0);
+ gtk_frame_set_shadow_type (GTK_FRAME (p->viewport), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (p->viewport), p->window);
+
+ gtk_table_attach (GTK_TABLE (table), p->viewport, 1, 2, 1, 2,
+ GTK_FILL | GTK_EXPAND | GTK_SHRINK,
+ GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
+
+ preview_update (p);
+
+ /* fill in action area: */
+
+ /* Preview button */
+ p->preview = gtk_button_new_with_label ("Acquire Preview");
+ gtk_signal_connect (GTK_OBJECT (p->preview), "clicked",
+ (GtkSignalFunc) start_button_clicked, p);
+ gtk_box_pack_start (GTK_BOX (hbox), p->preview, TRUE, TRUE, 0);
+ gtk_widget_show (p->preview);
+
+ /* Cancel button */
+ p->cancel = gtk_button_new_with_label ("Cancel Preview");
+ gtk_signal_connect (GTK_OBJECT (p->cancel), "clicked",
+ (GtkSignalFunc) cancel_button_clicked, p);
+ gtk_box_pack_start (GTK_BOX (hbox), p->cancel, TRUE, TRUE, 0);
+ gtk_widget_set_sensitive (p->cancel, FALSE);
+
+ gtk_widget_show (p->cancel);
+ gtk_widget_show (p->viewport);
+ gtk_widget_show (p->window);
+ gtk_widget_show (p->hruler);
+ gtk_widget_show (p->vruler);
+ gtk_widget_show (frame);
+ gtk_widget_show (table);
+ gtk_widget_show (p->top);
+
+ gtk_widget_pop_colormap ();
+#ifndef XSERVER_WITH_BUGGY_VISUALS
+ gtk_widget_pop_visual ();
+#endif
+ return p;
+}
+
+void
+preview_update (Preview * p)
+{
+ float val, width, height, max_width, max_height;
+ const SANE_Option_Descriptor *opt;
+ int i, surface_changed;
+ SANE_Value_Type type;
+ SANE_Unit unit;
+ float min, max;
+
+ surface_changed = 0;
+ unit = SANE_UNIT_PIXEL;
+ type = SANE_TYPE_INT;
+ for (i = 0; i < 4; ++i)
+ {
+ val = (i & 2) ? INF : -INF;
+ if (p->dialog->well_known.coord[i] > 0)
+ {
+ opt = sane_get_option_descriptor (p->dialog->dev,
+ p->dialog->well_known.coord[i]);
+ assert (opt->unit == SANE_UNIT_PIXEL || opt->unit == SANE_UNIT_MM);
+ unit = opt->unit;
+ type = opt->type;
+
+ get_bounds (opt, &min, &max);
+ if (i & 2)
+ val = max;
+ else
+ val = min;
+ }
+ if (p->surface[i] != val)
+ {
+ surface_changed = 1;
+ p->surface[i] = val;
+ }
+ }
+ if (p->surface_unit != unit)
+ {
+ surface_changed = 1;
+ p->surface_unit = unit;
+ }
+ if (p->surface_type != type)
+ {
+ surface_changed = 1;
+ p->surface_type = type;
+ }
+ if (surface_changed && p->image_data)
+ {
+ free (p->image_data);
+ p->image_data = 0;
+ p->image_width = 0;
+ p->image_height = 0;
+ }
+
+ /* guess the initial preview window size: */
+
+ width = p->surface[GSG_BR_X] - p->surface[GSG_TL_X];
+ height = p->surface[GSG_BR_Y] - p->surface[GSG_TL_Y];
+ if (p->surface_type == SANE_TYPE_INT)
+ {
+ width += 1.0;
+ height += 1.0;
+ }
+ else
+ {
+ width += SANE_UNFIX (1.0);
+ height += SANE_UNFIX (1.0);
+ }
+
+ assert (width > 0.0 && height > 0.0);
+
+ if (width >= INF || height >= INF)
+ p->aspect = 1.0;
+ else
+ p->aspect = width / height;
+
+ if (surface_changed)
+ {
+ max_width = 0.5 * gdk_screen_width ();
+ max_height = 0.5 * gdk_screen_height ();
+ }
+ else
+ {
+ max_width = p->window->allocation.width;
+ max_height = p->window->allocation.height;
+ }
+
+ if (p->surface_unit != SANE_UNIT_PIXEL)
+ {
+ width = max_width;
+ height = width / p->aspect;
+
+ if (height > max_height)
+ {
+ height = max_height;
+ width = height * p->aspect;
+ }
+ }
+ else
+ {
+ if (width > max_width)
+ width = max_width;
+
+ if (height > max_height)
+ height = max_height;
+ }
+
+ /* re-adjust so we maintain aspect without exceeding max size: */
+ if (width / height != p->aspect)
+ {
+ if (p->aspect > 1.0)
+ height = width / p->aspect;
+ else
+ width = height * p->aspect;
+ }
+
+ p->preview_width = width + 0.5;
+ p->preview_height = height + 0.5;
+ if (surface_changed)
+ {
+ gtk_widget_set_usize (GTK_WIDGET (p->window),
+ p->preview_width, p->preview_height);
+ if (GTK_WIDGET_DRAWABLE (p->window))
+ preview_area_resize (p->window);
+
+ if (preferences.preserve_preview)
+ restore_preview_image (p);
+ }
+ update_selection (p);
+}
+
+void
+preview_scan (Preview * p)
+{
+ float min, max, swidth, sheight, width, height, dpi = 0;
+ const SANE_Option_Descriptor *opt;
+ gint gwidth, gheight;
+ int i;
+ SANE_Status status;
+
+
+ save_option (p, p->dialog->well_known.dpi,
+ &p->saved_dpi, &p->saved_dpi_valid);
+ for (i = 0; i < 4; ++i)
+ save_option (p, p->dialog->well_known.coord[i],
+ &p->saved_coord[i], p->saved_coord_valid + i);
+
+ /* determine dpi, if necessary: */
+
+ if (p->dialog->well_known.dpi > 0)
+ {
+ opt = sane_get_option_descriptor (p->dialog->dev,
+ p->dialog->well_known.dpi);
+
+ gwidth = p->preview_width;
+ gheight = p->preview_height;
+
+ height = gheight;
+ width = height * p->aspect;
+ if (width > gwidth)
+ {
+ width = gwidth;
+ height = width / p->aspect;
+ }
+
+ swidth = (p->surface[GSG_BR_X] - p->surface[GSG_TL_X]);
+ if (swidth < INF)
+ dpi = MM_PER_INCH * width / swidth;
+ else
+ {
+ sheight = (p->surface[GSG_BR_Y] - p->surface[GSG_TL_Y]);
+ if (sheight < INF)
+ dpi = MM_PER_INCH * height / sheight;
+ else
+ dpi = 18.0;
+ }
+ get_bounds (opt, &min, &max);
+ if (dpi < min)
+ dpi = min;
+ if (dpi > max)
+ dpi = max;
+ status = set_option_float (p, p->dialog->well_known.dpi, dpi);
+ if (status != SANE_STATUS_GOOD)
+ return;
+ }
+
+ /* set the scan window (necessary since backends may default to
+ non-maximum size): */
+ for (i = 0; i < 4; ++i)
+ set_option_float (p, p->dialog->well_known.coord[i], p->surface[i]);
+ set_option_bool (p, p->dialog->well_known.preview, SANE_TRUE);
+
+ /* OK, all set to go */
+ scan_start (p);
+}
+
+void
+preview_destroy (Preview * p)
+{
+ char filename[PATH_MAX];
+ FILE *out;
+
+ if (p->scanning)
+ scan_done (p); /* don't save partial window */
+ else if (preferences.preserve_preview && p->image_data
+ && make_preview_image_path (p, sizeof (filename), filename) >= 0)
+ {
+ /* save preview image */
+ out = fopen (filename, "w");
+ if (out)
+ {
+ /* always save it as a PPM image: */
+ fprintf (out, "P6\n# surface: %g %g %g %g %u %u\n%d %d\n255\n",
+ p->surface[0], p->surface[1], p->surface[2], p->surface[3],
+ p->surface_type, p->surface_unit,
+ p->image_width, p->image_height);
+ fwrite (p->image_data, 3, p->image_width * p->image_height, out);
+ fclose (out);
+ }
+ }
+ if (p->image_data)
+ free (p->image_data);
+ if (p->preview_row)
+ free (p->preview_row);
+ if (p->gc)
+ gdk_gc_destroy (p->gc);
+ if (p->top)
+ gtk_widget_destroy (p->top);
+ free (p);
+}
diff --git a/src/preview.h b/src/preview.h
new file mode 100644
index 0000000..5b0b585
--- /dev/null
+++ b/src/preview.h
@@ -0,0 +1,87 @@
+/* sane - Scanner Access Now Easy.
+ Copyright (C) 1997 David Mosberger-Tang
+ This file is part of the SANE package.
+
+ SANE 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.
+
+ SANE 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 sane; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+#ifndef preview_h
+#define preview_h
+
+#include <sys/types.h>
+
+#include "../include/sane/config.h"
+#include <sane/sane.h>
+
+typedef struct
+ {
+ GSGDialog *dialog; /* the dialog for this preview */
+
+ SANE_Value_Type surface_type;
+ SANE_Unit surface_unit;
+ float surface[4]; /* the corners of the scan surface (device coords) */
+ float aspect; /* the aspect ratio of the scan surface */
+
+ int saved_dpi_valid;
+ SANE_Word saved_dpi;
+ int saved_coord_valid[4];
+ SANE_Word saved_coord[4];
+
+ /* desired/user-selected preview-window size: */
+ int preview_width;
+ int preview_height;
+ u_char *preview_row;
+
+ int scanning;
+ time_t image_last_time_updated;
+ gint input_tag;
+ SANE_Parameters params;
+ int image_offset;
+ int image_x;
+ int image_y;
+ int image_width;
+ int image_height;
+ u_char *image_data; /* 3 * image_width * image_height bytes */
+
+ GdkGC *gc;
+ int selection_drag;
+ struct
+ {
+ int active;
+ int coord[4];
+ }
+ selection, previous_selection;
+
+ GtkWidget *top; /* top-level widget */
+ GtkWidget *hruler;
+ GtkWidget *vruler;
+ GtkWidget *viewport;
+ GtkWidget *window; /* the preview window */
+ GtkWidget *cancel; /* the cancel button */
+ GtkWidget *preview; /* the preview button */
+ }
+Preview;
+
+/* Create a new preview based on the info in DIALOG. */
+extern Preview *preview_new (GSGDialog *dialog);
+
+/* Some of the parameters may have changed---update the preview. */
+extern void preview_update (Preview *p);
+
+/* Acquire a preview image and display it. */
+extern void preview_scan (Preview *p);
+
+/* Destroy a preview. */
+extern void preview_destroy (Preview *p);
+
+#endif /* preview_h */
diff --git a/src/progress.c b/src/progress.c
new file mode 100644
index 0000000..4957295
--- /dev/null
+++ b/src/progress.c
@@ -0,0 +1,93 @@
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ * Hacked (C) 1996 Tristan Tarrant
+ *
+ * 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.
+ */
+#include "../include/sane/config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include "progress.h"
+
+static const int progress_x = 5;
+static const int progress_y = 5;
+
+void
+progress_cancel (GtkWidget * widget, gpointer data)
+{
+ Progress_t *p = (Progress_t *) data;
+
+ (*p->callback) ();
+}
+
+
+Progress_t *
+progress_new (char *title, char *text,
+ GtkSignalFunc callback, gpointer callback_data)
+{
+ GtkWidget *button, *label;
+ GtkBox *vbox, *hbox;
+ Progress_t *p;
+
+ p = (Progress_t *) malloc (sizeof (Progress_t));
+ p->callback = callback;
+
+ p->shell = gtk_dialog_new ();
+ gtk_widget_set_uposition (p->shell, progress_x, progress_y);
+ gtk_window_set_title (GTK_WINDOW (p->shell), title);
+ vbox = GTK_BOX (GTK_DIALOG (p->shell)->vbox);
+ hbox = GTK_BOX (GTK_DIALOG (p->shell)->action_area);
+
+ gtk_container_border_width (GTK_CONTAINER (vbox), 7);
+
+ label = gtk_label_new (text);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_box_pack_start (vbox, label, FALSE, TRUE, 0);
+
+ p->pbar = gtk_progress_bar_new ();
+ gtk_widget_set_usize (p->pbar, 200, 20);
+ gtk_box_pack_start (vbox, p->pbar, TRUE, TRUE, 0);
+
+ button = gtk_toggle_button_new_with_label ("Cancel");
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) progress_cancel, p);
+ gtk_box_pack_start (hbox, button, TRUE, TRUE, 0);
+
+ gtk_widget_show (label);
+ gtk_widget_show (p->pbar);
+ gtk_widget_show (button);
+ gtk_widget_show (GTK_WIDGET (p->shell));
+ gtk_progress_bar_update (GTK_PROGRESS_BAR (p->pbar), 0);
+ return p;
+}
+
+void
+progress_free (Progress_t * p)
+{
+ if (p)
+ {
+ gtk_widget_destroy (p->shell);
+ free (p);
+ }
+}
+
+void
+progress_update (Progress_t * p, gfloat newval)
+{
+ if (p)
+ gtk_progress_bar_update (GTK_PROGRESS_BAR (p->pbar), newval);
+}
diff --git a/src/progress.h b/src/progress.h
new file mode 100644
index 0000000..13a924e
--- /dev/null
+++ b/src/progress.h
@@ -0,0 +1,34 @@
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 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.
+ */
+#ifndef progress_h
+#define progress_h
+
+typedef struct Progress_t
+ {
+ GtkSignalFunc callback;
+ gpointer callback_data;
+ GtkWidget *shell;
+ GtkWidget *pbar;
+ }
+Progress_t;
+
+extern Progress_t *progress_new (char *, char *, GtkSignalFunc, void *);
+extern void progress_free (Progress_t *);
+extern void progress_update (Progress_t *, gfloat);
+
+#endif /* progress_h */
diff --git a/src/sane-style.rc b/src/sane-style.rc
new file mode 100644
index 0000000..f85259c
--- /dev/null
+++ b/src/sane-style.rc
@@ -0,0 +1,21 @@
+# style <name> [= <name>]
+# {
+# <option>
+# }
+#
+# widget <widget_set> style <style_name>
+# widget_class <widget_class_set> style <style_name>
+# accelerator <widget_name> <accelerator>
+
+style "progressbar"
+{
+ bg[PRELIGHT] = { 22500, 53280, 22500 } # green
+}
+
+style "curve"
+{
+ fg[NORMAL] = { 58000, 0, 0 } # red
+}
+
+widget "*GtkCurve" style "curve"
+widget "*GtkProgressBar" style "progressbar"
diff --git a/src/scanadf.c b/src/scanadf.c
new file mode 100644
index 0000000..d4ff566
--- /dev/null
+++ b/src/scanadf.c
@@ -0,0 +1,1590 @@
+/* saneadf - a SANE front end for document scanning
+ based on
+ bnhscan by tummy.com and
+ scanimage by Andreas Beck and David Mosberger
+
+ Copyright (C) 1999 Tom Martone
+
+ 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 <lalloca.h> /* MUST come first for AIX! */
+#endif
+
+#include "sane/config.h"
+#include <lalloca.h>
+
+#include <assert.h>
+#include <getopt.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <limits.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "sane/sane.h"
+#include "sane/sanei.h"
+#include "sane/saneopts.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#ifndef sane_isbasicframe
+#define SANE_FRAME_TEXT 10
+#define SANE_FRAME_JPEG 11
+#define SANE_FRAME_G31D 12
+#define SANE_FRAME_G32D 13
+#define SANE_FRAME_G42D 14
+#define sane_strframe(f) ( (f) == SANE_FRAME_GRAY ? "gray" : \
+ (f) == SANE_FRAME_RGB ? "RGB" : \
+ (f) == SANE_FRAME_RED ? "red" : \
+ (f) == SANE_FRAME_GREEN ? "green" : \
+ (f) == SANE_FRAME_BLUE ? "blue" : \
+ (f) == SANE_FRAME_TEXT ? "text" : \
+ (f) == SANE_FRAME_JPEG ? "jpeg" : \
+ (f) == SANE_FRAME_G31D ? "g31d" : \
+ (f) == SANE_FRAME_G32D ? "g32d" : \
+ (f) == SANE_FRAME_G42D ? "g42d" : \
+ "unknown" )
+
+#define sane_isbasicframe(f) ( (f) == SANE_FRAME_GRAY || \
+ (f) == SANE_FRAME_RGB || \
+ (f) == SANE_FRAME_RED || \
+ (f) == SANE_FRAME_GREEN || \
+ (f) == SANE_FRAME_BLUE )
+
+#endif
+
+#ifndef HAVE_ATEXIT
+# define atexit(func) on_exit(func, 0) /* works for SunOS, at least */
+#endif
+
+typedef struct
+ {
+ u_char *data;
+ int Bpp; /* bytes/pixel */
+ int width;
+ int height;
+ int x;
+ int y;
+ }
+Image;
+
+static struct option basic_options[] =
+{
+ {"device-name", required_argument, NULL, 'd'},
+ {"list-devices", no_argument, NULL, 'L'},
+ {"help", no_argument, NULL, 'h'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {"no-overwrite", no_argument, NULL, 'N'},
+
+ { "output-file", required_argument, 0, 'o' },
+ { "start-count", required_argument, 0, 's' },
+ { "end-count", required_argument, 0, 'e' },
+ { "scan-script", required_argument, 0, 'S' },
+ { "script-wait", no_argument, 0, 128 },
+ { "raw", no_argument, 0, 'r' },
+ {0, }
+};
+
+#define BASE_OPTSTRING "d:hLvVNTo:s:e:S:r"
+#define STRIP_HEIGHT 256 /* # lines we increment image height */
+
+static struct option * all_options;
+static int option_number_len;
+static int * option_number;
+static SANE_Handle device;
+static int verbose;
+static int help;
+static const char * prog_name;
+static SANE_Option_Descriptor window_option[2];
+static int window[4];
+static int resolution_opt = -1, x_resolution_opt = -1, y_resolution_opt = -1;
+static SANE_Word window_val[2];
+static int window_val_user[2]; /* is width/height user-specified? */
+
+static const char usage[] = "Usage: %s [OPTION]...\n\
+\n\
+Start image acquisition on a scanner device and write image data to\n\
+output files.\n\
+\n\
+ [ -d | --device-name <device> ] use a given scanner device.\n\
+ [ -h | --help ] display this help message and exit.\n\
+ [ -L | --list-devices ] show available scanner devices.\n\
+ [ -v | --verbose ] give even more status messages.\n\
+ [ -V | --version ] print version information.\n\
+ [ -N | --no-overwrite ] don't overwrite existing files.\n\
+\n\
+ [ -o | --output-file <name> ] name of file to write image data\n\
+ (%%d replacement in output file name).\n\
+ [ -S | --scan-script <name> ] name of script to run after every scan.\n\
+ [ --script-wait ] wait for scripts to finish before exit\n\
+ [ -s | --start-count <num> ] page count of first scanned image.\n\
+ [ -e | --end-count <num> ] last page number to scan.\n\
+ [ -r | --raw ] write raw image data to file.\n";
+
+static RETSIGTYPE
+sighandler (int signum)
+{
+ if (device)
+ {
+ fprintf (stderr, "%s: stopping scanner...\n", prog_name);
+ sane_cancel (device);
+ }
+}
+
+static void
+print_unit (SANE_Unit unit)
+{
+ switch (unit)
+ {
+ case SANE_UNIT_NONE: break;
+ case SANE_UNIT_PIXEL: fputs ("pel", stdout); break;
+ case SANE_UNIT_BIT: fputs ("bit", stdout); break;
+ case SANE_UNIT_MM: fputs ("mm", stdout); break;
+ case SANE_UNIT_DPI: fputs ("dpi", stdout); break;
+ case SANE_UNIT_PERCENT: fputc ('%', stdout); break;
+ case SANE_UNIT_MICROSECOND: fputs ("us", stdout); break;
+ }
+}
+
+static void
+print_option (SANE_Device *device, int opt_num, char short_name)
+{
+ const char *str, *last_break, *start;
+ const SANE_Option_Descriptor *opt;
+ SANE_Bool not_first = SANE_FALSE;
+ int i, column;
+
+ opt = sane_get_option_descriptor (device, opt_num);
+
+ if (short_name)
+ printf (" -%c", short_name);
+ else
+ printf (" --%s", opt->name);
+
+ if (opt->type == SANE_TYPE_BOOL)
+ {
+ fputs ("[=(", stdout);
+ if (opt->cap & SANE_CAP_AUTOMATIC)
+ fputs ("auto|", stdout);
+ fputs ("yes|no)]", stdout);
+ }
+ else if (opt->type != SANE_TYPE_BUTTON)
+ {
+ fputc (' ', stdout);
+ if (opt->cap & SANE_CAP_AUTOMATIC)
+ {
+ fputs ("auto|", stdout);
+ not_first = SANE_TRUE;
+ }
+ switch (opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_NONE:
+ switch (opt->type)
+ {
+ case SANE_TYPE_INT: fputs ("<int>", stdout); break;
+ case SANE_TYPE_FIXED: fputs ("<float>", stdout); break;
+ case SANE_TYPE_STRING: fputs ("<string>", stdout); break;
+ default:
+ break;
+ }
+ if (opt->type != SANE_TYPE_STRING && opt->size > sizeof (SANE_Word))
+ fputs (",...", stdout);
+ break;
+
+ case SANE_CONSTRAINT_RANGE:
+ if (opt->type == SANE_TYPE_INT)
+ {
+ printf ("%d..%d",
+ opt->constraint.range->min, opt->constraint.range->max);
+ print_unit (opt->unit);
+ if (opt->size > sizeof (SANE_Word))
+ fputs (",...", stdout);
+ if (opt->constraint.range->quant)
+ printf (" (in steps of %d)",
+ opt->constraint.range->quant);
+ }
+ else
+ {
+ printf ("%g..%g",
+ SANE_UNFIX(opt->constraint.range->min),
+ SANE_UNFIX(opt->constraint.range->max));
+ print_unit (opt->unit);
+ if (opt->size > sizeof (SANE_Word))
+ fputs (",...", stdout);
+ if (opt->constraint.range->quant)
+ printf (" (in steps of %g)",
+ SANE_UNFIX(opt->constraint.range->quant));
+ }
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ for (i = 0; i < opt->constraint.word_list[0]; ++i)
+ {
+ if (not_first)
+ fputc ('|', stdout);
+
+ not_first = SANE_TRUE;
+
+ if (opt->type == SANE_TYPE_INT)
+ printf ("%d", opt->constraint.word_list[i + 1]);
+ else
+ printf ("%g",
+ SANE_UNFIX(opt->constraint.word_list[i + 1]));
+ }
+ print_unit (opt->unit);
+ if (opt->size > sizeof (SANE_Word))
+ fputs (",...", stdout);
+ break;
+
+ case SANE_CONSTRAINT_STRING_LIST:
+ for (i = 0; opt->constraint.string_list[i]; ++i)
+ {
+ if (i > 0)
+ fputc ('|', stdout);
+
+ fputs (opt->constraint.string_list[i], stdout);
+ }
+ break;
+ }
+ }
+ if (opt->type == SANE_TYPE_STRING ||
+ opt->type == SANE_TYPE_BOOL ||
+ opt->type == SANE_TYPE_INT ||
+ opt->type == SANE_TYPE_FIXED)
+ {
+ /* print current option value */
+ fputs (" [", stdout);
+ if (SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ void * val = alloca (opt->size);
+ sane_control_option (device, opt_num, SANE_ACTION_GET_VALUE, val, 0);
+ switch (opt->type)
+ {
+ case SANE_TYPE_BOOL:
+ fputs (*(SANE_Bool *)val ? "yes" : "no", stdout);
+ break;
+
+ case SANE_TYPE_INT:
+ printf ("%d", *(SANE_Int *)val);
+ break;
+
+ case SANE_TYPE_FIXED:
+ printf ("%g", SANE_UNFIX(*(SANE_Fixed *)val));
+ break;
+
+ case SANE_TYPE_STRING:
+ fputs ((char *) val, stdout);
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ fputs ("inactive", stdout);
+ fputc (']', stdout);
+ }
+ fputs ("\n ", stdout);
+
+ switch (short_name)
+ {
+ case 'x':
+ fputs ("Width of scan-area.", stdout);
+ break;
+
+ case 'y':
+ fputs ("Height of scan-area.", stdout);
+ break;
+
+ default:
+ column = 8; last_break = 0; start = opt->desc;
+ for (str = opt->desc; *str; ++str)
+ {
+ ++column;
+ if (*str == ' ')
+ last_break = str;
+ if (column >= 79 && last_break)
+ {
+ while (start < last_break)
+ fputc (*start++, stdout);
+ start = last_break + 1; /* skip blank */
+ fputs ("\n ", stdout);
+ column = 8 + (str - start);
+ }
+ }
+ while (*start)
+ fputc (*start++, stdout);
+ }
+ fputc ('\n', stdout);
+}
+
+/* A scalar has the following syntax:
+
+ V [ U ]
+
+ V is the value of the scalar. It is either an integer or a
+ floating point number, depending on the option type.
+
+ U is an optional unit. If not specified, the default unit is used.
+ The following table lists which units are supported depending on
+ what the option's default unit is:
+
+ Option's unit: Allowed units:
+
+ SANE_UNIT_NONE:
+ SANE_UNIT_PIXEL: pel
+ SANE_UNIT_BIT: b (bit), B (byte)
+ SANE_UNIT_MM: mm (millimeter), cm (centimeter), in or " (inches),
+ SANE_UNIT_DPI: dpi
+ SANE_UNIT_PERCENT: %
+ SANE_UNIT_PERCENT: us
+ */
+static const char *
+parse_scalar (const SANE_Option_Descriptor * opt, const char * str,
+ SANE_Word * value)
+{
+ char * end;
+ double v;
+
+ if (opt->type == SANE_TYPE_FIXED)
+ v = strtod (str, &end) * (1 << SANE_FIXED_SCALE_SHIFT);
+ else
+ v = strtol (str, &end, 10);
+
+ if (str == end)
+ {
+ fprintf (stderr,
+ "%s: option --%s: bad option value (rest of option: %s)\n",
+ prog_name, opt->name, str);
+ exit (1);
+ }
+ str = end;
+
+ switch (opt->unit)
+ {
+ case SANE_UNIT_NONE:
+ case SANE_UNIT_PIXEL:
+ break;
+
+ case SANE_UNIT_BIT:
+ if (*str == 'b' || *str == 'B')
+ {
+ if (*str++ == 'B')
+ v *= 8;
+ }
+ break;
+
+ case SANE_UNIT_MM:
+ if (str[0] == '\0')
+ v *= 1.0; /* default to mm */
+ else if (strcmp (str, "mm") == 0)
+ str += sizeof ("mm") - 1;
+ else if (strcmp (str, "cm") == 0)
+ {
+ str += sizeof ("cm") - 1;
+ v *= 10.0;
+ }
+ else if (strcmp (str, "in") == 0 || *str == '"')
+ {
+ if (*str++ != '"')
+ ++str;
+ v *= 25.4; /* 25.4 mm/inch */
+ }
+ else
+ {
+ fprintf (stderr,
+ "%s: option --%s: illegal unit (rest of option: %s)\n",
+ prog_name, opt->name, str);
+ return 0;
+ }
+ break;
+
+ case SANE_UNIT_DPI:
+ if (strcmp (str, "dpi") == 0)
+ str += sizeof ("dpi") - 1;
+ break;
+
+ case SANE_UNIT_PERCENT:
+ if (*str == '%')
+ ++str;
+ break;
+
+ case SANE_UNIT_MICROSECOND:
+ if (strcmp (str, "us") == 0)
+ str += sizeof ("us") - 1;
+ break;
+ }
+ *value = v + 0.5;
+ return str;
+}
+
+/* A vector has the following syntax:
+
+ [ '[' I ']' ] S { [','|'-'] [ '[' I ']' S }
+
+ The number in brackets (I), if present, determines the index of the
+ vector element to be set next. If I is not present, the value of
+ last index used plus 1 is used. The first index value used is 0
+ unless I is present.
+
+ S is a scalar value as defined by parse_scalar().
+
+ If two consecutive value specs are separated by a comma (,) their
+ values are set independently. If they are separated by a dash (-),
+ they define the endpoints of a line and all vector values between
+ the two endpoints are set according to the value of the
+ interpolated line. For example, [0]15-[255]15 defines a vector of
+ 256 elements whose value is 15. Similarly, [0]0-[255]255 defines a
+ vector of 256 elements whose value starts at 0 and increases to
+ 255. */
+static void
+parse_vector (const SANE_Option_Descriptor * opt, const char * str,
+ SANE_Word * vector, size_t vector_length)
+{
+ SANE_Word value, prev_value = 0;
+ int index = -1, prev_index = 0;
+ char * end, separator = '\0';
+
+ /* initialize vector to all zeroes: */
+ memset (vector, 0, vector_length * sizeof (SANE_Word));
+
+ do {
+ if (*str == '[')
+ {
+ /* read index */
+ index = strtol (++str, &end, 10);
+ if (str == end || *end != ']')
+ {
+ fprintf (stderr, "%s: option --%s: closing bracket missing "
+ "(rest of option: %s)\n", prog_name, opt->name, str);
+ exit (1);
+ }
+ str = end + 1;
+ }
+ else
+ ++index;
+
+ if (index < 0 || index >= vector_length)
+ {
+ fprintf (stderr, "%s: option --%s: index %d out of range [0..%ld]\n",
+ prog_name, opt->name, index, (long) vector_length - 1);
+ exit (1);
+ }
+
+ /* read value */
+ str = parse_scalar (opt, str, &value);
+ if (!str)
+ exit (1);
+
+ if (*str && *str != '-' && *str != ',')
+ {
+ fprintf (stderr,
+ "%s: option --%s: illegal separator (rest of option: %s)\n",
+ prog_name, opt->name, str);
+ exit (1);
+ }
+
+ /* store value: */
+ vector[index] = value;
+ if (separator == '-')
+ {
+ /* interpolate */
+ double v, slope;
+ int i;
+
+ v = (double) prev_value;
+ slope = ((double) value - v) / (index - prev_index);
+
+ for (i = prev_index + 1; i < index; ++i)
+ {
+ v += slope;
+ vector[i] = (SANE_Word) v;
+ }
+ }
+
+ prev_index = index;
+ prev_value = value;
+ separator = *str++;
+ } while (separator == ',' || separator == '-');
+
+ if (verbose > 2)
+ {
+ int i;
+
+ fprintf (stderr, "%s: value for --%s is: ", prog_name, opt->name);
+ for (i = 0; i < vector_length; ++i)
+ if (opt->type == SANE_TYPE_FIXED)
+ fprintf (stderr, "%g ", SANE_UNFIX(vector[i]));
+ else
+ fprintf (stderr, "%d ", vector[i]);
+ fputc ('\n', stderr);
+ }
+}
+
+void
+fetch_options (SANE_Device * device)
+{
+ const SANE_Option_Descriptor * opt;
+ SANE_Int num_dev_options;
+ int i, option_count;
+
+ /* and now build the full table of long options: */
+
+ sane_control_option (device, 0, SANE_ACTION_GET_VALUE, &num_dev_options, 0);
+
+ option_count = 0;
+ for (i = 0; i < num_dev_options; ++i)
+ {
+ opt = sane_get_option_descriptor (device, i);
+
+ if (!SANE_OPTION_IS_SETTABLE (opt->cap))
+ continue;
+
+ option_number[option_count] = i;
+
+ all_options[option_count].name = (char *) opt->name;
+ all_options[option_count].flag = 0;
+ all_options[option_count].val = 0;
+
+ if (opt->type == SANE_TYPE_BOOL)
+ all_options[option_count].has_arg = optional_argument;
+ else if (opt->type == SANE_TYPE_BUTTON)
+ all_options[option_count].has_arg = no_argument;
+ else
+ all_options[option_count].has_arg = required_argument;
+
+ /* Keep track of resolution options */
+ if (strcmp (opt->name, SANE_NAME_SCAN_RESOLUTION) == 0)
+ {
+ resolution_opt = i;
+ }
+ else if (strcmp (opt->name, SANE_NAME_SCAN_X_RESOLUTION) == 0)
+ {
+ x_resolution_opt = i;
+ }
+ if (strcmp (opt->name, SANE_NAME_SCAN_Y_RESOLUTION) == 0)
+ {
+ y_resolution_opt = i;
+ }
+
+ /* Keep track of top-left corner options (if they exist at
+ all) and replace the bottom-right corner options by a
+ width/height option (if they exist at all). */
+ if ((opt->type == SANE_TYPE_FIXED || opt->type == SANE_TYPE_INT)
+ && opt->size == sizeof (SANE_Int)
+ && (opt->unit == SANE_UNIT_MM || opt->unit == SANE_UNIT_PIXEL))
+ {
+ if (strcmp (opt->name, SANE_NAME_SCAN_TL_X) == 0)
+ {
+ window[2] = i;
+ all_options[option_count].val = 'l';
+ }
+ else if (strcmp (opt->name, SANE_NAME_SCAN_TL_Y) == 0)
+ {
+ window[3] = i;
+ all_options[option_count].val = 't';
+ }
+ else if (strcmp (opt->name, SANE_NAME_SCAN_BR_X) == 0)
+ {
+ window[0] = i;
+ all_options[option_count].name = "width";
+ all_options[option_count].val = 'x';
+ window_option[0] = *opt;
+ window_option[0].title = "Scan width";
+ window_option[0].desc = "Width of scanning area.";
+ if (!window_val_user[0])
+ sane_control_option (device, i, SANE_ACTION_GET_VALUE,
+ &window_val[0], 0);
+ }
+ else if (strcmp (opt->name, SANE_NAME_SCAN_BR_Y) == 0)
+ {
+ window[1] = i;
+ all_options[option_count].name = "height";
+ all_options[option_count].val = 'y';
+ window_option[1] = *opt;
+ window_option[1].title = "Scan height";
+ window_option[1].desc = "Height of scanning area.";
+ if (!window_val_user[1])
+ sane_control_option (device, i, SANE_ACTION_GET_VALUE,
+ &window_val[1], 0);
+ }
+ }
+ ++option_count;
+ }
+ memcpy (all_options + option_count, basic_options, sizeof (basic_options));
+ option_count += NELEMS(basic_options);
+ memset (all_options + option_count, 0, sizeof (all_options[0]));
+
+ /* Initialize width & height options based on backend default
+ values for top-left x/y and bottom-right x/y: */
+ for (i = 0; i < 2; ++i)
+ {
+ if (window[i] && window[i + 2] && !window_val_user[i])
+ {
+ SANE_Word pos;
+ sane_control_option (device, window[i + 2],
+ SANE_ACTION_GET_VALUE, &pos, 0);
+ window_val[i] = window_val[i] - pos + 1;
+ }
+ }
+}
+
+static void
+set_option (SANE_Handle device, int optnum, void * valuep)
+{
+ const SANE_Option_Descriptor * opt;
+ SANE_Status status;
+ SANE_Word orig = 0;
+ SANE_Int info;
+
+ opt = sane_get_option_descriptor (device, optnum);
+
+ if (opt->size == sizeof (SANE_Word) && opt->type != SANE_TYPE_STRING)
+ orig = *(SANE_Word *) valuep;
+
+ status = sane_control_option (device, optnum, SANE_ACTION_SET_VALUE,
+ valuep, &info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ fprintf (stderr, "%s: setting of option --%s failed (%s)\n",
+ prog_name, opt->name, sane_strstatus (status));
+ exit (1);
+ }
+
+ if ((info & SANE_INFO_INEXACT) && opt->size == sizeof (SANE_Word))
+ {
+ if (opt->type == SANE_TYPE_INT)
+ fprintf (stderr, "%s: rounded value of %s from %d to %d\n",
+ prog_name, opt->name, orig, *(SANE_Word *) valuep);
+ else if (opt->type == SANE_TYPE_FIXED)
+ fprintf (stderr, "%s: rounded value of %s from %g to %g\n",
+ prog_name, opt->name,
+ SANE_UNFIX(orig), SANE_UNFIX(*(SANE_Word *) valuep));
+ }
+
+ if (info & SANE_INFO_RELOAD_OPTIONS)
+ fetch_options (device);
+}
+
+static void
+process_backend_option (SANE_Handle device, int optnum, const char * optarg)
+{
+ static SANE_Word * vector = 0;
+ static size_t vector_size = 0;
+ const SANE_Option_Descriptor * opt;
+ size_t vector_length;
+ SANE_Status status;
+ SANE_Word value;
+ void * valuep;
+
+ opt = sane_get_option_descriptor (device, optnum);
+
+ if (!SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ fprintf (stderr, "%s: attempted to set inactive option %s\n",
+ prog_name, opt->name);
+ exit (1);
+ }
+
+ if ((opt->cap & SANE_CAP_AUTOMATIC) && strncasecmp (optarg, "auto", 4) == 0)
+ {
+ status = sane_control_option (device, optnum, SANE_ACTION_SET_AUTO,
+ 0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ fprintf (stderr, "%s: failed to set option --%s to automatic (%s)\n",
+ prog_name, opt->name, sane_strstatus (status));
+ exit (1);
+ }
+ return;
+ }
+
+ valuep = &value;
+ switch (opt->type)
+ {
+ case SANE_TYPE_BOOL:
+ value = 1; /* no argument means option is set */
+ if (optarg)
+ {
+ if (strncasecmp (optarg, "yes", strlen (optarg)) == 0)
+ value = 1;
+ else if (strncasecmp (optarg, "no", strlen (optarg)) == 0)
+ value = 0;
+ else
+ {
+ fprintf (stderr, "%s: option --%s: bad option value `%s'\n",
+ prog_name, opt->name, optarg);
+ exit (1);
+ }
+ }
+ break;
+
+ case SANE_TYPE_INT:
+ case SANE_TYPE_FIXED:
+ /* ensure vector is long enough: */
+ vector_length = opt->size / sizeof (SANE_Word);
+ if (vector_size < vector_length)
+ {
+ vector_size = vector_length;
+ vector = realloc (vector, vector_length * sizeof (SANE_Word));
+ if (!vector)
+ {
+ fprintf (stderr, "%s: out of memory\n", prog_name);
+ exit (1);
+ }
+ }
+ parse_vector (opt, optarg, vector, vector_length);
+ valuep = vector;
+ break;
+
+ case SANE_TYPE_STRING:
+ valuep = (void *) optarg;
+ break;
+
+ case SANE_TYPE_BUTTON:
+ value = 0; /* value doesn't matter */
+ break;
+
+ default:
+ fprintf (stderr, "%s: duh, got unknown option type %d\n",
+ prog_name, opt->type);
+ return;
+ }
+ set_option (device, optnum, valuep);
+}
+
+static void
+write_pnm_header_to_file (FILE *fp, SANE_Frame format, int width, int height, int depth)
+{
+ switch (format)
+ {
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ case SANE_FRAME_RGB:
+ fprintf (fp, "P6\n# SANE data follows\n%d %d\n255\n", width, height);
+ break;
+
+ case SANE_FRAME_GRAY:
+ if (depth == 1)
+ fprintf (fp, "P4\n# SANE data follows\n%d %d\n", width, height);
+ else
+ fprintf (fp, "P5\n# SANE data follows\n%d %d\n255\n", width, height);
+ break;
+ default:
+ /* default action for unknown frametypes; don't write a header */
+ break;
+ }
+#ifdef __EMX__ /* OS2 - write in binary mode. */
+ _fsetmode(fp, "b");
+#endif
+}
+
+static void *
+advance (Image *image)
+{
+ if (++image->x >= image->width)
+ {
+ image->x = 0;
+ if (++image->y >= image->height || !image->data)
+ {
+ size_t old_size = 0, new_size;
+
+ if (image->data)
+ old_size = image->height * image->width * image->Bpp;
+
+ image->height += STRIP_HEIGHT;
+ new_size = image->height * image->width * image->Bpp;
+
+ if (image->data)
+ image->data = realloc (image->data, new_size);
+ else
+ image->data = malloc (new_size);
+ if (image->data)
+ memset (image->data + old_size, 0, new_size - old_size);
+ }
+ }
+ if (!image->data)
+ fprintf (stderr, "%s: can't allocate image buffer (%dx%d)\n",
+ prog_name, image->width, image->height);
+ return image->data;
+}
+
+static SANE_Int
+get_resolution(SANE_Device *device)
+{
+ SANE_Int res = 200;
+ SANE_Word val;
+ const SANE_Option_Descriptor *opt;
+
+ if (resolution_opt >= 0)
+ {
+ opt = sane_get_option_descriptor (device, resolution_opt);
+
+ sane_control_option (device, resolution_opt,
+ SANE_ACTION_GET_VALUE, &val, 0);
+ switch (opt->type)
+ {
+ case SANE_TYPE_INT:
+ res = val;
+ break;
+
+ case SANE_TYPE_FIXED:
+ res = (SANE_Int) SANE_UNFIX(val);
+ break;
+
+ case SANE_TYPE_STRING:
+ case SANE_TYPE_BOOL:
+ default:
+ if (verbose)
+ fprintf(stderr,
+ "Peculiar option data type for resolution, "
+ "using default value.\n");
+ break;
+ }
+ }
+ else
+ {
+ if (verbose)
+ fprintf(stderr, "No resolution option found, using default value.\n");
+ }
+
+ return res;
+}
+
+static SANE_Status
+scan_it_raw (const char *fname, SANE_Bool raw, const char *script)
+{
+ int i, len, first_frame = 1, offset = 0, must_buffer = 0;
+ SANE_Byte buffer[32*1024], min = 0xff, max = 0;
+ SANE_Parameters parm;
+ SANE_Status status;
+ Image image = {0, };
+ FILE *fp = NULL;
+
+ do
+ {
+ status = sane_start (device);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (status != SANE_STATUS_NO_DOCS)
+ {
+ fprintf (stderr, "%s: sane_start: %s\n",
+ prog_name, sane_strstatus (status));
+ }
+ goto cleanup;
+ }
+
+ status = sane_get_parameters (device, &parm);
+ if (status != SANE_STATUS_GOOD)
+ {
+ fprintf (stderr, "%s: sane_get_parameters: %s\n",
+ prog_name, sane_strstatus (status));
+ goto cleanup;
+ }
+
+ fp = fopen(fname, "wb");
+ if (!fp) {
+ fprintf(stderr, "Error opening output `%s': %s (%d)\n",
+ fname, strerror(errno), errno);
+ status = SANE_STATUS_IO_ERROR;
+ goto cleanup;
+ }
+
+ if (verbose)
+ {
+ if (first_frame)
+ {
+ if (sane_isbasicframe(parm.format))
+ {
+ if (parm.lines >= 0)
+ fprintf (stderr, "%s: scanning image of size %dx%d pixels"
+ " at %d bits/pixel\n",
+ prog_name, parm.pixels_per_line, parm.lines,
+ 8 * parm.bytes_per_line / parm.pixels_per_line);
+ else
+ fprintf (stderr, "%s: scanning image %d pixels wide and "
+ "variable height at %d bits/pixel\n",
+ prog_name, parm.pixels_per_line,
+ 8 * parm.bytes_per_line / parm.pixels_per_line);
+ }
+ else
+ {
+ fprintf (stderr, "%s: receiving %s frame "
+ "bytes/line=%d, "
+ "pixels/line=%d, "
+ "lines=%d, "
+ "depth=%d\n",
+ prog_name, sane_strframe(parm.format),
+ parm.bytes_per_line,
+ parm.pixels_per_line,
+ parm.lines,
+ parm.depth);
+ }
+ }
+ fprintf (stderr, "%s: acquiring %s frame\n", prog_name,
+ sane_strframe(parm.format));
+ }
+
+ if (first_frame)
+ {
+ switch (parm.format)
+ {
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ assert (parm.depth == 8);
+ must_buffer = 1;
+ offset = parm.format - SANE_FRAME_RED;
+ break;
+
+ case SANE_FRAME_RGB:
+ assert (parm.depth == 8);
+ case SANE_FRAME_GRAY:
+ assert (parm.depth == 1 || parm.depth == 8);
+ /* if we're writing raw, we skip the header and never
+ * have to buffer a single frame format.
+ */
+ if (raw == SANE_FALSE)
+ {
+ if (parm.lines < 0)
+ {
+ must_buffer = 1;
+ offset = 0;
+ }
+ else
+ {
+ write_pnm_header_to_file (fp, parm.format,
+ parm.pixels_per_line,
+ parm.lines, parm.depth);
+ }
+ }
+ break;
+
+ case SANE_FRAME_TEXT:
+ case SANE_FRAME_JPEG:
+ case SANE_FRAME_G31D:
+ case SANE_FRAME_G32D:
+ case SANE_FRAME_G42D:
+ if (!parm.last_frame)
+ {
+ status = SANE_STATUS_INVAL;
+ fprintf (stderr, "%s: bad %s frame: must be last_frame\n",
+ prog_name, sane_strframe (parm.format));
+ goto cleanup;
+ }
+ /* write them out without a header; don't buffer */
+ break;
+
+ default:
+ /* Default action for unknown frametypes; write them out
+ * without a header; issue a warning in verbose mode.
+ * Since we're not writing a header, there's no need to
+ * buffer.
+ */
+ if (verbose)
+ {
+ fprintf(stderr, "scanadf: unknown frame format %d\n",
+ (int) parm.format);
+ }
+ if (!parm.last_frame)
+ {
+ status = SANE_STATUS_INVAL;
+ fprintf (stderr, "%s: bad %s frame: must be last_frame\n",
+ prog_name, sane_strframe (parm.format));
+ goto cleanup;
+ }
+ break;
+ }
+
+ if (must_buffer)
+ {
+ /* We're either scanning a multi-frame image or the
+ scanner doesn't know what the eventual image height
+ will be (common for hand-held scanners). In either
+ case, we need to buffer all data before we can write
+ the image. */
+ image.width = parm.pixels_per_line;
+ if (parm.lines >= 0)
+ /* See advance(); we allocate one extra line so we
+ don't end up realloc'ing in when the image has been
+ filled in. */
+ image.height = parm.lines - STRIP_HEIGHT + 1;
+ else
+ image.height = 0;
+ image.Bpp = 3;
+ if (parm.format == SANE_FRAME_GRAY ||
+ !sane_isbasicframe(parm.format))
+ image.Bpp = 1;
+ image.x = image.width - 1;
+ image.y = -1;
+ if (!advance (&image))
+ goto cleanup;
+ }
+ }
+ else
+ {
+ assert (parm.format >= SANE_FRAME_RED
+ && parm.format <= SANE_FRAME_BLUE);
+ offset = parm.format - SANE_FRAME_RED;
+ image.x = image.y = 0;
+ }
+
+ while (1)
+ {
+ status = sane_read (device, buffer, sizeof (buffer), &len);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (verbose && parm.depth == 8)
+ fprintf (stderr, "%s: min/max graylevel value = %d/%d\n",
+ prog_name, min, max);
+ if (status != SANE_STATUS_EOF)
+ {
+ fprintf (stderr, "%s: sane_read: %s\n",
+ prog_name, sane_strstatus (status));
+ goto cleanup;
+ }
+ break;
+ }
+ if (must_buffer)
+ {
+ switch (parm.format)
+ {
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ for (i = 0; i < len; ++i)
+ {
+ image.data[offset + 3*i] = buffer[i];
+ if (!advance (&image))
+ goto cleanup;
+ }
+ offset += 3*len;
+ break;
+
+ case SANE_FRAME_RGB:
+ for (i = 0; i < len; ++i)
+ {
+ image.data[offset + i] = buffer[i];
+ if ((offset + i) % 3 == 0 && !advance (&image))
+ goto cleanup;
+ }
+ offset += len;
+ break;
+
+ case SANE_FRAME_GRAY:
+ for (i = 0; i < len; ++i)
+ {
+ image.data[offset + i] = buffer[i];
+ if (!advance (&image))
+ goto cleanup;
+ }
+ offset += len;
+ break;
+ default:
+ /* optional frametypes are never buffered */
+ fprintf(stderr, "%s: ERROR: trying to buffer %s"
+ " frametype\n",
+ prog_name,
+ sane_strframe(parm.format));
+ break;
+ }
+ }
+ else
+ fwrite (buffer, 1, len, fp);
+
+ if (verbose && parm.depth == 8)
+ {
+ for (i = 0; i < len; ++i)
+ if (buffer[i] >= max)
+ max = buffer[i];
+ else if (buffer[i] < min)
+ min = buffer[i];
+ }
+ }
+ first_frame = 0;
+ }
+ while (!parm.last_frame);
+
+ if (must_buffer)
+ {
+ image.height = image.y;
+ if (raw == SANE_FALSE)
+ {
+ /* if we're writing raw, we skip the header */
+ write_pnm_header_to_file (fp, parm.format, image.width,
+ image.height, parm.depth);
+ }
+ fwrite (image.data, image.Bpp, image.height * image.width, fp);
+ }
+
+ if (fp)
+ {
+ fclose(fp);
+ fp = NULL;
+ }
+
+ if (script)
+ {
+ static char cmd[PATH_MAX * 2];
+ static char env[6][PATH_MAX * 2];
+ int pid;
+ SANE_Int res;
+ SANE_Frame format;
+ extern char **environ;
+
+ res = get_resolution(device);
+
+ format = parm.format;
+ if (format == SANE_FRAME_RED ||
+ format == SANE_FRAME_GREEN ||
+ format == SANE_FRAME_BLUE)
+ {
+ /* the resultant format is RGB */
+ format = SANE_FRAME_RGB;
+ }
+ sprintf(env[0], "SCAN_RES=%d", res);
+ if (putenv(env[0]))
+ fprintf(stderr, "putenv:failed\n");
+ sprintf(env[1], "SCAN_WIDTH=%d", parm.pixels_per_line);
+ if (putenv(env[1]))
+ fprintf(stderr, "putenv:failed\n");
+ sprintf(env[2], "SCAN_HEIGHT=%d", parm.lines);
+ if (putenv(env[2]))
+ fprintf(stderr, "putenv:failed\n");
+ sprintf(env[3], "SCAN_FORMAT_ID=%d", (int) parm.format);
+ if (putenv(env[3]))
+ fprintf(stderr, "putenv:failed\n");
+ sprintf(env[4], "SCAN_FORMAT=%s",
+ sane_strframe(parm.format));
+ if (putenv(env[4]))
+ fprintf(stderr, "putenv:failed\n");
+ sprintf(env[5], "SCAN_DEPTH=%d", parm.depth);
+ if (putenv(env[5]))
+ fprintf(stderr, "putenv:failed\n");
+ signal(SIGCHLD, SIG_IGN);
+ switch ((pid = fork()))
+ {
+ case -1:
+ /* fork failed */
+ fprintf(stderr, "Error forking: %s (%d)\n", strerror(errno), errno);
+ break;
+
+ case 0:
+ /* in child process */
+ sprintf(cmd, "%s '%s'", script, fname);
+ /* system(cmd); */
+ execle(script, script, fname, NULL, environ);
+ exit(0);
+
+ default:
+ if (verbose)
+ fprintf(stderr, "Started script `%s' as pid=%d\n", script, pid);
+ break;
+ }
+ }
+
+cleanup:
+ if (image.data)
+ free (image.data);
+ if (fp) fclose(fp);
+
+ return status;
+}
+
+static SANE_Int
+scan_docs (int start, int end, int no_overwrite, SANE_Bool raw, const char *outfmt, const char *script)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int scannedPages = 0;
+ SANE_Char fname[PATH_MAX];
+ struct stat statbuf;
+ int res;
+
+ while (end < 0 || start <= end)
+ {
+ /*!!! buffer overflow; need protection */
+ sprintf(fname, outfmt, start);
+
+ /* does the filename already exist? */
+ if (no_overwrite)
+ {
+ res = stat (fname, &statbuf);
+ if (res == 0)
+ {
+ status = SANE_STATUS_INVAL;
+ fprintf (stderr, "Filename %s already exists; will not overwrite\n", fname);
+ }
+ }
+
+ /* Scan the document */
+ if (status == SANE_STATUS_GOOD)
+ status = scan_it_raw(fname, raw, script);
+
+ /* Any scan errors? */
+ if (status == SANE_STATUS_NO_DOCS)
+ {
+ /* out of paper in the hopper; this is our normal exit */
+ status = SANE_STATUS_GOOD;
+ break;
+ }
+ else if (status == SANE_STATUS_EOF)
+ {
+ /* done with this doc */
+ status = SANE_STATUS_GOOD;
+ fprintf(stderr, "Scanned document %s\n", fname);
+ scannedPages++;
+ start++;
+ }
+ else
+ {
+ /* unexpected error */
+ fprintf(stderr, "%s\n", sane_strstatus(status));
+ break;
+ }
+ }
+
+ fprintf(stderr, "Scanned %d pages\n", scannedPages);
+
+ return status;
+}
+
+int
+main (int argc, char **argv)
+{
+ int ch, i, index, all_options_len;
+ const SANE_Option_Descriptor * opt;
+ const SANE_Device ** device_list;
+ SANE_Int num_dev_options = 0;
+ const char * devname = 0;
+ SANE_Status status;
+ char *full_optstring;
+ SANE_Bool raw = SANE_FALSE;
+ const char *scanScript = NULL; /* script to run at end of scan */
+ int scriptWait = 0;
+ const char *outputFile = "image-%04d"; /* file name(format) to write output to */
+ int startNum = 1, endNum = -1; /* start/end numbers of pages to scan */
+ int no_overwrite = 0;
+
+ atexit (sane_exit);
+
+ prog_name = strrchr (argv[0], '/');
+ if (prog_name)
+ ++prog_name;
+ else
+ prog_name = argv[0];
+
+ sane_init (0, 0);
+
+ /* make a first pass through the options with error printing and argument
+ permutation disabled: */
+ opterr = 0;
+ while ((ch = getopt_long (argc, argv, "-" BASE_OPTSTRING, basic_options,
+ &index))
+ != EOF)
+ {
+ switch (ch)
+ {
+ case ':':
+ case '?':
+ break; /* may be an option that we'll parse later on */
+
+ case 'd': devname = optarg; break;
+ case 'h': help = 1; break;
+ case 'N': no_overwrite = 1; break;
+ case 'v': ++verbose; break;
+ case 'L':
+ {
+ int i;
+
+ status = sane_get_devices (&device_list, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ fprintf (stderr, "%s: sane_get_devices() failed: %s\n",
+ prog_name, sane_strstatus (status));
+ exit (1);
+ }
+
+ for (i = 0; device_list[i]; ++i)
+ {
+ printf ("device `%s' is a %s %s %s\n",
+ device_list[i]->name, device_list[i]->vendor,
+ device_list[i]->model, device_list[i]->type);
+ }
+ exit (0);
+ }
+
+ case 'o': outputFile = optarg; break;
+ case 'S': scanScript = optarg; break;
+ case 128: scriptWait = 1; break;
+ case 's': startNum = atoi(optarg); break;
+ case 'e': endNum = atoi(optarg); break;
+ case 'r': raw = SANE_TRUE; break;
+
+ case 'V':
+ printf ("scanadf (%s) %s\n", PACKAGE, VERSION);
+ exit (0);
+
+ default:
+ break; /* ignore device specific options for now */
+ }
+ }
+
+ if (scriptWait && !scanScript)
+ scriptWait = 0;
+
+ if (help)
+ printf (usage, prog_name);
+
+ if (!devname)
+ {
+ /* If no device name was specified explicitly, we open the first
+ device we find (if any): */
+
+ status = sane_get_devices (&device_list, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ fprintf (stderr, "%s: sane_get_devices() failed: %s\n",
+ prog_name, sane_strstatus (status));
+ exit (1);
+ }
+ if (!device_list[0])
+ {
+ fprintf (stderr, "%s: no SANE devices found\n", prog_name);
+ exit (1);
+ }
+ devname = device_list[0]->name;
+ }
+
+ status = sane_open (devname, &device);
+ if (status != SANE_STATUS_GOOD)
+ {
+ fprintf (stderr, "%s: open of device %s failed: %s\n",
+ prog_name, devname, sane_strstatus (status));
+ if (help)
+ device = 0;
+ else
+ exit (1);
+ }
+
+ if (device)
+ {
+ /* We got a device, find out how many options it has: */
+ status = sane_control_option (device, 0, SANE_ACTION_GET_VALUE,
+ &num_dev_options, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ fprintf (stderr, "%s: unable to determine option count\n",
+ prog_name);
+ exit (1);
+ }
+
+ all_options_len = num_dev_options + NELEMS(basic_options) + 1;
+ all_options = malloc (all_options_len * sizeof (all_options[0]));
+ option_number_len = num_dev_options;
+ option_number = malloc (option_number_len * sizeof (option_number[0]));
+ if (!all_options || !option_number)
+ {
+ fprintf (stderr, "%s: out of memory in fetch_options()\n",
+ prog_name);
+ exit (1);
+ }
+
+ fetch_options (device);
+
+ {
+ char *larg, *targ, *xarg, *yarg;
+ larg = targ = xarg = yarg = "";
+
+ /* Maybe accept t, l, x, and y options. */
+ if (window[0])
+ xarg = "x:";
+
+ if (window[1])
+ yarg = "y:";
+
+ if (window[2])
+ larg = "l:";
+
+ if (window[3])
+ targ = "t:";
+
+ /* Now allocate the full option list. */
+ full_optstring = malloc (strlen (BASE_OPTSTRING)
+ + strlen (larg) + strlen (targ)
+ + strlen (xarg) + strlen (yarg) + 1);
+
+ if (!full_optstring)
+ {
+ fprintf (stderr, "%s: out of memory\n", prog_name);
+ exit (1);
+ }
+
+ strcpy (full_optstring, BASE_OPTSTRING);
+ strcat (full_optstring, larg);
+ strcat (full_optstring, targ);
+ strcat (full_optstring, xarg);
+ strcat (full_optstring, yarg);
+ }
+
+ optind = 0;
+ opterr = 1; /* re-enable error printing and arg permutation */
+ while ((ch = getopt_long (argc, argv, full_optstring, all_options,
+ &index))
+ != EOF)
+ {
+ switch (ch)
+ {
+ case ':':
+ case '?':
+ exit (1); /* error message is printed by getopt_long() */
+
+ case 'd': case 'h': case 'v': case 'V': case 'T':
+ case 'o': case 'S': case 's': case 'e': case 'r':
+
+ /* previously handled options */
+ break;
+
+ case 'x':
+ window_val_user[0] = 1;
+ parse_vector (&window_option[0], optarg, &window_val[0], 1);
+ break;
+
+ case 'y':
+ window_val_user[1] = 1;
+ parse_vector (&window_option[1], optarg, &window_val[1], 1);
+ break;
+
+ case 'l': /* tl-x */
+ process_backend_option (device, window[2], optarg);
+ break;
+
+ case 't': /* tl-y */
+ process_backend_option (device, window[3], optarg);
+ break;
+
+ case 0:
+ process_backend_option (device, option_number[index], optarg);
+ break;
+ }
+ }
+
+ free (full_optstring);
+
+ for (index = 0; index < 2; ++index)
+ if (window[index])
+ {
+ SANE_Word val, pos;
+
+ if (window[index + 2])
+ sane_control_option (device, window[index + 2],
+ SANE_ACTION_GET_VALUE, &pos, 0);
+ val = pos + window_val[index] - 1;
+ set_option (device, window[index], &val);
+ }
+ if (help)
+ {
+ printf ("\nOptions specific to device `%s':\n", devname);
+
+ for (i = 0; i < num_dev_options; ++i)
+ {
+ char short_name = '\0';
+ int j;
+
+ opt = 0;
+ for (j = 0; j < 4; ++j)
+ if (i == window[j])
+ {
+ short_name = "xylt"[j];
+ if (j < 2)
+ opt = window_option + j;
+ }
+ if (!opt)
+ opt = sane_get_option_descriptor (device, i);
+
+ if (opt->type == SANE_TYPE_GROUP)
+ printf (" %s:\n", opt->title);
+
+ if (!SANE_OPTION_IS_SETTABLE (opt->cap))
+ continue;
+
+ print_option (device, i, short_name);
+ }
+ if (num_dev_options)
+ fputc ('\n', stdout);
+ }
+ }
+
+ if (help)
+ {
+ printf ("\
+Type ``%s --help -d DEVICE'' to get list of all options for DEVICE.\n\
+\n\
+List of available devices:", prog_name);
+ if (device)
+ sane_close(device);
+
+ status = sane_get_devices (&device_list, SANE_FALSE);
+ if (status == SANE_STATUS_GOOD)
+ {
+ int column = 80;
+
+ for (i = 0; device_list[i]; ++i)
+ {
+ if (column + strlen (device_list[i]->name) + 1 >= 80)
+ {
+ printf ("\n ");
+ column = 4;
+ }
+ if (column > 4)
+ {
+ fputc (' ', stdout);
+ column += 1;
+ }
+ fputs (device_list[i]->name, stdout);
+ column += strlen (device_list[i]->name);
+ }
+ }
+ fputc ('\n', stdout);
+ exit (0);
+ }
+
+ signal (SIGHUP, sighandler);
+ signal (SIGINT, sighandler);
+ signal (SIGPIPE, sighandler);
+ signal (SIGTERM, sighandler);
+
+ status = scan_docs (startNum, endNum, no_overwrite, raw, outputFile, scanScript);
+
+ sane_cancel (device);
+ sane_close (device);
+
+ if (scriptWait)
+ while (wait(NULL) != -1);
+
+ return (status == SANE_STATUS_GOOD) ? 0 : 1;
+}
+
diff --git a/src/xcam.c b/src/xcam.c
new file mode 100644
index 0000000..2d494a5
--- /dev/null
+++ b/src/xcam.c
@@ -0,0 +1,1871 @@
+/* xcam -- X-based camera frontend
+ Uses the SANE library.
+ Copyright (C) 1997 David Mosberger and Tristan Tarrant
+
+ Update 2005 Gerard Klaver
+ The add_text routine and font_6x11.h file are taken from the (GPLed)
+ webcam.c file, part of xawtv, (c) 1998-2002 Gerd Knorr.
+ add_text was modified for this program (xcam_add_text).
+
+ 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. */
+
+#include "../include/sane/config.h"
+
+#include "../include/lalloca.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+/* for xcam_add-text routine */
+#include "font_6x11.h"
+/*-----------------------*/
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "gtkglue.h"
+#include "preferences.h"
+
+#include <sane/sane.h>
+#include <sane/saneopts.h>
+#include "../include/sane/sanei.h"
+
+#define BACKEND_NAME xcam
+#include "../include/sane/sanei_debug.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+
+#define OUTFILENAME "out.pnm"
+
+#define MAX_LUM 64 /* how many graylevels for 8 bit displays */
+
+#ifndef HAVE_ATEXIT
+# define atexit(func) on_exit(func, 0) /* works for SunOS, at least */
+#endif
+
+typedef struct Canvas
+{
+ GtkWidget *preview;
+ GdkGC *gc;
+ GdkImage *gdk_image;
+ GdkColormap *graylevel_cmap; /* for 8 bit displays */
+ guint32 graylevel[MAX_LUM]; /* graylevel pixels */
+ GdkColormap *cube_cmap;
+ GdkColor cube_colors[5 * 6 * 5];
+}
+Canvas;
+
+static const char *prog_name;
+static const SANE_Device **device;
+static GSGDialog *dialog;
+static char device_settings_filename[1024] = "device.rc";
+
+#define DBG_fatal 0
+#define DBG_error 1
+#define DBG_warning 2
+#define DBG_info 3
+#define DBG_debug 4
+
+static struct
+{
+ GtkWidget *shell;
+ GtkWidget *dialog_box;
+ GtkWidget *play_stop_label;
+ GtkWidget *info_label;
+ GtkWidget *device_info_label;
+ GtkWidget *save_frame_label;
+ GtkWidget *rgb_bgr_label;
+ GtkWidget *txt_label;
+ struct
+ {
+ GtkWidget *item; /* the menu bar item */
+ GtkWidget *menu; /* the associated menu */
+ }
+ devices;
+ Canvas canvas;
+ gint gdk_input_tag; /* tag returned by gdk_input_add () */
+ int playing; /* are we playing video? */
+ int saving; /* are we saving to file */
+
+ SANE_Byte *buf;
+ size_t buf_backend_size;
+ size_t remaining;
+ SANE_Parameters params;
+ gpointer data; /* image data */
+ int x, y; /* x and y position */
+ int input_tag;
+/* for standalone mode: */
+ GtkWidget *filename_entry;
+ FILE *out;
+ long header_size;
+ gboolean have_odd_byte;
+ guint8 odd_byte;
+
+ int num_bytes;
+ int bytes_read;
+ char picmsg_ps[50];
+ int value_rgb;
+ int value_txt;
+
+ double fps;
+ double fps_av;
+ double fps_old1;
+ double fps_old2;
+ double fps_old3;
+ long f_count;
+ time_t time1;
+ time_t time2;
+ int i_time;
+}
+win;
+
+/* forward declarations: */
+int main (int argc, char **argv);
+
+static void rescan_devices (GtkWidget * widget, gpointer client_data,
+ gpointer call_data);
+static void next_frame (void);
+
+static void save_frame (void);
+
+static void update_param (GSGDialog * dialog, void *arg);
+
+static void load_defaults (int silent);
+
+static struct option long_options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {"buffersize", no_argument, NULL, 'B'},
+ {0, 0, 0, 0}
+};
+
+/* 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;
+} */
+
+#define CANVAS_EVENT_MASK GDK_BUTTON1_MOTION_MASK | \
+ GDK_EXPOSURE_MASK | \
+ GDK_BUTTON_PRESS_MASK | \
+ GDK_ENTER_NOTIFY_MASK
+
+static void
+display_image (Canvas * canvas)
+{
+ if (canvas->gdk_image)
+ {
+ gdk_draw_image (canvas->preview->window, canvas->gc, canvas->gdk_image,
+ 0, 0, 0, 0,
+ canvas->gdk_image->width, canvas->gdk_image->height);
+ gdk_flush ();
+ }
+}
+
+static gint
+canvas_events (GtkWidget * widget, GdkEvent * event)
+{
+ Canvas *canvas = &win.canvas;
+ if (!canvas)
+ return FALSE;
+
+ switch (event->type)
+ {
+ case GDK_EXPOSE:
+ if (!canvas->gc)
+ canvas->gc = gdk_gc_new (canvas->preview->window);
+ display_image (canvas);
+ break;
+
+ case GDK_BUTTON_PRESS:
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ break;
+
+ case GDK_MOTION_NOTIFY:
+ break;
+
+ case GDK_ENTER_NOTIFY:
+#if 0
+ gdk_colors_store (win.canvas.cube_cmap, win.canvas.cube_colors,
+ NELEMS (win.canvas.cube_colors));
+#endif
+ break;
+
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+static void
+stop_camera (void)
+{
+ DBG (DBG_debug, "xcam: stop_camera: enter\n");
+
+ if (dialog)
+ sane_cancel (gsg_dialog_get_device (dialog));
+ if (win.gdk_input_tag >= 0)
+ {
+ gdk_input_remove (win.gdk_input_tag);
+ }
+ else
+ win.playing = FALSE;
+ win.gdk_input_tag = -1;
+ if (!win.playing)
+ gtk_label_set (GTK_LABEL (win.play_stop_label), " Play ");
+ else
+ gtk_label_set (GTK_LABEL (win.play_stop_label), " Stop ");
+
+ DBG (DBG_debug, "xcam: stop_camera: exit\n");
+}
+
+static void
+switch_device (const SANE_Device * dev)
+{
+ char buf[512];
+
+ DBG (DBG_debug, "xcam: switch_device: enter\n");
+ if (win.playing)
+ {
+ win.playing = FALSE;
+ stop_camera ();
+ }
+
+ if (dialog)
+ gsg_destroy_dialog (dialog);
+
+ dialog = gsg_create_dialog (GTK_WIDGET (win.dialog_box), dev->name,
+ 0, 0, 0, 0);
+ buf[0] = '\0';
+
+ if (dialog)
+ sprintf (buf, "%s %s %s", dev->vendor, dev->model, dev->type);
+ gtk_label_set (GTK_LABEL (win.device_info_label), buf);
+ DBG (DBG_debug, "xcam: switch_device: exit\n");
+}
+
+static void
+switch_device_by_name (const char *device_name)
+{
+ SANE_Device dev_info;
+ int i;
+
+ DBG (DBG_debug, "xcam: switch_device_by_name: enter\n");
+ for (i = 0; device[i]; ++i)
+ if (strcmp (device[i]->name, device_name) == 0)
+ {
+ switch_device (device[i]);
+ return;
+ }
+
+ /* the backends don't know about this device yet---make up an entry: */
+ dev_info.name = device_name;
+ dev_info.vendor = "Unknown";
+ dev_info.model = "";
+ dev_info.type = "";
+ switch_device (&dev_info);
+ DBG (DBG_debug, "xcam: switch_device_by_name: exit\n");
+}
+
+static void
+save_settings (const char *filename)
+{
+ int fd;
+
+ DBG (DBG_debug, "xcam: save_settings: enter\n");
+
+ 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;
+ }
+ write (fd, dialog->dev_name, strlen (dialog->dev_name));
+ write (fd, "\n", 1);
+ sanei_save_values (fd, dialog->dev);
+ close (fd);
+ DBG (DBG_debug, "xcam: save_settings: exit\n");
+}
+
+#define MSG_MAXLEN 45
+#define CHAR_HEIGHT 11
+#define CHAR_WIDTH 6
+#define CHAR_START 4
+
+static SANE_Status
+xcam_add_text (SANE_Byte * image, int width, int height, char *txt)
+{
+ SANE_Status status;
+ time_t t;
+ struct tm *tm;
+ char line[MSG_MAXLEN + 1];
+ SANE_Byte *ptr;
+ int i, x, y, f, len;
+ char fmtstring[25] = " %Y-%m-%d %H:%M:%S";
+ char fmttxt[46];
+
+ DBG (DBG_debug, "xcam_add_text: enter\n");
+ time (&t);
+ tm = localtime (&t);
+ if (strlen (txt) > (MSG_MAXLEN - 23))
+ strncpy (fmttxt, txt, (MSG_MAXLEN - 23));
+ else
+ strcpy (fmttxt, txt);
+ strcat (fmttxt, fmtstring);
+
+ len = strftime (line, MSG_MAXLEN, fmttxt, tm);
+
+ for (y = 0; y < CHAR_HEIGHT; y++)
+ {
+ ptr = image + 3 * width * (height - CHAR_HEIGHT - 2 + y) + 12;
+
+ for (x = 0; x < len; x++)
+ {
+ f = fontdata[line[x] * CHAR_HEIGHT + y];
+ for (i = CHAR_WIDTH - 1; i >= 0; i--)
+ {
+ if (f & (CHAR_START << i))
+ {
+ ptr[0] = 255;
+ ptr[1] = 255;
+ ptr[2] = 255;
+ }
+ ptr += 3;
+ } /* for i */
+ } /* for x */
+ } /* for y */
+
+ DBG (DBG_debug, "xcam_add_text: exit vw=%d, vh=%d\n", width, height);
+ status = (SANE_STATUS_GOOD);
+ return status;
+
+}
+
+
+/* Update the info line with the latest size information. */
+static void
+update_param (GSGDialog * dialog, void *arg)
+{
+ gchar buf[200];
+
+ DBG (DBG_debug, "xcam: update_param: enter\n");
+
+ if (dialog == NULL)
+ return;
+
+ if (!win.info_label)
+ return;
+
+ if (sane_get_parameters (gsg_dialog_get_device (dialog), &win.params)
+ == SANE_STATUS_GOOD)
+ {
+ double size =
+ (double) win.params.bytes_per_line * (double) win.params.lines;
+ const char *unit = "B";
+
+ if (win.params.lines == -1)
+ {
+ snprintf (buf, sizeof (buf), "%dxunknown: unknown size",
+ win.params.pixels_per_line);
+ }
+ else
+ {
+ if (win.params.format >= SANE_FRAME_RED
+ && win.params.format <= SANE_FRAME_BLUE)
+ size *= 3;
+
+ if (size >= 1024 * 1024)
+ {
+ size /= 1024 * 1024;
+ unit = "MByte";
+ }
+ else if (size >= 1024)
+ {
+ size /= 1024;
+ unit = "kByte";
+ }
+ snprintf (buf, sizeof (buf),
+ "%dx%d %1.1f %s \n%6ld f_count\n%2.2f fps %2.2f fps_av",
+ win.params.pixels_per_line, win.params.lines, size, unit,
+ win.f_count, win.fps, win.fps_av);
+ }
+ }
+ else
+ snprintf (buf, sizeof (buf), "Invalid parameters.");
+ gtk_label_set (GTK_LABEL (win.info_label), buf);
+
+ DBG (DBG_debug, "xcam: update_param: exit\n");
+}
+
+static void
+pref_xcam_save (void)
+{
+ char filename[PATH_MAX];
+ int fd;
+
+ DBG (DBG_debug, "xcam: pref_xcam_save: enter\n");
+ /* first save xcam-specific preferences: */
+ gsg_make_path (sizeof (filename), filename, "xcam", "xcam", 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, "xcam: pref_xcam_save: exit\n");
+}
+
+static void
+pref_xcam_restore (void)
+{
+ char filename[PATH_MAX];
+ int fd;
+
+ DBG (DBG_debug, "xcam: pref_xcam_restore: enter\n");
+ gsg_make_path (sizeof (filename), filename, "xcam", "xcam", 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, "xcam: pref_xcam_restore: exit\n");
+}
+
+
+static void
+load_settings (const char *filename, int silent)
+{
+ char buf[2 * PATH_MAX];
+ char *end;
+ int fd;
+
+ DBG (DBG_debug, "xcam: load_settings: enter\n");
+
+ fd = open (filename, O_RDONLY);
+ if (fd < 0)
+ {
+ if (!silent)
+ {
+ snprintf (buf, sizeof (buf), "Failed to open file %s: %s.",
+ filename, strerror (errno));
+ gsg_error (buf);
+ }
+ return; /* fail silently */
+ }
+
+ /* first, read off the devicename that these settings are for: */
+ read (fd, buf, sizeof (buf));
+ buf[sizeof (buf) - 1] = '\0';
+ end = strchr (buf, '\n');
+ if (!end)
+ {
+ if (!silent)
+ {
+ snprintf (buf, sizeof (buf), "File %s is malformed.", filename);
+ gsg_error (buf);
+ }
+ return;
+ }
+ *end = '\0';
+ if (strcmp (dialog->dev_name, buf) != 0)
+ switch_device_by_name (buf);
+
+ /* position right behind device name: */
+ lseek (fd, strlen (buf) + 1, SEEK_SET);
+
+ sanei_load_values (fd, dialog->dev);
+ close (fd);
+
+ gsg_refresh_dialog (dialog);
+
+ DBG (DBG_debug, "xcam: load_settings: exit\n");
+}
+
+static int
+make_default_filename (size_t buf_size, char *buf, const char *dev_name)
+{
+ return gsg_make_path (buf_size, buf, "xcam", 0, dev_name, ".rc");
+}
+
+static void
+load_defaults (int silent)
+{
+ char filename[PATH_MAX];
+ int fd;
+
+ DBG (DBG_debug, "xcam, load_defaults: enter\n");
+ if (make_default_filename (sizeof (filename), filename, dialog->dev_name)
+ < 0)
+ return;
+ if (fd < 0)
+ return;
+ load_settings (filename, silent);
+ sanei_load_values (fd, dialog->dev);
+ DBG (DBG_debug, "xcam, load_defaults: exit\n");
+}
+
+void
+device_name_dialog_cancel (GtkWidget * widget, gpointer data)
+{
+ gtk_widget_destroy (data);
+}
+
+void
+device_name_dialog_ok (GtkWidget * widget, gpointer data)
+{
+ GtkWidget *text = data;
+ const char *name;
+
+ name = gtk_entry_get_text (GTK_ENTRY (text));
+ if (!name)
+ return; /* huh? how come? */
+ switch_device_by_name (name);
+
+ gtk_widget_destroy (gtk_widget_get_toplevel (text));
+}
+
+static void
+prompt_for_device_name (GtkWidget * widget, gpointer data)
+{
+ GtkWidget *vbox, *hbox, *label, *text;
+ GtkWidget *button, *dialog;
+
+ DBG (DBG_debug, "xcam: prompt_for_device_name: enter\n");
+
+ dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
+ gtk_window_set_title (GTK_WINDOW (dialog), "Device name");
+
+ /* create the main vbox */
+ vbox = gtk_vbox_new (TRUE, 5);
+ gtk_container_border_width (GTK_CONTAINER (vbox), 5);
+ gtk_container_add (GTK_CONTAINER (dialog), vbox);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (vbox), hbox);
+
+ label = gtk_label_new ("Device name:");
+ gtk_container_add (GTK_CONTAINER (hbox), label);
+ gtk_widget_show (label);
+
+ text = gtk_entry_new ();
+ gtk_container_add (GTK_CONTAINER (hbox), text);
+
+ gtk_widget_show (hbox);
+
+ /* the confirmation button */
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (vbox), hbox);
+
+ button = gtk_button_new_with_label ("OK");
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) device_name_dialog_ok, text);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 5);
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+
+ button = gtk_button_new_with_label ("Cancel");
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) device_name_dialog_cancel, dialog);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 5);
+ gtk_widget_show (button);
+
+ gtk_widget_show (hbox);
+ gtk_widget_show (text);
+ gtk_widget_show (vbox);
+ gtk_widget_show (dialog);
+ DBG (DBG_debug, "xcam: prompt_for_device_name: exit\n");
+}
+
+static void
+exit_callback (GtkWidget * widget, gpointer data)
+{
+ if (dialog)
+ gsg_destroy_dialog (dialog);
+ dialog = 0;
+ exit (0);
+}
+
+static void
+save_defaults_callback (GtkWidget * widget, gpointer data)
+{
+ char buf[PATH_MAX];
+
+ if (make_default_filename (sizeof (buf), buf, dialog->dev_name) < 0)
+ return;
+ save_settings (buf);
+}
+
+static void
+load_defaults_callback (GtkWidget * widget, gpointer data)
+{
+ load_defaults (0);
+}
+
+static void
+save_as_callback (GtkWidget * widget, gpointer data)
+{
+ if (gsg_get_filename ("File to save settings to", device_settings_filename,
+ sizeof (device_settings_filename),
+ device_settings_filename) < 0)
+ return;
+ save_settings (device_settings_filename);
+}
+
+static void
+load_from_callback (GtkWidget * widget, gpointer data)
+{
+ if (gsg_get_filename
+ ("File to load settings from", device_settings_filename,
+ sizeof (device_settings_filename), device_settings_filename) < 0)
+ return;
+ load_settings (device_settings_filename, 0);
+}
+
+static void
+buttons_disable (void)
+{
+
+ DBG (DBG_debug, "xcam: buttons_disable: enter\n");
+
+ gsg_set_sensitivity (dialog, FALSE);
+ gtk_widget_set_sensitive (win.play_stop_label, FALSE);
+ gtk_widget_set_sensitive (win.save_frame_label, FALSE);
+ gtk_widget_set_sensitive (win.rgb_bgr_label, FALSE);
+ gtk_widget_set_sensitive (win.txt_label, FALSE);
+
+ DBG (DBG_debug, "xcam: buttons_disable: exit\n");
+}
+
+static void
+buttons_enable (void)
+{
+
+ DBG (DBG_debug, "xcam: buttons_enable: enter\n");
+
+ gsg_set_sensitivity (dialog, TRUE);
+ gtk_widget_set_sensitive (win.play_stop_label, TRUE);
+ gtk_widget_set_sensitive (win.save_frame_label, TRUE);
+ gtk_widget_set_sensitive (win.rgb_bgr_label, TRUE);
+ gtk_widget_set_sensitive (win.txt_label, TRUE);
+
+ DBG (DBG_debug, "xcam: buttons_enable: exit\n");
+}
+
+static GtkWidget *
+build_files_menu (void)
+{
+ GtkWidget *menu, *item;
+
+ DBG (DBG_debug, "xcam: build_files_menu: enter\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) exit_callback, 0);
+ gtk_widget_show (item);
+
+ DBG (DBG_debug, "xcam: build_files_menu: exit\n");
+ return menu;
+}
+
+static gint
+delayed_switch (gpointer data)
+{
+ switch_device (data);
+ load_defaults (1);
+ return FALSE;
+}
+
+static void
+device_activate_callback (GtkWidget * widget, gpointer data)
+{
+ gtk_idle_add (delayed_switch, data);
+}
+
+static GtkWidget *
+build_device_menu (void)
+{
+ GtkWidget *menu, *item;
+ SANE_Status status;
+ int i;
+
+ menu = gtk_menu_new ();
+
+ status = sane_get_devices (&device, SANE_FALSE);
+ if (status != SANE_STATUS_GOOD)
+ {
+ fprintf (stderr, "%s: %s\n", prog_name, sane_strstatus (status));
+ exit (1);
+ }
+
+ for (i = 0; device[i]; ++i)
+ {
+ item = gtk_menu_item_new_with_label ((char *) device[i]->name);
+ gtk_container_add (GTK_CONTAINER (menu), item);
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ (GtkSignalFunc) device_activate_callback,
+ (gpointer) device[i]);
+ gtk_widget_show (item);
+ }
+
+ item = gtk_menu_item_new ();
+ gtk_container_add (GTK_CONTAINER (menu), item);
+ gtk_widget_show (item);
+
+ item = gtk_menu_item_new_with_label ("Refresh device list...");
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ (GtkSignalFunc) rescan_devices, 0);
+ gtk_container_add (GTK_CONTAINER (menu), item);
+ gtk_widget_show (item);
+
+ item = gtk_menu_item_new_with_label ("Specify device name...");
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ (GtkSignalFunc) prompt_for_device_name, 0);
+ gtk_container_add (GTK_CONTAINER (menu), item);
+ gtk_widget_show (item);
+
+ return menu;
+}
+
+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_xcam_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_xcam_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_xcam_save ();
+}
+
+static GtkWidget *
+build_preferences_menu (GSGDialog * dialog)
+{
+ GtkWidget *menu, *item;
+
+ 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);
+
+ /* twocolumn 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);
+
+ item = gtk_menu_item_new ();
+ gtk_container_add (GTK_CONTAINER (menu), item);
+ gtk_widget_show (item);
+
+ item = gtk_menu_item_new_with_label ("Save as default settings");
+ gtk_container_add (GTK_CONTAINER (menu), item);
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ (GtkSignalFunc) save_defaults_callback, 0);
+ gtk_widget_show (item);
+
+ item = gtk_menu_item_new_with_label ("Load default settings");
+ gtk_container_add (GTK_CONTAINER (menu), item);
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ (GtkSignalFunc) load_defaults_callback, 0);
+ gtk_widget_show (item);
+
+ item = gtk_menu_item_new ();
+ gtk_container_add (GTK_CONTAINER (menu), item);
+ gtk_widget_show (item);
+
+ item = gtk_menu_item_new_with_label ("Save settings as...");
+ gtk_container_add (GTK_CONTAINER (menu), item);
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ (GtkSignalFunc) save_as_callback, 0);
+ gtk_widget_show (item);
+
+ item = gtk_menu_item_new_with_label ("Load settings from...");
+ gtk_container_add (GTK_CONTAINER (menu), item);
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ (GtkSignalFunc) load_from_callback, 0);
+ gtk_widget_show (item);
+
+ return menu;
+}
+
+static void
+rescan_devices (GtkWidget * widget, gpointer client_data, gpointer call_data)
+{
+ gtk_widget_destroy (GTK_WIDGET (win.devices.menu));
+ win.devices.menu = build_device_menu ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (win.devices.item),
+ win.devices.menu);
+}
+
+#define READ_SANE_PIXEL(buf, buf_end, format, depth, r, g, b) \
+{ \
+ switch (format) \
+ { \
+ case SANE_FRAME_GRAY: \
+ switch (depth) \
+ { \
+ case 1: \
+ if (buf + 1 > buf_end) \
+ goto end_of_buffer; \
+ (r) = (g) = (b) = (*buf & src_mask) ? 0x0000 : 0xffff; \
+ src_mask >>= 1; \
+ if (src_mask == 0x00) \
+ { \
+ ++buf; \
+ src_mask = 0x80; \
+ } \
+ break; \
+ \
+ case 8: \
+ if (buf + 1 > buf_end) \
+ goto end_of_buffer; \
+ (r) = (g) = (b) = (*buf++ << 8); \
+ break; \
+ \
+ case 16: \
+ if (buf + 2 > buf_end) \
+ goto end_of_buffer; \
+ (r) = (g) = (b) = *((guint16 *) buf); \
+ buf += 2; \
+ break; \
+ } \
+ break; \
+ \
+ case SANE_FRAME_RGB: \
+ switch (depth) \
+ { \
+ case 1: \
+ case 8: \
+ if (buf + 3 > buf_end) \
+ goto end_of_buffer; \
+ (r) = buf[0] << 8; (g) = buf[1] << 8; (b) = buf[2] << 8; \
+ buf += 3; \
+ break; \
+ \
+ case 16: \
+ if (buf + 3 > buf_end) \
+ goto end_of_buffer; \
+ (r) = ((guint16 *)buf)[0]; \
+ (g) = ((guint16 *)buf)[1]; \
+ (b) = ((guint16 *)buf)[2]; \
+ buf += 6; \
+ break; \
+ } \
+ break; \
+ \
+ case SANE_FRAME_RED: \
+ case SANE_FRAME_GREEN: \
+ case SANE_FRAME_BLUE: \
+ default: \
+ fprintf (stderr, "%s: format %d not yet supported\n", \
+ prog_name, (format)); \
+ goto end_of_buffer; \
+ } \
+}
+
+#define PUT_X11_PIXEL(buf, endian, depth, bpp, r, g, b, gl_map) \
+{ \
+ switch (depth) \
+ { \
+ case 1: /* duh? A Sun3 or what?? */ \
+ lum = 3*(r) + 5*(g) + 2*(b); \
+ if (lum >= 5*0x8000) \
+ *buf |= dst_mask; \
+ dst_mask <<= 1; \
+ if (dst_mask > 0xff) \
+ { \
+ buf += (bpp); \
+ dst_mask = 0x01; \
+ } \
+ break; \
+ \
+ case 8: \
+ lum = ((3*(r) + 5*(g) + 2*(b)) / (10 * 256/MAX_LUM)) >> 8; \
+ if (lum >= MAX_LUM) \
+ lum = MAX_LUM; \
+ buf[0] = (gl_map)[lum]; \
+ buf += (bpp); \
+ break; \
+ \
+ case 15: \
+ rgb = ( (((r) >> 11) << r_shift) /* 5 bits of red */ \
+ | (((g) >> 11) << g_shift) /* 5 bits of green */ \
+ | (((b) >> 11) << b_shift));/* 5 bits of blue */ \
+ ((guint16 *)buf)[0] = rgb; \
+ buf += (bpp); \
+ break; \
+ \
+ case 16: \
+ rgb = ( (((r) >> 11) << r_shift) /* 5 bits of red */ \
+ | (((g) >> 10) << g_shift)/* 6 bits of green */ \
+ | (((b) >> 11) << b_shift));/* 5 bits of blue */ \
+ ((guint16 *)buf)[0] = rgb; \
+ buf += (bpp); \
+ break; \
+ \
+ case 24: \
+ case 32: \
+ if (bpp == 4) \
+ { \
+ rgb = ( (((r) >> 8) << r_shift) \
+ | (((g) >> 8) << g_shift) \
+ | (((b) >> 8) << b_shift)); \
+ ((guint32 *)buf)[0] = rgb; \
+ } \
+ else \
+ { \
+ if ( ((endian) == GDK_LSB_FIRST && r_shift == 0) \
+ || ((endian) == GDK_MSB_FIRST && b_shift == 0)) \
+ { \
+ buf[0] = (r) >> 8; buf[1] = (g) >> 8; buf[2] = (b) >> 8; \
+ } \
+ else \
+ { \
+ buf[0] = (b) >> 8; buf[1] = (g) >> 8; buf[2] = (r) >> 8; \
+ } \
+ } \
+ buf += (bpp); \
+ break; \
+ } \
+}
+
+static void
+input_available (gpointer ignore, gint source, GdkInputCondition cond)
+{
+ int x, pixels_per_line, bytes_per_line, dst_depth, src_depth;
+ guint32 r = 0, g = 0, b = 0, lum, rgb, src_mask, dst_mask;
+ gint r_shift, b_shift, g_shift;
+ size_t buf_size, remaining = win.remaining;
+ SANE_Byte *src, *src_end;
+ GdkByteOrder byte_order;
+ u_long bytes_per_pixel;
+ SANE_Frame format;
+ SANE_Status status;
+ SANE_Int len;
+ u_char *dst;
+
+ double max_value = 60; /* min. 1 frame per min. */
+ double frame_time = 50; /* dummy value */
+
+ DBG (DBG_debug, "xcam: input available: enter\n");
+
+ if (!win.playing)
+ /* looks like we got cancelled */
+ goto stop_and_exit;
+
+ buf_size = win.buf_backend_size;
+ format = win.params.format;
+ src_depth = win.params.depth;
+ dst_depth = win.canvas.gdk_image->depth;
+ pixels_per_line = win.params.pixels_per_line;
+ bytes_per_line = win.canvas.gdk_image->bpl;
+ bytes_per_pixel = win.canvas.gdk_image->bpp;
+ byte_order = win.canvas.gdk_image->byte_order;
+
+ x = win.x;
+ dst = win.data;
+ src_mask = 0x80; /* SANE has left most bit is most significant bit */
+ dst_mask = 0x01;
+
+ r_shift = win.canvas.gdk_image->visual->red_shift;
+ g_shift = win.canvas.gdk_image->visual->green_shift;
+ b_shift = win.canvas.gdk_image->visual->blue_shift;
+
+ while (1)
+ {
+ DBG (DBG_debug, "input available: enter sane_read\n");
+ status = sane_read (gsg_dialog_get_device (dialog),
+ win.buf + remaining, buf_size - remaining, &len);
+ if (status != SANE_STATUS_GOOD)
+ {
+ if (status == SANE_STATUS_EOF)
+ {
+ display_image (&win.canvas);
+ stop_camera ();
+ if (win.playing)
+ {
+ next_frame (); /* arrange for next frame */
+ return;
+ }
+ }
+ else
+ {
+ char buf[256];
+ sprintf (buf, "Error during read: %s.",
+ sane_strstatus (status));
+ gsg_error (buf);
+ }
+ win.playing = FALSE;
+ stop_camera ();
+ return;
+ }
+ win.f_count++;
+ update_param (dialog, 0);
+ win.i_time++;
+ if (win.i_time >= 30)
+ {
+ time (&win.time2); /* time marker */
+
+ frame_time = difftime (win.time2, win.time1);
+
+ if (frame_time > max_value)
+ {
+ frame_time = max_value;
+ }
+
+ win.fps_old3 = win.fps_old2;
+ win.fps_old2 = win.fps_old1;
+ win.fps_old1 = win.fps;
+ win.fps = 30 / frame_time; /* correction for loop 30 */
+ /* avarage last 4 frames times */
+ win.fps_av =
+ (win.fps_old3 + win.fps_old2 + win.fps_old1 + win.fps) / 4;
+
+ DBG (DBG_debug,
+ "xcam: input_available fps count=%d, frame_time * 30 = %2.3f, fps=%2.3f, fps_av=%2.3f\n",
+ win.f_count, frame_time, win.fps, win.fps_av);
+ win.i_time = 0;
+
+ time (&win.time1); /* time marker for new sequence */
+ }
+ update_param (dialog, 0);
+
+ if (!len)
+ break;
+
+ if (win.value_txt == 1)
+ {
+ strcpy (win.picmsg_ps, "xcam ");
+
+ status =
+ xcam_add_text (win.buf, win.params.pixels_per_line,
+ win.params.lines, win.picmsg_ps);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_info, "xcam: input available status NOK\n");
+ return;
+ }
+ }
+
+ src = win.buf;
+ src_end = src + len + remaining;
+
+ while (1)
+ {
+ if (win.value_rgb == 0)
+ {
+ READ_SANE_PIXEL (src, src_end, format, src_depth, r, g, b);
+ }
+ else if (win.value_rgb == 1)
+ {
+ READ_SANE_PIXEL (src, src_end, format, src_depth, b, g, r);
+ }
+ PUT_X11_PIXEL (dst, byte_order, dst_depth, bytes_per_pixel, r, g, b,
+ win.canvas.graylevel);
+ if (++x >= pixels_per_line)
+ {
+ x = 0;
+ dst += bytes_per_line - pixels_per_line * bytes_per_pixel;
+ }
+ }
+ end_of_buffer:
+ remaining = src_end - src;
+ }
+ win.data = dst;
+ win.x = x;
+ win.remaining = remaining;
+ DBG (DBG_debug, "xcam: input available: exit\n");
+ return;
+
+stop_and_exit:
+ win.playing = FALSE;
+ stop_camera ();
+ DBG (DBG_debug, "xcam: input available: stop and exit\n");
+ return;
+}
+
+static void
+next_frame (void)
+{
+ char buf[256];
+ SANE_Status status;
+ int fd;
+ DBG (DBG_debug, "xcam: next frame enter\n");
+ buttons_disable ();
+
+ DBG (DBG_debug, "xcam: next frame, start gsg_sync\n");
+ gsg_sync (dialog);
+
+ status = sane_start (gsg_dialog_get_device (dialog));
+ if (status != SANE_STATUS_GOOD)
+ {
+ sprintf (buf, "Failed to start webcam: %s.", sane_strstatus (status));
+ gsg_error (buf);
+ win.playing = FALSE;
+ stop_camera ();
+ return;
+ }
+
+ status = sane_get_parameters (gsg_dialog_get_device (dialog), &win.params);
+ if (status != SANE_STATUS_GOOD)
+ {
+ sprintf (buf, "Failed to get parameters: %s.", sane_strstatus (status));
+ gsg_error (buf);
+ win.playing = FALSE;
+ stop_camera ();
+ return;
+ }
+
+ if (!win.canvas.gdk_image
+ || win.canvas.gdk_image->width != win.params.pixels_per_line
+ || win.canvas.gdk_image->height != win.params.lines)
+ {
+ GdkImageType image_type = GDK_IMAGE_FASTEST;
+
+ if (win.canvas.gdk_image)
+ gdk_image_destroy (win.canvas.gdk_image);
+#ifdef __alpha__
+ /* Some X servers seem to have a problem with shared images that
+ have a width that is not a multiple of 8. Duh... ;-( */
+ if (win.params.pixels_per_line % 8)
+ image_type = GDK_IMAGE_NORMAL;
+#endif
+ win.canvas.gdk_image =
+ gdk_image_new (image_type,
+ gdk_window_get_visual (win.canvas.preview->window),
+ win.params.pixels_per_line, win.params.lines);
+ gtk_widget_set_usize (win.canvas.preview,
+ win.params.pixels_per_line, win.params.lines);
+ }
+ win.data = win.canvas.gdk_image->mem;
+ win.x = 0;
+ win.remaining = 0;
+
+
+
+ buttons_enable ();
+ if (sane_set_io_mode (gsg_dialog_get_device (dialog), SANE_TRUE)
+ == SANE_STATUS_GOOD
+ && sane_get_select_fd (gsg_dialog_get_device (dialog), &fd)
+ == SANE_STATUS_GOOD)
+ win.gdk_input_tag =
+ gdk_input_add (fd, GDK_INPUT_READ, input_available, 0);
+ else
+ input_available (0, -1, 0);
+
+ DBG (DBG_debug, "xcam: next frame: exit\n");
+}
+
+static void
+play_stop_button (GtkWidget * widget, gpointer client_data,
+ gpointer call_data)
+{
+ DBG (DBG_debug, "xcam: play_stop_button: enter\n");
+ if (!dialog)
+ return;
+
+ if (win.playing)
+ {
+ win.playing = FALSE;
+ gtk_label_set (GTK_LABEL (win.play_stop_label), " Play ");
+ DBG (DBG_debug, "xcam: wait for play button to be pushed\n");
+ }
+ else if (win.gdk_input_tag < 0)
+ {
+ win.playing = TRUE;
+ gtk_label_set (GTK_LABEL (win.play_stop_label), " Stop ");
+ DBG (DBG_debug, "xcam: wait for stop button to be pushed\n");
+ next_frame ();
+ }
+ DBG (DBG_debug, "xcam: play_stop_button: exit\n");
+}
+
+/* Invoked when the save frame button is pressed */
+static void
+save_frame_button (GtkWidget * widget, gpointer client_data,
+ gpointer call_data)
+{
+ char buf[256];
+ char testfilename[256];
+
+ DBG (DBG_debug, "xcam: save_frame_button\n");
+ if (!dialog)
+ return;
+
+ if (win.saving)
+ win.saving = FALSE;
+ else if (win.gdk_input_tag < 0)
+ {
+ win.saving = TRUE;
+ gtk_label_set (GTK_LABEL (win.save_frame_label), "Saving started");
+/* ------------------------------------------ */
+
+ /* 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;
+ }
+ win.out = fopen (preferences.filename, "w");
+ if (!win.out)
+ {
+ snprintf (buf, sizeof (buf), "Failed to open `%s': %s",
+ preferences.filename, strerror (errno));
+ gsg_error (buf);
+ return;
+ }
+ }
+ buttons_disable ();
+ save_frame ();
+ buttons_enable ();
+ gsg_sync (dialog);
+ gtk_label_set (GTK_LABEL (win.save_frame_label), "Save\nFrame");
+ DBG (DBG_debug, "xcam: save_frame_button: exit\n");
+}
+
+/* Invoked when the TXT button is pressed */
+static void
+txt_button (GtkWidget * widget, gpointer client_data, gpointer call_data)
+{
+ DBG (DBG_debug, "xcam: txt_button\n");
+ if (!dialog)
+ return;
+
+ if (win.saving)
+ {
+ win.saving = FALSE;
+ win.value_txt = 0;
+ gtk_label_set (GTK_LABEL (win.txt_label), " TXT \n OFF ");
+ }
+ else if (win.gdk_input_tag < 0)
+ {
+ win.saving = TRUE;
+ gtk_label_set (GTK_LABEL (win.txt_label), " TXT \n ON ");
+ win.value_txt = 1;
+ }
+ gsg_sync (dialog);
+ DBG (DBG_debug, "xcam: txt_button: exit\n");
+}
+
+/* Invoked when the RGB-BGR button is pressed */
+static void
+rgb_bgr_button (GtkWidget * widget, gpointer client_data, gpointer call_data)
+{
+ DBG (DBG_debug, "xcam: rgb_bgr_button\n");
+ if (!dialog)
+ return;
+
+ if (win.saving)
+ {
+ win.saving = FALSE;
+ win.value_rgb = 0;
+ gtk_label_set (GTK_LABEL (win.rgb_bgr_label), "RGB");
+ }
+ else if (win.gdk_input_tag < 0)
+ {
+ win.saving = TRUE;
+ gtk_label_set (GTK_LABEL (win.rgb_bgr_label), "BGR");
+ win.value_rgb = 1;
+ }
+ gsg_sync (dialog);
+ DBG (DBG_debug, "xcam: rgb_bgr_button: exit\n");
+}
+
+static void
+save_frame (void)
+{
+ SANE_Handle dev = gsg_dialog_get_device (dialog);
+
+ const char *frame_type = 0;
+ char buf[256];
+ int fd;
+
+ DBG (DBG_debug, "xcam: save_frame: enter\n");
+
+ win.x = win.y = 0;
+
+ win.num_bytes = win.params.lines * win.params.bytes_per_line;
+ win.bytes_read = 0;
+ win.have_odd_byte = FALSE;
+
+ switch (win.params.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 (!win.header_size)
+ {
+ switch (win.params.format)
+ {
+ case SANE_FRAME_RED:
+ case SANE_FRAME_GREEN:
+ case SANE_FRAME_BLUE:
+ if (win.params.depth > 8)
+ {
+ gsg_set_sensitivity (dialog, TRUE);
+ snprintf (buf, sizeof (buf),
+ "Separate channel transfers are not supported "
+ "with %d bits/channel.", win.params.depth);
+ gsg_error (buf);
+
+ buttons_enable ();
+ return;
+ }
+ /*FALLTHROUGH*/ case SANE_FRAME_RGB:
+ fprintf (win.out, "P6\n# SANE data follows\n%d %d\n%d\n",
+ win.params.pixels_per_line, win.params.lines,
+ (win.params.depth <= 8) ? 255 : 65535);
+ break;
+
+ case SANE_FRAME_GRAY:
+ if (win.params.depth == 1)
+ fprintf (win.out, "P4\n# SANE data follows\n%d %d\n",
+ win.params.pixels_per_line, win.params.lines);
+ else
+ fprintf (win.out, "P5\n# SANE data follows\n%d %d\n%d\n",
+ win.params.pixels_per_line, win.params.lines,
+ (win.params.depth <= 8) ? 255 : 65535);
+ break;
+ }
+ win.header_size = ftell (win.out);
+ fwrite (win.buf, 1, win.num_bytes, win.out);
+ fclose (win.out);
+ win.out = 0;
+ }
+ if (win.params.format >= SANE_FRAME_RED
+ && win.params.format <= SANE_FRAME_BLUE)
+ fseek (win.out,
+ win.header_size + win.params.format - SANE_FRAME_RED, SEEK_SET);
+ snprintf (buf, sizeof (buf), "Receiving %s data for `%s'...",
+ frame_type, preferences.filename);
+
+ win.input_tag = -1;
+ if (sane_set_io_mode (dev, SANE_TRUE) == SANE_STATUS_GOOD
+ && sane_get_select_fd (dev, &fd) == SANE_STATUS_GOOD)
+ 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, "xcam: save_frame: exit\n");
+}
+
+static void
+xcam_exit (void)
+{
+ static int active = 0;
+
+ DBG (DBG_debug, "xcam: xcam_exit: enter\n");
+ if (active)
+ return;
+
+ active = 1;
+ pref_xcam_save ();
+ sane_exit ();
+ /* this has the habit of calling exit itself: */
+ gtk_exit (0);
+ DBG (DBG_debug, "xcam: xcam_exit: exit\n");
+}
+
+/* Invoked when window manager's "delete" (or "close") function is
+ invoked. */
+static gint
+xcam_win_delete (GtkWidget * w, gpointer data)
+{
+ xcam_exit ();
+ return FALSE;
+}
+
+static void
+browse_filename_callback (GtkWidget * widget, gpointer data)
+{
+ char filename[1024];
+
+ DBG (DBG_debug, "xcam: 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 (win.filename_entry), filename);
+
+ if (preferences.filename)
+ free ((void *) preferences.filename);
+ preferences.filename = strdup (filename);
+ DBG (DBG_debug, "xcam: browse_filename_callback: exit\n");
+}
+
+static void
+filename_changed_callback (GtkWidget * widget, gpointer data)
+{
+ DBG (DBG_debug, "xcam: filename_changed_callback\n");
+ if (preferences.filename)
+ free ((void *) preferences.filename);
+ preferences.filename = strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
+ pref_xcam_save ();
+ DBG (DBG_debug, "xcam: filename_changed_callbackcallback: exit\n");
+}
+
+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\
+-B, --buffersize set buffersize 1024 * 1024\n\
+-V, --version print version information\n", prog_name);
+}
+
+int
+main (int argc, char **argv)
+{
+ GtkWidget *menu, *menu_bar, *menu_bar_item, *preview_vbox;
+ GtkWidget *hbox, *vbox, *button, *alignment, *frame, *label, *text,
+ *scrolled_window;
+ int i;
+ int ch;
+ SANE_Status status;
+
+ DBG_INIT ();
+
+ DBG (DBG_debug, "xcam: main\n");
+ DBG (DBG_error, "xcam (version: %s, package: %s) starting\n", VERSION,
+ PACKAGE);
+
+ win.buf_backend_size = (32 * 1024);
+
+ pref_xcam_restore ();
+
+ prog_name = strrchr (argv[0], '/');
+ if (prog_name)
+ ++prog_name;
+ else
+ prog_name = argv[0];
+
+ /* turn on by default as we don't support graphical geometry selection */
+ preferences.advanced = 1;
+
+ status = sane_init (0, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (DBG_fatal, "init: sane_main failed: %s\n",
+ sane_strstatus (status));
+ exit (1);
+ }
+
+ if (argc > 1)
+ {
+
+ while ((ch = getopt_long (argc, argv, "hBV", long_options, 0)) != EOF)
+ {
+ switch (ch)
+ {
+ case 'V':
+ printf ("xcam (%s) %s\n", PACKAGE, VERSION);
+ exit (0);
+
+ case 'B':
+ win.buf_backend_size = 1024 * 1024;
+ break;
+ case 'h':
+ default:
+ usage ();
+ exit (0);
+ }
+ }
+ }
+
+ DBG (DBG_debug, "xcam.main: buf_backend_size 0x%x\n", win.buf_backend_size);
+
+ win.buf = malloc (win.buf_backend_size);
+
+ gdk_set_show_events (0);
+ gtk_init (&argc, &argv);
+
+ atexit (xcam_exit);
+
+ win.gdk_input_tag = -1;
+ win.shell = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title (GTK_WINDOW (win.shell), (char *) prog_name);
+ gtk_signal_connect (GTK_OBJECT (win.shell), "delete_event",
+ GTK_SIGNAL_FUNC (xcam_win_delete), NULL);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_border_width (GTK_CONTAINER (vbox), 0);
+ gtk_container_add (GTK_CONTAINER (win.shell), vbox);
+ gtk_widget_show (vbox);
+
+ menu_bar = gtk_menu_bar_new ();
+
+ win.devices.menu = build_device_menu ();
+
+ /* "Files" entry: */
+ menu_bar_item = gtk_menu_item_new_with_label ("File");
+ gtk_container_add (GTK_CONTAINER (menu_bar), menu_bar_item);
+ menu = build_files_menu ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_bar_item), menu);
+ gtk_widget_show (menu_bar_item);
+
+ /* "Devices" entry: */
+ win.devices.item = gtk_menu_item_new_with_label ("Devices");
+ gtk_container_add (GTK_CONTAINER (menu_bar), win.devices.item);
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (win.devices.item),
+ win.devices.menu);
+ gtk_widget_show (win.devices.item);
+
+ /* "Preferences" entry: */
+ menu_bar_item = gtk_menu_item_new_with_label ("Preferences");
+ gtk_container_add (GTK_CONTAINER (menu_bar), menu_bar_item);
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_bar_item),
+ build_preferences_menu (dialog));
+ gtk_widget_show (menu_bar_item);
+
+ gtk_box_pack_start (GTK_BOX (vbox), menu_bar, FALSE, FALSE, 0);
+ gtk_widget_show (menu_bar);
+
+ /* add device info at top: */
+ frame = gtk_frame_new (0);
+ gtk_container_border_width (GTK_CONTAINER (frame), 8);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ win.device_info_label = gtk_label_new ("");
+ gtk_widget_show (win.device_info_label);
+ gtk_container_add (GTK_CONTAINER (frame), win.device_info_label);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (vbox), hbox);
+ gtk_widget_show (hbox);
+
+ /* create the device dialog box: */
+ win.dialog_box = gtk_vbox_new (FALSE, 0);
+ gtk_widget_show (win.dialog_box);
+
+ DBG (DBG_debug, "xcam main, preview vbox on the left hand side \n");
+ /* the preview vbox on the left hand side: */
+ preview_vbox = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), preview_vbox, TRUE, TRUE, 0);
+
+ frame = gtk_frame_new ("Image view");
+ gtk_container_border_width (GTK_CONTAINER (frame), 8);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
+ gtk_container_add (GTK_CONTAINER (preview_vbox), frame);
+
+ alignment = gtk_alignment_new (0, 0.5, 1.0, 1.0);
+ gtk_box_pack_start (GTK_BOX (preview_vbox), alignment, TRUE, TRUE, 0);
+
+ win.canvas.preview = gtk_drawing_area_new ();
+ gtk_drawing_area_size (GTK_DRAWING_AREA (win.canvas.preview), 320, 200);
+ gtk_widget_set_events (win.canvas.preview, CANVAS_EVENT_MASK);
+ gtk_signal_connect (GTK_OBJECT (win.canvas.preview), "event",
+ (GtkSignalFunc) canvas_events, 0);
+ gtk_container_add (GTK_CONTAINER (frame), win.canvas.preview);
+
+ gtk_widget_show (win.canvas.preview);
+ gtk_widget_show (alignment);
+ gtk_widget_show (frame);
+ gtk_widget_show (preview_vbox);
+
+ win.canvas.graylevel_cmap = gdk_colormap_get_system ();
+ for (i = 0; i < NELEMS (win.canvas.graylevel); ++i)
+ {
+ GdkColor color;
+ color.red = color.green = color.blue =
+ i * 0xffff / (NELEMS (win.canvas.graylevel) - 1);
+ gdk_color_alloc (win.canvas.graylevel_cmap, &color);
+ win.canvas.graylevel[i] = color.pixel;
+ }
+
+#if 0
+ {
+ win.canvas.cube_cmap
+ = gdk_colormap_new (win.canvas.preview->window->visual, 1);
+ for (i = 0; i < NELEMS (win.canvas.cube_colors); ++i)
+ {
+ win.canvas.cube_colors[i].pixel = i;
+ win.canvas.cube_colors[i].red = ((i / 30) % 5) * 0xffff / 4;
+ win.canvas.cube_colors[i].green = ((i / 5) % 6) * 0xffff / 5;
+ win.canvas.cube_colors[i].blue = ((i % 5)) * 0xffff / 4;
+ }
+ gdk_colors_store (win.canvas.cube_cmap, win.canvas.cube_colors,
+ NELEMS (win.canvas.cube_colors));
+ gdk_window_set_colormap (win.shell->window, win.canvas.cube_cmap);
+ }
+#endif
+
+ DBG (DBG_debug, "xcam main, use a scrolled window \n");
+
+ /* use a scrolled window to show the device options, as in xscanimage */
+ 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);
+ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW
+ (scrolled_window), win.dialog_box);
+ gtk_container_add (GTK_CONTAINER (hbox), scrolled_window);
+
+ gtk_widget_show (GTK_WIDGET (scrolled_window));
+
+ if (device[0])
+ {
+ switch_device (device[0]);
+ load_defaults (1);
+ }
+ else
+ {
+ DBG (DBG_fatal,
+ " No vidcams were identified. If you were expecting something\n"
+ " different, check that the vidcam 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");
+ atexit (xcam_exit);
+ }
+
+ if (dialog && gsg_dialog_get_device (dialog)
+ && (sane_get_parameters (gsg_dialog_get_device (dialog), &win.params)
+ == SANE_STATUS_GOOD))
+ {
+ gtk_widget_set_usize (win.canvas.preview,
+ win.params.pixels_per_line, win.params.lines);
+ }
+
+ /* The bottom row */
+
+ DBG (DBG_debug, "xcam main, button row: info\n");
+
+ /* 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);
+
+ win.info_label =
+ gtk_label_new ("0 x 0 0 kByte \n0 f_count \n0 fps 0 fps_av");
+ gtk_box_pack_start (GTK_BOX (hbox), win.info_label, FALSE, FALSE, 0);
+ gtk_widget_show (win.info_label);
+
+ win.f_count = 0;
+ win.fps = 0;
+ win.fps_av = 0;
+ win.i_time = 0;
+ time (&win.time1); /* first time marker */
+ update_param (dialog, 0);
+
+ DBG (DBG_debug, "xcam main, bottom row: rgb-bgr button\n");
+
+ /* The TXT button */
+ button = gtk_button_new_with_label (" TXT ");
+ win.txt_label = GTK_BIN (button)->child;
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) txt_button, dialog);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+ DBG (DBG_debug, "xcam main, bottom row: txt button\n");
+
+ /* The RGB-BGR button */
+ button = gtk_button_new_with_label ("RGB");
+ win.rgb_bgr_label = GTK_BIN (button)->child;
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) rgb_bgr_button, dialog);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ DBG (DBG_debug, "xcam main, bottom row: play button\n");
+
+ /* The Play button */
+ button = gtk_button_new_with_label (" Play ");
+ win.play_stop_label = GTK_BIN (button)->child;
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) play_stop_button, dialog);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ DBG (DBG_debug, "xcam main, bottom row: save frame button\n");
+
+ /* The Save Frame button */
+ button = gtk_button_new_with_label ("Save\nFrame");
+ win.save_frame_label = GTK_BIN (button)->child;
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ (GtkSignalFunc) save_frame_button, dialog);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show (button);
+
+ DBG (DBG_debug, "xcam main, bottom row: output filename part\n");
+
+ /* output filename part */
+ 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 (hbox), 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);
+
+ 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);
+
+ gtk_widget_show (win.shell);
+ gtk_main ();
+
+ pref_xcam_save ();
+
+ DBG (DBG_debug, "xcam main exit\n");
+ return 0;
+}
diff --git a/src/xscanimage-gimp-1_0-compat.h b/src/xscanimage-gimp-1_0-compat.h
new file mode 100644
index 0000000..1647962
--- /dev/null
+++ b/src/xscanimage-gimp-1_0-compat.h
@@ -0,0 +1,47 @@
+/* xscanimage-gimp-1_0-compat.h -- stay compatible with gimp 1.0
+
+ 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.
+
+ Copied from xsane (written by Oliver Rauch)
+*/
+
+#ifndef XSCANIMAGE_GIMP_1_0_COMPAT_H
+#define XSCANIMAGE_GIMP_1_0_COMPAT_H
+
+#define GimpPlugInInfo GPlugInInfo
+#define GimpParam GParam
+#define GimpParamDef GParamDef
+#define GimpDrawable GDrawable
+#define GimpPixelRgn GPixelRgn
+#define GimpRunModeType GRunModeType
+#define GimpImageType GImageType
+
+#define GIMP_PDB_INT32 PARAM_INT32
+#define GIMP_PDB_STATUS PARAM_STATUS
+#define GIMP_PDB_CALLING_ERROR STATUS_CALLING_ERROR
+#define GIMP_PDB_SUCCESS STATUS_SUCCESS
+#define GIMP_RUN_INTERACTIVE RUN_INTERACTIVE
+#define GIMP_RUN_NONINTERACTIVE RUN_NONINTERACTIVE
+#define GIMP_RUN_WITH_LAST_VALS RUN_WITH_LAST_VALS
+#define GIMP_EXTENSION PROC_EXTENSION
+#define GIMP_RGB RGB
+#define GIMP_RGB_IMAGE RGB_IMAGE
+#define GIMP_GRAY GRAY
+#define GIMP_GRAY_IMAGE GRAY_IMAGE
+#define GIMP_RGBA_IMAGE RGBA_IMAGE
+#define GIMP_NORMAL_MODE NORMAL_MODE
+
+#endif
+
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;
+}