[cheese] widget: turn the widget into a basic state machine
- From: Filippo Argiolas <fargiolas src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [cheese] widget: turn the widget into a basic state machine
- Date: Sun, 21 Feb 2010 21:37:51 +0000 (UTC)
commit 3dbd5ff54ee7b3031462cc767ac5b174eb9aab93
Author: Filippo Argiolas <filippo argiolas gmail com>
Date: Sun Feb 21 21:11:56 2010 +0100
widget: turn the widget into a basic state machine
Get rid of "ready" and "error" signals and use a "state" property to keep
track of the current widget state (none, ready, error).
Users can now connect to the notify::state signal to get notified about
state changes.
If an error occurred (i.e. the widget is in the error state) users can
call cheese_widget_get_error() to get a GError containing more details
about what went wrong.
Makefile.am.enums | 43 ++++++++++++++
configure.ac | 2 +
docs/reference/Makefile.am | 1 +
libcheese/Makefile.am | 5 ++
libcheese/cheese-enum-types.c.in | 40 +++++++++++++
libcheese/cheese-enum-types.h.in | 26 ++++++++
libcheese/cheese-gtk.symbols | 3 +
libcheese/cheese-widget.c | 118 +++++++++++++++++++-------------------
libcheese/cheese-widget.h | 21 ++++++-
9 files changed, 198 insertions(+), 61 deletions(-)
---
diff --git a/Makefile.am.enums b/Makefile.am.enums
new file mode 100644
index 0000000..6948509
--- /dev/null
+++ b/Makefile.am.enums
@@ -0,0 +1,43 @@
+# Rules for generating enumeration types using glib-mkenums
+#
+# Define:
+# glib_enum_h = header template file
+# glib_enum_c = source template file
+# glib_enum_headers = list of headers to parse
+#
+# before including Makefile.am.enums. You will also need to have
+# the following targets already defined:
+#
+# CLEANFILES
+# DISTCLEANFILES
+# BUILT_SOURCES
+# EXTRA_DIST
+#
+# Author: Emmanuele Bassi <ebassi linux intel com>
+
+enum_tmpl_h=$(glib_enum_h:.h=.h.in)
+enum_tmpl_c=$(glib_enum_c:.c=.c.in)
+
+CLEANFILES += stamp-enum-types
+DISTCLEANFILES += $(glib_enum_h) $(glib_enum_c)
+BUILT_SOURCES += $(glib_enum_h) $(glib_enum_c)
+EXTRA_DIST += $(srcdir)/$(enum_tmpl_h) $(srcdir)/$(enum_tmpl_c)
+
+stamp-enum-types: $(glib_enum_headers) $(srcdir)/$(enum_tmpl_h)
+ $(AM_V_GEN) $(GLIB_MKENUMS) \
+ --template $(srcdir)/$(enum_tmpl_h) \
+ $(glib_enum_headers) > xgen-eh \
+ && (cmp -s xgen-eh $(glib_enum_h) || cp -f xgen-eh $(glib_enum_h)) \
+ && rm -f xgen-eh \
+ && echo timestamp > $(@F)
+
+$(glib_enum_h): stamp-enum-types
+ @true
+
+$(glib_enum_c): $(glib_enum_h) $(srcdir)/$(enum_tmpl_c)
+ $(AM_V_GEN) $(GLIB_MKENUMS) \
+ --template $(srcdir)/$(enum_tmpl_c) \
+ $(glib_enum_headers) > xgen-ec \
+ && cp -f xgen-ec $(glib_enum_c) \
+ && rm -f xgen-ec
+
diff --git a/configure.ac b/configure.ac
index 6e5f8a7..c93ac0e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -148,7 +148,9 @@ CHEESE_LIBS="$CHEESE_LIBS -lgstinterfaces-0.10"
AC_SUBST(CHEESE_LIBS)
GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0`
+GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
AC_SUBST(GLIB_GENMARSHAL)
+AC_SUBST(GLIB_MKENUMS)
AC_PATH_PROG(GCONFTOOL, gconftool-2)
AM_GCONF_SOURCE_2
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
index 93e3dce..7b14792 100644
--- a/docs/reference/Makefile.am
+++ b/docs/reference/Makefile.am
@@ -58,6 +58,7 @@ IGNORE_HFILES= \
cheese-flash.h \
cheese-gconf.h \
cheese-widget-private.h \
+ cheese-enum-types.h \
um-crop-area.h
# Images to copy into HTML directory.
diff --git a/libcheese/Makefile.am b/libcheese/Makefile.am
index 2e652b9..e02387b 100644
--- a/libcheese/Makefile.am
+++ b/libcheese/Makefile.am
@@ -51,6 +51,7 @@ libcheese_gtk_la_SOURCES = \
EXTRA_DIST = cheese-gtk.symbols cheese-marshal.list
CLEANFILES = $(BUILT_SOURCES)
+DISTCLEANFILES =
# FIXME when we have a .pc file, and sonames
cheesedir = $(includedir)/cheese
@@ -65,3 +66,7 @@ libcheese_gtk_la_LDFLAGS = \
-no-undefined \
$(AM_LDFLAGS)
+glib_enum_h = cheese-enum-types.h
+glib_enum_c = cheese-enum-types.c
+glib_enum_headers = cheese-widget.h
+include $(top_srcdir)/Makefile.am.enums
diff --git a/libcheese/cheese-enum-types.c.in b/libcheese/cheese-enum-types.c.in
new file mode 100644
index 0000000..622f8c5
--- /dev/null
+++ b/libcheese/cheese-enum-types.c.in
@@ -0,0 +1,40 @@
+/*** BEGIN file-header ***/
+#include "cheese-enum-types.h"
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+#include "@filename@"
+
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+ enum_name@_get_type (void)
+{
+ static volatile gsize g_enum_type_id__volatile = 0;
+
+ if (g_once_init_enter (&g_enum_type_id__volatile))
+ {
+ static const G Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+ GType g_enum_type_id;
+
+ g_enum_type_id =
+ g_ type@_register_static (g_intern_static_string ("@EnumName@"), values);
+
+ g_once_init_leave (&g_enum_type_id__volatile, g_enum_type_id);
+ }
+
+ return g_enum_type_id__volatile;
+}
+/*** END value-tail ***/
diff --git a/libcheese/cheese-enum-types.h.in b/libcheese/cheese-enum-types.h.in
new file mode 100644
index 0000000..a882619
--- /dev/null
+++ b/libcheese/cheese-enum-types.h.in
@@ -0,0 +1,26 @@
+/*** BEGIN file-header ***/
+#ifndef __CHEESE_ENUM_TYPES_H__
+#define __CHEESE_ENUM_TYPES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* !__CHEESE_ENUM_TYPES_H__ */
+/*** END file-tail ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name _get_type (void) G_GNUC_CONST;
+#define CHEESE_TYPE_ ENUMSHORT@ (@enum_name _get_type())
+
+/*** END value-header ***/
+
diff --git a/libcheese/cheese-gtk.symbols b/libcheese/cheese-gtk.symbols
index 4c26468..bd8dcd9 100644
--- a/libcheese/cheese-gtk.symbols
+++ b/libcheese/cheese-gtk.symbols
@@ -3,8 +3,11 @@ cheese_widget_new
cheese_widget_get_camera
cheese_widget_get_gconf
cheese_widget_get_video_area
+cheese_widget_get_error
+cheese_widget_state_get_type
cheese_gconf_get_type
cheese_camera_get_type
+cheese_camera_cat
cheese_camera_device_get_type
cheese_camera_device_cat
cheese_video_format_get_type
diff --git a/libcheese/cheese-widget.c b/libcheese/cheese-widget.c
index 2841cce..79a8d2b 100644
--- a/libcheese/cheese-widget.c
+++ b/libcheese/cheese-widget.c
@@ -24,6 +24,7 @@
#include "cheese-widget.h"
#include "cheese-gconf.h"
#include "cheese-camera.h"
+#include "cheese-enum-types.h"
enum
{
@@ -35,7 +36,7 @@ enum
enum
{
PROP_0,
- PROP_WIDGET
+ PROP_STATE
};
enum
@@ -45,8 +46,6 @@ enum
PROBLEM_PAGE = 2,
};
-static guint widget_signals[LAST_SIGNAL] = {0};
-
typedef struct
{
GtkWidget *spinner;
@@ -54,6 +53,8 @@ typedef struct
GtkWidget *problem;
CheeseGConf *gconf;
CheeseCamera *webcam;
+ CheeseWidgetState state;
+ GError *error;
} CheeseWidgetPrivate;
#define CHEESE_WIDGET_GET_PRIVATE(o) \
@@ -185,6 +186,9 @@ cheese_widget_init (CheeseWidget *widget)
CheeseWidgetPrivate *priv = CHEESE_WIDGET_GET_PRIVATE (widget);
GtkWidget *box;
+ priv->state = CHEESE_WIDGET_STATE_NONE;
+ priv->error = NULL;
+
/* XXX
* remove this line if you want to debug */
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
@@ -247,14 +251,15 @@ cheese_widget_set_property (GObject *object, guint prop_id,
switch (prop_id)
{
- case PROP_WIDGET:
- priv->widget = GTK_WIDGET (g_value_get_object (value));
+ case PROP_STATE:
+ priv->state = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
+#endif
static void
cheese_widget_get_property (GObject *object, guint prop_id,
@@ -266,8 +271,8 @@ cheese_widget_get_property (GObject *object, guint prop_id,
switch (prop_id)
{
- case PROP_WIDGET:
- g_value_set_object (value, priv->widget);
+ case PROP_STATE:
+ g_value_set_enum (value, priv->state);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -275,7 +280,6 @@ cheese_widget_get_property (GObject *object, guint prop_id,
}
}
-#endif
#if 0
static void
cheese_widget_changed (CheeseWidget *self)
@@ -284,19 +288,6 @@ cheese_widget_changed (CheeseWidget *self)
#endif
-#if 0
-static gboolean
-cheese_widget_emit_error_idle (CheeseWidget *widget)
-{
- CheeseWidgetPrivate *priv = CHEESE_WIDGET_GET_PRIVATE (widget);
- g_signal_emit (widget, widget_signals[ERROR_SIGNAL], 0,
- (priv->error && priv->error->message) ?
- priv->error->message :
- _("Camera setup failed"));
- return FALSE;
-}
-#endif
-
void
setup_camera (CheeseWidget *widget)
{
@@ -308,7 +299,6 @@ setup_camera (CheeseWidget *widget)
gdouble contrast;
gdouble saturation;
gdouble hue;
- GError *error = NULL;
g_object_get (priv->gconf,
"gconf_prop_x_resolution", &x_resolution,
@@ -328,20 +318,22 @@ setup_camera (CheeseWidget *widget)
g_free (webcam_device);
- cheese_camera_setup (priv->webcam, NULL, &error);
+ cheese_camera_setup (priv->webcam, NULL, &priv->error);
gdk_threads_enter ();
gtk_spinner_stop (GTK_SPINNER (priv->spinner));
- if (error != NULL)
+ if (priv->error != NULL)
{
- cheese_widget_set_problem_page (CHEESE_WIDGET (widget), "no-webcam");
- g_signal_emit (widget, widget_signals[ERROR_SIGNAL], 0, error->message);
+ priv->state = CHEESE_WIDGET_STATE_ERROR;
+ g_object_notify (G_OBJECT (widget), "state");
+ cheese_widget_set_problem_page (CHEESE_WIDGET (widget), "error");
}
else
{
- g_signal_emit (widget, widget_signals[READY_SIGNAL], 0, TRUE);
+ priv->state = CHEESE_WIDGET_STATE_READY;
+ g_object_notify (G_OBJECT (widget), "state");
cheese_camera_play (priv->webcam);
gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), WEBCAM_PAGE);
}
@@ -354,7 +346,6 @@ cheese_widget_realize (GtkWidget *widget)
{
GdkWindow *window;
CheeseWidgetPrivate *priv = CHEESE_WIDGET_GET_PRIVATE (widget);
- GError *error = NULL;
GTK_WIDGET_CLASS (cheese_widget_parent_class)->realize (widget);
@@ -373,9 +364,9 @@ cheese_widget_realize (GtkWidget *widget)
gtk_widget_set_app_paintable (priv->screen, TRUE);
gtk_widget_set_double_buffered (priv->screen, FALSE);
- if (!g_thread_create ((GThreadFunc) setup_camera, widget, FALSE, &error))
+ if (!g_thread_create ((GThreadFunc) setup_camera, widget, FALSE, &priv->error))
{
- g_warning ("Failed to create setup thread: %s", error->message);
+ g_warning ("Failed to create setup thread: %s", priv->error->message);
goto error;
}
@@ -387,9 +378,9 @@ cheese_widget_realize (GtkWidget *widget)
error:
gtk_spinner_stop (GTK_SPINNER (priv->spinner));
+ priv->state = CHEESE_WIDGET_STATE_ERROR;
+ g_object_notify (G_OBJECT (widget), "state");
cheese_widget_set_problem_page (CHEESE_WIDGET (widget), "error");
- g_signal_emit (widget, widget_signals[ERROR_SIGNAL], 0,
- error->message);
}
static void
@@ -401,39 +392,26 @@ cheese_widget_class_init (CheeseWidgetClass *klass)
object_class->finalize = cheese_widget_finalize;
#if 0
object_class->set_property = cheese_widget_set_property;
- object_class->get_property = cheese_widget_get_property;
#endif
+ object_class->get_property = cheese_widget_get_property;
widget_class->realize = cheese_widget_realize;
/**
- * CheeseWidget::ready:
- *
- * @is_ready: Whether the camera is ready for use by the widget.
- *
- * The ::ready signal is emitted when the camera is ready to be
- * used by the widget and other applications.
- */
- widget_signals[READY_SIGNAL] = g_signal_new ("ready", G_OBJECT_CLASS_TYPE (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (CheeseWidgetClass, ready),
- NULL, NULL,
- g_cclosure_marshal_VOID__BOOLEAN,
- G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
-
- /**
- * CheeseWidget::error:
+ * CheeseWidget:state:
*
- * @error: The error message.
+ * Current state of the widget.
*
- * The ::error signal is emitted when the widget cannot access
- * the camera.
+ * Connect to notify::state signal to get notified about state
+ * changes. Useful to update other widgets sensitiveness when the
+ * camera is ready or to handle errors if camera setup fails.
*/
- widget_signals[ERROR_SIGNAL] = g_signal_new ("error", G_OBJECT_CLASS_TYPE (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (CheeseWidgetClass, error),
- NULL, NULL,
- g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE, 1, G_TYPE_STRING);
+ g_object_class_install_property (object_class, PROP_STATE,
+ g_param_spec_enum ("state",
+ NULL,
+ NULL,
+ CHEESE_TYPE_WIDGET_STATE,
+ CHEESE_WIDGET_STATE_NONE,
+ G_PARAM_READABLE));
g_type_class_add_private (klass, sizeof (CheeseWidgetPrivate));
}
@@ -487,6 +465,30 @@ cheese_widget_get_video_area (CheeseWidget *widget)
return priv->screen;
}
+/**
+ * cheese_widget_get_error:
+ * @widget: a #CheeseWidget
+ * @error: return location for the error
+ *
+ * Listen for notify::state signals and call this when the current state is %CHEESE_WIDGET_STATE_ERROR.
+ *
+ * The returned #GError will contain more details on what went wrong.
+ **/
+
+void
+cheese_widget_get_error (CheeseWidget *widget, GError **error)
+{
+ CheeseWidgetPrivate *priv;
+
+ g_return_if_fail (CHEESE_WIDGET (widget));
+
+ priv = CHEESE_WIDGET_GET_PRIVATE (widget);
+
+ g_propagate_error (error, priv->error);
+
+ priv->error = NULL;
+}
+
/*
* vim: sw=2 ts=8 cindent noai bs=2
*/
diff --git a/libcheese/cheese-widget.h b/libcheese/cheese-widget.h
index b8872ea..62c2092 100644
--- a/libcheese/cheese-widget.h
+++ b/libcheese/cheese-widget.h
@@ -41,9 +41,6 @@ typedef struct _CheeseWidget CheeseWidget;
struct _CheeseWidgetClass
{
GtkNotebookClass parent_class;
-
- void (*ready)(CheeseWidget *widget, gboolean is_ready);
- void (*error)(CheeseWidget *widget, const char *error);
};
struct _CheeseWidget
@@ -54,6 +51,24 @@ struct _CheeseWidget
GType cheese_widget_get_type (void) G_GNUC_CONST;
GtkWidget *cheese_widget_new (void);
+void cheese_widget_get_error (CheeseWidget *widget, GError **error);
+
+
+/**
+ * CheeseWidgetState:
+ * @CHEESE_WIDGET_STATE_NONE: Default state, camera uninitialized
+ * @CHEESE_WIDGET_STATE_READY: The camera should be ready and the widget should be displaying the preview
+ * @CHEESE_WIDGET_STATE_ERROR: An error occurred while setting up the camera, check what went wrong with cheese_widget_get_error()
+ *
+ * Current #CheeseWidget state.
+ *
+ */
+typedef enum
+{
+ CHEESE_WIDGET_STATE_NONE,
+ CHEESE_WIDGET_STATE_READY,
+ CHEESE_WIDGET_STATE_ERROR
+} CheeseWidgetState;
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]