diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-12-02 20:17:04 +0100 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-12-02 20:17:04 +0100 |
commit | 7d8191b83e163d76bb05e13b373638e4eeb7da95 (patch) | |
tree | fe29c36a3cb4ef2267b2253da4dde8ce360b3cb5 /src/xcam.c |
Initial import of sane-frontends version 1.0.14-9
Diffstat (limited to 'src/xcam.c')
-rw-r--r-- | src/xcam.c | 1871 |
1 files changed, 1871 insertions, 0 deletions
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; +} |