Logo Search packages:      
Sourcecode: sane-frontends version File versions

xcam.c

/* xcam -- X-based camera frontend
   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.  */

#include "../include/sane/config.h"

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/types.h>

#include "gtkglue.h"
#include "preferences.h"
#include <sane/sane.h>
#include "../include/sane/sanei.h"

#ifndef PATH_MAX
# define PATH_MAX 1024
#endif

#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 settings_filename[1024] = "settings.dat";

static struct
  {
    GtkWidget *shell;
    GtkWidget *dialog_box;
    GtkWidget *play_stop_label;
    GtkWidget *device_info_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? */

    SANE_Byte buf[32768];
    SANE_Int remaining;
    SANE_Parameters params;
    gpointer data;      /* image data */
    int x;        /* x position */
  }
win;

/* forward declarations: */
static void rescan_devices (GtkWidget *widget, gpointer client_data,
                      gpointer call_data);
static void next_frame (void);


#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)
{
  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");
}

static void
switch_device (const SANE_Device *dev)
{
  char buf[512];

  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);
}

static void
switch_device_by_name (const char *device_name)
{
  SANE_Device dev_info;
  int i;

  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);
}

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
save_settings (const char *filename)
{
  int fd;

  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);
}

static void
load_settings (const char *filename, int silent)
{
  char buf[2*PATH_MAX];
  char *end;
  int fd;

  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);
}

static void
load_defaults (int silent)
{
  char filename[PATH_MAX];

  if (make_default_filename (sizeof (filename), filename, dialog->dev_name)
      < 0)
    return;
  load_settings (filename, silent);
}

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;

  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);
}

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", settings_filename,
                  sizeof (settings_filename), settings_filename) < 0)
    return;
  save_settings (settings_filename);
}

static void
load_from_callback (GtkWidget *widget, gpointer data)
{
  if (gsg_get_filename ("File to load settings from", settings_filename,
                  sizeof (settings_filename), settings_filename) < 0)
    return;
  load_settings (settings_filename, 0);
}

static GtkWidget *
build_files_menu (void)
{
  GtkWidget *menu, *item;

  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);

  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 result;
  int i;

  menu = gtk_menu_new ();

  result = sane_get_devices (&device, SANE_FALSE);
  if (result != SANE_STATUS_GOOD)
    {
      fprintf (stderr, "%s: %s\n", prog_name, sane_strstatus (result));
      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);
}

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);
}

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);
}

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 result;
  SANE_Int len;
  u_char *dst;

  if (!win.playing)
    /* looks like we got cancelled */
    goto stop_and_exit;

  buf_size = sizeof (win.buf);
  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)
    {
      result = sane_read (gsg_dialog_get_device (dialog),
                    win.buf + remaining, buf_size - remaining, &len);
      if (result != SANE_STATUS_GOOD)
      {
        if (result == 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 (result));
            gsg_error (buf);
          }
        win.playing = FALSE;
        stop_camera ();
        return;
      }
      if (!len)
      break;

      src = win.buf;
      src_end = src + len + remaining;
      while (1)
      {
        READ_SANE_PIXEL(src, src_end, format, src_depth, r, g, b);
        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;
  return;

stop_and_exit:
  win.playing = FALSE;
  stop_camera ();
  return;
}

static void
next_frame (void)
{
  char buf[256];
  SANE_Status result;
  int fd;

  gsg_sync (dialog);

  result = sane_start (gsg_dialog_get_device (dialog));
  if (result != SANE_STATUS_GOOD)
    {
      sprintf (buf, "Failed to start scanner: %s.", sane_strstatus (result));
      gsg_error (buf);
      win.playing = FALSE;
      stop_camera ();
      return;
    }

  result = sane_get_parameters (gsg_dialog_get_device (dialog), &win.params);
  if (result != SANE_STATUS_GOOD)
    {
      sprintf (buf, "Failed to get parameters: %s.", sane_strstatus (result));
      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;

  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);
}

static void
play_stop_button (GtkWidget *widget, gpointer client_data, gpointer call_data)
{
  if (!dialog)
    return;

  if (win.playing)
    win.playing = FALSE;
  else if (win.gdk_input_tag < 0)
    {
      win.playing = TRUE;
      gtk_label_set (GTK_LABEL (win.play_stop_label), "Stop");

      next_frame ();
    }
}

static void
xcam_exit (void)
{
  static int active = 0;

  if (active)
    return;

  active = 1;
  sane_exit ();
  /* this has the habit of calling exit itself: */
  gtk_exit (0);
}

/* Invoked when window manager's "delete" (or "close") function is
   invoked.  */
static gint
xcam_win_delete (GtkWidget *w, gpointer data)
{
  xcam_exit();
  return FALSE;
}

int
main (int argc, char **argv)
{
  GtkWidget *menu, *menu_bar, *menu_bar_item, *preview_vbox;
  GtkWidget *hbox, *vbox, *button, *alignment, *frame, *scrolled_window;
  int i;

  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;

  sane_init (NULL, 0);

  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);

  /* 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 ("Preview");
  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

  /* 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);
    }

  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);

  /* buttons at the bottom: */
  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 (vbox), button, FALSE, FALSE, 0);
  gtk_widget_show (button);

  gtk_widget_show (win.shell);
  gtk_main ();

  return 0;
}

Generated by  Doxygen 1.6.0   Back to index