diff options
Diffstat (limited to 'zbarcam')
-rw-r--r-- | zbarcam/Makefile.am.inc | 44 | ||||
-rw-r--r-- | zbarcam/scan_video.c | 227 | ||||
-rw-r--r-- | zbarcam/zbarcam-gtk.c | 231 | ||||
-rw-r--r-- | zbarcam/zbarcam-qt.cpp | 1050 | ||||
-rw-r--r-- | zbarcam/zbarcam.c | 375 | ||||
-rw-r--r-- | zbarcam/zbarcam.rc | 30 |
6 files changed, 1957 insertions, 0 deletions
diff --git a/zbarcam/Makefile.am.inc b/zbarcam/Makefile.am.inc new file mode 100644 index 0000000..df6794d --- /dev/null +++ b/zbarcam/Makefile.am.inc @@ -0,0 +1,44 @@ +bin_PROGRAMS += zbarcam/zbarcam +zbarcam_zbarcam_SOURCES = zbarcam/zbarcam.c +zbarcam_zbarcam_LDADD = zbar/libzbar.la +zbarcam_zbarcam_CPPFLAGS = $(AM_CPPFLAGS) +# automake bug in "monolithic mode"? +CLEANFILES += zbarcam/.libs/zbarcam zbarcam/moc_zbarcam_qt.h + +if HAVE_GTK +if !WIN32 +bin_PROGRAMS += zbarcam/zbarcam-gtk +zbarcam_zbarcam_gtk_SOURCES = zbarcam/zbarcam-gtk.c zbarcam/scan_video.c +zbarcam_zbarcam_gtk_CPPFLAGS = $(GTK_CFLAGS) $(AM_CPPFLAGS) +zbarcam_zbarcam_gtk_LDADD = $(GTK_LIBS) gtk/libzbargtk.la zbar/libzbar.la \ + $(AM_LDADD) + +endif +endif + +if HAVE_QT +bin_PROGRAMS += zbarcam/zbarcam-qt + +zbarcam_zbarcam_qt_SOURCES = zbarcam/zbarcam-qt.cpp zbarcam/scan_video.c +nodist_zbarcam_zbarcam_qt_SOURCES = zbarcam/moc_zbarcam_qt.h +zbarcam_zbarcam_qt_CPPFLAGS = -Izbarcam $(QT_CFLAGS) $(AM_CPPFLAGS) +zbarcam_zbarcam_qt_LDADD = $(QT_LIBS) qt/libzbarqt.la $(AM_LDADD) +BUILT_SOURCES += $(nodist_zbarcam_zbarcam_qt_SOURCES) +DISTCLEANFILES += $(nodist_zbarcam_zbarcam_qt_SOURCES) zbarcam/moc_zbarcam_qt.h + + +zbarcam/moc_zbarcam_qt.h: zbarcam/zbarcam-qt.cpp + $(mkdir_p) zbarcam + $(MOC) -i $(zbarcam_zbarcam_qt_CPPFLAGS) $< -o $@ +endif + + +if WIN32 +zbarcam_zbarcam_SOURCES += zbarcam/zbarcam.rc +zbarcam_zbarcam_LDADD += zbarcam/zbarcam-rc.o @LTLIBINTL@ + +if WITH_DIRECTSHOW +zbarcam_zbarcam_CPPFLAGS += -DDIRECTSHOW +endif + +endif diff --git a/zbarcam/scan_video.c b/zbarcam/scan_video.c new file mode 100644 index 0000000..c83e53e --- /dev/null +++ b/zbarcam/scan_video.c @@ -0,0 +1,227 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" + +#include <fcntl.h> +#include <ftw.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <linux/videodev2.h> + +#include <sys/ioctl.h> +#include <sys/stat.h> +#ifdef MAJOR_IN_SYSMACROS +#include <sys/sysmacros.h> +#endif +typedef void(cb_t)(void *userdata, const char *device); + +struct devnodes { + char *fname; + int minor; + int is_valid; +}; + +static unsigned int n_devices = 0; +static struct devnodes *devices = NULL; + +/* + * Sort order: + * + * - Valid devices comes first + * - Lowest minors comes first + * + * For devnode names, it sorts on this order: + * - custom udev given names + * - /dev/v4l/by-id/ + * - /dev/v4l/by-path/ + * - /dev/video + * - /dev/char/ + * + * - Device name is sorted alphabetically if follows same pattern + */ +static int sort_devices(const void *__a, const void *__b) +{ + const struct devnodes *a = __a; + const struct devnodes *b = __b; + int val_a, val_b; + + if (a->is_valid != b->is_valid) + return !a->is_valid - !b->is_valid; + + if (a->minor != b->minor) + return a->minor - b->minor; + + /* Ensure that /dev/video* devices will stay at the top */ + + if (strstr(a->fname, "by-id")) + val_a = 1; + if (strstr(a->fname, "by-path")) + val_a = 2; + else if (strstr(a->fname, "/dev/video")) + val_a = 3; + else if (strstr(a->fname, "char")) + val_a = 4; + else /* Customized names comes first */ + val_a = 0; + + if (strstr(b->fname, "by-id")) + val_b = 1; + if (strstr(b->fname, "by-path")) + val_b = 2; + else if (strstr(b->fname, "/dev/video")) + val_b = 3; + else if (strstr(b->fname, "char")) + val_b = 4; + else /* Customized names comes first */ + val_b = 0; + + if (val_a != val_b) + return val_a - val_b; + + /* Finally, just use alphabetic order */ + return strcmp(a->fname, b->fname); +} + +static int handle_video_devs(const char *file, const struct stat *st, int flag) +{ + int dev_minor, first_device = -1, fd; + unsigned int i; + struct v4l2_capability vid_cap = { 0 }; + + /* Discard devices that can't be a videodev */ + if (!S_ISCHR(st->st_mode) || major(st->st_rdev) != 81) + return 0; + + dev_minor = minor(st->st_rdev); + + /* check if it is an already existing device */ + if (devices) { + for (i = 0; i < n_devices; i++) { + if (dev_minor == devices[i].minor) { + first_device = i; + break; + } + } + } + + devices = realloc(devices, (n_devices + 1) * sizeof(struct devnodes)); + if (!devices) { + perror("Can't allocate memory to store devices"); + exit(1); + } + memset(&devices[n_devices], 0, sizeof(struct devnodes)); + + if (first_device < 0) { + fd = open(file, O_RDWR); + if (fd < 0) { + devices[n_devices].is_valid = 0; + } else { + if (ioctl(fd, VIDIOC_QUERYCAP, &vid_cap) == -1) { + devices[n_devices].is_valid = 0; + } else { +#ifdef V4L2_CID_ALPHA_COMPONENT + /* + * device_caps was added on Kernel 3.3. The preferred + * way to handle such compat stuff would be to include + * a recent videodev2.h at ZBar's source and check the + * V4L2 API returned by VIDIOC_QUERYCAP. + * However, doing that require some care, as other + * compat code should be checked to see if they would work. + * Also, it is painful to keep updating the Kernel headers. + * Thankfully, V4L2_CID_ALPHA_COMPONENT was also added on + * Kernel 3.3, so just checking if this is defined should + * be enough to do the right thing. + */ + if (!(vid_cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)) + devices[n_devices].is_valid = 0; + else + devices[n_devices].is_valid = 1; +#else + if (!(vid_cap.device_caps & V4L2_CAP_VIDEO_CAPTURE)) + devices[n_devices].is_valid = 0; + else + devices[n_devices].is_valid = 1; +#endif + } + } + + close(fd); + } else { + devices[n_devices].is_valid = devices[first_device].is_valid; + } + + devices[n_devices].fname = strdup(file); + devices[n_devices].minor = dev_minor; + + n_devices++; + + return (0); +} + +/* scan /dev for v4l video devices and call add_device for each. + * also looks for a specified "default" device (if not NULL) + * if not found, the default will be appended to the list. + * returns the index+1 of the default_device, or 0 if the default + * was not specified. NB *not* reentrant + */ +int scan_video(cb_t add_dev, void *userdata, const char *default_dev) +{ + unsigned int i, idx = 0; + int default_idx = -1, last_minor = -1; + + if (ftw("/dev", handle_video_devs, 4)) { + perror("search for video devices failed"); + return -1; + } + qsort(devices, n_devices, sizeof(struct devnodes), sort_devices); + + for (i = 0; i < n_devices; i++) { + if (!devices[i].is_valid) + continue; + + if (devices[i].minor == last_minor) + continue; + + add_dev(userdata, devices[i].fname); + last_minor = devices[i].minor; + idx++; + + if (default_dev && !strcmp(default_dev, devices[i].fname)) + default_idx = idx; + else if (!default_dev && default_idx < 0) + default_idx = idx; + } + + for (i = 0; i < n_devices; i++) + free(devices[i].fname); + free(devices); + + n_devices = 0; + devices = NULL; + + return (default_idx); +} diff --git a/zbarcam/zbarcam-gtk.c b/zbarcam/zbarcam-gtk.c new file mode 100644 index 0000000..1c7930a --- /dev/null +++ b/zbarcam/zbarcam-gtk.c @@ -0,0 +1,231 @@ +/*------------------------------------------------------------------------ + * Copyright 2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include <gtk/gtk.h> +#include <zbar/zbargtk.h> + +static GtkWidget *window = NULL; +static GtkWidget *status_image = NULL; +static GtkTextView *results = NULL; +static gchar *open_file = NULL; + +int scan_video(void *add_device, void *userdata, const char *default_device); + +/* decode signal callback + * puts the decoded result in the textbox + */ +static void decoded(GtkWidget *widget, zbar_symbol_type_t symbol, + const char *result, gpointer data) +{ + GtkTextBuffer *resultbuf = gtk_text_view_get_buffer(results); + GtkTextIter end; + gtk_text_buffer_get_end_iter(resultbuf, &end); + gtk_text_buffer_insert(resultbuf, &end, zbar_get_symbol_name(symbol), -1); + gtk_text_buffer_insert(resultbuf, &end, ":", -1); + gtk_text_buffer_insert(resultbuf, &end, result, -1); + gtk_text_buffer_insert(resultbuf, &end, "\n", -1); + gtk_text_view_scroll_to_iter(results, &end, 0, FALSE, 0, 0); +} + +/* update button state when video state changes + */ +static void video_enabled(GObject *object, GParamSpec *param, gpointer data) +{ + ZBarGtk *zbar = ZBAR_GTK(object); + gboolean enabled = zbar_gtk_get_video_enabled(zbar); + gboolean opened = zbar_gtk_get_video_opened(zbar); + + GtkToggleButton *button = GTK_TOGGLE_BUTTON(data); + gboolean active = gtk_toggle_button_get_active(button); + if (active != (opened && enabled)) + gtk_toggle_button_set_active(button, enabled); +} + +static void video_opened(GObject *object, GParamSpec *param, gpointer data) +{ + ZBarGtk *zbar = ZBAR_GTK(object); + gboolean opened = zbar_gtk_get_video_opened(zbar); + gboolean enabled = zbar_gtk_get_video_enabled(zbar); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data), opened && enabled); + gtk_widget_set_sensitive(GTK_WIDGET(data), opened); +} + +/* (re)open the video when a new device is selected + */ +static void video_changed(GtkWidget *widget, gpointer data) +{ + const char *video_device = + gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(widget)); + zbar_gtk_set_video_device( + ZBAR_GTK(data), + ((video_device && video_device[0] != '<') ? video_device : NULL)); +} + +static void status_button_toggled(GtkToggleButton *button, gpointer data) +{ + ZBarGtk *zbar = ZBAR_GTK(data); + gboolean opened = zbar_gtk_get_video_opened(zbar); + gboolean enabled = zbar_gtk_get_video_enabled(zbar); + gboolean active = gtk_toggle_button_get_active(button); + if (opened && (active != enabled)) + zbar_gtk_set_video_enabled(ZBAR_GTK(data), active); + gtk_image_set_from_icon_name(GTK_IMAGE(status_image), + (opened && active) ? "gtk-yes" : "gtk-no", + GTK_ICON_SIZE_BUTTON); + gtk_button_set_label(GTK_BUTTON(button), (!opened) ? "closed" : + (active) ? "enabled" : + "disabled"); +} + +static void open_button_clicked(GtkButton *button, gpointer data) +{ + GtkWidget *dialog = + gtk_file_chooser_dialog_new("Open Image File", GTK_WINDOW(window), + GTK_FILE_CHOOSER_ACTION_OPEN, "gtk-cancel", + GTK_RESPONSE_CANCEL, "gtk-open", + GTK_RESPONSE_ACCEPT, NULL); + GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog); + if (open_file) + gtk_file_chooser_set_filename(chooser, open_file); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + gchar *file = gtk_file_chooser_get_filename(chooser); + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(file, NULL); + if (pixbuf) + zbar_gtk_scan_image(ZBAR_GTK(data), pixbuf); + else + fprintf(stderr, "ERROR: unable to open image file: %s\n", file); + + if (open_file && file) + g_free(open_file); + open_file = file; + } + gtk_widget_destroy(dialog); +} + +/* build a simple gui w/: + * - a combo box to select the desired video device + * - the zbar widget to display video + * - a non-editable text box to display any results + */ +int main(int argc, char *argv[]) +{ + const char *video_arg = NULL; + + gdk_set_allowed_backends("x11,*"); + gtk_init(&argc, &argv); + + if (argc > 1) + video_arg = argv[1]; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + gtk_window_set_title(GTK_WINDOW(window), "test_gtk"); + gtk_container_set_border_width(GTK_CONTAINER(window), 8); + + g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), + NULL); + + GtkWidget *zbar = zbar_gtk_new(); + + g_signal_connect(G_OBJECT(zbar), "decoded", G_CALLBACK(decoded), NULL); + + /* video device list combo box */ + GtkWidget *video_list = gtk_combo_box_text_new(); + + g_signal_connect(G_OBJECT(video_list), "changed", G_CALLBACK(video_changed), + zbar); + + /* enable/disable status button */ + GtkWidget *status_button = gtk_toggle_button_new(); + status_image = gtk_image_new_from_icon_name("gtk-no", GTK_ICON_SIZE_BUTTON); + gtk_button_set_image(GTK_BUTTON(status_button), status_image); + gtk_button_set_label(GTK_BUTTON(status_button), "closed"); + gtk_widget_set_sensitive(status_button, FALSE); + + /* bind status button state and video state */ + g_signal_connect(G_OBJECT(status_button), "toggled", + G_CALLBACK(status_button_toggled), zbar); + g_signal_connect(G_OBJECT(zbar), "notify::video-enabled", + G_CALLBACK(video_enabled), status_button); + g_signal_connect(G_OBJECT(zbar), "notify::video-opened", + G_CALLBACK(video_opened), status_button); + + /* open image file button */ +#if GTK_MAJOR_VERSION >= 3 + GtkWidget *open_button = + gtk_button_new_from_icon_name("gtk-open", GTK_ICON_SIZE_BUTTON); +#else + GtkWidget *open_button = gtk_button_new_from_stock(GTK_STOCK_OPEN); +#endif + + g_signal_connect(G_OBJECT(open_button), "clicked", + G_CALLBACK(open_button_clicked), zbar); + + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(video_list), "<none>"); + int active = + scan_video(gtk_combo_box_text_append_text, video_list, video_arg); + if (active >= 0) + gtk_combo_box_set_active(GTK_COMBO_BOX(video_list), active); + + /* hbox for combo box and buttons */ +#if GTK_MAJOR_VERSION >= 3 + GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); +#else + GtkWidget *hbox = gtk_hbox_new(FALSE, 8); +#endif + + gtk_box_pack_start(GTK_BOX(hbox), video_list, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox), status_button, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), open_button, FALSE, FALSE, 0); + + /* text box for holding results */ + results = GTK_TEXT_VIEW(gtk_text_view_new()); + gtk_widget_set_size_request(GTK_WIDGET(results), 320, 64); + gtk_text_view_set_editable(results, FALSE); + gtk_text_view_set_cursor_visible(results, FALSE); + gtk_text_view_set_left_margin(results, 4); + + /* vbox for hbox, zbar test widget and result text box */ +#if GTK_MAJOR_VERSION >= 3 + GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8); +#else + GtkWidget *vbox = gtk_vbox_new(FALSE, 8); +#endif + gtk_container_add(GTK_CONTAINER(window), vbox); + + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), zbar, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(results), FALSE, FALSE, 0); + + GdkGeometry hints; + hints.min_width = 320; + hints.min_height = 240; + gtk_window_set_geometry_hints(GTK_WINDOW(window), zbar, &hints, + GDK_HINT_MIN_SIZE); + + gtk_widget_show_all(window); + gtk_main(); + return (0); +} diff --git a/zbarcam/zbarcam-qt.cpp b/zbarcam/zbarcam-qt.cpp new file mode 100644 index 0000000..b98a32a --- /dev/null +++ b/zbarcam/zbarcam-qt.cpp @@ -0,0 +1,1050 @@ +//------------------------------------------------------------------------ +// Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader 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 Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#include <QApplication> +#include <QCheckBox> +#include <QComboBox> +#include <QCommandLineParser> +#include <QFileDialog> +#include <QImage> +#include <QLayout> +#include <QPushButton> +#include <QTextEdit> +#include <QWidget> +#include <QtGlobal> +#include <config.h> +#include <zbar.h> +#include <zbar/QZBar.h> + +#define TEST_IMAGE_FORMATS \ + "Image Files (*.png *.jpg *.jpeg *.bmp *.gif *.ppm *.pgm *.pbm *.tiff " \ + "*.xpm *.xbm)" + +#define SYM_GROUP "Symbology" +#define CAM_GROUP "Camera" +#define DBUS_NAME "D-Bus" +#define OPTION_BAR "option_bar.enable" +#define CONTROL_BAR "control_bar.enable" + +extern "C" { +int scan_video(void *add_device, void *userdata, const char *default_device); +} + +struct configs_s { + QString name; + zbar::zbar_symbol_type_t sym; +}; + +static const struct configs_s configs[] = { + { "Composite codes", zbar::ZBAR_COMPOSITE }, + { "Image Scanner", zbar::ZBAR_PARTIAL }, +#if ENABLE_CODABAR == 1 + { "Codabar", zbar::ZBAR_CODABAR }, +#endif +#if ENABLE_CODE128 == 1 + { "Code-128", zbar::ZBAR_CODE128 }, +#endif +#if ENABLE_I25 == 1 + { "Code 2 of 5 interlaced", zbar::ZBAR_I25 }, +#endif +#if ENABLE_CODE39 == 1 + { "Code-39", zbar::ZBAR_CODE39 }, +#endif +#if ENABLE_CODE93 == 1 + { "Code-93", zbar::ZBAR_CODE93 }, +#endif +#if ENABLE_DATABAR == 1 + { "DataBar", zbar::ZBAR_DATABAR }, + { "DataBar expanded", zbar::ZBAR_DATABAR_EXP }, +#endif +#if ENABLE_EAN == 1 + { "EAN-2", zbar::ZBAR_EAN2 }, + { "EAN-5", zbar::ZBAR_EAN5 }, + { "EAN-8", zbar::ZBAR_EAN8 }, + { "EAN-13", zbar::ZBAR_EAN13 }, + { "ISBN-10", zbar::ZBAR_ISBN10 }, + { "ISBN-13", zbar::ZBAR_ISBN13 }, + { "UPC-A", zbar::ZBAR_UPCA }, + { "UPC-E", zbar::ZBAR_UPCE }, +#endif +#if ENABLE_PDF417 == 1 + { "PDF417", zbar::ZBAR_PDF417 }, +#endif +#if ENABLE_QRCODE == 1 + { "QR code", zbar::ZBAR_QRCODE }, +#endif +#if ENABLE_SQCODE == 1 + { "SQ code", zbar::ZBAR_SQCODE }, +#endif +}; + +#define CONFIGS_SIZE (sizeof(configs) / sizeof(*configs)) + +struct settings_s { + QString name; + zbar::zbar_config_t ctrl; + bool is_bool; +}; + +static const struct settings_s settings[] = { + { "x-density", zbar::ZBAR_CFG_Y_DENSITY, false }, + { "y-density", zbar::ZBAR_CFG_Y_DENSITY, false }, + { "min-length", zbar::ZBAR_CFG_MIN_LEN, false }, + { "max-length", zbar::ZBAR_CFG_MAX_LEN, false }, + { "uncertainty", zbar::ZBAR_CFG_UNCERTAINTY, false }, + { "ascii", zbar::ZBAR_CFG_ASCII, true }, + { "binary", zbar::ZBAR_CFG_BINARY, true }, + { "add-check", zbar::ZBAR_CFG_ADD_CHECK, true }, + { "emit-check", zbar::ZBAR_CFG_EMIT_CHECK, true }, + { "position", zbar::ZBAR_CFG_POSITION, true }, + { "test-inverted", zbar::ZBAR_CFG_TEST_INVERTED, true }, +}; +#define SETTINGS_SIZE (sizeof(settings) / sizeof(*settings)) + +// Represents an integer control + +class IntegerControl : public QSpinBox +{ + Q_OBJECT + +private: + char *name; + zbar::QZBar *zbar; + +private slots: + void updateControl(int value); + +public: + IntegerControl(QGroupBox *parent, zbar::QZBar *_zbar, char *_name, int min, + int max, int def, int step) + : QSpinBox(parent) + { + int val; + + zbar = _zbar; + name = _name; + + setRange(min, max); + setSingleStep(step); + if (!zbar->get_control(name, &val)) + setValue(val); + else + setValue(def); + + connect(this, SIGNAL(valueChanged(int)), this, + SLOT(updateControl(int))); + } +}; + +void IntegerControl::updateControl(int value) +{ + zbar->set_control(name, value); +} + +// Represents a menu control +class MenuControl : public QComboBox +{ + Q_OBJECT + +private: + char *name; + zbar::QZBar *zbar; + QVector<QPair<int, QString> > vector; + +private slots: + void updateControl(int value); + +public: + MenuControl(QGroupBox *parent, zbar::QZBar *_zbar, char *_name, + QVector<QPair<int, QString> > _vector) + : QComboBox(parent) + { + int val; + + zbar = _zbar; + name = _name; + vector = _vector; + + if (zbar->get_control(name, &val)) + val = 0; + for (int i = 0; i < vector.size(); ++i) { + QPair<int, QString> pair = vector.at(i); + addItem(pair.second, pair.first); + + if (val == pair.first) + setCurrentIndex(i); + } + connect(this, SIGNAL(currentIndexChanged(int)), this, + SLOT(updateControl(int))); + } +}; + +void MenuControl::updateControl(int index) +{ + zbar->set_control(name, vector.at(index).first); +} + +class IntegerSetting : public QSpinBox +{ + Q_OBJECT + +public: + QString name; + + IntegerSetting(QString _name, int val = 0) : name(_name) + { + setValue(val); + } +}; + +class SettingsDialog : public QDialog +{ + Q_OBJECT + +private: + QVector<int> val; + zbar::QZBar *zbar; + zbar::zbar_symbol_type_t sym; + +private slots: + + void accept() + { + for (unsigned i = 0; i < SETTINGS_SIZE; i++) + zbar->set_config(sym, settings[i].ctrl, val[i]); + QDialog::accept(); + }; + void reject() + { + QDialog::reject(); + }; + void clicked() + { + QCheckBox *button = qobject_cast<QCheckBox *>(sender()); + if (!button) + return; + + QString name = button->text(); + + for (unsigned i = 0; i < SETTINGS_SIZE; i++) { + if (settings[i].name == name) { + val[i] = button->isChecked(); + return; + } + } + // ERROR! + }; + void update(int value) + { + IntegerSetting *setting = qobject_cast<IntegerSetting *>(sender()); + if (!setting) + return; + + for (unsigned i = 0; i < SETTINGS_SIZE; i++) { + if (settings[i].name == setting->name) { + val[i] = value; + return; + } + } + // ERROR! + }; + +public: + SettingsDialog(zbar::QZBar *_zbar, QString &name, + zbar::zbar_symbol_type_t _sym) + : zbar(_zbar), sym(_sym) + { + val = QVector<int>(SETTINGS_SIZE); + + QGridLayout *layout = new QGridLayout(this); + + this->setWindowTitle(name); + + for (unsigned i = 0; i < SETTINGS_SIZE; i++) { + int value = 0; + + if (zbar->get_config(sym, settings[i].ctrl, value)) + continue; + val[i] = value; + + if (settings[i].is_bool) { + QCheckBox *button = new QCheckBox(settings[i].name, this); + + button->setChecked(value); + + layout->addWidget(button, i, 0, 1, 2, + Qt::AlignTop | Qt::AlignLeft); + connect(button, SIGNAL(clicked()), this, SLOT(clicked())); + } else { + QLabel *label = new QLabel(settings[i].name); + + layout->addWidget(label, i, 0, 1, 1, + Qt::AlignTop | Qt::AlignLeft); + IntegerSetting *spin = + new IntegerSetting(settings[i].name, value); + layout->addWidget(spin, i, 1, 1, 1, + Qt::AlignTop | Qt::AlignLeft); + connect(spin, SIGNAL(valueChanged(int)), this, + SLOT(update(int))); + } + } + QDialogButtonBox *buttonBox = new QDialogButtonBox( + QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + layout->addWidget(buttonBox); + } +}; + +class SettingsButton : public QPushButton +{ + Q_OBJECT + +private: + QString name; + zbar::QZBar *zbar; + zbar::zbar_symbol_type_t sym; + +public: + SettingsButton(zbar::QZBar *_zbar, const QIcon &_icon, QString _name, + zbar::zbar_symbol_type_t _sym) + : QPushButton(_icon, ""), name(_name), zbar(_zbar), sym(_sym) + { + int size = font().pointSize(); + + if (size < 0) + size = font().pixelSize(); + + if (size > 0) + setIconSize(QSize(size, size)); + }; + +public Q_SLOTS: + void button_clicked() + { + SettingsButton *button = qobject_cast<SettingsButton *>(sender()); + if (!button) + return; + + QString name = button->name; + + SettingsDialog *dialog = new SettingsDialog(zbar, name, sym); + dialog->setModal(true); + dialog->show(); + } +}; + +struct CamRes { + unsigned width; + unsigned height; + float max_fps; +}; + +class ZbarcamQZBar : public QWidget +{ + Q_OBJECT + +protected: + static void add_device(QComboBox *list, const char *device) + { + list->addItem(QString(device)); + } + +public Q_SLOTS: + void turn_show_options() + { + QPushButton *button = qobject_cast<QPushButton *>(sender()); + if (!button) + return; + + show_options = !show_options; + if (show_options) { + button->setText("Hide Options"); + optionsGroup->show(); + } else { + button->setText("Show Options"); + optionsGroup->hide(); + } + } + + void turn_show_controls() + { + QPushButton *button = qobject_cast<QPushButton *>(sender()); + if (!button) + return; + + show_controls = !show_controls; + if (show_controls) { + button->setText("Hide Controls"); + controlGroup->show(); + } else { + button->setText("Show Controls"); + controlGroup->hide(); + } + } + +public: + ~ZbarcamQZBar() + { + saveSettings(); + } + ZbarcamQZBar(const QStringList *names, int verbose = 0) : resolutions(NULL) + { + // drop-down list of video devices + QComboBox *videoList = new QComboBox; + + // toggle button to disable/enable video + statusButton = new QPushButton; + + QStyle *style = QApplication::style(); + QIcon statusIcon = style->standardIcon(QStyle::SP_DialogNoButton); + QIcon yesIcon = style->standardIcon(QStyle::SP_DialogYesButton); + statusIcon.addPixmap(yesIcon.pixmap(QSize(128, 128), QIcon::Normal, + QIcon::On), + QIcon::Normal, QIcon::On); + + statusButton->setIcon(statusIcon); + statusButton->setText("&Enable"); + statusButton->setCheckable(true); + statusButton->setEnabled(false); + statusButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + // command button to open image files for scanning + QPushButton *openButton = new QPushButton("&Open"); + QIcon openIcon = style->standardIcon(QStyle::SP_DialogOpenButton); + openButton->setIcon(openIcon); + openButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + // collect video list and buttons horizontally + ZBarMenu = new QHBoxLayout; + ZBarMenu->setAlignment(Qt::AlignLeft); + ZBarMenu->addWidget(videoList, 5); + ZBarMenu->addWidget(statusButton, 1); + ZBarMenu->addWidget(openButton, 1); + + // video barcode scanner + zbar = new zbar::QZBar(NULL, verbose); + zbar->setAcceptDrops(true); + + // text box for results + QTextEdit *results = new QTextEdit; + results->setReadOnly(true); + + QGridLayout *grid = new QGridLayout; + grid->addLayout(ZBarMenu, 0, 0, 1, -1); + grid->addWidget(zbar, 1, 0, 1, 1); + grid->addWidget(results, 2, 0, 1, 1); + + // Group box where controls will be added + optionsGroup = new QGroupBox(tr("Options"), this); + QGridLayout *optionsBoxLayout = new QGridLayout(optionsGroup); + optionsGroup->setAlignment(Qt::AlignHCenter); + optionsBoxLayout->setContentsMargins(0, 0, 16, 0); + grid->addWidget(optionsGroup, 1, 1, -1, 1, Qt::AlignTop); + + controlGroup = new QGroupBox(this); + controlBoxLayout = new QGridLayout(controlGroup); + controlBoxLayout->setContentsMargins(0, 0, 0, 0); + grid->addWidget(controlGroup, 1, 2, -1, 1, Qt::AlignTop); + + loadSettings(); + zbar->request_size(curWidth, curHeight, false); + + int pos = 0; + +#ifdef HAVE_DBUS + QCheckBox *button = new QCheckBox(DBUS_NAME, this); + button->setChecked(dbus_enabled); + optionsBoxLayout->addWidget(button, ++pos, 0, 1, 1, + Qt::AlignTop | Qt::AlignLeft); + connect(button, SIGNAL(clicked()), this, SLOT(code_clicked())); + zbar->request_dbus(0); +#endif + + for (unsigned i = 0; i < CONFIGS_SIZE; i++) { + int val = 0; + + if (configs[i].sym == zbar::ZBAR_PARTIAL) { + QLabel *label = new QLabel(configs[i].name, this); + optionsBoxLayout->addWidget(label, ++pos, 0, 1, 1, + Qt::AlignTop | Qt::AlignLeft); + } else { + QCheckBox *button = new QCheckBox(configs[i].name, this); + + zbar->get_config(configs[i].sym, zbar::ZBAR_CFG_ENABLE, val); + + button->setChecked(val); + optionsBoxLayout->addWidget(button, ++pos, 0, 1, 1, + Qt::AlignTop | Qt::AlignLeft); + connect(button, SIGNAL(clicked()), this, SLOT(code_clicked())); + } + + /* Composite doesn't have configuration */ + if (configs[i].sym == zbar::ZBAR_COMPOSITE) + continue; + + QIcon icon = QIcon::fromTheme(QLatin1String("configure-toolbars")); + SettingsButton *settings = + new SettingsButton(zbar, icon, configs[i].name, configs[i].sym); + optionsBoxLayout->addWidget(settings, pos, 1, 1, 1, + Qt::AlignTop | Qt::AlignLeft); + + connect(settings, &SettingsButton::clicked, settings, + &SettingsButton::button_clicked); + } + + // Allow showing/hiding options/controls menus + QPushButton *showOptionsButton, *showControlsButton; + + if (show_options) { + showOptionsButton = new QPushButton("Hide Options"); + optionsGroup->show(); + } else { + showOptionsButton = new QPushButton("Show Options"); + optionsGroup->hide(); + } + showOptionsButton->setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); + ZBarMenu->addWidget(showOptionsButton); + connect(showOptionsButton, SIGNAL(clicked()), this, + SLOT(turn_show_options())); + + if (show_controls) { + showControlsButton = new QPushButton("Hide Controls"); + controlGroup->show(); + } else { + showControlsButton = new QPushButton("Show Controls"); + controlGroup->hide(); + } + showControlsButton->setSizePolicy(QSizePolicy::Fixed, + QSizePolicy::Fixed); + ZBarMenu->addWidget(showControlsButton); + connect(showControlsButton, SIGNAL(clicked()), this, + SLOT(turn_show_controls())); + + if (!geometry.isEmpty()) + restoreGeometry(geometry); + + setLayout(grid); + + videoList->addItem(""); + + int active = 0; + for (int i = 0; i < names->size(); i++) + active += scan_video((void *)add_device, videoList, + names->at(i).toUtf8()); + + if (names->isEmpty()) + active += scan_video((void *)add_device, videoList, NULL); + + // directly connect combo box change signal to scanner video open + connect(videoList, SIGNAL(currentIndexChanged(const QString &)), zbar, + SLOT(setVideoDevice(const QString &))); + + // directly connect status button state to video enabled state + connect(statusButton, SIGNAL(toggled(bool)), zbar, + SLOT(setVideoEnabled(bool))); + + // also update status button state when video is opened/closed + connect(zbar, SIGNAL(videoOpened(bool)), this, SLOT(setEnabled(bool))); + + // prompt for image file to scan when openButton is clicked + connect(openButton, SIGNAL(clicked()), SLOT(openImage())); + + // directly connect video scanner decode result to display in text box + connect(zbar, SIGNAL(decodedText(const QString &)), results, + SLOT(append(const QString &))); + + if (active >= 0) + videoList->setCurrentIndex(active); + } + +public Q_SLOTS: + void openImage() + { + file = QFileDialog::getOpenFileName(this, "Open Image", file, + TEST_IMAGE_FORMATS); + if (!file.isEmpty()) + zbar->scanImage(QImage(file)); + } + + void control_clicked() + { + QCheckBox *button = qobject_cast<QCheckBox *>(sender()); + if (!button) + return; + + QString name = button->text(); + bool val = button->isChecked(); + + zbar->set_control(name.toUtf8().data(), val); + } + + void code_clicked() + { + QCheckBox *button = qobject_cast<QCheckBox *>(sender()); + if (!button) + return; + + QString name = button->text(); + bool val = button->isChecked(); + + if (name == DBUS_NAME) { + zbar->request_dbus(val); + dbus_enabled = val; + return; + } + + for (unsigned i = 0; i < CONFIGS_SIZE; i++) { + if (configs[i].name == name) { + zbar->set_config(configs[i].sym, zbar::ZBAR_CFG_ENABLE, val); + return; + } + } + } + + void clearLayout(QLayout *layout) + { + QLayoutItem *item; + while ((item = layout->takeAt(0))) { + if (item->layout()) { + clearLayout(item->layout()); + delete item->layout(); + } + if (item->widget()) { + delete item->widget(); + } + delete item; + } + } + + void setVideoResolution(int index) + { + struct CamRes *cur_res; + + if (index < 0 || res.isEmpty()) + return; + + cur_res = &res[index]; + + unsigned width = zbar->videoWidth(); + unsigned height = zbar->videoHeight(); + + if (width == cur_res->width && height == cur_res->height) + return; + + zbar->request_size(cur_res->width, cur_res->height); + + curWidth = cur_res->width; + curHeight = cur_res->height; + } + + void setEnabled(bool videoEnabled) + { + zbar->setVideoEnabled(videoEnabled); + + // Update the status button + statusButton->setEnabled(videoEnabled); + statusButton->setChecked(videoEnabled); + + // Delete items before creating a new set of controls + clearLayout(controlBoxLayout); + + if (!videoEnabled) + return; + + // get_controls + loadSettings(false); + + // FIXME: clear a previous resolutions box + + bool isNewResolutions = false; + + if (!resolutions) { + resolutions = new QComboBox; + resolutions->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + isNewResolutions = true; + } + + resolutions->blockSignals(true); + res.clear(); + resolutions->clear(); + + for (int i = 0;; i++) { + QString new_res, fps; + struct CamRes cur_res; + + if (!zbar->get_resolution(i, cur_res.width, cur_res.height, + cur_res.max_fps)) + break; + + fps.setNum(cur_res.max_fps, 'f', 2); + new_res = QString("%1x%2 - %3 fps (max)") + .arg(cur_res.width) + .arg(cur_res.height) + .arg(fps); + + resolutions->addItem(new_res); + res.append(cur_res); + + if (curWidth == cur_res.width && curHeight == cur_res.height) + resolutions->setCurrentIndex(i); + } + + if (isNewResolutions) { + ZBarMenu->addWidget(resolutions); + connect(resolutions, SIGNAL(currentIndexChanged(int)), this, + SLOT(setVideoResolution(int))); + } + resolutions->blockSignals(false); + + // Restore saved resolution + unsigned width = zbar->videoWidth(); + unsigned height = zbar->videoHeight(); + + if (width != curWidth || height != curHeight) { + for (int i = 0; i < res.size(); i++) { + if (res[i].width == curWidth && res[i].height == curHeight) { + resolutions->setCurrentIndex(i); + break; + } + } + } + + int pos = 0; + QString oldGroup = ""; + for (int i = 0;; i++) { + char *name, *group; + enum zbar::QZBar::ControlType type; + int min, max, def, step; + + int ret = zbar->get_controls(i, &name, &group, &type, &min, &max, + &def, &step); + if (!ret) + break; + + QString newGroup = + "<strong>" + QString::fromUtf8(group) + " Controls</strong>"; + + if (newGroup != oldGroup) { + if (oldGroup != "") + controlBoxLayout->addItem(new QSpacerItem(0, 12), pos++, 2, + 1, 2, Qt::AlignLeft); + QLabel *label = new QLabel(newGroup); + controlBoxLayout->addWidget(label, pos++, 0, 1, 2, + Qt::AlignTop | Qt::AlignHCenter); + pos++; + oldGroup = newGroup; + } + + switch (type) { + case zbar::QZBar::Button: + case zbar::QZBar::Boolean: { + bool val; + + QCheckBox *button = new QCheckBox(name, controlGroup); + controlBoxLayout->addWidget(button, pos++, 0, 1, 2, + Qt::AlignLeft); + + if (!zbar->get_control(name, &val)) + button->setChecked(val); + else + button->setChecked(def); + connect(button, SIGNAL(clicked()), this, + SLOT(control_clicked())); + break; + } + case zbar::QZBar::Integer: { + IntegerControl *ctrl; + + QLabel *label = new QLabel(QString::fromUtf8(name)); + ctrl = new IntegerControl(controlGroup, zbar, name, min, max, + def, step); + + controlBoxLayout->addWidget(label, pos, 0, Qt::AlignLeft); + controlBoxLayout->addWidget(ctrl, pos++, 1, Qt::AlignLeft); + break; + } + case zbar::QZBar::Menu: { + MenuControl *ctrl; + + QLabel *label = new QLabel(QString::fromUtf8(name)); + + QVector<QPair<int, QString> > vector; + vector = zbar->get_menu(i); + ctrl = new MenuControl(controlGroup, zbar, name, vector); + + controlBoxLayout->addWidget(label, pos, 0, Qt::AlignLeft); + controlBoxLayout->addWidget(ctrl, pos++, 1, Qt::AlignLeft); + break; + } + default: + // Just ignore other types + break; + } + } + } + +private: + QString file; + zbar::QZBar *zbar; + QHBoxLayout *ZBarMenu; + QPushButton *statusButton; + QGroupBox *controlGroup, *optionsGroup; + QComboBox *resolutions; + QGridLayout *controlBoxLayout; + QSignalMapper *signalMapper; + bool dbus_enabled, show_options, show_controls; + QByteArray geometry; + QVector<struct CamRes> res; + unsigned curWidth, curHeight; + + void loadSettings(bool getRes = true) + { + QSettings qSettings(QCoreApplication::organizationName(), + QCoreApplication::applicationName()); + QString key; + QVariant qVal; + + geometry = qSettings.value("geometry").toByteArray(); + + key = OPTION_BAR; + qVal = qSettings.value(key, true); + show_options = qVal.toBool(); + + key = CONTROL_BAR; + qVal = qSettings.value(key, true); + show_controls = qVal.toBool(); + + if (getRes) { + qVal = qSettings.value("width"); + curWidth = qVal.toUInt(); + qVal = qSettings.value("height"); + curHeight = qVal.toUInt(); + } + +#ifdef HAVE_DBUS + key = DBUS_NAME ".enable"; + qVal = qSettings.value(key, false); + dbus_enabled = qVal.toBool(); +#endif + + qSettings.beginGroup(SYM_GROUP); + + for (unsigned i = 0; i < CONFIGS_SIZE; i++) { + int val = 0; + if (zbar->get_config(configs[i].sym, zbar::ZBAR_CFG_ENABLE, val)) + continue; + key = QString(configs[i].name) + QString(".enable"); + key.replace(" ", "_"); + qVal = qSettings.value(key, val); + zbar->set_config(configs[i].sym, zbar::ZBAR_CFG_ENABLE, + qVal.toInt()); + + if (configs[i].sym == zbar::ZBAR_COMPOSITE) + continue; + + for (unsigned j = 0; j < SETTINGS_SIZE; j++) { + int val = 0; + + if (zbar->get_config(configs[i].sym, settings[j].ctrl, val)) + continue; + key = QString(configs[i].name) + QString(".") + + QString(settings[j].name); + key.replace(" ", "_"); + + qVal = qSettings.value(key, val); + zbar->set_config(configs[i].sym, settings[j].ctrl, + qVal.toInt()); + } + } + qSettings.endGroup(); + + qSettings.beginGroup(CAM_GROUP); + for (unsigned i = 0;; i++) { + char *name, *group; + enum zbar::QZBar::ControlType type; + int min, max, def, step, val; + + int ret = zbar->get_controls(i, &name, &group, &type, &min, &max, + &def, &step); + if (!ret) + break; + + switch (type) { + case zbar::QZBar::Button: + case zbar::QZBar::Boolean: + case zbar::QZBar::Menu: + case zbar::QZBar::Integer: { + key = QString::fromUtf8(name); + + if (zbar->get_control(name, &val)) + continue; + + key.replace(QRegularExpression("[^\\w\\d]+"), "_"); + key.replace(QRegularExpression("_$"), ""); + + qVal = qSettings.value(key, val); + zbar->set_control(name, qVal.toInt()); + break; + } + default: + // Just ignore other types + break; + } + } + qSettings.endGroup(); + } + + void saveSettings() + { + QSettings qSettings(QCoreApplication::organizationName(), + QCoreApplication::applicationName()); + QString key; + unsigned int i; + + qSettings.setValue("geometry", saveGeometry()); + + key = OPTION_BAR; + qSettings.setValue(key, show_options); + + key = CONTROL_BAR; + qSettings.setValue(key, show_controls); + + curWidth = zbar->videoWidth(); + curHeight = zbar->videoHeight(); + qSettings.setValue("width", curWidth); + qSettings.setValue("height", curHeight); + +#ifdef HAVE_DBUS + // FIXME: track dbus enable-disable and store last state + key = DBUS_NAME ".enable"; + qSettings.setValue(key, dbus_enabled); +#endif + + qSettings.beginGroup(SYM_GROUP); + for (i = 0; i < CONFIGS_SIZE; i++) { + int val = 0; + + if (zbar->get_config(configs[i].sym, zbar::ZBAR_CFG_ENABLE, val)) + continue; + key = QString(configs[i].name) + QString(".enable"); + key.replace(" ", "_"); + qSettings.setValue(key, val); + + if (configs[i].sym == zbar::ZBAR_COMPOSITE) + continue; + + for (unsigned j = 0; j < SETTINGS_SIZE; j++) { + int val = 0; + + if (zbar->get_config(configs[i].sym, settings[j].ctrl, val)) + continue; + key = QString(configs[i].name) + QString(".") + + QString(settings[j].name); + key.replace(" ", "_"); + qSettings.setValue(key, val); + } + } + qSettings.endGroup(); + + for (i = 0;; i++) { + char *name, *group; + enum zbar::QZBar::ControlType type; + int min, max, def, step, val; + + int ret = zbar->get_controls(i, &name, &group, &type, &min, &max, + &def, &step); + if (!ret) + break; + + if (i == 0) + qSettings.beginGroup(CAM_GROUP); + + switch (type) { + case zbar::QZBar::Button: + case zbar::QZBar::Boolean: + case zbar::QZBar::Menu: + case zbar::QZBar::Integer: { + key = QString::fromUtf8(name); + + if (zbar->get_control(name, &val)) + continue; + + key.replace(QRegularExpression("[^\\w\\d]+"), "_"); + key.replace(QRegularExpression("_$"), ""); + qSettings.setValue(key, val); + break; + } + default: + // Just ignore other types + break; + } + } + if (i > 0) + qSettings.endGroup(); + } +}; + +#include "moc_zbarcam_qt.h" + +int main(int argc, char *argv[]) +{ + int verbose = 0; + QApplication app(argc, argv); + app.setApplicationName("zbarcam_qt"); + app.setOrganizationName("LinuxTV"); + app.setOrganizationDomain("linuxtv.org"); + app.setAttribute(Qt::AA_UseHighDpiPixmaps, true); + + QCommandLineParser parser; + parser.setApplicationDescription("ZBar bar code reader Qt application"); + parser.addHelpOption(); + + parser.addPositionalArgument("name", QObject::tr("device or file name")); + + QCommandLineOption debugOption(QStringList() << "d" + << "debug", + QObject::tr("Enable debug mode.")); + parser.addOption(debugOption); + + QCommandLineOption verboseOption(QStringList() << "v" + << "verbosity", + QObject::tr("Verbosity level."), + QObject::tr("value")); + parser.addOption(verboseOption); + + parser.process(app); + + if (parser.isSet(verboseOption)) + verbose = parser.value(verboseOption).toInt(); + + if (parser.isSet(debugOption)) + verbose = 127; + + const QStringList args = parser.positionalArguments(); + + ZbarcamQZBar window(&args, verbose); + window.show(); + return (app.exec()); +} diff --git a/zbarcam/zbarcam.c b/zbarcam/zbarcam.c new file mode 100644 index 0000000..040c030 --- /dev/null +++ b/zbarcam/zbarcam.c @@ -0,0 +1,375 @@ +/*------------------------------------------------------------------------ + * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net> + * + * This file is part of the ZBar Bar Code Reader. + * + * The ZBar Bar Code Reader is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * The ZBar Bar Code Reader 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 Lesser Public License for more details. + * + * You should have received a copy of the GNU Lesser Public License + * along with the ZBar Bar Code Reader; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * http://sourceforge.net/projects/zbar + *------------------------------------------------------------------------*/ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef _WIN32 +#include <fcntl.h> +#include <io.h> +#include <objbase.h> +#endif +#include <assert.h> + +#include <zbar.h> + +#ifdef ENABLE_NLS +#include <libintl.h> +#include <locale.h> +#define _(string) gettext(string) +#else +#define _(string) string +#endif + +#define N_(string) string + +#define BELL "\a" + +static const char *note_usage = N_( + "usage: zbarcam [options] [/dev/video?]\n" + "\n" + "scan and decode bar codes from a video stream\n" + "\n" + "options:\n" + " -h, --help display this help text\n" + " --version display version information and exit\n" + " -q, --quiet disable beep when symbol is decoded\n" + " -v, --verbose increase debug output level\n" + " --verbose=N set specific debug output level\n" + " --xml use XML output format\n" + " --raw output decoded symbol data without converting charsets\n" + " -1, --oneshot exit after scanning one bar code\n" + " --nodisplay disable video display window\n" + " --prescale=<W>x<H>\n" + " request alternate video image size from driver\n" + " -S<CONFIG>[=<VALUE>], --set <CONFIG>[=<VALUE>]\n" + " set decoder/scanner <CONFIG> to <VALUE> (or 1)\n" + /* FIXME overlay level */ + "\n"); + +#ifdef HAVE_DBUS +static const char *note_usage2 = + N_(" --nodbus disable dbus message\n"); +#endif + +static const char *xml_head = + "<barcodes xmlns='http://zbar.sourceforge.net/2008/barcode'>" + "<source device='%s'>\n"; +static const char *xml_foot = "</source></barcodes>\n"; + +static zbar_processor_t *proc; +static int quiet = 0, oneshot = 0; +static enum +{ + DEFAULT, + RAW, + XML +} format = DEFAULT; + +static char *xml_buf = NULL; +static unsigned xml_len = 0; + +static int usage(int rc) +{ + FILE *out = (rc) ? stderr : stdout; + fprintf(out, "%s", _(note_usage)); +#ifdef HAVE_DBUS + fprintf(out, "%s", _(note_usage2)); +#endif + return (rc); +} + +static inline int parse_config(const char *cfgstr, int i, int n, char *arg) +{ + if (i >= n || !*cfgstr) { + fprintf(stderr, "ERROR: need argument for option: %s\n", arg); + return (1); + } + + if (zbar_processor_parse_config(proc, cfgstr)) { + fprintf(stderr, "ERROR: invalid configuration setting: %s\n", cfgstr); + return (1); + } + return (0); +} + +static void data_handler(zbar_image_t *img, const void *userdata) +{ + int n = 0; + const zbar_symbol_t *sym = zbar_image_first_symbol(img); + assert(sym); + for (; sym; sym = zbar_symbol_next(sym)) { + zbar_symbol_type_t type; + if (zbar_symbol_get_count(sym)) + continue; + + type = zbar_symbol_get_type(sym); + if (type == ZBAR_PARTIAL) + continue; + + if (!format) { + printf("%s:", zbar_get_symbol_name(type)); + if (fwrite(zbar_symbol_get_data(sym), + zbar_symbol_get_data_length(sym), 1, stdout) != 1) + continue; + } else if (format == RAW) { + if (fwrite(zbar_symbol_get_data(sym), + zbar_symbol_get_data_length(sym), 1, stdout) != 1) + continue; + } else if (format == XML) { + if (!n) + printf("<index num='%u'>\n", zbar_image_get_sequence(img)); + zbar_symbol_xml(sym, &xml_buf, &xml_len); + if (fwrite(xml_buf, xml_len, 1, stdout) != 1) + continue; + } + n++; + + if (oneshot) { + if (format != RAW) + printf("\n"); + break; + } else + printf("\n"); + } + + if (format == XML && n) + printf("</index>\n"); + fflush(stdout); + + if (!quiet && n) + fprintf(stderr, BELL); +} + +int main(int argc, const char *argv[]) +{ + const char *video_device; + int display; + unsigned long infmt, outfmt; + int i, active; + +#ifdef ENABLE_NLS + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +#endif + +#ifdef DIRECTSHOW + HRESULT res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + if (FAILED(res)) { + fprintf(stderr, "ERROR: failed to initialize COM library\n"); + return (1); + } +#endif + + /* setup zbar library standalone processor, + * threads will be used if available + */ + proc = zbar_processor_create(1); + if (!proc) { + fprintf(stderr, "ERROR: unable to allocate memory?\n"); + return (1); + } + zbar_processor_set_data_handler(proc, data_handler, NULL); + + video_device = ""; +#ifdef HAVE_DBUS + int dbus = 1; +#endif + display = 1; + infmt = 0, outfmt = 0; + for (i = 1; i < argc; i++) { + if (argv[i][0] != '-') + video_device = argv[i]; + else if (argv[i][1] != '-') { + int j; + for (j = 1; argv[i][j]; j++) { + if (argv[i][j] == 'S') { + if (!argv[i][++j]) { + i++; + j = 0; + } + if (parse_config(&argv[i][j], i, argc, "-S")) + return (usage(1)); + break; + } + switch (argv[i][j]) { + case 'h': + return (usage(0)); + case 'v': + zbar_increase_verbosity(); + break; + case 'q': + quiet = 1; + break; + case '1': + oneshot = 1; + break; + default: + fprintf(stderr, "ERROR: unknown bundled config: -%c\n\n", + argv[i][j]); + return (usage(1)); + } + } + } else if (!argv[i][2]) { + if (i < argc - 1) + video_device = argv[argc - 1]; + break; + } else if (!strcmp(argv[i], "--help")) + return (usage(0)); + else if (!strcmp(argv[i], "--version")) + return (printf(PACKAGE_VERSION "\n") <= 0); + else if (!strcmp(argv[i], "--set")) { + i++; + if (parse_config(argv[i], i, argc, "--set")) + return (usage(1)); + } else if (!strncmp(argv[i], "--set=", 6)) { + if (parse_config(&argv[i][6], i, argc, "--set=")) + return (usage(1)); + } else if (!strcmp(argv[i], "--quiet")) + quiet = 1; + else if (!strcmp(argv[i], "--oneshot")) + oneshot = 1; + else if (!strcmp(argv[i], "--xml")) + format = XML; + else if (!strcmp(argv[i], "--raw")) + format = RAW; + else if (!strcmp(argv[i], "--nodbus")) +#ifdef HAVE_DBUS + dbus = 0; +#else + ; /* silently ignore the option */ +#endif + else if (!strcmp(argv[i], "--nodisplay")) + display = 0; + else if (!strcmp(argv[i], "--verbose")) + zbar_increase_verbosity(); + else if (!strncmp(argv[i], "--verbose=", 10)) + zbar_set_verbosity(strtol(argv[i] + 10, NULL, 0)); + else if (!strncmp(argv[i], "--prescale=", 11)) { + char *x = NULL; + long int w = strtol(argv[i] + 11, &x, 10); + long int h = 0; + if (x && *x == 'x') + h = strtol(x + 1, NULL, 10); + if (!w || !h || !x || *x != 'x') { + fprintf(stderr, "ERROR: invalid prescale: %s\n\n", argv[i]); + return (usage(1)); + } + zbar_processor_request_size(proc, w, h); + } else if (!strncmp(argv[i], "--v4l=", 6)) { + long int v = strtol(argv[i] + 6, NULL, 0); + zbar_processor_request_interface(proc, v); + } else if (!strncmp(argv[i], "--iomode=", 9)) { + long int v = strtol(argv[i] + 9, NULL, 0); + zbar_processor_request_iomode(proc, v); + } else if (!strncmp(argv[i], "--infmt=", 8) && strlen(argv[i]) == 12) + infmt = (argv[i][8] | (argv[i][9] << 8) | (argv[i][10] << 16) | + (argv[i][11] << 24)); + else if (!strncmp(argv[i], "--outfmt=", 9) && strlen(argv[i]) == 13) + outfmt = (argv[i][9] | (argv[i][10] << 8) | (argv[i][11] << 16) | + (argv[i][12] << 24)); + else { + fprintf(stderr, "ERROR: unknown option argument: %s\n\n", argv[i]); + return (usage(1)); + } + } + + if (infmt || outfmt) + zbar_processor_force_format(proc, infmt, outfmt); + +#ifdef HAVE_DBUS + zbar_processor_request_dbus(proc, dbus); +#endif + + /* open video device, open window */ + if (zbar_processor_init(proc, video_device, display) || + /* show window */ + (display && zbar_processor_set_visible(proc, 1))) + return (zbar_processor_error_spew(proc, 0)); + +#ifdef _WIN32 + if (format == XML || format == RAW) { + fflush(stdout); + if (_setmode(_fileno(stdout), _O_BINARY) == -1) { + fprintf(stderr, "ERROR: failed to set stdout mode: %i\n", errno); + return (1); + } + } +#endif + + if (format == XML) { + printf(xml_head, video_device); + fflush(stdout); + } + + /* start video */ + active = 1; + if (zbar_processor_set_active(proc, active)) + return (zbar_processor_error_spew(proc, 0)); + + if (oneshot) { + if (zbar_process_one(proc, -1) < 0) + if (zbar_processor_get_error_code(proc) != ZBAR_ERR_CLOSED) + return zbar_processor_error_spew(proc, 0); + } else { + /* let the callback handle data */ + int rc; + while ((rc = zbar_processor_user_wait(proc, -1)) >= 0) { + if (rc == 'q' || rc == 'Q') + break; + // HACK: controls are known on V4L2 by ID, not by name. This is also + // not compatible with other platforms + if (rc == 'b' || rc == 'B') { + int value; + zbar_processor_get_control(proc, "Brightness", &value); + zbar_processor_set_control(proc, "Brightness", ++value); + } + if (rc == 'n' || rc == 'N') { + int value; + zbar_processor_get_control(proc, "Brightness", &value); + zbar_processor_set_control(proc, "Brightness", --value); + } + if (rc == ' ') { + active = !active; + if (zbar_processor_set_active(proc, active)) + return (zbar_processor_error_spew(proc, 0)); + } + } + + /* report any errors that aren't "window closed" */ + if (rc && rc != 'q' && rc != 'Q' && + zbar_processor_get_error_code(proc) != ZBAR_ERR_CLOSED) + return (zbar_processor_error_spew(proc, 0)); + } + + /* free resources (leak check) */ + zbar_processor_destroy(proc); + + if (format == XML) { + printf("%s", xml_foot); + fflush(stdout); + } + return (0); +} diff --git a/zbarcam/zbarcam.rc b/zbarcam/zbarcam.rc new file mode 100644 index 0000000..f5e0800 --- /dev/null +++ b/zbarcam/zbarcam.rc @@ -0,0 +1,30 @@ +#include <config.h> +#include <winver.h> + +VS_VERSION_INFO VERSIONINFO + FILEVERSION ZBAR_VERSION_MAJOR, ZBAR_VERSION_MINOR, ZBAR_VERSION_PATCH, 0 + PRODUCTVERSION ZBAR_VERSION_MAJOR, ZBAR_VERSION_MINOR, ZBAR_VERSION_PATCH, 0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP +{ + BLOCK "StringFileInfo" { + BLOCK "040904E4" { + VALUE "ProductName", "ZBar Bar Code Reader" + VALUE "Company Name", "ZBar Bar Code Reader" + VALUE "InternalName", "zbarcam" + VALUE "OriginalFilename", "zbarcam.exe" + + VALUE "FileVersion", PACKAGE_VERSION + VALUE "ProductVersion", PACKAGE_VERSION + + VALUE "FileDescription", "Scan bar codes from video devices" + + VALUE "LegalCopyright", "Copyright 2007-2010 (c) Jeff Brown <spadix@users.sourceforge.net>" + } + } + BLOCK "VarFileInfo" { + VALUE "Translation", 0x0409, 0x04e4 + } +} + +APP_ICON ICON "zbar.ico" |