/*------------------------------------------------------------------------ * Copyright 2008-2010 (c) Jeff Brown * * 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 #ifdef HAVE_X #include #elif defined(_WIN32) #include #endif #include "zbargtkprivate.h" #include "zbarmarshal.h" #include #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))); }