diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-03-09 00:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-03-09 00:06:44 +0000 |
commit | 44cf8ec67278bd1ab6c7f83a9993f7a5686a9541 (patch) | |
tree | 5eec4b0d1a3f163d279c3c27c03324ba49fa235a /gtk/zbargtk.c | |
parent | Initial commit. (diff) | |
download | zbar-44cf8ec67278bd1ab6c7f83a9993f7a5686a9541.tar.xz zbar-44cf8ec67278bd1ab6c7f83a9993f7a5686a9541.zip |
Adding upstream version 0.23.93.upstream/0.23.93upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gtk/zbargtk.c')
-rw-r--r-- | gtk/zbargtk.c | 908 |
1 files changed, 908 insertions, 0 deletions
diff --git a/gtk/zbargtk.c b/gtk/zbargtk.c new file mode 100644 index 0000000..8c86016 --- /dev/null +++ b/gtk/zbargtk.c @@ -0,0 +1,908 @@ +/*------------------------------------------------------------------------ + * Copyright 2008-2010 (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> +#ifdef HAVE_X +#include <gdk/gdkx.h> +#elif defined(_WIN32) +#include <gdk/gdkwin32.h> +#endif + +#include "zbargtkprivate.h" +#include "zbarmarshal.h" +#include <zbar/zbargtk.h> + +#ifndef G_PARAM_STATIC_STRINGS +#define G_PARAM_STATIC_STRINGS \ + (G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB) +#endif + +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + +enum +{ + DECODED, + DECODED_TEXT, + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_VIDEO_DEVICE, + PROP_VIDEO_ENABLED, + PROP_VIDEO_OPENED, +}; + +static guint zbar_gtk_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE(ZBarGtk, zbar_gtk, GTK_TYPE_WIDGET); + +/* FIXME what todo w/errors? OOM? */ +/* FIXME signal failure notifications to main gui idle handler */ + +void zbar_gtk_release_pixbuf(zbar_image_t *img) +{ + GdkPixbuf *pixbuf = zbar_image_get_userdata(img); + + g_assert(GDK_IS_PIXBUF(pixbuf)); + + /* remove reference */ + zbar_image_set_userdata(img, NULL); + + /* release reference to associated pixbuf and it's data */ + g_object_unref(pixbuf); +} + +gboolean zbar_gtk_image_from_pixbuf(zbar_image_t *zimg, GdkPixbuf *pixbuf) +{ + unsigned pitch, width, height; + GdkColorspace colorspace; + unsigned long datalen; + int nchannels, bps; + long type; + + /* apparently should always be packed RGB? */ + colorspace = gdk_pixbuf_get_colorspace(pixbuf); + + if (colorspace != GDK_COLORSPACE_RGB) { + g_warning("non-RGB color space not supported: %d\n", colorspace); + return (FALSE); + } + + nchannels = gdk_pixbuf_get_n_channels(pixbuf); + bps = gdk_pixbuf_get_bits_per_sample(pixbuf); + type = 0; + + /* these are all guesses... */ + if (nchannels == 3 && bps == 8) + type = zbar_fourcc('R', 'G', 'B', '3'); + else if (nchannels == 4 && bps == 8) + type = zbar_fourcc('B', 'G', 'R', '4'); /* FIXME alpha flipped?! */ + else if (nchannels == 1 && bps == 8) + type = zbar_fourcc('Y', '8', '0', '0'); + else if (nchannels == 3 && bps == 5) + type = zbar_fourcc('R', 'G', 'B', 'R'); + else if (nchannels == 3 && bps == 4) + type = zbar_fourcc('R', '4', '4', '4'); /* FIXME maybe? */ + else { + g_warning("unsupported combination: nchannels=%d bps=%d\n", nchannels, + bps); + return (FALSE); + } + zbar_image_set_format(zimg, type); + + /* FIXME we don't deal w/bpl... + * this will cause problems w/unpadded pixbufs :| + */ + pitch = gdk_pixbuf_get_rowstride(pixbuf); + width = pitch / ((nchannels * bps) / 8); + + if ((width * nchannels * 8 / bps) != pitch) { + g_warning("unsupported: width=%d nchannels=%d bps=%d rowstride=%d\n", + width, nchannels, bps, pitch); + return (FALSE); + } + height = gdk_pixbuf_get_height(pixbuf); + /* FIXME this isn't correct either */ + datalen = width * height * nchannels; + + zbar_image_set_size(zimg, width, height); + + /* when the zbar image is released, the pixbuf will be + * automatically be released + */ + zbar_image_set_userdata(zimg, pixbuf); + zbar_image_set_data(zimg, gdk_pixbuf_get_pixels(pixbuf), datalen, + zbar_gtk_release_pixbuf); +#ifdef DEBUG_ZBARGTK + g_message("colorspace=%d nchannels=%d bps=%d type=%.4s(%08lx)\n" + "\tpitch=%d width=%d height=%d datalen=0x%lx\n", + colorspace, nchannels, bps, (char *)&type, type, pitch, width, + height, datalen); +#endif + return (TRUE); +} + +static inline gboolean zbar_gtk_video_open(ZBarGtk *self, + const char *video_device) +{ + ZBarGtkPrivate *zbar = ZBAR_GTK_PRIVATE(self->_private); + gboolean video_opened = FALSE; + + zbar->video_opened = FALSE; + if (zbar->idle_id) + g_object_notify(G_OBJECT(self), "video-opened"); + + if (zbar->window) { + /* ensure old video doesn't have image ref + * (FIXME handle video destroyed w/images outstanding) + */ + zbar_window_draw(zbar->window, NULL); + gtk_widget_queue_draw(GTK_WIDGET(self)); + } + + if (zbar->video) { + zbar_video_destroy(zbar->video); + zbar->video = NULL; + } + + if (video_device && video_device[0] && zbar->idle_id) { + /* create video + * FIXME video should support re-open + */ + zbar->video = zbar_video_create(); + g_assert(zbar->video); + + if (zbar_video_open(zbar->video, video_device)) { + zbar_video_error_spew(zbar->video, 0); + zbar_video_destroy(zbar->video); + zbar->video = NULL; + /* FIXME error propagation */ + return (FALSE); + } + + /* negotiation accesses the window format list, + * so we hold the lock for this part + */ + if (zbar->video_width && zbar->video_height) + zbar_video_request_size(zbar->video, zbar->video_width, + zbar->video_height); + + video_opened = !zbar_negotiate_format(zbar->video, zbar->window); + + if (video_opened) { + zbar->req_width = zbar_video_get_width(zbar->video); + zbar->req_height = zbar_video_get_height(zbar->video); + } + gtk_widget_queue_resize(GTK_WIDGET(self)); + + zbar->video_opened = video_opened; + if (zbar->idle_id) + g_object_notify(G_OBJECT(self), "video-opened"); + } + + return (video_opened); +} + +static inline int zbar_gtk_process_image(ZBarGtk *self, zbar_image_t *image) +{ + ZBarGtkPrivate *zbar; + int rc; + + zbar = ZBAR_GTK_PRIVATE(self->_private); + + if (!image) + return (-1); + + zbar_image_t *tmp = + zbar_image_convert(image, zbar_fourcc('Y', '8', '0', '0')); + if (!tmp) + return (-1); + + zbar_image_scanner_recycle_image(zbar->scanner, image); + rc = zbar_scan_image(zbar->scanner, tmp); + zbar_image_set_symbols(image, zbar_image_get_symbols(tmp)); + zbar_image_destroy(tmp); + if (rc < 0) + return (rc); + + if (rc && zbar->idle_id) { + /* update decode results */ + const zbar_symbol_t *sym; + for (sym = zbar_image_first_symbol(image); sym; + sym = zbar_symbol_next(sym)) + if (!zbar_symbol_get_count(sym)) { + zbar_symbol_type_t type = zbar_symbol_get_type(sym); + const char *data = zbar_symbol_get_data(sym); + g_signal_emit(self, zbar_gtk_signals[DECODED], 0, type, data); + + /* FIXME skip this when unconnected? */ + gchar *text = + g_strconcat(zbar_get_symbol_name(type), ":", data, NULL); + g_signal_emit(self, zbar_gtk_signals[DECODED_TEXT], 0, text); + g_free(text); + } + } + + if (zbar->window) { + rc = zbar_window_draw(zbar->window, image); + gtk_widget_queue_draw(GTK_WIDGET(self)); + } else + rc = -1; + + return (rc); +} + +static gboolean zbar_processing_idle_callback(gpointer data) +{ + ZBarGtk *self = data; + ZBarGtkPrivate *zbar = ZBAR_GTK_PRIVATE(self->_private); + GValue *msg = g_async_queue_try_pop(zbar->queue); + GType type; + + if (!msg) { + if (zbar->video_enabled_state) { + zbar_image_t *image = zbar_video_next_image(zbar->video); + if (zbar_gtk_process_image(self, image) < 0) + zbar->video_enabled_state = FALSE; + if (image) + zbar_image_destroy(image); + + if (zbar->video_enabled_state) + return TRUE; + + if (zbar_video_enable(zbar->video, 0)) { + zbar_video_error_spew(zbar->video, 0); + zbar->video_enabled_state = FALSE; + } + + zbar_image_scanner_enable_cache(zbar->scanner, 0); + /* release video image and revert to logo */ + if (zbar->window) { + zbar_window_draw(zbar->window, NULL); + gtk_widget_queue_draw(GTK_WIDGET(self)); + } + + /* must have been an error while streaming */ + zbar_gtk_video_open(self, NULL); + } + + return TRUE; + } + + g_assert(G_IS_VALUE(msg)); + + type = G_VALUE_TYPE(msg); + if (type == G_TYPE_INT) { + /* video state change */ + int state = g_value_get_int(msg); + if (state < 0) { + /* error identifying state */ + g_value_unset(msg); + g_free(msg); + return TRUE; + } + g_assert(state >= 0 && state <= 1); + zbar->video_enabled_state = (state != 0); + } else if (type == G_TYPE_STRING) { + /* open new video device */ + const char *video_device = g_value_get_string(msg); + + zbar->video_enabled_state = zbar_gtk_video_open(self, video_device); + } else if (type == GDK_TYPE_PIXBUF) { + /* scan provided image and broadcast results */ + zbar_image_t *image = zbar_image_create(); + GdkPixbuf *pixbuf = GDK_PIXBUF(g_value_dup_object(msg)); + + if (zbar_gtk_image_from_pixbuf(image, pixbuf)) + zbar_gtk_process_image(self, image); + else + g_object_unref(pixbuf); + zbar_image_destroy(image); + } else { + gchar *dbg = g_strdup_value_contents(msg); + + g_warning("unknown message type (%x) received: %s\n", (unsigned)type, + dbg); + g_free(dbg); + } + g_value_unset(msg); + g_free(msg); + msg = NULL; + + if (zbar->video_enabled_state) { + /* release reference to any previous pixbuf */ + if (zbar->window) + zbar_window_draw(zbar->window, NULL); + + if (zbar_video_enable(zbar->video, 1)) { + zbar_video_error_spew(zbar->video, 0); + zbar->video_enabled_state = FALSE; + return TRUE; + } + zbar_image_scanner_enable_cache(zbar->scanner, 1); + + return TRUE; + } + + return TRUE; +} + +static void zbar_gtk_realize(GtkWidget *widget) +{ + ZBarGtk *self = ZBAR_GTK(widget); + ZBarGtkPrivate *zbar; + GdkWindowAttr attributes; + GtkAllocation allocation; + + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + gtk_widget_set_realized(widget, TRUE); + +#if GTK_CHECK_VERSION(3, 14, 0) + // FIXME: this is deprecated after 3.14 - no idea what replaces it +#else + gtk_widget_set_double_buffered(widget, FALSE); +#endif + + gtk_widget_get_allocation(widget, &allocation); + attributes.x = allocation.x; + attributes.y = allocation.y; + attributes.width = allocation.width; + attributes.height = allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.window_type = GDK_WINDOW_CHILD; + attributes.event_mask = (gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK); + + GdkWindow *window = gdk_window_new(gtk_widget_get_parent_window(widget), + &attributes, GDK_WA_X | GDK_WA_Y); + gtk_widget_set_window(widget, window); + gdk_window_set_user_data(window, widget); +#if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 18 + gdk_window_set_background_pattern(window, NULL); +#elif GTK_MAJOR_VERSION < 3 + gdk_window_set_back_pixmap(window, NULL, TRUE); +#endif + + /* attach zbar_window to underlying X window */ +#ifdef HAVE_X + if (zbar_window_attach(zbar->window, + GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), + GDK_WINDOW_XID(window))) +#elif defined(_WIN32) + if (zbar_window_attach(zbar->window, GDK_WINDOW_HWND(window), 0)) +#endif + zbar_window_error_spew(zbar->window, 0); +} + +static inline GValue *zbar_gtk_new_value(GType type) +{ + return (g_value_init(g_malloc0(sizeof(GValue)), type)); +} + +static void zbar_gtk_unrealize(GtkWidget *widget) +{ + ZBarGtkPrivate *zbar; + GdkWindow *window; + ZBarGtk *self; + + if (gtk_widget_get_mapped(widget)) + gtk_widget_unmap(widget); + + gtk_widget_set_mapped(widget, FALSE); + self = ZBAR_GTK(widget); + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + if (zbar->video_enabled) { + zbar->video_enabled = FALSE; + GValue *msg = zbar_gtk_new_value(G_TYPE_INT); + g_value_set_int(msg, 0); + g_async_queue_push(zbar->queue, msg); + } + + zbar_window_attach(zbar->window, NULL, 0); + + gtk_widget_set_realized(widget, FALSE); + + window = gtk_widget_get_window(widget); + gdk_window_set_user_data(window, NULL); + gdk_window_destroy(window); + gtk_widget_set_window(widget, NULL); +} + +#if GTK_MAJOR_VERSION >= 3 + +static void zbar_get_preferred_width(GtkWidget *widget, gint *minimum_width, + gint *natural_width) +{ + unsigned int screen_width; + ZBarGtkPrivate *zbar; + ZBarGtk *self; +#if GTK_CHECK_VERSION(3, 22, 0) + GdkDisplay *display; + GdkMonitor *monitor; + GdkRectangle geo; +#endif + self = ZBAR_GTK(widget); + + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + /* use native video size (max) if available, + * arbitrary defaults otherwise. + * video attributes maintained under main gui idle handler + */ +#if GTK_CHECK_VERSION(3, 22, 0) + display = gdk_display_get_default(); + monitor = gdk_display_get_monitor(display, 0); + gdk_monitor_get_geometry(monitor, &geo); + + screen_width = geo.width; +#else + screen_width = gdk_screen_width(); +#endif + + if (zbar->req_width > screen_width) { + float scale = screen_width * .8 / zbar->req_width; + + zbar->req_width *= scale; + zbar->req_height *= scale; + } + + *minimum_width = zbar->req_width; + *natural_width = zbar->req_width; +} + +static void zbar_get_preferred_height(GtkWidget *widget, gint *minimum_height, + gint *natural_height) +{ + ZBarGtk *self = ZBAR_GTK(widget); + unsigned int screen_height; +#if GTK_CHECK_VERSION(3, 22, 0) + GdkDisplay *display; + GdkMonitor *monitor; + GdkRectangle geo; +#endif + + if (!self->_private) + return; + ZBarGtkPrivate *zbar = ZBAR_GTK_PRIVATE(self->_private); + + /* use native video size (max) if available, + * arbitrary defaults otherwise. + * video attributes maintained under main gui idle handler + */ +#if GTK_CHECK_VERSION(3, 22, 0) + display = gdk_display_get_default(); + monitor = gdk_display_get_monitor(display, 0); + gdk_monitor_get_geometry(monitor, &geo); + + screen_height = geo.height; +#else + screen_height = gdk_screen_height(); +#endif + + if (zbar->req_height > screen_height) { + float scale = screen_height * .8 / zbar->req_height; + + zbar->req_width *= scale; + zbar->req_height *= scale; + } + + *minimum_height = zbar->req_height; + *natural_height = zbar->req_height; +} + +static gboolean zbar_gtk_scale_draw(GtkWidget *widget, cairo_t *cr) +{ + // NOTE: should we change something here? + + ZBarGtk *self = ZBAR_GTK(widget); + + if (!self->_private) + return (FALSE); + ZBarGtkPrivate *zbar = ZBAR_GTK_PRIVATE(self->_private); + + if (gtk_widget_get_visible(widget) && gtk_widget_get_mapped(widget) && + zbar_window_redraw(zbar->window)) + return (TRUE); + return (FALSE); +} + +#else +static void zbar_gtk_size_request(GtkWidget *widget, + GtkRequisition *requisition) +{ + ZBarGtk *self = ZBAR_GTK(widget); + ZBarGtkPrivate *zbar; + + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + /* use native video size (max) if available, + * arbitrary defaults otherwise. + * video attributes maintained under main gui idle handler + */ + requisition->width = zbar->req_width; + requisition->height = zbar->req_height; +} + +static gboolean zbar_gtk_expose(GtkWidget *widget, GdkEventExpose *event) +{ + ZBarGtk *self = ZBAR_GTK(widget); + ZBarGtkPrivate *zbar; + + if (!self->_private) + return (FALSE); + zbar = ZBAR_GTK_PRIVATE(self->_private); + + if (gtk_widget_get_visible(widget) && gtk_widget_get_mapped(widget) && + zbar_window_redraw(zbar->window)) + return (TRUE); + return (FALSE); +} +#endif + +static void zbar_gtk_size_allocate(GtkWidget *widget, GtkAllocation *allocation) +{ + ZBarGtk *self = ZBAR_GTK(widget); + ZBarGtkPrivate *zbar; + + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + (*GTK_WIDGET_CLASS(zbar_gtk_parent_class)->size_allocate)(widget, + allocation); + if (zbar->window) + zbar_window_resize(zbar->window, allocation->width, allocation->height); +} + +void zbar_gtk_scan_image(ZBarGtk *self, GdkPixbuf *img) +{ + ZBarGtkPrivate *zbar; + + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + g_object_ref(G_OBJECT(img)); + + /* queue for scanning by the processor idle handler */ + GValue *msg = zbar_gtk_new_value(GDK_TYPE_PIXBUF); + + /* this grabs a new reference to the image, + * eventually released by the processor idle handler + */ + g_value_set_object(msg, img); + g_async_queue_push(zbar->queue, msg); +} + +const char *zbar_gtk_get_video_device(ZBarGtk *self) +{ + ZBarGtkPrivate *zbar; + + if (!self->_private) + return (NULL); + zbar = ZBAR_GTK_PRIVATE(self->_private); + if (zbar->video_device) + return (zbar->video_device); + else + return (""); +} + +void zbar_gtk_set_video_device(ZBarGtk *self, const char *video_device) +{ + ZBarGtkPrivate *zbar; + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + g_free((void *)zbar->video_device); + zbar->video_device = g_strdup(video_device); + zbar->video_enabled = video_device && video_device[0]; + + /* push another copy to processor idle handler */ + GValue *msg = zbar_gtk_new_value(G_TYPE_STRING); + if (video_device) + g_value_set_string(msg, video_device); + else + g_value_set_static_string(msg, ""); + g_async_queue_push(zbar->queue, msg); + + g_object_freeze_notify(G_OBJECT(self)); + g_object_notify(G_OBJECT(self), "video-device"); + g_object_notify(G_OBJECT(self), "video-enabled"); + g_object_thaw_notify(G_OBJECT(self)); +} + +gboolean zbar_gtk_get_video_enabled(ZBarGtk *self) +{ + ZBarGtkPrivate *zbar; + + if (!self->_private) + return (FALSE); + zbar = ZBAR_GTK_PRIVATE(self->_private); + return (zbar->video_enabled); +} + +void zbar_gtk_set_video_enabled(ZBarGtk *self, gboolean video_enabled) +{ + ZBarGtkPrivate *zbar; + + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + video_enabled = (video_enabled != FALSE); + if (zbar->video_enabled != video_enabled) { + zbar->video_enabled = video_enabled; + + /* push state change to processor idle handler */ + GValue *msg = zbar_gtk_new_value(G_TYPE_INT); + g_value_set_int(msg, zbar->video_enabled); + g_async_queue_push(zbar->queue, msg); + + g_object_notify(G_OBJECT(self), "video-enabled"); + } +} + +gboolean zbar_gtk_get_video_opened(ZBarGtk *self) +{ + ZBarGtkPrivate *zbar; + + if (!self->_private) + return (FALSE); + zbar = ZBAR_GTK_PRIVATE(self->_private); + + return (zbar->video_opened); +} + +void zbar_gtk_request_video_size(ZBarGtk *self, int width, int height) +{ + ZBarGtkPrivate *zbar; + + if (!self->_private || width < 0 || height < 0) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + zbar->req_width = zbar->video_width = width; + zbar->req_height = zbar->video_height = height; + gtk_widget_queue_resize(GTK_WIDGET(self)); +} + +static void zbar_gtk_set_property(GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + ZBarGtk *self = ZBAR_GTK(object); + + switch (prop_id) { + case PROP_VIDEO_DEVICE: + zbar_gtk_set_video_device(self, g_value_get_string(value)); + break; + case PROP_VIDEO_ENABLED: + zbar_gtk_set_video_enabled(self, g_value_get_boolean(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +static void zbar_gtk_get_property(GObject *object, guint prop_id, GValue *value, + GParamSpec *pspec) +{ + ZBarGtk *self = ZBAR_GTK(object); + ZBarGtkPrivate *zbar; + + if (!self->_private) + return; + zbar = ZBAR_GTK_PRIVATE(self->_private); + + switch (prop_id) { + case PROP_VIDEO_DEVICE: + if (zbar->video_device) + g_value_set_string(value, zbar->video_device); + else + g_value_set_static_string(value, ""); + break; + case PROP_VIDEO_ENABLED: + g_value_set_boolean(value, zbar->video_enabled); + break; + case PROP_VIDEO_OPENED: + g_value_set_boolean(value, zbar->video_opened); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + } +} + +static void zbar_gtk_init(ZBarGtk *self) +{ + ZBarGtkPrivate *zbar = g_object_new(ZBAR_TYPE_GTK_PRIVATE, NULL); + self->_private = (void *)zbar; + + zbar->window = zbar_window_create(); + g_assert(zbar->window); + + zbar->req_width = zbar->video_width = DEFAULT_WIDTH; + zbar->req_height = zbar->video_height = DEFAULT_HEIGHT; + + /* use a queue to signalize about the need to handle decoding and video */ + zbar->queue = g_async_queue_new(); + zbar->idle_id = g_idle_add(zbar_processing_idle_callback, self); + zbar->video_enabled_state = FALSE; + + /* Prepare for idle handler */ + g_object_ref(zbar); + g_assert(zbar->queue); + g_async_queue_ref(zbar->queue); + + zbar->scanner = zbar_image_scanner_create(); + g_assert(zbar->scanner); +} + +static void zbar_gtk_dispose(GObject *object) +{ + ZBarGtk *self = ZBAR_GTK(object); + + if (!self->_private) + return; + + ZBarGtkPrivate *zbar = ZBAR_GTK_PRIVATE(self->_private); + self->_private = NULL; + + g_free((void *)zbar->video_device); + zbar->video_device = NULL; + + /* signal processor idle handler to exit */ + GValue *msg = zbar_gtk_new_value(G_TYPE_INT); + g_value_set_int(msg, -1); + g_async_queue_push(zbar->queue, msg); + zbar->idle_id = 0; + + /* there are no external references which might call other APIs */ + g_async_queue_unref(zbar->queue); + + g_object_unref(G_OBJECT(zbar)); +} + +static void zbar_gtk_private_finalize(GObject *object) +{ + ZBarGtkPrivate *zbar = ZBAR_GTK_PRIVATE(object); + + if (zbar->idle_id) { + if (zbar->window) + zbar_window_draw(zbar->window, NULL); + g_object_unref(zbar); + g_source_remove(zbar->idle_id); + zbar->idle_id = 0; + } + + if (zbar->window) { + zbar_window_destroy(zbar->window); + zbar->window = NULL; + } + if (zbar->scanner) { + zbar_image_scanner_destroy(zbar->scanner); + zbar->scanner = NULL; + } + if (zbar->video) { + zbar_video_destroy(zbar->video); + zbar->video = NULL; + } + g_async_queue_unref(zbar->queue); + zbar->queue = NULL; +} + +static void zbar_gtk_class_init(ZBarGtkClass *klass) +{ + GParamSpec *p; + + zbar_gtk_parent_class = g_type_class_peek_parent(klass); + + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->dispose = zbar_gtk_dispose; + object_class->set_property = zbar_gtk_set_property; + object_class->get_property = zbar_gtk_get_property; + + GtkWidgetClass *widget_class = (GtkWidgetClass *)klass; + widget_class->realize = zbar_gtk_realize; + widget_class->unrealize = zbar_gtk_unrealize; +#if GTK_MAJOR_VERSION >= 3 + widget_class->get_preferred_width = zbar_get_preferred_width; + widget_class->get_preferred_height = zbar_get_preferred_height; + widget_class->draw = zbar_gtk_scale_draw; +#else + widget_class->size_request = zbar_gtk_size_request; + widget_class->expose_event = zbar_gtk_expose; +#endif + widget_class->size_allocate = zbar_gtk_size_allocate; + + widget_class->unmap = NULL; + + zbar_gtk_signals[DECODED] = + g_signal_new("decoded", G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_CLEANUP, + G_STRUCT_OFFSET(ZBarGtkClass, decoded), NULL, NULL, + zbar_marshal_VOID__INT_STRING, G_TYPE_NONE, 2, G_TYPE_INT, + G_TYPE_STRING); + + zbar_gtk_signals[DECODED_TEXT] = g_signal_new( + "decoded-text", G_TYPE_FROM_CLASS(object_class), G_SIGNAL_RUN_CLEANUP, + G_STRUCT_OFFSET(ZBarGtkClass, decoded_text), NULL, NULL, + g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); + + p = g_param_spec_string("video-device", "Video device", + "the platform specific name of the video device", + NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property(object_class, PROP_VIDEO_DEVICE, p); + + p = g_param_spec_boolean("video-enabled", "Video enabled", + "controls streaming from the video device", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property(object_class, PROP_VIDEO_ENABLED, p); + + p = g_param_spec_boolean("video-opened", "Video opened", + "current opened state of the video device", FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + g_object_class_install_property(object_class, PROP_VIDEO_OPENED, p); +} + +static void zbar_gtk_private_class_init(ZBarGtkPrivateClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = zbar_gtk_private_finalize; +} + +static GType zbar_gtk_private_get_type(void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo info = { + sizeof(ZBarGtkPrivateClass), + NULL, + NULL, + (GClassInitFunc)zbar_gtk_private_class_init, + NULL, + NULL, + sizeof(ZBarGtkPrivate), + }; + type = + g_type_register_static(G_TYPE_OBJECT, "ZBarGtkPrivate", &info, 0); + } + return (type); +} + +GtkWidget *zbar_gtk_new(void) +{ + return (GTK_WIDGET(g_object_new(ZBAR_TYPE_GTK, NULL))); +} |